source: EcnlProtoTool/trunk/ntshell/src/ntp_cli.c@ 441

Last change on this file since 441 was 441, checked in by coas-nagasima, 4 years ago

NTShellタスクを更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 15.7 KB
Line 
1/*
2 * TOPPERS PROJECT Home Network Working Group Software
3 *
4 * Copyright (C) 2018-2019 Cores Co., Ltd. Japan
5 *
6 * 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
7 * ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
8 * 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
9 * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
10 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
11 * スコード中に含まれていること.
12 * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
13 * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
14 * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
15 * の無保証規定を掲載すること.
16 * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
17 * 用できない形で再配布する場合には,次のいずれかの条件を満たすこ
18 * と.
19 * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
20 * 作権表示,この利用条件および下記の無保証規定を掲載すること.
21 * (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
22 * 報告すること.
23 * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
24 * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
25 * また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
26 * 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
27 * 免責すること.
28 *
29 * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
30 * よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
31 * に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
32 * アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
33 * の責任を負わない.
34 *
35 * @(#) $Id$
36 */
37
38/*
39 * NTP クライアント
40 */
41
42#include <string.h>
43#include <string.h>
44#include <time.h>
45
46#if defined(TARGET_KERNEL_ASP)
47
48#include <kernel.h>
49#include <sil.h>
50#include <t_syslog.h>
51#include "kernel_cfg.h"
52#include <stdio.h>
53
54#endif /* of #if defined(TARGET_KERNEL_ASP) */
55
56#if defined(TARGET_KERNEL_JSP)
57
58#include <t_services.h>
59#include "kernel_id.h"
60#include "tinet_id.h"
61
62#endif /* of #if defined(TARGET_KERNEL_JSP) */
63
64#include <netinet/in.h>
65#include <netinet/in_itron.h>
66
67#include <netapp/netapp.h>
68#include <netapp/netapp_var.h>
69#include "ntp_cli.h"
70
71/* NTP サーバのポート番号 */
72
73#define NTP_SRV_PORTNO UINT_C(123)
74
75/* NTP サーバのURL */
76
77#define NTP_SRV_URL "ntp.nict.jp"
78
79/* 送信間隔 */
80
81#define SLP_ITV (60*SYSTIM_HZ)
82
83/* 関数 */
84
85void ntp_cli_update_time(T_NTP_CLI_CONTEXT *nc);
86ER ntp_cli_time_synchronization(T_NTP_CLI_CONTEXT *nc, ntp_mode_t mode);
87void ntp_cli_read_time(T_NTP_CLI_CONTEXT *nc, struct timespec *tp);
88void ntp_cli_write_time(T_NTP_CLI_CONTEXT *nc, struct timespec *tp);
89void ntp_cli_read_data(T_NTP_CLI_CONTEXT *nc);
90
91/*
92 * 全域変数
93 */
94
95T_NTP_CLI_CONTEXT ntp_cli;
96
97/* UNIX:1970/1/1 - NTP:1900/1/1 */
98#define TIME_OFS (25567ll * 24 * 60 * 60)
99
100void set_systime_to_ntptime(struct timespec *tp, T_NTP_TIMESTAMP *timestamp)
101{
102 timestamp->integer = htonl((uint32_t)(tp->tv_sec + TIME_OFS));
103 timestamp->fraction = htonl((((unsigned long long)tp->tv_nsec) << 32) / 1000000000ll);
104}
105
106void set_ntptime_to_systime(struct timespec *tp, T_NTP_TIMESTAMP *timestamp)
107{
108 tp->tv_sec = ntohl(timestamp->integer) - TIME_OFS;
109 tp->tv_nsec = ((unsigned long long)(ntohl(timestamp->fraction) * 1000000000ll)) >> 32;
110}
111
112void add_timestamp(T_NTP_TIMESTAMP *dst, T_NTP_TIMESTAMP *a, T_NTP_TIMESTAMP *b)
113{
114 uint32_t ai = ntohl(a->integer);
115 uint32_t af = ntohl(a->fraction);
116 uint32_t bi = ntohl(b->integer);
117 uint32_t bf = ntohl(b->fraction);
118 uint32_t f = af + bf;
119 uint32_t o = ((f < af) || (f < bf)) ? 1u : 0;
120 uint32_t i = ai + bi + o;
121 dst->integer = htonl(i);
122 dst->fraction = htonl(f);
123}
124
125void sub_timestamp(T_NTP_TIMESTAMP *dst, T_NTP_TIMESTAMP *a, T_NTP_TIMESTAMP *b)
126{
127 uint32_t ai = ntohl(a->integer);
128 uint32_t af = ntohl(a->fraction);
129 uint32_t bi = ntohl(b->integer);
130 uint32_t bf = ntohl(b->fraction);
131 uint32_t o = (af < bf) ? 1u : 0u;
132 uint32_t f = af - bf;
133 uint32_t i = ai - bi - o;
134 dst->integer = htonl(i);
135 dst->fraction = htonl(f);
136}
137
138void div2_timestamp(T_NTP_TIMESTAMP *a)
139{
140 uint32_t ai = ntohl(a->integer);
141 uint32_t af = ntohl(a->fraction);
142
143 if ((ai & 1) != 0) {
144 ai >>= 1;
145 af >>= 1;
146 af |= 0x80000000u;
147 }
148 else {
149 ai >>= 1;
150 af >>= 1;
151 }
152
153 a->integer = htonl(ai);
154 a->fraction = htonl(af);
155}
156
157#define NTP_POLL_ASYNC 10 // 1024秒毎に時刻問い合わせ
158#define NTP_POLL_NORMAL 16 // 65536秒毎に時刻問い合わせ
159
160void ntp_cli_initialize(T_NTP_CLI_CONTEXT *nc, ID cepid)
161{
162 nc->cepid = cepid;
163 nc->state = NTP_CLI_STATE_ASYNC;
164 nc->timer = TMO_FEVR;
165 nc->snd_rmt.portno = NTP_SRV_PORTNO;
166 nc->snd_rmt.ipaddr = IPV4_ADDRANY;
167 nc->rcv_rmt.portno = 0;
168 nc->rcv_rmt.ipaddr = IPV4_ADDRANY;
169}
170
171void ntp_cli_set_state_changed_cb(ntp_cli_state_changed_cb_t state_changed_cb)
172{
173 ntp_cli.state_changed_cb = state_changed_cb;
174}
175
176void ntp_cli_execute()
177{
178 ntp_cli.exe_flag = 1;
179 syscall(wup_tsk(ntp_cli.tskid));
180}
181
182void ntp_cli_wakeup(T_NTP_CLI_CONTEXT *nc)
183{
184 int rcv_flag = nc->rcv_flag;
185 nc->rcv_flag = 0;
186
187 if (rcv_flag > 0) {
188 ntp_cli_read_data(nc);
189 return;
190 }
191
192 if (nc->exe_flag != 0) {
193 nc->exe_flag = 0;
194
195 nc->state = NTP_CLI_STATE_ASYNC;
196 nc->timer = 0;
197 }
198}
199
200void ntp_cli_read_data(T_NTP_CLI_CONTEXT *nc)
201{
202 struct timespec tp;
203 T_NTP_MSG *ntp = &nc->ntp_msg;
204
205 // NTP時刻応答の場合
206 if ((ntp->mode == NTP_MODE_SERVER) || (ntp->mode == NTP_MODE_BROADCAST)) {
207 switch (nc->state) {
208 case NTP_CLI_STATE_RESOLVE_ADDR:
209 case NTP_CLI_STATE_ASYNC:
210 break;
211 case NTP_CLI_STATE_REQUEST:
212 // サーバーからの応答時刻を取得
213 memcpy(&nc->receive_timestamp, &ntp->receive_timestamp, sizeof(nc->receive_timestamp));
214 memcpy(&nc->transmit_timestamp, &ntp->transmit_timestamp, sizeof(nc->transmit_timestamp));
215
216 // 現在時刻取得
217 ntp_cli_read_time(nc, &tp);
218
219 // NTP参照時刻に現在時刻を設定
220 set_systime_to_ntptime(&tp, &nc->recv_resp_timestamp);
221
222 // 時刻を更新
223 ntp_cli_update_time(nc);
224 if (nc->state_changed_cb != NULL) {
225 nc->state_changed_cb(NTP_CLI_STATE_SYNC);
226 }
227
228 nc->state = NTP_CLI_STATE_SYNC;
229 nc->poll = NTP_POLL_NORMAL;
230 nc->timer = (1 << nc->poll) * 1000 * 1000;
231 break;
232 case NTP_CLI_STATE_SYNC:
233 break;
234 }
235 }
236 // NTP時刻要求の場合
237 else if (ntp->mode == NTP_MODE_CLIENT) {
238 // 現在時刻取得
239 ntp_cli_read_time(nc, &tp);
240
241 // NTP受信・送信時刻に現在時刻を設定
242 set_systime_to_ntptime(&tp, &nc->receive_timestamp);
243 set_systime_to_ntptime(&tp, &nc->transmit_timestamp);
244
245 // NTPパケット送信
246 ntp_cli_time_synchronization(nc, NTP_MODE_SERVER);
247 }
248}
249
250void ntp_cli_update_time(T_NTP_CLI_CONTEXT *nc)
251{
252 struct timespec tp;
253 T_NTP_TIMESTAMP dt1, dt2, ofs;
254
255 sub_timestamp(&dt1, &nc->receive_timestamp, &nc->originate_timestamp);
256 sub_timestamp(&dt2, &nc->recv_resp_timestamp, &nc->transmit_timestamp);
257 sub_timestamp(&ofs, &dt1, &dt2);
258 div2_timestamp(&ofs);
259
260 // 差が大きい場合はサーバーの送信時間を設定
261 if (ntohl(ofs.integer) > 2) {
262 memcpy(&nc->reference_timestamp, &nc->transmit_timestamp, sizeof(nc->transmit_timestamp));
263 }
264 else {
265 add_timestamp(&nc->reference_timestamp, &nc->recv_resp_timestamp, &ofs);
266 }
267
268 set_ntptime_to_systime(&tp, &nc->reference_timestamp);
269
270 ntp_cli_write_time(nc, &tp);
271
272 nc->buf[0] = '\0';
273#ifdef __NEED_struct_timespec
274 if (ctime_r(&tp.tv_sec, nc->buf) != NULL)
275#else
276 if (ctime_s(nc->buf, sizeof(nc->buf), &tp.tv_sec) == 0)
277#endif
278 {
279 /* 改行コードの削除 */
280 int len = strnlen(nc->buf, sizeof(nc->buf) - 1);
281 nc->buf[len - 1] = '\0';
282 }
283 else {
284 nc->buf[0] = '\0';
285 }
286
287 syslog(LOG_NOTICE, "[NTP CLI,%d] recv time: %s .%09u",
288 nc->cepid, nc->buf, tp.tv_nsec);
289}
290
291int ntp_cli_get_timer(T_NTP_CLI_CONTEXT *nc)
292{
293 return nc->timer;
294}
295
296void ntp_cli_progress(T_NTP_CLI_CONTEXT *nc, TMO elapse)
297{
298 if (nc->timer <= 0)
299 return;
300
301 nc->timer -= elapse;
302 if (nc->timer < 0)
303 nc->timer = 0;
304}
305
306void ntp_cli_timeout(T_NTP_CLI_CONTEXT *nc)
307{
308 struct timespec tp;
309 char *line;
310
311 if (nc->timer != 0)
312 return;
313
314 switch (nc->state) {
315 case NTP_CLI_STATE_ASYNC:
316#if defined(SUPPORT_INET6) && defined(SUPPORT_INET4)
317 line = lookup_ipaddr(&nc->ipaddr6, NTP_SRV_URL, API_PROTO_IPV4);
318 if (line == NULL || !in6_is_addr_ipv4mapped(&nc->ipaddr6)) {
319 syslog(LOG_NOTICE, "[NTP CLI,%d] sleep %d.%03u[s], unknown host.",
320 nc->cepid, SLP_ITV / SYSTIM_HZ, SLP_ITV % SYSTIM_HZ);
321 nc->timer = SLP_ITV;
322 break;
323 }
324 nc->snd_rmt.ipaddr = ntohl(nc->ipaddr6.s6_addr32[3]);
325#else /* of #if defined(SUPPORT_INET6) && defined(SUPPORT_INET4) */
326 if ((line = lookup_ipaddr(&nc->snd_rmt.ipaddr, NTP_SRV_URL, DEFAULT_API_PROTO)) == NULL) {
327 syslog(LOG_NOTICE, "[NTP CLI,%d] sleep %d.%03u[s], unknown host.",
328 nc->cepid, SLP_ITV / SYSTIM_HZ, SLP_ITV % SYSTIM_HZ);
329 nc->timer = SLP_ITV;
330 break;
331 }
332#endif /* of #if defined(SUPPORT_INET6) && defined(SUPPORT_INET4) */
333 nc->state = NTP_CLI_STATE_RESOLVE_ADDR;
334 nc->timer = 1000;
335 /* through */
336 case NTP_CLI_STATE_RESOLVE_ADDR:
337 // 現在時刻取得
338 ntp_cli_read_time(nc, &tp);
339
340 // NTP開始時刻に現在時刻を設定
341 set_systime_to_ntptime(&tp, &nc->originate_timestamp);
342 memset(&nc->receive_timestamp, 0, sizeof(nc->receive_timestamp));
343 memset(&nc->transmit_timestamp, 0, sizeof(nc->transmit_timestamp));
344
345 // NTPパケット送信
346 ntp_cli_time_synchronization(nc, NTP_MODE_CLIENT);
347
348 // NTP応答待ちに遷移、500msでリトライ
349 nc->retry = 0;
350 nc->state = NTP_CLI_STATE_REQUEST;
351 nc->timer = 500 * 1000;
352 break;
353 case NTP_CLI_STATE_REQUEST:
354 nc->retry++;
355 if (nc->retry > 3) {
356 if (nc->state_changed_cb != NULL) {
357 nc->state_changed_cb(NTP_CLI_STATE_ASYNC);
358 }
359 nc->state = NTP_CLI_STATE_ASYNC;
360 nc->poll = NTP_POLL_ASYNC;
361 nc->timer = (1 << nc->poll) * 1000 * 1000; // 1024秒後に時刻同期
362 break;
363 }
364 // 現在時刻取得
365 ntp_cli_read_time(nc, &tp);
366
367 // NTP開始時刻に現在時刻を設定
368 set_systime_to_ntptime(&tp, &nc->originate_timestamp);
369 memset(&nc->receive_timestamp, 0, sizeof(nc->receive_timestamp));
370 memset(&nc->transmit_timestamp, 0, sizeof(nc->transmit_timestamp));
371
372 // NTPパケット送信
373 ntp_cli_time_synchronization(nc, NTP_MODE_CLIENT);
374
375 // NTP応答待ちに遷移、500msでリトライ
376 nc->state = NTP_CLI_STATE_REQUEST;
377 nc->timer = 500 * 1000;
378 break;
379 case NTP_CLI_STATE_SYNC:
380 // 現在時刻取得
381 ntp_cli_read_time(nc, &tp);
382
383 // NTP開始時刻に現在時刻を設定
384 set_systime_to_ntptime(&tp, &nc->originate_timestamp);
385 memset(&nc->receive_timestamp, 0, sizeof(nc->receive_timestamp));
386 memset(&nc->transmit_timestamp, 0, sizeof(nc->transmit_timestamp));
387
388 // NTPパケット送信
389 ntp_cli_time_synchronization(nc, NTP_MODE_CLIENT);
390
391 // NTP応答待ちに遷移、500msでリトライ
392 nc->state = NTP_CLI_STATE_REQUEST;
393 nc->timer = 500 * 1000;
394 break;
395 }
396}
397
398ER ntp_cli_time_synchronization(T_NTP_CLI_CONTEXT *nc, ntp_mode_t mode)
399{
400 ER_UINT len = sizeof(nc->ntp_msg);
401 T_NTP_MSG *ntp = &nc->ntp_msg;
402 ER error;
403
404 memset(ntp, 0, len);
405
406 ntp->leap_indicator = 0;
407 ntp->version_number = 4;
408 ntp->mode = mode;
409 ntp->stratum = (mode == NTP_MODE_CLIENT) ? 2 : 1;
410 ntp->poll = nc->poll;
411 ntp->precision = 0xfa; // 精度は10^-6(1μs)
412 ntp->root_delay = htonl(0);
413 ntp->root_dispersion = htonl(0);
414 // 同期のための外部的な手段のないサブネット
415 ntp->reference_identifier[0] = 'L';
416 ntp->reference_identifier[1] = 'O';
417 ntp->reference_identifier[2] = 'C';
418 ntp->reference_identifier[3] = 'L';
419 // 参照タイムスタンプ
420 memcpy(&ntp->reference_timestamp, &nc->reference_timestamp, sizeof(ntp->reference_timestamp));
421 // 開始タイムスタンプ
422 memcpy(&ntp->originate_timestamp, &nc->originate_timestamp, sizeof(ntp->originate_timestamp));
423 // 受信タイムスタンプ
424 memcpy(&ntp->receive_timestamp, &nc->receive_timestamp, sizeof(ntp->receive_timestamp));
425 // 送信タイムスタンプ
426 memcpy(&ntp->transmit_timestamp, &nc->transmit_timestamp, sizeof(ntp->transmit_timestamp));
427 // 鍵識別子
428 ntp->key_identifier[0] = 0x08;
429 ntp->key_identifier[1] = 0x18;
430 ntp->key_identifier[2] = 0x00;
431 ntp->key_identifier[3] = 0x00;
432
433 if ((error = udp_snd_dat(nc->cepid, &nc->snd_rmt, ntp, len, TMO_NBLK)) != E_WBLK) {
434 syslog(LOG_NOTICE, "[NTP CLI,%d] udp_snd_dat error: %s",
435 nc->cepid, itron_strerror(error));
436 return error;
437 }
438 else
439 syslog(LOG_DEBUG, "[NTP CLI,%d] udp_snd_dat: to: %s.%d",
440 nc->cepid, ip2str(NULL, &nc->snd_rmt.ipaddr), nc->snd_rmt.portno);
441
442 return E_OK;
443}
444
445void ntp_cli_read_time(T_NTP_CLI_CONTEXT *nc, struct timespec *tp)
446{
447 SYSTIM time;
448 ER ret;
449
450 ret = get_tim(&time);
451 if (ret != E_OK) {
452 syslog(LOG_NOTICE, "[NTP CLI,%d] get_tim error: %s",
453 nc->cepid, itron_strerror(ret));
454 tp->tv_sec = 0;
455 tp->tv_nsec = 0;
456 return;
457 }
458
459 tp->tv_sec = time / 1000000ll;
460 tp->tv_nsec = (time % 1000000ll) * 1000ll;
461}
462
463void ntp_cli_write_time(T_NTP_CLI_CONTEXT *nc, struct timespec *tp)
464{
465 SYSTIM time;
466 ER ret;
467
468 time = (tp->tv_sec * 1000000ll) + (tp->tv_nsec / 1000ll);
469
470 ret = set_tim(time);
471 if (ret != E_OK) {
472 syslog(LOG_NOTICE, "[NTP CLI,%d] set_tim error: %s",
473 nc->cepid, itron_strerror(ret));
474 }
475}
476
477/*
478 * ノンブロッキングコールのコールバック関数
479 */
480
481ER
482callback_nblk_ntp_cli (ID cepid, FN fncd, void *p_parblk)
483{
484 T_NTP_CLI_CONTEXT *nc = &ntp_cli;
485 ER_UINT len;
486
487 len = *(ER_UINT*)p_parblk;
488 if (len < 0 && len != E_RLWAI) {
489 /* E_RLWAI 以外で、0 以下の場合は、エラーを意味している。*/
490 syslog(LOG_NOTICE, "[NTP CLI,%d] callback error: %s, fncd: %s", nc->cepid,
491 itron_strerror(len), in_strtfn(fncd));
492 }
493 else {
494 if (fncd == TEV_UDP_RCV_DAT) {
495 if ((len = udp_rcv_dat(nc->cepid, &nc->rcv_rmt, &nc->ntp_msg, len, TMO_POL)) < 0)
496 {
497 syslog(LOG_NOTICE, "[NTP CLI,%d] udp_rcv_dat error: %s", nc->cepid,
498 itron_strerror(len));
499 }
500 else
501 syslog(LOG_DEBUG, "[NTP CLI,%d] udp_rcv_dat len: %d", nc->cepid, len);
502
503 if (len >= sizeof(nc->ntp_msg))
504 nc->rcv_flag = len;
505 else
506 nc->rcv_flag = 0;
507 }
508 syscall(wup_tsk(nc->tskid));
509 }
510
511 return E_OK;
512}
513
514#ifdef NTP_CLI_TASK
515
516/*
517 * NTP クライアント送信タスク
518 */
519
520void
521ntp_cli_task (intptr_t exinf)
522{
523 T_NTP_CLI_CONTEXT *nc = &ntp_cli;
524 ER error, ret;
525 SYSTIM prev, time;
526 int timer;
527
528 get_tid(&nc->tskid);
529 syslog(LOG_NOTICE, "[NTP CLI:%d,%d] started.", nc->tskid, (ID)exinf);
530
531 /* 初期化 */
532 ntp_cli_initialize(nc, (ID)exinf);
533
534 ret = get_tim(&time);
535 if (ret != E_OK) {
536 syslog(LOG_NOTICE, "[NTP CLI,%d] get_tim error: %7lu,%s",
537 nc->cepid, time / SYSTIM_HZ, itron_strerror(ret));
538 return;
539 }
540
541 while (true) {
542 prev = time;
543
544 /* タイマー取得 */
545 timer = ntp_cli_get_timer(nc);
546
547 /* 待ち */
548 error = tslp_tsk(timer);
549 if ((error != E_OK) && (error != E_TMOUT)) {
550 syslog(LOG_NOTICE, "[NTP CLI,%d] tslp_tsk error: %s %d",
551 nc->cepid, itron_strerror(error), timer);
552 break;
553 }
554
555 ret = get_tim(&time);
556 if (ret != E_OK) {
557 syslog(LOG_NOTICE, "[NTP CLI,%d] get_tim error: %s",
558 nc->cepid, itron_strerror(ret));
559 break;
560 }
561
562 /* 時間経過 */
563 ntp_cli_progress(nc, time - prev);
564
565 /* 起こされた場合 */
566 if (error == E_OK) {
567 ntp_cli_wakeup(nc);
568 }
569
570 /* タイムアウト処理 */
571 ntp_cli_timeout(nc);
572 }
573}
574
575#endif
Note: See TracBrowser for help on using the repository browser.