BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * SNTP client module |
||
4 | */ |
||
5 | |||
6 | /* |
||
7 | * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt |
||
8 | * All rights reserved. |
||
9 | * |
||
10 | * Redistribution and use in source and binary forms, with or without modification, |
||
11 | * are permitted provided that the following conditions are met: |
||
12 | * |
||
13 | * 1. Redistributions of source code must retain the above copyright notice, |
||
14 | * this list of conditions and the following disclaimer. |
||
15 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
||
16 | * this list of conditions and the following disclaimer in the documentation |
||
17 | * and/or other materials provided with the distribution. |
||
18 | * 3. The name of the author may not be used to endorse or promote products |
||
19 | * derived from this software without specific prior written permission. |
||
20 | * |
||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
22 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
23 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||
24 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||
26 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||
29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||
30 | * OF SUCH DAMAGE. |
||
31 | * |
||
32 | * This file is part of the lwIP TCP/IP stack. |
||
33 | * |
||
34 | * Author: Frédéric Bernon, Simon Goldschmidt |
||
35 | */ |
||
36 | |||
37 | |||
38 | /** |
||
39 | * @defgroup sntp SNTP |
||
40 | * @ingroup apps |
||
41 | * |
||
42 | * This is simple "SNTP" client for the lwIP raw API. |
||
43 | * It is a minimal implementation of SNTPv4 as specified in RFC 4330. |
||
44 | * |
||
45 | * For a list of some public NTP servers, see this link: |
||
46 | * http://support.ntp.org/bin/view/Servers/NTPPoolServers |
||
47 | * |
||
48 | * @todo: |
||
49 | * - complete SNTP_CHECK_RESPONSE checks 3 and 4 |
||
50 | */ |
||
51 | |||
52 | #include "lwip/apps/sntp.h" |
||
53 | |||
54 | #include "lwip/opt.h" |
||
55 | #include "lwip/timeouts.h" |
||
56 | #include "lwip/udp.h" |
||
57 | #include "lwip/dns.h" |
||
58 | #include "lwip/ip_addr.h" |
||
59 | #include "lwip/pbuf.h" |
||
60 | #include "lwip/dhcp.h" |
||
61 | |||
62 | #include <string.h> |
||
63 | #include <time.h> |
||
64 | |||
65 | #if LWIP_UDP |
||
66 | |||
67 | /* Handle support for more than one server via SNTP_MAX_SERVERS */ |
||
68 | #if SNTP_MAX_SERVERS > 1 |
||
69 | #define SNTP_SUPPORT_MULTIPLE_SERVERS 1 |
||
70 | #else /* NTP_MAX_SERVERS > 1 */ |
||
71 | #define SNTP_SUPPORT_MULTIPLE_SERVERS 0 |
||
72 | #endif /* NTP_MAX_SERVERS > 1 */ |
||
73 | |||
74 | #ifndef SNTP_SUPPRESS_DELAY_CHECK |
||
75 | #if SNTP_UPDATE_DELAY < 15000 |
||
76 | #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!" |
||
77 | #endif |
||
78 | #endif |
||
79 | |||
80 | /* the various debug levels for this file */ |
||
81 | #define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) |
||
82 | #define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) |
||
83 | #define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) |
||
84 | #define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) |
||
85 | #define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) |
||
86 | |||
87 | #define SNTP_ERR_KOD 1 |
||
88 | |||
89 | /* SNTP protocol defines */ |
||
90 | #define SNTP_MSG_LEN 48 |
||
91 | |||
92 | #define SNTP_OFFSET_LI_VN_MODE 0 |
||
93 | #define SNTP_LI_MASK 0xC0 |
||
94 | #define SNTP_LI_NO_WARNING (0x00 << 6) |
||
95 | #define SNTP_LI_LAST_MINUTE_61_SEC (0x01 << 6) |
||
96 | #define SNTP_LI_LAST_MINUTE_59_SEC (0x02 << 6) |
||
97 | #define SNTP_LI_ALARM_CONDITION (0x03 << 6) /* (clock not synchronized) */ |
||
98 | |||
99 | #define SNTP_VERSION_MASK 0x38 |
||
100 | #define SNTP_VERSION (4/* NTP Version 4*/<<3) |
||
101 | |||
102 | #define SNTP_MODE_MASK 0x07 |
||
103 | #define SNTP_MODE_CLIENT 0x03 |
||
104 | #define SNTP_MODE_SERVER 0x04 |
||
105 | #define SNTP_MODE_BROADCAST 0x05 |
||
106 | |||
107 | #define SNTP_OFFSET_STRATUM 1 |
||
108 | #define SNTP_STRATUM_KOD 0x00 |
||
109 | |||
110 | #define SNTP_OFFSET_ORIGINATE_TIME 24 |
||
111 | #define SNTP_OFFSET_RECEIVE_TIME 32 |
||
112 | #define SNTP_OFFSET_TRANSMIT_TIME 40 |
||
113 | |||
114 | /* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */ |
||
115 | #define DIFF_SEC_1970_2036 ((s32_t)2085978496L) |
||
116 | |||
117 | /** Convert NTP timestamp fraction to microseconds. |
||
118 | */ |
||
119 | #ifndef SNTP_FRAC_TO_US |
||
120 | # if LWIP_HAVE_INT64 |
||
121 | # define SNTP_FRAC_TO_US(f) ((u32_t)(((u64_t)(f) * 1000000UL) >> 32)) |
||
122 | # else |
||
123 | # define SNTP_FRAC_TO_US(f) ((u32_t)(f) / 4295) |
||
124 | # endif |
||
125 | #endif /* !SNTP_FRAC_TO_US */ |
||
126 | |||
127 | /* Configure behaviour depending on native, microsecond or second precision. |
||
128 | * Treat NTP timestamps as signed two's-complement integers. This way, |
||
129 | * timestamps that have the MSB set simply become negative offsets from |
||
130 | * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from |
||
131 | * 1968 to 2104. |
||
132 | */ |
||
133 | #ifndef SNTP_SET_SYSTEM_TIME_NTP |
||
134 | # ifdef SNTP_SET_SYSTEM_TIME_US |
||
135 | # define SNTP_SET_SYSTEM_TIME_NTP(s, f) \ |
||
136 | SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f)) |
||
137 | # else |
||
138 | # define SNTP_SET_SYSTEM_TIME_NTP(s, f) \ |
||
139 | SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036)) |
||
140 | # endif |
||
141 | #endif /* !SNTP_SET_SYSTEM_TIME_NTP */ |
||
142 | |||
143 | /* Get the system time either natively as NTP timestamp or convert from |
||
144 | * Unix time in seconds and microseconds. Take care to avoid overflow if the |
||
145 | * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to |
||
146 | * avoid special values like 0, and to mask round-off errors that would |
||
147 | * otherwise break round-trip conversion identity. |
||
148 | */ |
||
149 | #ifndef SNTP_GET_SYSTEM_TIME_NTP |
||
150 | # define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \ |
||
151 | u32_t sec_, usec_; \ |
||
152 | SNTP_GET_SYSTEM_TIME(sec_, usec_); \ |
||
153 | (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \ |
||
154 | (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \ |
||
155 | } while (0) |
||
156 | #endif /* !SNTP_GET_SYSTEM_TIME_NTP */ |
||
157 | |||
158 | /* Start offset of the timestamps to extract from the SNTP packet */ |
||
159 | #define SNTP_OFFSET_TIMESTAMPS \ |
||
160 | (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps)) |
||
161 | |||
162 | /* Round-trip delay arithmetic helpers */ |
||
163 | #if SNTP_COMP_ROUNDTRIP |
||
164 | # if !LWIP_HAVE_INT64 |
||
165 | # error "SNTP round-trip delay compensation requires 64-bit arithmetic" |
||
166 | # endif |
||
167 | # define SNTP_SEC_FRAC_TO_S64(s, f) \ |
||
168 | ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f))) |
||
169 | # define SNTP_TIMESTAMP_TO_S64(t) \ |
||
170 | SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac)) |
||
171 | #endif /* SNTP_COMP_ROUNDTRIP */ |
||
172 | |||
173 | /** |
||
174 | * 64-bit NTP timestamp, in network byte order. |
||
175 | */ |
||
176 | struct sntp_time { |
||
177 | u32_t sec; |
||
178 | u32_t frac; |
||
179 | }; |
||
180 | |||
181 | /** |
||
182 | * Timestamps to be extracted from the NTP header. |
||
183 | */ |
||
184 | struct sntp_timestamps { |
||
185 | #if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2 |
||
186 | struct sntp_time orig; |
||
187 | struct sntp_time recv; |
||
188 | #endif |
||
189 | struct sntp_time xmit; |
||
190 | }; |
||
191 | |||
192 | /** |
||
193 | * SNTP packet format (without optional fields) |
||
194 | * Timestamps are coded as 64 bits: |
||
195 | * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1) |
||
196 | * - unsigned 32 bits seconds fraction (2^32 = 1 second) |
||
197 | */ |
||
198 | #ifdef PACK_STRUCT_USE_INCLUDES |
||
199 | # include "arch/bpstruct.h" |
||
200 | #endif |
||
201 | PACK_STRUCT_BEGIN |
||
202 | struct sntp_msg { |
||
203 | PACK_STRUCT_FLD_8(u8_t li_vn_mode); |
||
204 | PACK_STRUCT_FLD_8(u8_t stratum); |
||
205 | PACK_STRUCT_FLD_8(u8_t poll); |
||
206 | PACK_STRUCT_FLD_8(u8_t precision); |
||
207 | PACK_STRUCT_FIELD(u32_t root_delay); |
||
208 | PACK_STRUCT_FIELD(u32_t root_dispersion); |
||
209 | PACK_STRUCT_FIELD(u32_t reference_identifier); |
||
210 | PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); |
||
211 | PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); |
||
212 | PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); |
||
213 | PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); |
||
214 | } PACK_STRUCT_STRUCT; |
||
215 | PACK_STRUCT_END |
||
216 | #ifdef PACK_STRUCT_USE_INCLUDES |
||
217 | # include "arch/epstruct.h" |
||
218 | #endif |
||
219 | |||
220 | /* function prototypes */ |
||
221 | static void sntp_request(void *arg); |
||
222 | |||
223 | /** The operating mode */ |
||
224 | static u8_t sntp_opmode; |
||
225 | |||
226 | /** The UDP pcb used by the SNTP client */ |
||
227 | static struct udp_pcb *sntp_pcb; |
||
228 | /** Names/Addresses of servers */ |
||
229 | struct sntp_server { |
||
230 | #if SNTP_SERVER_DNS |
||
231 | char *name; |
||
232 | #endif /* SNTP_SERVER_DNS */ |
||
233 | ip_addr_t addr; |
||
234 | }; |
||
235 | static struct sntp_server sntp_servers[SNTP_MAX_SERVERS]; |
||
236 | |||
237 | #if SNTP_GET_SERVERS_FROM_DHCP |
||
238 | static u8_t sntp_set_servers_from_dhcp; |
||
239 | #endif /* SNTP_GET_SERVERS_FROM_DHCP */ |
||
240 | #if SNTP_SUPPORT_MULTIPLE_SERVERS |
||
241 | /** The currently used server (initialized to 0) */ |
||
242 | static u8_t sntp_current_server; |
||
243 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ |
||
244 | #define sntp_current_server 0 |
||
245 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ |
||
246 | |||
247 | #if SNTP_RETRY_TIMEOUT_EXP |
||
248 | #define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT |
||
249 | /** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ |
||
250 | static u32_t sntp_retry_timeout; |
||
251 | #else /* SNTP_RETRY_TIMEOUT_EXP */ |
||
252 | #define SNTP_RESET_RETRY_TIMEOUT() |
||
253 | #define sntp_retry_timeout SNTP_RETRY_TIMEOUT |
||
254 | #endif /* SNTP_RETRY_TIMEOUT_EXP */ |
||
255 | |||
256 | #if SNTP_CHECK_RESPONSE >= 1 |
||
257 | /** Saves the last server address to compare with response */ |
||
258 | static ip_addr_t sntp_last_server_address; |
||
259 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ |
||
260 | |||
261 | #if SNTP_CHECK_RESPONSE >= 2 |
||
262 | /** Saves the last timestamp sent (which is sent back by the server) |
||
263 | * to compare against in response. Stored in network byte order. */ |
||
264 | static struct sntp_time sntp_last_timestamp_sent; |
||
265 | #endif /* SNTP_CHECK_RESPONSE >= 2 */ |
||
266 | |||
267 | #if defined(LWIP_DEBUG) && !defined(sntp_format_time) |
||
268 | /* Debug print helper. */ |
||
269 | static const char * |
||
270 | sntp_format_time(s32_t sec) |
||
271 | { |
||
272 | time_t ut; |
||
273 | ut = (time_t)((time_t)sec + (time_t)DIFF_SEC_1970_2036); |
||
274 | return ctime(&ut); |
||
275 | } |
||
276 | #endif /* LWIP_DEBUG && !sntp_format_time */ |
||
277 | |||
278 | /** |
||
279 | * SNTP processing of received timestamp |
||
280 | */ |
||
281 | static void |
||
282 | sntp_process(const struct sntp_timestamps *timestamps) |
||
283 | { |
||
284 | s32_t sec; |
||
285 | u32_t frac; |
||
286 | |||
287 | sec = (s32_t)lwip_ntohl(timestamps->xmit.sec); |
||
288 | frac = lwip_ntohl(timestamps->xmit.frac); |
||
289 | |||
290 | #if SNTP_COMP_ROUNDTRIP |
||
291 | # if SNTP_CHECK_RESPONSE >= 2 |
||
292 | if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0) |
||
293 | # endif |
||
294 | { |
||
295 | s32_t dest_sec; |
||
296 | u32_t dest_frac; |
||
297 | u32_t step_sec; |
||
298 | |||
299 | /* Get the destination time stamp, i.e. the current system time */ |
||
300 | SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac); |
||
301 | |||
302 | step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec) |
||
303 | : ((u32_t)dest_sec - (u32_t)sec); |
||
304 | /* In order to avoid overflows, skip the compensation if the clock step |
||
305 | * is larger than about 34 years. */ |
||
306 | if ((step_sec >> 30) == 0) { |
||
307 | s64_t t1, t2, t3, t4; |
||
308 | |||
309 | t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac); |
||
310 | t3 = SNTP_SEC_FRAC_TO_S64(sec, frac); |
||
311 | t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig); |
||
312 | t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv); |
||
313 | /* Clock offset calculation according to RFC 4330 */ |
||
314 | t4 += ((t2 - t1) + (t3 - t4)) / 2; |
||
315 | |||
316 | sec = (s32_t)((u64_t)t4 >> 32); |
||
317 | frac = (u32_t)((u64_t)t4); |
||
318 | } |
||
319 | } |
||
320 | #endif /* SNTP_COMP_ROUNDTRIP */ |
||
321 | |||
322 | SNTP_SET_SYSTEM_TIME_NTP(sec, frac); |
||
323 | LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */ |
||
324 | LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n", |
||
325 | sntp_format_time(sec), SNTP_FRAC_TO_US(frac))); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Initialize request struct to be sent to server. |
||
330 | */ |
||
331 | static void |
||
332 | sntp_initialize_request(struct sntp_msg *req) |
||
333 | { |
||
334 | memset(req, 0, SNTP_MSG_LEN); |
||
335 | req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; |
||
336 | |||
337 | #if SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP |
||
338 | { |
||
339 | s32_t secs; |
||
340 | u32_t sec, frac; |
||
341 | /* Get the transmit timestamp */ |
||
342 | SNTP_GET_SYSTEM_TIME_NTP(secs, frac); |
||
343 | sec = lwip_htonl((u32_t)secs); |
||
344 | frac = lwip_htonl(frac); |
||
345 | |||
346 | # if SNTP_CHECK_RESPONSE >= 2 |
||
347 | sntp_last_timestamp_sent.sec = sec; |
||
348 | sntp_last_timestamp_sent.frac = frac; |
||
349 | # endif |
||
350 | req->transmit_timestamp[0] = sec; |
||
351 | req->transmit_timestamp[1] = frac; |
||
352 | } |
||
353 | #endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */ |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Retry: send a new request (and increase retry timeout). |
||
358 | * |
||
359 | * @param arg is unused (only necessary to conform to sys_timeout) |
||
360 | */ |
||
361 | static void |
||
362 | sntp_retry(void *arg) |
||
363 | { |
||
364 | LWIP_UNUSED_ARG(arg); |
||
365 | |||
366 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", |
||
367 | sntp_retry_timeout)); |
||
368 | |||
369 | /* set up a timer to send a retry and increase the retry delay */ |
||
370 | sys_timeout(sntp_retry_timeout, sntp_request, NULL); |
||
371 | |||
372 | #if SNTP_RETRY_TIMEOUT_EXP |
||
373 | { |
||
374 | u32_t new_retry_timeout; |
||
375 | /* increase the timeout for next retry */ |
||
376 | new_retry_timeout = sntp_retry_timeout << 1; |
||
377 | /* limit to maximum timeout and prevent overflow */ |
||
378 | if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && |
||
379 | (new_retry_timeout > sntp_retry_timeout)) { |
||
380 | sntp_retry_timeout = new_retry_timeout; |
||
381 | } |
||
382 | } |
||
383 | #endif /* SNTP_RETRY_TIMEOUT_EXP */ |
||
384 | } |
||
385 | |||
386 | #if SNTP_SUPPORT_MULTIPLE_SERVERS |
||
387 | /** |
||
388 | * If Kiss-of-Death is received (or another packet parsing error), |
||
389 | * try the next server or retry the current server and increase the retry |
||
390 | * timeout if only one server is available. |
||
391 | * (implicitly, SNTP_MAX_SERVERS > 1) |
||
392 | * |
||
393 | * @param arg is unused (only necessary to conform to sys_timeout) |
||
394 | */ |
||
395 | static void |
||
396 | sntp_try_next_server(void *arg) |
||
397 | { |
||
398 | u8_t old_server, i; |
||
399 | LWIP_UNUSED_ARG(arg); |
||
400 | |||
401 | old_server = sntp_current_server; |
||
402 | for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) { |
||
403 | sntp_current_server++; |
||
404 | if (sntp_current_server >= SNTP_MAX_SERVERS) { |
||
405 | sntp_current_server = 0; |
||
406 | } |
||
407 | if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr) |
||
408 | #if SNTP_SERVER_DNS |
||
409 | || (sntp_servers[sntp_current_server].name != NULL) |
||
410 | #endif |
||
411 | ) { |
||
412 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", |
||
413 | (u16_t)sntp_current_server)); |
||
414 | /* new server: reset retry timeout */ |
||
415 | SNTP_RESET_RETRY_TIMEOUT(); |
||
416 | /* instantly send a request to the next server */ |
||
417 | sntp_request(NULL); |
||
418 | return; |
||
419 | } |
||
420 | } |
||
421 | /* no other valid server found */ |
||
422 | sntp_current_server = old_server; |
||
423 | sntp_retry(NULL); |
||
424 | } |
||
425 | #else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ |
||
426 | /* Always retry on error if only one server is supported */ |
||
427 | #define sntp_try_next_server sntp_retry |
||
428 | #endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ |
||
429 | |||
430 | /** UDP recv callback for the sntp pcb */ |
||
431 | static void |
||
432 | sntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) |
||
433 | { |
||
434 | struct sntp_timestamps timestamps; |
||
435 | u8_t mode; |
||
436 | u8_t stratum; |
||
437 | err_t err; |
||
438 | |||
439 | LWIP_UNUSED_ARG(arg); |
||
440 | LWIP_UNUSED_ARG(pcb); |
||
441 | |||
442 | err = ERR_ARG; |
||
443 | #if SNTP_CHECK_RESPONSE >= 1 |
||
444 | /* check server address and port */ |
||
445 | if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) && |
||
446 | (port == SNTP_PORT)) |
||
447 | #else /* SNTP_CHECK_RESPONSE >= 1 */ |
||
448 | LWIP_UNUSED_ARG(addr); |
||
449 | LWIP_UNUSED_ARG(port); |
||
450 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ |
||
451 | { |
||
452 | /* process the response */ |
||
453 | if (p->tot_len == SNTP_MSG_LEN) { |
||
454 | mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_MODE) & SNTP_MODE_MASK; |
||
455 | /* if this is a SNTP response... */ |
||
456 | if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) || |
||
457 | ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) { |
||
458 | stratum = pbuf_get_at(p, SNTP_OFFSET_STRATUM); |
||
459 | |||
460 | if (stratum == SNTP_STRATUM_KOD) { |
||
461 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ |
||
462 | err = SNTP_ERR_KOD; |
||
463 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); |
||
464 | } else { |
||
465 | pbuf_copy_partial(p, ×tamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS); |
||
466 | #if SNTP_CHECK_RESPONSE >= 2 |
||
467 | /* check originate_timetamp against sntp_last_timestamp_sent */ |
||
468 | if (timestamps.orig.sec != sntp_last_timestamp_sent.sec || |
||
469 | timestamps.orig.frac != sntp_last_timestamp_sent.frac) { |
||
470 | LWIP_DEBUGF(SNTP_DEBUG_WARN, |
||
471 | ("sntp_recv: Invalid originate timestamp in response\n")); |
||
472 | } else |
||
473 | #endif /* SNTP_CHECK_RESPONSE >= 2 */ |
||
474 | /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ |
||
475 | { |
||
476 | /* correct answer */ |
||
477 | err = ERR_OK; |
||
478 | } |
||
479 | } |
||
480 | } else { |
||
481 | LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); |
||
482 | /* wait for correct response */ |
||
483 | err = ERR_TIMEOUT; |
||
484 | } |
||
485 | } else { |
||
486 | LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); |
||
487 | } |
||
488 | } |
||
489 | #if SNTP_CHECK_RESPONSE >= 1 |
||
490 | else { |
||
491 | /* packet from wrong remote address or port, wait for correct response */ |
||
492 | err = ERR_TIMEOUT; |
||
493 | } |
||
494 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ |
||
495 | |||
496 | pbuf_free(p); |
||
497 | |||
498 | if (err == ERR_OK) { |
||
499 | /* correct packet received: process it it */ |
||
500 | sntp_process(×tamps); |
||
501 | |||
502 | /* Set up timeout for next request (only if poll response was received)*/ |
||
503 | if (sntp_opmode == SNTP_OPMODE_POLL) { |
||
504 | u32_t sntp_update_delay; |
||
505 | sys_untimeout(sntp_try_next_server, NULL); |
||
506 | sys_untimeout(sntp_request, NULL); |
||
507 | |||
508 | /* Correct response, reset retry timeout */ |
||
509 | SNTP_RESET_RETRY_TIMEOUT(); |
||
510 | |||
511 | sntp_update_delay = (u32_t)SNTP_UPDATE_DELAY; |
||
512 | sys_timeout(sntp_update_delay, sntp_request, NULL); |
||
513 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", |
||
514 | sntp_update_delay)); |
||
515 | } |
||
516 | } else if (err == SNTP_ERR_KOD) { |
||
517 | /* KOD errors are only processed in case of an explicit poll response */ |
||
518 | if (sntp_opmode == SNTP_OPMODE_POLL) { |
||
519 | /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ |
||
520 | sntp_try_next_server(NULL); |
||
521 | } |
||
522 | } else { |
||
523 | /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */ |
||
524 | } |
||
525 | } |
||
526 | |||
527 | /** Actually send an sntp request to a server. |
||
528 | * |
||
529 | * @param server_addr resolved IP address of the SNTP server |
||
530 | */ |
||
531 | static void |
||
532 | sntp_send_request(const ip_addr_t *server_addr) |
||
533 | { |
||
534 | struct pbuf *p; |
||
535 | p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); |
||
536 | if (p != NULL) { |
||
537 | struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; |
||
538 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); |
||
539 | /* initialize request message */ |
||
540 | sntp_initialize_request(sntpmsg); |
||
541 | /* send request */ |
||
542 | udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); |
||
543 | /* free the pbuf after sending it */ |
||
544 | pbuf_free(p); |
||
545 | /* set up receive timeout: try next server or retry on timeout */ |
||
546 | sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); |
||
547 | #if SNTP_CHECK_RESPONSE >= 1 |
||
548 | /* save server address to verify it in sntp_recv */ |
||
549 | ip_addr_set(&sntp_last_server_address, server_addr); |
||
550 | #endif /* SNTP_CHECK_RESPONSE >= 1 */ |
||
551 | } else { |
||
552 | LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", |
||
553 | (u32_t)SNTP_RETRY_TIMEOUT)); |
||
554 | /* out of memory: set up a timer to send a retry */ |
||
555 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); |
||
556 | } |
||
557 | } |
||
558 | |||
559 | #if SNTP_SERVER_DNS |
||
560 | /** |
||
561 | * DNS found callback when using DNS names as server address. |
||
562 | */ |
||
563 | static void |
||
564 | sntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg) |
||
565 | { |
||
566 | LWIP_UNUSED_ARG(hostname); |
||
567 | LWIP_UNUSED_ARG(arg); |
||
568 | |||
569 | if (ipaddr != NULL) { |
||
570 | /* Address resolved, send request */ |
||
571 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); |
||
572 | sntp_send_request(ipaddr); |
||
573 | } else { |
||
574 | /* DNS resolving failed -> try another server */ |
||
575 | LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n")); |
||
576 | sntp_try_next_server(NULL); |
||
577 | } |
||
578 | } |
||
579 | #endif /* SNTP_SERVER_DNS */ |
||
580 | |||
581 | /** |
||
582 | * Send out an sntp request. |
||
583 | * |
||
584 | * @param arg is unused (only necessary to conform to sys_timeout) |
||
585 | */ |
||
586 | static void |
||
587 | sntp_request(void *arg) |
||
588 | { |
||
589 | ip_addr_t sntp_server_address; |
||
590 | err_t err; |
||
591 | |||
592 | LWIP_UNUSED_ARG(arg); |
||
593 | |||
594 | /* initialize SNTP server address */ |
||
595 | #if SNTP_SERVER_DNS |
||
596 | if (sntp_servers[sntp_current_server].name) { |
||
597 | /* always resolve the name and rely on dns-internal caching & timeout */ |
||
598 | ip_addr_set_zero(&sntp_servers[sntp_current_server].addr); |
||
599 | err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address, |
||
600 | sntp_dns_found, NULL); |
||
601 | if (err == ERR_INPROGRESS) { |
||
602 | /* DNS request sent, wait for sntp_dns_found being called */ |
||
603 | LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); |
||
604 | return; |
||
605 | } else if (err == ERR_OK) { |
||
606 | sntp_servers[sntp_current_server].addr = sntp_server_address; |
||
607 | } |
||
608 | } else |
||
609 | #endif /* SNTP_SERVER_DNS */ |
||
610 | { |
||
611 | sntp_server_address = sntp_servers[sntp_current_server].addr; |
||
612 | err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK; |
||
613 | } |
||
614 | |||
615 | if (err == ERR_OK) { |
||
616 | LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n", |
||
617 | ipaddr_ntoa(&sntp_server_address))); |
||
618 | sntp_send_request(&sntp_server_address); |
||
619 | } else { |
||
620 | /* address conversion failed, try another server */ |
||
621 | LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); |
||
622 | sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); |
||
623 | } |
||
624 | } |
||
625 | |||
626 | /** |
||
627 | * @ingroup sntp |
||
628 | * Initialize this module. |
||
629 | * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC). |
||
630 | */ |
||
631 | void |
||
632 | sntp_init(void) |
||
633 | { |
||
634 | #ifdef SNTP_SERVER_ADDRESS |
||
635 | #if SNTP_SERVER_DNS |
||
636 | sntp_setservername(0, SNTP_SERVER_ADDRESS); |
||
637 | #else |
||
638 | #error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0 |
||
639 | #endif |
||
640 | #endif /* SNTP_SERVER_ADDRESS */ |
||
641 | |||
642 | if (sntp_pcb == NULL) { |
||
643 | sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); |
||
644 | LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); |
||
645 | if (sntp_pcb != NULL) { |
||
646 | udp_recv(sntp_pcb, sntp_recv, NULL); |
||
647 | |||
648 | if (sntp_opmode == SNTP_OPMODE_POLL) { |
||
649 | SNTP_RESET_RETRY_TIMEOUT(); |
||
650 | #if SNTP_STARTUP_DELAY |
||
651 | sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL); |
||
652 | #else |
||
653 | sntp_request(NULL); |
||
654 | #endif |
||
655 | } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) { |
||
656 | ip_set_option(sntp_pcb, SOF_BROADCAST); |
||
657 | udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT); |
||
658 | } |
||
659 | } |
||
660 | } |
||
661 | } |
||
662 | |||
663 | /** |
||
664 | * @ingroup sntp |
||
665 | * Stop this module. |
||
666 | */ |
||
667 | void |
||
668 | sntp_stop(void) |
||
669 | { |
||
670 | if (sntp_pcb != NULL) { |
||
671 | sys_untimeout(sntp_request, NULL); |
||
672 | sys_untimeout(sntp_try_next_server, NULL); |
||
673 | udp_remove(sntp_pcb); |
||
674 | sntp_pcb = NULL; |
||
675 | } |
||
676 | } |
||
677 | |||
678 | /** |
||
679 | * @ingroup sntp |
||
680 | * Get enabled state. |
||
681 | */ |
||
682 | u8_t sntp_enabled(void) |
||
683 | { |
||
684 | return (sntp_pcb != NULL) ? 1 : 0; |
||
685 | } |
||
686 | |||
687 | /** |
||
688 | * @ingroup sntp |
||
689 | * Sets the operating mode. |
||
690 | * @param operating_mode one of the available operating modes |
||
691 | */ |
||
692 | void |
||
693 | sntp_setoperatingmode(u8_t operating_mode) |
||
694 | { |
||
695 | LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY); |
||
696 | LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL); |
||
697 | sntp_opmode = operating_mode; |
||
698 | } |
||
699 | |||
700 | /** |
||
701 | * @ingroup sntp |
||
702 | * Gets the operating mode. |
||
703 | */ |
||
704 | u8_t |
||
705 | sntp_getoperatingmode(void) |
||
706 | { |
||
707 | return sntp_opmode; |
||
708 | } |
||
709 | |||
710 | #if SNTP_GET_SERVERS_FROM_DHCP |
||
711 | /** |
||
712 | * Config SNTP server handling by IP address, name, or DHCP; clear table |
||
713 | * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp |
||
714 | */ |
||
715 | void |
||
716 | sntp_servermode_dhcp(int set_servers_from_dhcp) |
||
717 | { |
||
718 | u8_t new_mode = set_servers_from_dhcp ? 1 : 0; |
||
719 | if (sntp_set_servers_from_dhcp != new_mode) { |
||
720 | sntp_set_servers_from_dhcp = new_mode; |
||
721 | } |
||
722 | } |
||
723 | #endif /* SNTP_GET_SERVERS_FROM_DHCP */ |
||
724 | |||
725 | /** |
||
726 | * @ingroup sntp |
||
727 | * Initialize one of the NTP servers by IP address |
||
728 | * |
||
729 | * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS |
||
730 | * @param server IP address of the NTP server to set |
||
731 | */ |
||
732 | void |
||
733 | sntp_setserver(u8_t idx, const ip_addr_t *server) |
||
734 | { |
||
735 | if (idx < SNTP_MAX_SERVERS) { |
||
736 | if (server != NULL) { |
||
737 | sntp_servers[idx].addr = (*server); |
||
738 | } else { |
||
739 | ip_addr_set_zero(&sntp_servers[idx].addr); |
||
740 | } |
||
741 | #if SNTP_SERVER_DNS |
||
742 | sntp_servers[idx].name = NULL; |
||
743 | #endif |
||
744 | } |
||
745 | } |
||
746 | |||
747 | #if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP |
||
748 | /** |
||
749 | * Initialize one of the NTP servers by IP address, required by DHCP |
||
750 | * |
||
751 | * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS |
||
752 | * @param dnsserver IP address of the NTP server to set |
||
753 | */ |
||
754 | void |
||
755 | dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server) |
||
756 | { |
||
757 | LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n", |
||
758 | (sntp_set_servers_from_dhcp ? "Got" : "Rejected"), |
||
759 | ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num)); |
||
760 | if (sntp_set_servers_from_dhcp && num) { |
||
761 | u8_t i; |
||
762 | for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) { |
||
763 | ip_addr_t addr; |
||
764 | ip_addr_copy_from_ip4(addr, server[i]); |
||
765 | sntp_setserver(i, &addr); |
||
766 | } |
||
767 | for (i = num; i < SNTP_MAX_SERVERS; i++) { |
||
768 | sntp_setserver(i, NULL); |
||
769 | } |
||
770 | } |
||
771 | } |
||
772 | #endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */ |
||
773 | |||
774 | /** |
||
775 | * @ingroup sntp |
||
776 | * Obtain one of the currently configured by IP address (or DHCP) NTP servers |
||
777 | * |
||
778 | * @param idx the index of the NTP server |
||
779 | * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP |
||
780 | * server has not been configured by address (or at all). |
||
781 | */ |
||
782 | const ip_addr_t * |
||
783 | sntp_getserver(u8_t idx) |
||
784 | { |
||
785 | if (idx < SNTP_MAX_SERVERS) { |
||
786 | return &sntp_servers[idx].addr; |
||
787 | } |
||
788 | return IP_ADDR_ANY; |
||
789 | } |
||
790 | |||
791 | #if SNTP_SERVER_DNS |
||
792 | /** |
||
793 | * Initialize one of the NTP servers by name |
||
794 | * |
||
795 | * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS |
||
796 | * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time |
||
797 | */ |
||
798 | void |
||
799 | sntp_setservername(u8_t idx, char *server) |
||
800 | { |
||
801 | if (idx < SNTP_MAX_SERVERS) { |
||
802 | sntp_servers[idx].name = server; |
||
803 | } |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Obtain one of the currently configured by name NTP servers. |
||
808 | * |
||
809 | * @param numdns the index of the NTP server |
||
810 | * @return IP address of the indexed NTP server or NULL if the NTP |
||
811 | * server has not been configured by name (or at all) |
||
812 | */ |
||
813 | char * |
||
814 | sntp_getservername(u8_t idx) |
||
815 | { |
||
816 | if (idx < SNTP_MAX_SERVERS) { |
||
817 | return sntp_servers[idx].name; |
||
818 | } |
||
819 | return NULL; |
||
820 | } |
||
821 | #endif /* SNTP_SERVER_DNS */ |
||
822 | |||
823 | #endif /* LWIP_UDP */ |