doubao_chat/lib/net/libduer-device/modules/ntp/lightduer_net_ntp.c

334 lines
10 KiB
C

/**
* Copyright (2017) Baidu Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: Chang Li (changli@baidu.com)
//
// Description: NTP client API implementation.
/**
* NTP timestamp format,
* coarse: integer part (uint32)
* fine : fraction part (uint32)
*
* NTP packet format,
* |0 1|2 4|5 7|8 15|16 23|24 31|
* |LI |VN |Mode|Stratum |Poll |Precision|
* | Root Delay (32 bits) |
* | Root Dispersion (32 bits) |
* | Reference Identifier(32 bits) |
* | Reference Timestamp (64 bits) |
* | Originate Timestamp (64 bits) |
* | Receive Timestamp (64 bits) |
* | Transmit Timestamp (54 bits) |
* | (opt) Auth KeyID (32 bits) |
* | (opt) MAC (128 bits) |
*/
#include "lightduer_net_ntp.h"
#include "lightduer_lib.h"
#include "lightduer_timestamp.h"
#include "lightduer_log.h"
#include "lightduer_sleep.h"
#include "lightduer_net_transport_wrapper.h"
#include "lightduer_net_util.h"
#include "mbedtls/md5.h"
//#define DUER_DEBUG_LEVEL 4
#define NTP_SERVER "s1a.time.edu.cn" // 202.112.10.60
#define NTP_PORT (123)
#define JAN_1970 (0x83aa7e80) // 2208988800 1970 - 1900 in seconds
#define LI (0) // Leap Indicator, normal
#define VN (3) // Version, v3
#define MODE (3) // Mode, client mode
#define STRATUM (0) // Stratum, 0 for unspecified.
#define POLL (4) // Poll Interval, log2 interval(32 sec)
#define PREC (-6) // Precision, nearest power of two(64 ms)
/* Compute the micro second part to NTP fraction.
* i.e. ( usec / 1e6 * UINT_MAX), UINT_MAX is 4294967296
* Multiply by 4294.967296 quickly while not quite exactly.
*/
#define NTPFRAC(x) ( 4294 * (x) + ( (1981 * (x)) >> 11 ) )
/* Compute the NTP fraction part to micro seconds.
* The reverse of NTPFRAC
*/
#define USEC(x) ( ( (x) >> 12 ) - 759 * ( ( ( (x) >> 10 ) + 32768 ) >> 16 ) )
const int RECV_TIMEOUT = 2000; // 2000 ms by default.
typedef struct _NtpTime {
uint32_t coarse;
uint32_t fine;
} NtpTime;
static void ntp_get_current_time(NtpTime *time);
static void ntp_set_time(NtpTime *new_time, uint32_t adjust_time,
DuerTime *result_time);
static int ntp_send_packet(duer_trans_handler hdlr, const duer_addr_t *addr, Md5Key *key);
static int ntp_rfc1305_calc_set(uint32_t *data, NtpTime *arrival,
DuerTime *result_time);
/*
* @Brief: generate message digest and append it to packet.
* @Param key: the key string.
* @Param pkt: packet which contains key ID and digest.
* @Return: .
*/
static int md5_authencrypt(
const char *key, /* key pointer */
unsigned int *pkt, /* packet pointer */
int length /* packet length 48B*/
)
{
unsigned char digest[MD5_DIGEST_LEN] = {0};
mbedtls_md5_context ctx;
mbedtls_md5_init(&ctx);
mbedtls_md5_starts(&ctx);
mbedtls_md5_update(&ctx, (const duer_u8_t *)key, DUER_STRLEN(key));
mbedtls_md5_update(&ctx, (duer_u8_t *)pkt, length);
mbedtls_md5_finish(&ctx, digest);
mbedtls_md5_free(&ctx);
#ifdef NTP_DEBUG
DUER_LOGD("###digest=\n");
int i = 0;
for (i = 0; i < MD5_DIGEST_LEN; i++) {
DUER_LOGD("%02x ", digest[i]);
}
DUER_LOGD("###\n");
#endif
DUER_MEMMOVE((duer_u8_t *)pkt + length + MD5_KEYID_LEN, digest, sizeof(digest));
return 0;
}
/* return the value in micro seconds */
static uint32_t ntp_diff(NtpTime *start, NtpTime *stop)
{
int a = 0;
uint32_t b = 0;
uint32_t offset = 0;
a = stop->coarse - start->coarse;
if (stop->fine >= start->fine) {
b = stop->fine - start->fine;
} else {
b = start->fine - stop->fine;
b = ~b;
a -= 1;
}
offset = a * 1000000 + USEC(b);
return offset;
}
void ntp_get_current_time(NtpTime *time)
{
uint32_t ts = duer_timestamp();
DUER_LOGD("%s: Timestamp=%ld\n", __FUNCTION__, ts);
time->coarse = ts / 1000;
time->fine = NTPFRAC((ts % 1000) * 1000);
DUER_LOGD("%s: current_time=%u, %u.\n", __FUNCTION__, time->coarse, time->fine);
}
void ntp_set_time(NtpTime *new_time, uint32_t adjust_time,
DuerTime *result_time)
{
result_time->sec = new_time->coarse - JAN_1970 + adjust_time / 1000000;
result_time->usec = USEC(new_time->fine);
result_time->usec += adjust_time % 1000000;
if (result_time->usec > 999999) {
result_time->usec -= 1000000;
result_time->sec += 1;
}
}
int ntp_rfc1305_calc_set(uint32_t *data, NtpTime *arrival,
DuerTime *result_time)
{
int li = 0;
int vn = 0;
int mode = 0;
int stratum = 0;
int poll = 0;
int prec = 0;
int delay = 0;
int disp = 0;
int refid = 0;
uint32_t el_time = 0;
uint32_t st_time = 0;
uint32_t adjust_time = 0;
int ret = -1;
NtpTime reftime;
NtpTime orgtime;
NtpTime rectime;
NtpTime xmttime;
ALLOW_UNUSED_LOCAL(reftime);
ALLOW_UNUSED_LOCAL(ret);
ALLOW_UNUSED_LOCAL(refid);
ALLOW_UNUSED_LOCAL(disp);
ALLOW_UNUSED_LOCAL(delay);
ALLOW_UNUSED_LOCAL(poll);
ALLOW_UNUSED_LOCAL(stratum);
ALLOW_UNUSED_LOCAL(mode);
ALLOW_UNUSED_LOCAL(vn);
ALLOW_UNUSED_LOCAL(li);
li = duer_ntohl(((uint32_t *)data)[0]) >> 30 & 0x03;
vn = duer_ntohl(((uint32_t *)data)[0]) >> 27 & 0x07;
mode = duer_ntohl(((uint32_t *)data)[0]) >> 24 & 0x07;
stratum = duer_ntohl(((uint32_t *)data)[0]) >> 16 & 0xff;
poll = duer_ntohl(((uint32_t *)data)[0]) >> 8 & 0xff;
prec = duer_ntohl(((uint32_t *)data)[0]) & 0xff;
if (prec & 0x80) {
prec |= 0xffffff00;
}
delay = duer_ntohl(((uint32_t *)data)[1]);
disp = duer_ntohl(((uint32_t *)data)[2]);
refid = duer_ntohl(((uint32_t *)data)[3]);
reftime.coarse = duer_ntohl(((uint32_t *)data)[4]);
reftime.fine = duer_ntohl(((uint32_t *)data)[5]);
orgtime.coarse = duer_ntohl(((uint32_t *)data)[6]);
orgtime.fine = duer_ntohl(((uint32_t *)data)[7]);
rectime.coarse = duer_ntohl(((uint32_t *)data)[8]);
rectime.fine = duer_ntohl(((uint32_t *)data)[9]);
xmttime.coarse = duer_ntohl(((uint32_t *)data)[10]);
xmttime.fine = duer_ntohl(((uint32_t *)data)[11]);
DUER_LOGD("%s: li=%d, vn=%d, mode=%d, stratum=%d, poll=%d, prec=%d;\n"
"delay=%d, disp=%d, refid=%d;\n"
"xmttime=%u,%u\n", __FUNCTION__, li, vn, mode, stratum, poll, prec, delay, disp,
refid, xmttime.coarse, xmttime.fine);
el_time = ntp_diff(&orgtime, arrival);
st_time = ntp_diff(&rectime, &xmttime);
adjust_time = (el_time - st_time) / 2;
ntp_set_time(&xmttime, adjust_time, result_time);
return 0;
}
int ntp_send_packet(duer_trans_handler hdlr, const duer_addr_t *addr, Md5Key *key)
{
uint32_t data[NTP_PKG_LEN / sizeof(uint32_t) + NTP_MAC_LEN / sizeof(uint32_t)];
NtpTime time_sent;
int ret = -1;
duer_size_t size = NTP_PKG_LEN;
DUER_MEMSET(data, 0, sizeof(data));
data[0] = duer_htonl((LI << 30) | (VN << 27) | (MODE << 24)
| (STRATUM << 16) | (POLL << 8) | (PREC & 0xff));
data[1] = duer_htonl(1 << 16); // Root Delay (seconds)
data[2] = duer_htonl(1 << 16); // Root Dispersion (seconds)
ntp_get_current_time(&time_sent);
data[10] = duer_htonl(time_sent.coarse); // Transmit Timestamp coarse
data[11] = duer_htonl(time_sent.fine); // Transmit Timestamp fine
if (key != NULL) {
data[12] = duer_htonl(key->key_id); /* KeyID */
md5_authencrypt(key->key_str, data, NTP_PKG_LEN);
size = NTP_PKG_LEN + NTP_MAC_LEN;
}
ret = duer_trans_send(hdlr, data, size, addr);
return ret;
}
int duer_ntp_client(char *host, int timeout, DuerTime *result_time, Md5Key *key)
{
duer_trans_handler hdlr = NULL;
duer_addr_t svr_addr;
uint32_t incoming_data[255] = {0};
NtpTime time_recv;
int ret = -1;
if (host == NULL) {
svr_addr.host = NTP_SERVER;
svr_addr.host_size = DUER_STRLEN(NTP_SERVER);
} else {
svr_addr.host = host;
svr_addr.host_size = DUER_STRLEN(host);
}
svr_addr.port = NTP_PORT;
svr_addr.type = DUER_PROTO_UDP;
hdlr = duer_trans_acquire(NULL, NULL);
duer_trans_connect(hdlr, &svr_addr);
ret = ntp_send_packet(hdlr, &svr_addr, key); //svr_addr is useless?
DUER_LOGD("%s: send return=%d.\n", __FUNCTION__, ret);
if ((ret != NTP_PKG_LEN) && (ret != NTP_PKG_LEN + NTP_MAC_LEN)) {
DUER_LOGW("send nok, ret=%d, should resend.", ret);
duer_trans_close(hdlr);
return -1;
}
int wait_time = 0; // ms
if (timeout == 0) {
timeout = RECV_TIMEOUT;
}
while (1) {
ret = duer_trans_recv(hdlr, incoming_data, sizeof(incoming_data),
&svr_addr); //svr_addr is useless?
DUER_LOGD("%s: receive return=%d.\n", __FUNCTION__, ret);
if (ret < 0) {
DUER_LOGD("recv nok, wait for 500 ms.");
wait_time += 500;
if (wait_time > timeout) {
break;
}
duer_sleep(500);
continue;
}
ntp_get_current_time(&time_recv);
ret = ntp_rfc1305_calc_set(incoming_data, &time_recv, result_time);
DUER_LOGD("%s: calc_set return=%d.\n", __FUNCTION__, ret);
if (ret == 0) {
DUER_LOGD("NTP: success.\n");
DUER_LOGI("%s: result time=%u, %u\n", __FUNCTION__, result_time->sec,
result_time->usec);
break;
} else {
DUER_LOGE("%s: NTP received data, but not ok.\n", __FUNCTION__);
break;
}
}
duer_trans_close(hdlr);
return ret;
}