1 /*
2 * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the
12 * distribution.
13 * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <unistd.h>
31
32 #include "sntp/sntp.h"
33 #include "sys/socket.h"
34 #include "netdb.h"
35 #include "sys/time.h"
36 #include "string.h"
37 #include "stdlib.h"
38 #include "errno.h"
39
40 #include "ulog/ulog.h"
41
42 #define TAG "sntp"
43
44 #define SNTP_DEBUG(format, ...)
45 #define SNTP_INFO(format, ...) LOGI(TAG, format, ##__VA_ARGS__)
46 #define SNTP_ERROR(format, ...) LOGE(TAG, format, ##__VA_ARGS__)
47 #define SNTP_WRANING(format, ...) LOGW(TAG, format, ##__VA_ARGS__)
48
49 /* SNTP protocol defines */
50 #define SNTP_MSG_LEN 48
51 #define SNTP_LI_NO_WARNING 0x00
52 #define SNTP_VERSION 3 /* NTP Version */
53 #define SNTP_MODE_MASK 0x07
54 #define SNTP_MODE_CLIENT 0x03
55 #define SNTP_MODE_SERVER 0x04
56 #define SNTP_MODE_BROADCAST 0x05
57 #define SNTP_STRATUM_KOD 0x00
58 /* number of seconds between 1900 and 1970 */
59 #define DIFF_SEC_1900_1970 (2208988800UL)
60 /* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
61 #define DIFF_SEC_1970_2036 (2085978496UL)
62
63 typedef struct
64 {
65 uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
66 // li. Two bits. Leap indicator.
67 // vn. Three bits. Version number of the protocol.
68 // mode. Three bits. Client will pick mode 3 for client.
69
70 uint8_t stratum; // Eight bits. Stratum level of the local clock.
71 uint8_t poll; // Eight bits. Maximum interval between successive messages.
72 uint8_t precision; // Eight bits. Precision of the local clock.
73
74 uint32_t rootDelay; // 32 bits. Total round trip delay time.
75 uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
76 uint32_t refId; // 32 bits. Reference clock identifier.
77
78 uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
79 uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
80
81 uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
82 uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
83
84 uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
85 uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
86
87 uint32_t txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
88 uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
89 } ntp_packet; // Total: 384 bits or 48 bytes.
90
91 static sntp_time g_time;
92
93 static struct timezone g_timezone = { 0, 0 };
94
95 static int g_last_query_status = -1;
96
97 // Sets time zone.
sntp_set_timezone(const struct timezone * tz)98 void sntp_set_timezone(const struct timezone *tz) {
99 if (tz) {
100 g_timezone = *tz;
101 } else {
102 g_timezone.tz_minuteswest = 0;
103 g_timezone.tz_dsttime = 0;
104 }
105 }
106
107 #if SNTP_SUPPORT_MULTIPLE_SERVERS
108 typedef struct {
109 struct in_addr addr;
110 } sntp_server_info;
111
112 static sntp_server_info sntp_servers[SNTP_MAX_SERVERS];
113
sntp_get_server_addr(uint8_t idx)114 static const struct in_addr* sntp_get_server_addr(uint8_t idx)
115 {
116 if (idx < SNTP_MAX_SERVERS) {
117 return &sntp_servers[idx].addr;
118 }
119 return INADDR_ANY;
120 }
121
sntp_set_server_addr(uint8_t idx,struct in_addr * server_addr)122 static int sntp_set_server_addr(uint8_t idx, struct in_addr *server_addr)
123 {
124 if (idx >= SNTP_MAX_SERVERS)
125 return -1;
126 sntp_servers[idx].addr.s_addr = server_addr->s_addr;
127 return 0;
128 }
129
130 /**
131 * @brief Set the remote ntp server address by name.
132 * @note
133 * @param idx: numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS.
134 * arg: servers name that want to set. such as: "cn.ntp.org.cn" "118.24.4.66"
135 * @retval 0:success -1:fail
136 */
sntp_set_server(uint8_t idx,char * server)137 int sntp_set_server(uint8_t idx, char *server)
138 {
139 struct hostent *host;
140
141 if (idx >= SNTP_MAX_SERVERS)
142 return -1;
143
144 host = gethostbyname(server);
145 if (!host) {
146 SNTP_ERROR("invalid address parameter '%s'",server);
147 return -1;
148 }
149
150 sntp_set_server_addr(idx, (struct in_addr *)host->h_addr);
151 return 0;
152 }
153 #endif
154
sntp_init_request_packet(ntp_packet * packet)155 static void sntp_init_request_packet(ntp_packet *packet)
156 {
157 struct timeval local_time;
158
159 memset(packet, 0, sizeof(ntp_packet));
160 gettimeofday(&local_time, NULL);
161 packet->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION << 3 | SNTP_MODE_CLIENT;
162 packet->txTm_s = htonl(local_time.tv_sec);
163 packet->txTm_f = htonl(local_time.tv_usec);
164 }
165
sntp_process_time(ntp_packet * packet,struct timeval * time)166 static int sntp_process_time(ntp_packet *packet, struct timeval *time)
167 {
168 struct timeval local_time;
169 uint32_t t0_s, t1_s, t2_s, t3_s, real_time_s;
170 uint32_t t0_us, t1_us, t2_us, t3_us, real_time_us;
171 int deta1, deta2, transmit_time;
172
173 if (((packet->li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_SERVER) ||
174 ((packet->li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_BROADCAST)) {
175 gettimeofday(&local_time, NULL);
176
177 t0_s = ntohl(packet->origTm_s);
178 t0_us = ntohl(packet->origTm_f);
179
180 t3_s = local_time.tv_sec;
181 t3_us = local_time.tv_usec;
182
183 t1_s = ntohl(packet->rxTm_s);
184 t1_us = ntohl(packet->rxTm_f) / 4295;
185
186 t2_s = ntohl(packet->txTm_s);
187 t2_us = ntohl(packet->txTm_f) / 4295;
188
189 deta1 = (t3_s - t0_s) * 1000000 + (t3_us - t0_us); /* us */
190 deta2 = (t2_s - t1_s) * 1000000 + (t2_us - t1_us); /* us */
191
192 transmit_time = (deta1 - deta2) / 2; /* us */
193
194 real_time_s = t2_s + (t2_us + transmit_time) / 1000000;
195 real_time_us = (t2_us + transmit_time) % 1000000;
196
197 int is_1900_based = ((real_time_s & 0x80000000) != 0);
198 real_time_s = is_1900_based ? (real_time_s - DIFF_SEC_1900_1970) : (real_time_s + DIFF_SEC_1970_2036);
199 time->tv_sec = real_time_s + (g_timezone.tz_minuteswest + g_timezone.tz_dsttime * 60) * 60;
200 time->tv_usec = real_time_us;
201 } else {
202 SNTP_ERROR("sntp_request: not response frame code");
203 return -1;
204 }
205 return 0;
206 }
207
sntp_query_server(sntp_arg * arg,struct timeval * ntp_time)208 static int sntp_query_server(sntp_arg *arg, struct timeval *ntp_time)
209 {
210 ntp_packet packet;
211 int sockfd = -1;
212 struct sockaddr_in server_addr;
213 struct hostent *host;
214 char *server_name = NULL;
215 int ret = 0;
216 struct timeval timeout_val;
217 #if SNTP_SUPPORT_MULTIPLE_SERVERS
218 uint8_t idx = 0;
219 #endif
220
221 memset(&server_addr, 0, sizeof(server_addr));
222
223 /* choose the right server */
224 do {
225 if (arg && arg->server_name) {
226 server_name = arg->server_name;
227 break;
228 }
229 #if SNTP_SUPPORT_MULTIPLE_SERVERS
230 while(idx < SNTP_MAX_SERVERS && sntp_get_server_addr(idx)->s_addr == INADDR_ANY)
231 idx++;
232 if (idx < SNTP_MAX_SERVERS) {
233 server_name = NULL;
234 server_addr.sin_addr.s_addr = sntp_get_server_addr(idx)->s_addr;
235 break;
236 }
237 #endif
238 server_name = SNTP_SERVER_ADDRESS;
239 } while(0);
240
241 if (server_name) {
242 host = gethostbyname(server_name);
243 if (!host) {
244 SNTP_ERROR("invalid address parameter '%s'",server_name);
245 ret = -1;
246 goto exit;
247 }
248 server_addr.sin_addr.s_addr = (*(struct in_addr *)host->h_addr).s_addr;
249 }
250
251 server_addr.sin_family = AF_INET;
252 server_addr.sin_port = htons(SNTP_PORT);
253
254 sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
255 if (sockfd < 0) {
256 SNTP_ERROR("socket create err!");
257 ret = -1;
258 goto exit;
259 }
260
261 int val = 1;
262 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) != 0) {
263 SNTP_ERROR("setsockopt(SO_REUSEADDR) failed");
264 ret = -1;
265 goto exit;
266 }
267
268 int timeout_ms = arg && arg->recv_timeout > 0 ? arg->recv_timeout : SNTP_RECV_TIMEOUT;
269 timeout_val.tv_sec = timeout_ms / 1000;
270 timeout_val.tv_usec = timeout_ms % 1000 * 1000;
271 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout_val, sizeof(struct timeval)) != 0) {
272 SNTP_ERROR("setsockopt(SO_RCVTIMEO) failed %d %d", arg->recv_timeout, errno);
273 ret = -1;
274 goto exit;
275 }
276
277 #if SNTP_SUPPORT_MULTIPLE_SERVERS
278 send_request:
279 #endif
280 SNTP_DEBUG("server name:%s", server_name ? server_name : "NULL");
281 SNTP_DEBUG("server ip:%s", inet_ntoa(server_addr.sin_addr));
282
283 int retry_times = arg ? arg->retry_times : SNTP_RETRY_TIMES;
284
285 do {
286 sntp_init_request_packet(&packet);
287
288 ret = sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
289 if (ret <= 0) {
290 SNTP_ERROR("send err,ret=%d,errno:%d", ret, errno);
291 ret = -1;
292 continue;
293 }
294
295 ret = recvfrom(sockfd, &packet, sizeof(packet), 0, NULL, NULL);
296 if (ret != SNTP_MSG_LEN) {
297 if (errno != EAGAIN)
298 SNTP_ERROR("recv err,ret=%d,errno:%d", ret, errno);
299 ret = -1;
300 continue;
301 } else {
302 if ((ret = sntp_process_time(&packet, ntp_time)) != 0)
303 ret = -1;
304 else
305 goto exit;
306 }
307 } while (--retry_times && retry_times > 0);
308
309 #if SNTP_SUPPORT_MULTIPLE_SERVERS
310 /* try and get the next ntp server */
311 if (server_name == NULL) {
312 for (idx++; idx < SNTP_MAX_SERVERS; idx++) {
313 if (sntp_get_server_addr(idx)->s_addr != INADDR_ANY) {
314 server_name = NULL;
315 server_addr.sin_addr.s_addr = sntp_get_server_addr(idx)->s_addr;
316 goto send_request;
317 }
318 }
319 }
320 #endif
321
322 exit:
323 if (sockfd >= 0)
324 close(sockfd);
325 if (ret)
326 return -1;
327
328 return 0;
329 }
330
331 #define MIN_SNTP_SET_INTERVAL_MS 5000
332 static long long m_sntp_set_time = 0;
333
334 /**
335 * @brief Get time from the remote server.
336 * @note This a blocking interface.
337 * @param ntp_time: Pointer to the struct timeval.
338 * arg: The pointer of sntp module parameter
339 * @retval 0:success -1:fail
340 */
sntp_get_time(sntp_arg * arg,struct timeval * ntp_time)341 int sntp_get_time(sntp_arg *arg, struct timeval *ntp_time)
342 {
343 int ret = 0;
344
345 ret = sntp_query_server(arg, ntp_time);
346 if (ret == 0) {
347 if (m_sntp_set_time == 0 ||
348 aos_now_ms() - m_sntp_set_time > MIN_SNTP_SET_INTERVAL_MS) {
349 aos_calendar_time_set(ntp_time->tv_sec * (uint64_t)1000 + (uint64_t)(ntp_time->tv_usec / 1000));
350 m_sntp_set_time = aos_now_ms();
351 }
352 }
353
354 g_last_query_status = ret;
355
356 return ret;
357 }
358
359 /**
360 * Send an SNTP request via sockets. This interface has been deprecated.
361 * This is a very minimal implementation that does not fully conform
362 * to the SNTPv4 RFC, especially regarding server load and error procesing.
363 */
sntp_request(void * arg)364 int sntp_request(void *arg)
365 {
366 struct timeval ntp_time;
367 struct tm *gt;
368 int ret;
369
370 ret = sntp_query_server(NULL, &ntp_time);
371 if (ret != 0)
372 return -1;
373
374 gt = gmtime(&ntp_time.tv_sec);
375 if (gt==NULL) {
376 return -1;
377 }
378
379 g_time.year = gt->tm_year % 100;
380 g_time.mon = gt->tm_mon + 1;
381 g_time.day = gt->tm_mday;
382 g_time.week = gt->tm_wday;
383 g_time.hour = gt->tm_hour + 8;
384 g_time.min = gt->tm_min;
385 g_time.sec = gt->tm_sec;
386
387 return 0;
388 }
389
390 /**
391 * obtain time
392 * This interface has been deprecated.
393 */
sntp_obtain_time(void)394 sntp_time *sntp_obtain_time(void)
395 {
396 return &g_time;
397 }
398
399 /*
400 * check if last sntp query is OK
401 * @param: None
402 * @retval: 0 success -1 fail
403 */
sntp_last_query_status(void)404 int sntp_last_query_status(void)
405 {
406 return g_last_query_status;
407 }
408
409 /*
410 * set system calender time directly
411 * @param: current time in millisecond
412 * @retval: 0 success -1 fail
413 */
sntp_set_time_direct(long long now_ms)414 int sntp_set_time_direct(long long now_ms)
415 {
416 aos_calendar_time_set(now_ms);
417
418 return 0;
419 }
420
421
422