2025-04-11 09:40:32 +08:00

608 lines
18 KiB
C

/******************************************************************************
* (C) Copyright 2018 EIGENCOMM International Ltd.
* All Rights Reserved
*******************************************************************************
* Filename: sntp.c
*
* Description: sntp
*
* History:creat by xwang
*
* Notes:
*
******************************************************************************/
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "posix/netdb.h"
#include "lwip/api.h"
#include "lwip/priv/api_msg.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "appmgr.h"
#include "networkmgr.h"
#include DEBUG_LOG_HEADER_FILE
#if (defined CHIP_EC618) || (defined CHIP_EC718) || (defined CHIP_EC716)
#include "slpman.h"
#elif defined CHIP_EC617
#include "slpman_ec617.h"
#endif
#include "sntp.h"
#define SNTP_THREAD_STACKSIZE 2048
#define SNTP_THREAD_PRIO 16
#define SNTP_ERR_KOD 1
/* SNTP protocol defines */
#define SNTP_MSG_LEN 48
#define SNTP_OFFSET_LI_VN_MODE 0
#define SNTP_LI_MASK 0xC0
#define SNTP_LI_NO_WARNING 0x00
#define SNTP_LI_LAST_MINUTE_61_SEC 0x01
#define SNTP_LI_LAST_MINUTE_59_SEC 0x02
#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
#define SNTP_VERSION_MASK 0x38
#define SNTP_VERSION (4/* NTP Version 4*/<<3)
/* SNTP operating modes: default is to poll using unicast.
The mode has to be set before calling sntp_init(). */
#define SNTP_OPMODE_POLL 0
#define SNTP_OPMODE_LISTENONLY 1
#define SNTP_MODE_MASK 0x07
#define SNTP_MODE_CLIENT 0x03
#define SNTP_MODE_SERVER 0x04
#define SNTP_MODE_BROADCAST 0x05
#define SNTP_OFFSET_STRATUM 1
#define SNTP_STRATUM_KOD 0x00
#define SNTP_OFFSET_ORIGINATE_TIME 24
#define SNTP_OFFSET_RECEIVE_TIME 32
#define SNTP_OFFSET_TRANSMIT_TIME 40
/* number of seconds between 1900 and 1970 (MSB=1)*/
#define DIFF_SEC_1900_1970 (2208988800UL)
/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
#define DIFF_SEC_1970_2036 (2085978496UL)
#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0)
/**
* SNTP packet format (without optional fields)
* Timestamps are coded as 64 bits:
* - 32 bits seconds since Jan 01, 1970, 00:00
* - 32 bits seconds fraction (0-padded)
* For future use, if the MSB in the seconds part is set, seconds are based
* on Feb 07, 2036, 06:28:16.
*/
#ifdef PACK_STRUCT_USE_INCLUDES
#include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct SntpMsg {
PACK_STRUCT_FLD_8(u8_t li_vn_mode);
PACK_STRUCT_FLD_8(u8_t stratum);
PACK_STRUCT_FLD_8(u8_t poll);
PACK_STRUCT_FLD_8(u8_t precision);
PACK_STRUCT_FIELD(u32_t root_delay);
PACK_STRUCT_FIELD(u32_t root_dispersion);
PACK_STRUCT_FIELD(u32_t reference_identifier);
PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
#include "arch/epstruct.h"
#endif
#if 0
struct SntpMsg {
UINT8 li_vn_mode;
UINT8 stratum;
UINT8 poll;
UINT8 precision;
UINT32 root_delay;
UINT32 root_dispersion;
UINT32 reference_identifier;
UINT32 reference_timestamp[2];
UINT32 originate_timestamp[2];
UINT32 receive_timestamp[2];
UINT32 transmit_timestamp[2];
};
#endif
typedef struct SntpConfig_Tag{
UINT16 serverPort;
UINT16 reqHandler;
UINT16 autoSync;
UINT8 cid;
UINT8 rsvd;
CHAR serverAddr[DNS_MAX_NAME_LENGTH];
}SntpConfig;
static NET_ZI_DEF BOOL sntpIsOngoing = FALSE;
static NET_ZI_DEF sys_thread_t sntpThread = NULL;
static NET_ZI_DEF BOOL sntpTerminalFlag = FALSE;
static NET_ZI_DEF SntpConfig gSntpConfig;
/** Saves the last timestamp sent (which is sent back by the server)
* to compare against in response */
static NET_ZI_DEF UINT32 sntpLastTimeStampSent[2];
void SntpCallResultCallback(UINT32 result, UINT32 seconds, UINT32 us)
{
NmAtiSntpResultInt resultInd;
memset(&resultInd, 0, sizeof(NmAtiSntpResultInt));
resultInd.result = result;
resultInd.autoSync = gSntpConfig.autoSync;
resultInd.time = seconds;
resultInd.us = us;
AppMgrSendSntpResultInd(&resultInd, gSntpConfig.reqHandler);
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpCallResultCallback_1, P_INFO, 1, "SntpCallResultCallback result %u", result);
}
BOOL SntpRecvResponse(struct SntpMsg *response, UINT32 *seconds, UINT32 *useconds)
{
UINT8 mode;
UINT8 stratum;
UINT32 rxSecs;
int is1900Based;
if(response == NULL || seconds == NULL || useconds == NULL)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpRecvResponse_1, P_WARNING, 0, "SntpRecvResponse: invalid parameter");
return FALSE;
}
//init seconds and useconds
*seconds = 0;
*useconds = 0;
//parse the sntp response message
mode = response->li_vn_mode;
mode &= SNTP_MODE_MASK;
/* if this is a SNTP response... */
if (mode == SNTP_MODE_SERVER || mode == SNTP_MODE_BROADCAST)
{
stratum = response->stratum;
if (stratum == SNTP_STRATUM_KOD)
{
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpRecvResponse_2, P_WARNING, 0, "SntpRecvResponse: Received Kiss-of-Death");
}
else
{
/* check originate_timetamp against sntp_last_timestamp_sent */
if ((response->originate_timestamp[0] != sntpLastTimeStampSent[0]) ||
(response->originate_timestamp[1] != sntpLastTimeStampSent[1]))
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpRecvResponse_3, P_WARNING, 0,
"SntpRecvResponse: Invalid originate timestamp in response");
}
else
{
/* correct answer */
rxSecs = ntohl(response->receive_timestamp[0]);
is1900Based = ((rxSecs & 0x80000000) != 0);
*seconds = is1900Based ? (rxSecs - DIFF_SEC_1900_1970) : (rxSecs + DIFF_SEC_1970_2036);
*useconds = ntohl(response->receive_timestamp[1]) / 4295;
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpRecvResponse_4, P_INFO, 2,
"SntpRecvResponse: correct response seconds %u,useconds %u", *seconds, *useconds);
return TRUE;
}
}
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, sntp_recv_3, P_WARNING, 1,
"sntp_recv: Invalid mode in response: %u", mode);
}
return FALSE;
}
BOOL SntpSendRequest(INT32 sockFd, ip_addr_t *serverAddr, UINT16 ServerPort)
{
struct sockaddr_storage to;
INT32 ret;
struct SntpMsg sntpReq;
UINT32 sntpTimeSec;
UINT32 sntpTimeUs;
//check paramter
if(sockFd < 0 || serverAddr == NULL)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpSendRequest_1, P_ERROR, 0, "SntpSendRequest parameter invalid");
return FALSE;
}
if (IP_IS_V4(serverAddr))
{
struct sockaddr_in *to4 = (struct sockaddr_in*)&to;
to4->sin_len = sizeof(struct sockaddr_in);
to4->sin_family = AF_INET;
to4->sin_port = htons(ServerPort);
inet_addr_from_ip4addr(&to4->sin_addr, ip_2_ip4(serverAddr));
}
else if(IP_IS_V6(serverAddr))
{
struct sockaddr_in6 *to6 = (struct sockaddr_in6*)&to;
to6->sin6_len = sizeof(struct sockaddr_in6);
to6->sin6_family = AF_INET6;
to6->sin6_port = htons(ServerPort);
inet6_addr_from_ip6addr(&to6->sin6_addr, ip_2_ip6(serverAddr));
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpSendRequest_2, P_ERROR, 0, "SntpSendRequest parameter invalid");
return FALSE;
}
//prepare sntp request message
memset(&sntpReq, 0, SNTP_MSG_LEN);
sntpReq.li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
/* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
SNTP_GET_SYSTEM_TIME(sntpTimeSec, sntpTimeUs);
sntpLastTimeStampSent[0] = htonl(sntpTimeSec + DIFF_SEC_1900_1970);
sntpReq.transmit_timestamp[0] = sntpLastTimeStampSent[0];
/* we send/save us instead of fraction to be faster... */
sntpLastTimeStampSent[1] = htonl(sntpTimeUs);
sntpReq.transmit_timestamp[1] = sntpLastTimeStampSent[1];
//send sntp client reqeust
ret = sendto(sockFd, &sntpReq, SNTP_MSG_LEN, 0, (struct sockaddr*)&to, sizeof(to));
if(ret == SNTP_MSG_LEN)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpSendRequest_4, P_INFO, 0, "SntpSendRequest send sntp request success");
return TRUE;
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpSendRequest_5, P_ERROR, 0, "SntpSendRequest send sntp request fail");
}
return FALSE;
}
static void SntpTask(void *arg)
{
INT32 ret;
UINT32 result = SNTP_RESULT_FAIL;
slpManRet_t pmuRet = RET_UNKNOW_FALSE;
UINT8 sntpPmuHdr = 0;
ip_addr_t sntpServerAddr;
INT32 sockFd = -1;
UINT8 retryTimes = 0;
UINT32 secondsResponse;
UINT32 usecondsResponse;
struct SntpMsg sntpResponse;
NmAtiNetifInfo netInfo;
struct addrinfo hints, *target_address = PNULL;
SntpConfig *sntpParam = PNULL;
sntpParam = (SntpConfig*)arg;
//check sntp parameter
if (!sntpParam)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_1, P_ERROR, 0, "SntpTask start error, as sntp parameter invalid");
sntpIsOngoing = FALSE;
sntpThread = NULL;
sntpTerminalFlag = FALSE;
SntpCallResultCallback(SNTP_RESULT_PARAMTER_INVALID, 0, 0);
osThreadExit();
}
//check pdp context status
if(NetMgrGetNetInfo(sntpParam->cid, &netInfo) != NM_SUCCESS)
{
ECOMM_TRACE(UNILOG_ATCMD_SOCK, sntp_netinfo_check_1, P_INFO, 1, "sntp get cid %d net info fail", sntpParam->cid);
sntpIsOngoing = FALSE;
sntpThread = NULL;
sntpTerminalFlag = FALSE;
SntpCallResultCallback(SNTP_RESULT_PDP_CONTEXT_STATUS_INVALID, 0, 0);
osThreadExit();
}
else
{
if(netInfo.netStatus <= NM_NO_NETIF_OR_DEACTIVATED)
{
ECOMM_TRACE(UNILOG_ATCMD_SOCK, sntp_netinfo_check_2, P_INFO, 2, "sntp get cid %d net status %d is invalid ", sntpParam->cid, netInfo.netStatus);
sntpIsOngoing = FALSE;
sntpThread = NULL;
sntpTerminalFlag = FALSE;
SntpCallResultCallback(SNTP_RESULT_PDP_CONTEXT_STATUS_INVALID, 0, 0);
osThreadExit();
}
}
/*
* start sntp, sntp task don't allow to enter HIB/Sleep2 state before ping terminated
*/
pmuRet = slpManApplyPlatVoteHandle("sntp", &sntpPmuHdr);
OsaCheck(pmuRet == RET_TRUE, pmuRet, 0, 0);
pmuRet = slpManPlatVoteDisableSleep(sntpPmuHdr, SLP_SLP2_STATE);
OsaCheck(pmuRet == RET_TRUE, pmuRet, 0, 0);
//get ping target address if target is url type
if(ipaddr_aton((const char*)sntpParam->serverAddr, &sntpServerAddr) == 0)
{
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
// hints.ai_socktype = SOCK_DGRAM;
// hints.ai_protocol = IPPROTO_UDP;
if (getaddrinfowithcid(sntpParam->serverAddr, NULL, &hints, &target_address, sntpParam->cid) != 0)
{
// OsaPrintf(P_ERROR, "Ping thread start error, can not get ip from url");
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_2, P_ERROR, 0, "SntpTask start error, can not get ip from url");
result = SNTP_RESULT_URL_RESOLVE_FAIL;
goto failAction;
}
else
{
if (target_address->ai_family == AF_INET)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_3, P_SIG, 0, "SntpTaskresolve url success, it is ipv4 type");
struct sockaddr_in *to = (struct sockaddr_in *)target_address->ai_addr;
IP_SET_TYPE(&sntpServerAddr, IPADDR_TYPE_V4);
inet_addr_to_ip4addr(ip_2_ip4(&sntpServerAddr), &to->sin_addr);
}
else if (target_address->ai_family == AF_INET6)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_4, P_SIG, 0, "SntpTask resolve url success, it is ipv6 type");
struct sockaddr_in6 *to = (struct sockaddr_in6 *)target_address->ai_addr;
IP_SET_TYPE(&sntpServerAddr, IPADDR_TYPE_V6);
inet6_addr_to_ip6addr(ip_2_ip6(&sntpServerAddr),&to->sin6_addr);
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_5, P_ERROR, 0, "SntpTask start error, get ip from url is invalid");
freeaddrinfo(target_address);
result = SNTP_RESULT_URL_RESOLVE_FAIL;
goto failAction;
}
freeaddrinfo(target_address);
}
}
#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
int timeout = SNTP_RCV_TIMEOUT;
#else
struct timeval timeout;
timeout.tv_sec = SNTP_RCV_TIMEOUT;
timeout.tv_usec = 0;
#endif
if (IP_IS_V4(&sntpServerAddr))
{
sockFd = lwip_socket(AF_INET, SOCK_DGRAM, IP_PROTO_UDP);
}
else
{
sockFd = lwip_socket(AF_INET6, SOCK_DGRAM, IP_PROTO_UDP);
}
if (sockFd < 0)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_6, P_ERROR, 0, "SntpTask socket create error");
result = SNTP_RESULT_CREATE_CLIENT_SOCK_FAIL;
goto failAction;
}
if(sntpParam->cid != LWIP_PS_INVALID_CID)
{
if(lwip_bind_cid(sockFd, sntpParam->cid) != 0)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_bind_cid_1, P_ERROR, 0, "SntpTask socket create with cid fail");
result = SNTP_RESULT_CREATE_CLIENT_SOCK_FAIL;
goto failAction;
}
}
ret = lwip_setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
if (ret != 0)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_7, P_ERROR, 0, "SntpTask setting receive timeout failed");
result = SNTP_RESULT_CREATE_CLIENT_SOCK_FAIL;
goto failAction;
}
LWIP_UNUSED_ARG(ret);
while ((retryTimes < SNTP_RETRY_TIMES) && (sntpTerminalFlag == FALSE))
{
if (SntpSendRequest(sockFd, &sntpServerAddr, sntpParam->serverPort) == TRUE)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_8, P_SIG, 0, "SntpTask: send request success");
memset(&sntpResponse, 0, sizeof(struct SntpMsg));
ret = recv(sockFd, &sntpResponse, SNTP_MSG_LEN, 0);
if(ret == SNTP_MSG_LEN)
{
if(SntpRecvResponse(&sntpResponse, &secondsResponse, &usecondsResponse) == TRUE)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_9, P_INFO, 0, "SntpTask: rcv response success");
result = SNTP_RESULT_OK;
break;
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_10, P_WARNING, 0, "SntpTask: rcv response, but process fail");
}
}
else
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_11, P_WARNING, 0, "SntpTask: rcv response fail");
}
}
else // send request fail, try again
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_12, P_WARNING, 0, "SntpTask: send fail, need rty again");
sys_msleep(SNTP_RESEND_REQUEST_DELAY);
}
retryTimes ++;
}
if(retryTimes >= SNTP_RETRY_TIMES)
{
result = SNTP_RESULT_RETRY_REACH_MAX_TIMES;
}
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpTask_13, P_SIG, 0, "sntp task terminal");
failAction:
sntpIsOngoing = FALSE;
sntpThread = NULL;
sntpTerminalFlag = FALSE;
if(sockFd >= 0)
{
lwip_close(sockFd);
}
if(result != SNTP_RESULT_OK)
{
SntpCallResultCallback(result, 0, 0);
}
else
{
SntpCallResultCallback(SNTP_RESULT_OK, secondsResponse, usecondsResponse);
}
/* before ping task exit, should allow to enter HIB/Sleep2, and release hander */
pmuRet = slpManPlatVoteEnableSleep(sntpPmuHdr, SLP_SLP2_STATE);
OsaCheck(pmuRet == RET_TRUE, pmuRet, 0, 0);
pmuRet = slpManGivebackPlatVoteHandle(sntpPmuHdr);
OsaCheck(pmuRet == RET_TRUE, pmuRet, 0, 0);
osThreadExit();
}
void SntpTerminat(void)
{
if(sntpThread)
{
sntpTerminalFlag = TRUE;
}
return;
}
BOOL SntpInit(CHAR* serverAddr, UINT16 serverPort, UINT16 reqHandler, BOOL autoSync, UINT8 cid)
{
/* check the input */
if(!serverAddr || strlen(serverAddr) > DNS_MAX_NAME_LENGTH || strlen(serverAddr) == 0)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpInit_1, P_ERROR, 0,
"ERROR, SntpInit PARAMETER invalid");
return FALSE;
}
if (sntpIsOngoing)
{
ECOMM_TRACE(UNILOG_TCPIP_APP, SntpInit_2, P_ERROR, 0,
"ERROR, SntpInit has run");
return FALSE;
}
/* if set it in "sntp" task, maybe modify gSntpConfig for mutiple sntp request */
sntpIsOngoing = TRUE;
gSntpConfig.serverPort = serverPort?serverPort:SNTP_DEFAULT_PORT;
gSntpConfig.reqHandler = reqHandler;
gSntpConfig.cid = cid;
gSntpConfig.autoSync = (UINT16)autoSync;
//ping target setting
strcpy((char *)gSntpConfig.serverAddr, serverAddr);
sntpThread = sys_thread_new("sntp_task", SntpTask, (void*)&gSntpConfig, SNTP_THREAD_STACKSIZE, SNTP_THREAD_PRIO);
if (sntpThread == NULL)
{
sntpIsOngoing = FALSE;
return FALSE;
}
return TRUE;
}
//check sntp whether running,TRUE->running, FALSE->not run
BOOL SntpChkStatus(CHAR *serverAddr, UINT16 *serverPort, UINT8 *cid)
{
if (sntpIsOngoing)
{
strcpy(serverAddr, gSntpConfig.serverAddr);
*serverPort = gSntpConfig.serverPort;
*cid = gSntpConfig.cid;
return TRUE;
}
return FALSE;
}