source: asp3_tinet_ecnl_rx/trunk/ntshell/src/ntp_cli.c@ 340

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

NTPクライアント処理を追加

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