feat(sntp): 板端 SNTP 校时接入——coreSNTP 序列化器 + [sntp] 段 + 设置页(#73)
CI / 静态门禁 (pre-commit) (push) Failing after 5m33s
CI / 主机侧单测 (MSVC) (push) Successful in 45s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 5m20s
CI / 主机侧单测 (MSVC) (pull_request) Successful in 31s

- vendored coreSNTP v2.0.0 序列化器(MIT,仅 serializer+LICENSE,零堆),
  48 字节报文编解码在库,节奏与准入判据在本方(RFC 4330 §10 调研口径)
- App/sync/time_sntp.c 挂 timeSyncTask 的 8888 事件循环,等待切片 ≤50 ms,
  不饿任务狗与 IWDG;懒建 UDP socket,代际变化/禁用/死错误码即关闭归还
- 防伪 = OriginTimestamp 逐位等于发出时 T1(含低 16 位随机小数位);
  mode/stratum/闰秒指示/KoD 按 RFC 拒收;DENY·RSTR 换服务器、RATE 只退避
- 主备轮换 + 失败指数退避(5 s 起、60 s 封顶)+ 首轮 1~5 分钟随机错峰;
  成功恢复 poll_min 周期(1~1440 分,出厂默认 60,默认不启用不发包)
- 钟面 = 本地时间:UTC+8 固定偏移落 sys_clock_set_unix(§1.1 时基裁定,
  第三个写入口,装置仍不存时区);|offset|<2 s 只记日志不步进,冷启动或
  更大偏差步进并回写 SD2506,留痕 [SNTP] step 与 syslog OPER
- 首个校验通过的同步置 SYS_EVT_TIMESYNC_READY(就绪位,消费方按需 NoClear)
- [sntp] 配置段(enable/server1/server2/poll_min)属主 sntp_cfg.c,
  注册在 net_cfg_load();UI 页 31「SNTP 设置」(密码叶):启用开关 +
  主/备服务器 octet 编辑 + 周期(分) + 保存,复用 net_cfg_save_request 链
- 测试与门禁:test/host/sntp 新套件(报文/防伪/KoD/退避周期/阶跃域/配置
  注册表)、net_cfg 与 time_sync 套件补 fake、黄金参考 66→74 张零漂移、
  字符集 228 字、Keil 0 错 0 警(RO +5,848 B 含字体)、覆盖率基线回填
- 文档:墙钟维护说明 §16、网络配置说明 [sntp] 节、配置文件设计说明变更记录、
  产品需求 FR-TIME-3 与 8.3/9.1.2 台账、UI 补齐规划/UI补齐 31 叶口径、
  登记簿销项并登记「SNTP 校时真机判据」等台架(PC 时间源 192.168.31.161/.13)
This commit is contained in:
2026-11-04 15:27:41 +08:00
parent c1beec50e7
commit c2dd6f34c0
69 changed files with 4641 additions and 272 deletions
+19
View File
@@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+845
View File
@@ -0,0 +1,845 @@
/*
* coreSNTP
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_serializer.c
* @brief Implementation of the Serializer API of the coreSNTP library.
*/
/* Standard includes. */
#include <string.h>
#include <stdbool.h>
#include <assert.h>
/* Include API header. */
#include "core_sntp_serializer.h"
#include "core_sntp_config_defaults.h"
/**
* @brief The version of SNTP supported by the coreSNTP library by complying
* with the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
*/
#define SNTP_VERSION ( 4U )
/**
* @brief The bit mask for the "Mode" information in the first byte of an SNTP packet.
* The "Mode" field occupies bits 0-2 of the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_BITS_MASK ( 0x07U )
/**
* @brief The value indicating a "client" in the "Mode" field of an SNTP packet.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_CLIENT ( 3U )
/**
* @brief The value indicating a "server" in the "Mode" field of an SNTP packet.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_SERVER ( 4U )
/**
* @brief The position of the least significant bit of the "Leap Indicator" field
* in first byte of an SNTP packet. The "Leap Indicator" field occupies bits 6-7 of the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_LEAP_INDICATOR_LSB_POSITION ( 6 )
/**
* @brief Value of Stratum field in SNTP packet representing a Kiss-o'-Death message
* from server.
*/
#define SNTP_KISS_OF_DEATH_STRATUM ( 0U )
/**
* @brief The position of least significant bit of the "Version" information
* in the first byte of an SNTP packet. "Version" field occupies bits 3-5 of
* the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_VERSION_LSB_POSITION ( 3 )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "DENY", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_DENY_UINT_VALUE ( 0x44454e59U )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "RSTR", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_RSTR_UINT_VALUE ( 0x52535452U )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "RATE", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_RATE_UINT_VALUE ( 0x52415445U )
/**
* @brief Macro to represent exactly half of the total number of seconds in an NTP era.
* As 32 bits are used to represent the "seconds" part of an SNTP timestamp, the half of
* the total range of seconds in an NTP era is 2^31 seconds, which represents ~68 years.
*
* @note This macro represents the edge case of calculating the client system clock-offset
* relative to server time as the library ASSUMES that the client and server times are within
* 2^31 seconds apart in duration. This is done to support calculating clock-offset for the
* cases when server and client systems are in adjacent NTP eras, which can occur when NTP time
* wraps around in 2036, and the relative NTP era presence of client and server times is
* determined based on comparing first order difference values between all possible NTP era
* configurations of the systems.
*/
#define CLOCK_OFFSET_MAX_TIME_DIFFERENCE ( ( ( ( int64_t ) INT32_MAX + 1 ) * 1000 ) )
/**
* @brief Macro to represent the total number of milliseconds that are represented in an
* NTP era period. This macro represents a duration of ~136 years.
*
* @note Rationale for calculation: The "seconds" part of an NTP timestamp is represented in
* unsigned 32 bit width, thus, the total range of seconds it represents is 2^32,
* i.e. (UINT32_MAX + 1).
*/
#define TOTAL_MILLISECONDS_IN_NTP_ERA ( ( ( int64_t ) UINT32_MAX + 1 ) * 1000 )
/**
* @brief Structure representing an SNTP packet header.
* For more information on SNTP packet format, refer to
* [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4).
*
* @note This does not include extension fields for authentication data
* for secure SNTP communication. Authentication data follows the
* packet header represented by this structure.
*/
typedef struct SntpPacket
{
/**
* @brief Bits 6-7 leap indicator, bits 3-5 are version number, bits 0-2 are mode
*/
uint8_t leapVersionMode;
/**
* @brief stratum
*/
uint8_t stratum;
/**
* @brief poll interval
*/
uint8_t poll;
/**
* @brief precision
*/
uint8_t precision;
/**
* @brief root delay
*/
uint32_t rootDelay;
/**
* @brief root dispersion
*/
uint32_t rootDispersion;
/**
* @brief reference ID
*/
uint32_t refId;
/**
* @brief reference time
*/
SntpTimestamp_t refTime;
/**
* @brief origin timestamp
*/
SntpTimestamp_t originTime;
/**
* @brief receive timestamp
*/
SntpTimestamp_t receiveTime;
/**
* @brief transmit timestamp
*/
SntpTimestamp_t transmitTime;
} SntpPacket_t;
/**
* @brief Utility macro to fill 32-bit integer in word-sized
* memory in network byte (or Big Endian) order.
*
* @param[out] pWordMemory Pointer to the word-sized memory in which
* the 32-bit integer will be filled.
* @param[in] data The 32-bit integer to fill in the @p wordMemory
* in network byte order.
*
* @note This utility ensures that data is filled in memory
* in expected network byte order, as an assignment operation
* (like *pWordMemory = word) can cause undesired side-effect
* of network-byte ordering getting reversed on Little Endian platforms.
*/
static void fillWordMemoryInNetworkOrder( uint32_t * pWordMemory,
uint32_t data )
{
uint8_t * pByteMemory = ( uint8_t * ) pWordMemory;
assert( pWordMemory != NULL );
pByteMemory[ 0 ] = ( uint8_t ) ( data >> 24 );
pByteMemory[ 1 ] = ( uint8_t ) ( ( data >> 16 ) & 0x000000FFU );
pByteMemory[ 2 ] = ( uint8_t ) ( ( data >> 8 ) & 0x000000FFU );
pByteMemory[ 3 ] = ( uint8_t ) ( ( data ) & 0x000000FFU );
}
/**
* @brief Utility macro to generate a 32-bit integer from memory containing
* integer in network (or Big Endian) byte order.
* @param[in] ptr Pointer to the memory containing 32-bit integer in network
* byte order.
*
* @return The host representation of the 32-bit integer in the passed word
* memory.
*/
static uint32_t readWordFromNetworkByteOrderMemory( const uint32_t * ptr )
{
const uint8_t * pMemStartByte = ( const uint8_t * ) ptr;
assert( ptr != NULL );
return ( uint32_t ) ( ( ( ( uint32_t ) pMemStartByte[ 0 ] ) << 24 ) |
( 0x00FF0000U & ( ( ( uint32_t ) pMemStartByte[ 1 ] ) << 16 ) ) |
( 0x0000FF00U & ( ( ( uint32_t ) pMemStartByte[ 2 ] ) << 8 ) ) |
( ( uint32_t ) pMemStartByte[ 3 ] ) );
}
/**
* @brief Utility to return absolute (or positively signed) value of an signed
* 64 bit integer.
*
* @param[in] value The integer to return the absolute value of.
*
* @return The absolute value of @p value.
*/
static int64_t absoluteOf( int64_t value )
{
return ( value >= 0 ) ? value : ( ( int64_t ) 0 - value );
}
/**
* @brief Utility to determine whether a timestamp represents a zero
* timestamp value.
*
* @note This utility is used to determine whether a timestamp value is
* invalid. According to the SNTPv4 specification, a zero timestamp value
* is considered invalid.
*
* @param[in] pTime The timestamp whose value is to be inspected for
* zero value.
*
* @return `true` if the timestamp is zero; otherwise `false`.
*/
static bool isZeroTimestamp( const SntpTimestamp_t * pTime )
{
bool isSecondsZero = ( pTime->seconds == 0U ) ? true : false;
bool isFractionsZero = ( pTime->fractions == 0U ) ? true : false;
return( isSecondsZero && isFractionsZero );
}
/**
* @brief Utility to convert the "fractions" part of an SNTP timestamp to milliseconds
* duration of time.
* @param[in] fractions The fractions value.
*
* @return The milliseconds equivalent of the @p fractions value.
*/
static uint32_t fractionsToMs( uint32_t fractions )
{
return( fractions / ( 1000U * SNTP_FRACTION_VALUE_PER_MICROSECOND ) );
}
/**
* @brief Utility to safely calculate difference between server and client timestamps and
* return the difference in the resolution of milliseconds as a signed 64 bit integer.
* The calculated value represents the effective subtraction as ( @p serverTimeSec - @p clientTimeSec ).
*
* @note This utility SUPPORTS the cases of server and client timestamps being in different NTP eras,
* and ASSUMES that the server and client systems are within 68 years of each other.
* To handle the case of different NTP eras, this function calculates difference values for all
* possible combinations of NTP eras of server and client times (i.e. 1. both timestamps in same era,
* 2. server timestamp one era ahead, and 3. client timestamp being one era ahead), and determines
* the NTP era configuration by choosing the difference value of the smallest absolute value.
*
* @param[in] pServerTime The server timestamp.
* @param[in] pClientTime The client timestamp.
*
* @return The calculated difference between server and client times as a signed 64 bit integer.
*/
static int64_t safeTimeDifference( const SntpTimestamp_t * pServerTime,
const SntpTimestamp_t * pClientTime )
{
int64_t eraAdjustedDiff = 0;
/* Convert the timestamps into 64 bit signed integer values of milliseconds. */
int64_t serverTime = ( ( int64_t ) pServerTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pServerTime->fractions );
int64_t clientTime = ( ( int64_t ) pClientTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pClientTime->fractions );
/* The difference between the 2 timestamps is calculated by determining the whether the timestamps
* are present in the same NTP era or adjacent NTP eras (i.e. the NTP timestamp overflow case). */
/* First, calculate the first order time difference assuming that server and client times
* are in the same NTP era. */
int64_t diffWithNoEraAdjustment = serverTime - clientTime;
/* Store the absolute value of the time difference which will be used for comparison with
* different cases of relative NTP era configuration of client and server times. */
int64_t absSameEraDiff = absoluteOf( diffWithNoEraAdjustment );
/* If the absolute difference value is 2^31 seconds, it means that the server and client times are
* away by exactly half the range of SNTP timestamp "second" values representable in unsigned 32 bits.
* In such a case, irrespective of whether the 2 systems exist in the same or adjacent NTP eras, the
* time difference calculated between the systems will ALWAYS yield the same value when viewed from
* all NTP era configurations of the times.
* For such a case, we will ASSUME that the server time is AHEAD of client time, and thus, generate
* a positive clock-offset value.
*/
if( absSameEraDiff == CLOCK_OFFSET_MAX_TIME_DIFFERENCE )
{
/* It does not matter whether server and client are in the same era for this
* special case as the difference value for both same and adjacent eras will yield
* the same absolute value of 2^31.*/
eraAdjustedDiff = CLOCK_OFFSET_MAX_TIME_DIFFERENCE;
}
else
{
/* Determine if server time belongs to an NTP era different than the client time, and accordingly
* choose the 64 bit representation of the first order difference to account for the era.
* The logic for determining the relative era presence of the timestamps is by calculating the
* first order difference (of "Server Time - Client Time") for all the 3 different era combinations
* (1. both timestamps in same era, 2. server time one era ahead, 3. client time one era ahead)
* and choosing the NTP era configuration that has the smallest first order difference value.
*/
int64_t diffWithServerEraAdjustment = serverTime + TOTAL_MILLISECONDS_IN_NTP_ERA -
clientTime; /* This helps determine whether server is an
* era ahead of client time. */
int64_t diffWithClientEraAdjustment = serverTime -
( TOTAL_MILLISECONDS_IN_NTP_ERA + clientTime ); /* This helps determine whether server is an
* era behind of client time. */
/* Store the absolute value equivalents of all the time difference configurations
* for easier comparison to smallest value from them. */
int64_t absServerEraAheadDiff = absoluteOf( diffWithServerEraAdjustment );
int64_t absClientEraAheadDiff = absoluteOf( diffWithClientEraAdjustment );
/* Determine the correct relative era of client and server times by checking which era
* configuration of difference value represents the least difference. */
if( ( absSameEraDiff <= absServerEraAheadDiff ) && ( absSameEraDiff <= absClientEraAheadDiff ) )
{
/* Both server and client times are in the same era. */
eraAdjustedDiff = diffWithNoEraAdjustment;
}
/* Check if server time is an NTP era ahead of client time. */
else if( absServerEraAheadDiff < absSameEraDiff )
{
/* Server time is in NTP era 1 while client time is in NTP era 0. */
eraAdjustedDiff = diffWithServerEraAdjustment;
}
/* Now, we know that the client time is an era ahead of server time. */
else
{
/* Server time is in NTP era 0 while client time is in NTP era 1. */
eraAdjustedDiff = diffWithClientEraAdjustment;
}
}
return eraAdjustedDiff;
}
/**
* @brief Utility to calculate clock offset of system relative to the
* server using the on-wire protocol specified in the NTPv4 specification.
* For more information on on-wire protocol, refer to
* [RFC 5905 Section 8](https://tools.ietf.org/html/rfc5905#section-8).
*
* @note The following diagram explains the calculation of the clock
* offset:
*
* T2 T3
* --------------------------------- <----- *SNTP/NTP server*
* /\ \
* / \
* Request* / \ *Response*
* / \/
* --------------------------------- <----- *SNTP client*
* T1 T4
*
* The four most recent timestamps, T1 through T4, are used to compute
* the clock offset of SNTP client relative to the server where:
*
* T1 = Client Request Transmit Time
* T2 = Server Receive Time (of client request)
* T3 = Server Response Transmit Time
* T4 = Client Receive Time (of server response)
*
* Clock Offset = T(NTP/SNTP server) - T(SNTP client)
* = [( T2 - T1 ) + ( T3 - T4 )]
* ---------------------------
* 2
*
* @note Both NTPv4 and SNTPv4 specifications suggest calculating the
* clock offset value, if possible. As the timestamp format uses 64 bit
* integer and there exist 2 orders of arithmetic calculations on the
* timestamp values (subtraction followed by addition as shown in the
* diagram above), the clock offset for the system can be calculated
* ONLY if the value can be represented in 62 significant bits and 2 sign
* bits i.e. if the system clock is within 34 years (in the future or past)
* of the server time.
*
* @param[in] pClientTxTime The system time of sending the SNTP request.
* This is the same as "T1" in the above diagram.
* @param[in] pServerRxTime The server time of receiving the SNTP request
* packet from the client. This is the same as "T2" in the above diagram.
* @param[in] pServerTxTime The server time of sending the SNTP response
* packet. This is the same as "T3" in the above diagram.
* @param[in] pClientRxTime The system time of receiving the SNTP response
* from the server. This is the same as "T4" in the above diagram.
* @param[out] pClockOffset This will be filled with the calculated offset value
* of the system clock relative to the server time with the assumption that the
* system clock is within 68 years of server time.
*/
static void calculateClockOffset( const SntpTimestamp_t * pClientTxTime,
const SntpTimestamp_t * pServerRxTime,
const SntpTimestamp_t * pServerTxTime,
const SntpTimestamp_t * pClientRxTime,
int64_t * pClockOffset )
{
/* Variable for storing the first-order difference between timestamps. */
int64_t firstOrderDiffSend = 0;
int64_t firstOrderDiffRecv = 0;
assert( pClientTxTime != NULL );
assert( pServerRxTime != NULL );
assert( pServerTxTime != NULL );
assert( pClientRxTime != NULL );
assert( pClockOffset != NULL );
/* Perform first order difference of timestamps on the network send path i.e. T2 - T1.
* Note: The calculated difference value will always represent years in the range of
*[-68 years, +68 years]. */
firstOrderDiffSend = safeTimeDifference( pServerRxTime, pClientTxTime );
/* Perform first order difference of timestamps on the network receive path i.e. T3 - T4.
* Note: The calculated difference value will always represent years in the range of
*[-68 years, +68 years]. */
firstOrderDiffRecv = safeTimeDifference( pServerTxTime, pClientRxTime );
/* Now calculate the system clock-offset relative to server time as the average of the
* first order difference of timestamps in both directions of network path.
* Note: This will ALWAYS represent offset in the range of [-68 years, +68 years]. */
*pClockOffset = ( firstOrderDiffSend + firstOrderDiffRecv ) / 2;
}
/**
* @brief Parse a SNTP response packet by determining whether it is a rejected
* or accepted response to an SNTP request, and accordingly, populate the
* @p pParsedResponse parameter with the parsed data.
*
* @note If the server has rejected the request with the a Kiss-o'-Death message,
* then this function will set the associated rejection code in the output parameter
* while setting the remaining members to zero.
* If the server has accepted the time request, then the function will set the
* rejectedResponseCode member of the output parameter to #SNTP_KISS_OF_DEATH_CODE_NONE,
* and set the other the members with appropriate data extracted from the response
* packet.
*
* @param[in] pResponsePacket The SNTP response packet from server to parse.
* @param[in] pRequestTxTime The system time (in SNTP timestamp format) of
* sending the SNTP request to server.
* @param[in] pResponseRxTime The system time (in SNTP timestamp format) of
* receiving the SNTP response from server.
* @param[out] pParsedResponse The parameter that will be populated with data
* parsed from the response packet, @p pResponsePacket.
*
* @return This function returns one of the following:
* - #SntpSuccess if the server response does not represent a Kiss-o'-Death
* message.
* - #SntpRejectedResponseChangeServer if the server rejected with a code
* indicating that client cannot be retry requests to it.
* - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
* indicating that client should back-off before retrying request.
* - #SntpRejectedResponseOtherCode if the server rejected with a code
* other than "DENY", "RSTR" and "RATE".
*/
static SntpStatus_t parseValidSntpResponse( const SntpPacket_t * pResponsePacket,
const SntpTimestamp_t * pRequestTxTime,
const SntpTimestamp_t * pResponseRxTime,
SntpResponseData_t * pParsedResponse )
{
SntpStatus_t status = SntpSuccess;
assert( pResponsePacket != NULL );
assert( pResponseRxTime != NULL );
assert( pParsedResponse != NULL );
/* Clear the output parameter memory to zero. */
( void ) memset( pParsedResponse, 0, sizeof( *pParsedResponse ) );
/* Determine if the server has accepted or rejected the request for time. */
if( pResponsePacket->stratum == SNTP_KISS_OF_DEATH_STRATUM )
{
/* Server has sent a Kiss-o'-Death message i.e. rejected the request. */
/* Extract the kiss-code sent by the server from the "Reference ID" field
* of the SNTP packet. */
pParsedResponse->rejectedResponseCode =
readWordFromNetworkByteOrderMemory( &pResponsePacket->refId );
/* Determine the return code based on the Kiss-o'-Death code. */
switch( pParsedResponse->rejectedResponseCode )
{
case KOD_CODE_DENY_UINT_VALUE:
case KOD_CODE_RSTR_UINT_VALUE:
status = SntpRejectedResponseChangeServer;
break;
case KOD_CODE_RATE_UINT_VALUE:
status = SntpRejectedResponseRetryWithBackoff;
break;
default:
status = SntpRejectedResponseOtherCode;
break;
}
}
else
{
/* Server has responded successfully to the time request. */
SntpTimestamp_t serverRxTime;
/* Map of integer value to SntpLeapSecondInfo_t enumeration type for the
* "Leap Indicator" field in the first byte of an SNTP packet.
* Note: This map is used to not violate MISRA Rule 10.5 when directly
* converting an integer to enumeration type.
*/
const SntpLeapSecondInfo_t leapIndicatorTypeMap[] =
{
NoLeapSecond,
LastMinuteHas61Seconds,
LastMinuteHas59Seconds,
AlarmServerNotSynchronized
};
/* Set the Kiss-o'-Death code value to NULL as server has responded favorably
* to the time request. */
pParsedResponse->rejectedResponseCode = SNTP_KISS_OF_DEATH_CODE_NONE;
/* Fill the output parameter with the server time which is the
* "transmit" time in the response packet. */
pParsedResponse->serverTime.seconds =
readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.seconds );
pParsedResponse->serverTime.fractions =
readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.fractions );
/* Extract information of any upcoming leap second from the response. */
pParsedResponse->leapSecondType = leapIndicatorTypeMap[
( pResponsePacket->leapVersionMode >> SNTP_LEAP_INDICATOR_LSB_POSITION ) ];
/* Store the "receive" time in SNTP response packet in host order. */
serverRxTime.seconds =
readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.seconds );
serverRxTime.fractions =
readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.fractions );
/* Calculate system clock offset relative to server time, if possible, within
* the 64 bit integer width of the SNTP timestamp. */
calculateClockOffset( pRequestTxTime,
&serverRxTime,
&pParsedResponse->serverTime,
pResponseRxTime,
&pParsedResponse->clockOffsetMs );
}
return status;
}
SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
uint32_t randomNumber,
void * pBuffer,
size_t bufferSize )
{
SntpStatus_t status = SntpSuccess;
if( pRequestTime == NULL )
{
status = SntpErrorBadParameter;
}
else if( pBuffer == NULL )
{
status = SntpErrorBadParameter;
}
else if( bufferSize < SNTP_PACKET_BASE_SIZE )
{
status = SntpErrorBufferTooSmall;
}
/* Zero timestamps for client request time is not allowed to protect against
* attack spoofing server response containing zero value for "originate timestamp".
* Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
* matches the "transmit timestamp" in corresponding client request packet. */
else if( isZeroTimestamp( pRequestTime ) == true )
{
status = SntpErrorBadParameter;
}
else
{
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
SntpPacket_t * pRequestPacket = ( SntpPacket_t * ) pBuffer;
/* Fill the buffer with zero as most fields are zero for a standard SNTP
* request packet.*/
( void ) memset( pBuffer, 0, sizeof( SntpPacket_t ) );
/* Set the first byte of the request packet for "Version" and "Mode" fields */
pRequestPacket->leapVersionMode = 0U /* Leap Indicator */ |
( SNTP_VERSION << SNTP_VERSION_LSB_POSITION ) /* Version Number */ |
SNTP_MODE_CLIENT /* Mode */;
/* Add passed random number to non-significant bits of the fractions part
* of the transmit timestamp.
* This is suggested by the SNTPv4 (and NTPv4) specification(s)
* to protect against replay attacks. Refer to RFC 4330 Section 3 for
* more information.
* Adding random bits to the least significant 16 bits of the fractions
* part of the timestamp affects only ~15 microseconds of information
* (calculated as 0xFFFF * 232 picoseconds).
*/
pRequestTime->fractions = ( pRequestTime->fractions
| ( randomNumber >> 16 ) );
/* Update the request buffer with request timestamp in network byte order. */
fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.seconds,
pRequestTime->seconds );
fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.fractions,
pRequestTime->fractions );
}
return status;
}
SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
const SntpTimestamp_t * pResponseRxTime,
const void * pResponseBuffer,
size_t bufferSize,
SntpResponseData_t * pParsedResponse )
{
SntpStatus_t status = SntpSuccess;
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
const SntpPacket_t * pResponsePacket = ( const SntpPacket_t * ) pResponseBuffer;
if( ( pRequestTime == NULL ) || ( pResponseRxTime == NULL ) ||
( pResponseBuffer == NULL ) || ( pParsedResponse == NULL ) )
{
status = SntpErrorBadParameter;
}
else if( bufferSize < SNTP_PACKET_BASE_SIZE )
{
status = SntpErrorBufferTooSmall;
}
/* Zero timestamps for client request time is not allowed to protect against
* attack spoofing server response containing zero value for "originate timestamp".
* Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
* matches the "transmit timestamp" in corresponding client request packet. */
else if( isZeroTimestamp( pRequestTime ) == true )
{
status = SntpErrorBadParameter;
}
/* Check if the packet represents a server in the "Mode" field. */
else if( ( pResponsePacket->leapVersionMode & SNTP_MODE_BITS_MASK ) != SNTP_MODE_SERVER )
{
status = SntpInvalidResponse;
}
/* Check if any of the timestamps in the response packet are zero, which is invalid.
* Note: This is done to protect against a nuanced server spoofing attack where if the
* SNTP client resets its internal state of "Client transmit timestamp" (OR "originate
* timestamp" from server perspective) to zero as a protection against replay attack, an
* an attacker with this knowledge of the client operation can spoof a server response
* containing the "originate timestamp" as zero. Thus, to protect against such attack,
* a server response packet with any zero timestamp is rejected. */
else if( ( isZeroTimestamp( &pResponsePacket->originTime ) == true ) ||
( isZeroTimestamp( &pResponsePacket->receiveTime ) == true ) ||
( isZeroTimestamp( &pResponsePacket->transmitTime ) == true ) )
{
status = SntpInvalidResponse;
}
/* Validate that the server has sent the client's request timestamp in the
* "originate" timestamp field of the response. */
else if( ( pRequestTime->seconds !=
readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.seconds ) ) ||
( pRequestTime->fractions !=
readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.fractions ) ) )
{
status = SntpInvalidResponse;
}
else
{
/* As the response packet is valid, parse more information from it and
* populate the output parameter. */
status = parseValidSntpResponse( pResponsePacket,
pRequestTime,
pResponseRxTime,
pParsedResponse );
}
return status;
}
SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
uint16_t desiredAccuracy,
uint32_t * pPollInterval )
{
SntpStatus_t status = SntpSuccess;
if( ( clockFreqTolerance == 0U ) || ( desiredAccuracy == 0U ) || ( pPollInterval == NULL ) )
{
status = SntpErrorBadParameter;
}
else
{
uint32_t exactIntervalForAccuracy = 0U;
uint8_t log2PollInterval = 0U;
/* Calculate the poll interval required for achieving the exact desired clock accuracy
* with the following formulae:
*
* System Clock Drift Rate ( microseconds / second ) = Clock Frequency Tolerance (in PPM )
* Maximum Clock Drift ( milliseconds ) = Desired Accuracy ( milliseconds )
*
* Poll Interval ( seconds ) = Maximum Clock Drift
* ---------------------------
* System Clock Drift Rate
*
* = Maximum Drift ( milliseconds ) * 1000 ( microseconds / millisecond )
* ------------------------------------------------------------------------
* System Clock Drift Rate ( microseconds / second )
*
* = Desired Accuracy * 1000
* ------------------------------
* Clock Frequency Tolerance
*/
exactIntervalForAccuracy = ( ( uint32_t ) desiredAccuracy * 1000U ) / clockFreqTolerance;
/* Check if calculated poll interval value falls in the supported range of seconds. */
if( exactIntervalForAccuracy == 0U )
{
/* Poll interval value is less than 1 second, and is not supported by the function. */
status = SntpZeroPollInterval;
}
else
{
/* To calculate the log 2 value of the exact poll interval value, first determine the highest
* bit set in the value. */
while( exactIntervalForAccuracy != 0U )
{
log2PollInterval++;
exactIntervalForAccuracy /= 2U;
}
/* Convert the highest bit in the exact poll interval value to the nearest integer
* value lower or equal to the log2 of the exact poll interval value. */
log2PollInterval--;
/* Calculate the poll interval as the closest exponent of 2 value that achieves
* equal or higher accuracy than the desired accuracy. */
*pPollInterval = ( ( ( uint32_t ) 1U ) << log2PollInterval );
}
}
return status;
}
SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
uint32_t * pUnixTimeSecs,
uint32_t * pUnixTimeMicrosecs )
{
SntpStatus_t status = SntpSuccess;
if( ( pSntpTime == NULL ) || ( pUnixTimeSecs == NULL ) || ( pUnixTimeMicrosecs == NULL ) )
{
status = SntpErrorBadParameter;
}
if( status == SntpSuccess )
{
/* Handle case when SNTP timestamp is in SNTP era 0 time range
* (i.e. time before 7 Feb 2036 6:28:15 UTC). */
if( pSntpTime->seconds >= SNTP_TIME_AT_UNIX_EPOCH_SECS )
{
*pUnixTimeSecs = pSntpTime->seconds - SNTP_TIME_AT_UNIX_EPOCH_SECS;
}
else
{
/* Handle case when timestamp represents date in SNTP era 1
* (i.e. time from 7 Feb 2036 6:28:16 UTC onwards). */
*pUnixTimeSecs = UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME + pSntpTime->seconds;
}
/* Convert SNTP fractions to microseconds for UNIX time. */
*pUnixTimeMicrosecs = pSntpTime->fractions / SNTP_FRACTION_VALUE_PER_MICROSECOND;
}
return status;
}
+24
View File
@@ -0,0 +1,24 @@
/*
* 模块名称:coreSNTP 定制配置(本仓 glue 文件,非 vendor 原文)
* 模块功能:core_sntp_config_defaults.h 在**未**定义 SNTP_DO_NOT_USE_CUSTOM_CONFIG
* 时会包含本文件;本仓使用 vendor 库的 serializer 层,默认值即所需
* ——日志宏 LogError/LogWarn/LogInfo/LogDebug 保持 defaults 的编译期
* 空实现:serializer 的失败路径都已有应用层 DBG 判读(time_sntp.c
* 在调用点按返回码记录),库内再开日志只增栈/Flash 不增信息量。
* 错误/丢弃类留痕统一放调用方(App/sync/time_sntp.c),
* 避免 vendor 文件与本仓日志栈耦合。
* 适用平台:STM32F407ZGTx / 主机单测(test/host/sntp)
* 作者:王建锋
* 创建日期:2026-11-04
* 修改记录:
* v1.0 2026-11-04 王建锋 创建初始版本(SNTP 客户端接入 #57)
*/
#ifndef CORE_SNTP_CONFIG_H
#define CORE_SNTP_CONFIG_H
/* 本文件刻意不覆盖任何默认宏;如将来需要库内日志,在此映射:
* #define LogError(message) ...
* (注意 vendor 调用形态为 LogError(("..."))——参数自带外括号) */
#endif /* CORE_SNTP_CONFIG_H */
@@ -0,0 +1,144 @@
/*
* coreSNTP
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_config_defaults.h
* @brief This file represents the default values for the configuration macros
* of the coreSNTP library.
*
* @note This file SHOULD NOT be modified. If custom values are needed for
* any configuration macro, a core_sntp_config.h file should be provided to
* the SNTP library to override the default values defined in this file.
* To build the library with the core_sntp_config.h file, make sure to
* not set the SNTP_DO_NOT_USE_CUSTOM_CONFIG preprocessor macro.
*/
#ifndef CORE_SNTP_CONFIG_DEFAULTS_H_
#define CORE_SNTP_CONFIG_DEFAULTS_H_
/* The macro definition for SNTP_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
* documentation only. */
/**
* @brief Define this macro to build the SNTP library without the custom config
* file core_sntp_config.h.
*
* Without the custom config, the SNTP library builds with
* default values of config macros defined in core_sntp_config_defaults.h file.
*
* If a custom config is provided, then SNTP_DO_NOT_USE_CUSTOM_CONFIG should not
* be defined.
*/
#ifdef DOXYGEN
#define SNTP_DO_NOT_USE_CUSTOM_CONFIG
#endif
/* SNTP_DO_NOT_USE_CUSTOM_CONFIG allows building the SNTP library
* without a custom config. If a custom config is provided, the
* SNTP_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */
#ifndef SNTP_DO_NOT_USE_CUSTOM_CONFIG
#include "core_sntp_config.h"
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Error" level
* messages.
*
* To enable error level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports error logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Error logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogError
#define LogError( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Warning" level
* messages.
*
* To enable warning level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports warning logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Warning logs are turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogWarn
#define LogWarn( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Info" level
* messages.
*
* To enable info level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports info logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Info logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogInfo
#define LogInfo( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Debug" level
* messages.
*
* To enable debug level logging from SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports debug logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Debug logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogDebug
#define LogDebug( message )
#endif
#endif /* ifndef CORE_SNTP_CONFIG_DEFAULTS_H_ */
+521
View File
@@ -0,0 +1,521 @@
/*
* coreSNTP
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_serializer.h
* @brief API for serializing SNTP request packets and, and de-serializing SNTP
* response packets.
* This API layer adheres to the SNTPv4 specification defined in
* [RFC 4330](https://tools.ietf.org/html/rfc4330).
*/
#ifndef CORE_SNTP_SERIALIZER_H_
#define CORE_SNTP_SERIALIZER_H_
/* Standard include. */
#include <stdint.h>
#include <stddef.h>
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/**
* @ingroup sntp_constants
* @brief The base packet size of request and response of the (S)NTP protocol.
* @note This is the packet size without any authentication headers for security
* mechanism. If the application uses a security mechanism for communicating with
* an (S)NTP server, it can add authentication data after the SNTP packet is
* serialized with the @ref Sntp_SerializeRequest API function.
*/
#define SNTP_PACKET_BASE_SIZE ( 48U )
/**
* @ingroup sntp_constants
* @brief Number of SNTP timestamp fractions in 1 microsecond.
*
* The fraction's part of an SNTP timestamp is 32-bits wide, thereby, giving a
* resolution of 2^(-32) seconds ~ 232 picoseconds.
*
* @note The application can use this value to convert microseconds part of system
* time into SNTP timestamp fractions. For example, if the microseconds
* part of system time is n microseconds, the fractions value to be used for the
* SNTP timestamp part will be n * SNTP_FRACTION_VALUE_PER_MICROSECOND.
*/
#define SNTP_FRACTION_VALUE_PER_MICROSECOND ( 4295U )
/**
* @ingroup sntp_constants
* @brief The seconds part of SNTP time at the UNIX epoch time, which represents
* an offset of 70 years (in seconds) between SNTP epoch and UNIX epoch time.
* SNTP uses 1st Jan 1900 UTC as the epoch time, whereas UNIX standard uses
* 1st Jan 1970 UTC as the epoch time, thereby, causing an offset of 70 years
* between them.
*
* Difference of 70 years = ((70 * 365) + 17 leap days) * 24 * 3600 seconds
*
* @note If your system follows UNIX time, the application can use this value to
* convert seconds part of a system time to seconds part of the equivalent SNTP
* time. For example, if the seconds part of system time is n seconds, the seconds
* value to be used for the SNTP timestamp will be n + SNTP_TO_UNIX_OFFSET_SECS.
*/
#define SNTP_TIME_AT_UNIX_EPOCH_SECS ( 2208988800U )
/**
* @ingroup sntp_constants
* @brief The seconds value of SNTP timestamp for the largest UNIX time when
* using signed 32-bit integer for seconds.
* The largest time representable with a 32-bit signed integer in UNIX time
* is 19 Jan 2038 3h 14m 7s UTC. However, as the SNTP time overflows at
* 7 Feb 2036 6h 28m 16s UTC, therefore, the SNTP time for the largest UNIX time
* represents the time duration between the 2 timestamps.
*
* SNTP Time at Largest Time Duration in the range
* Signed 32-bit UNIX time = [7 Feb 2036 6:28:16, 19 Jan 2038 3:14:07]
*/
#define SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS ( 61505151U )
/**
* @ingroup sntp_constants
* @brief The UNIX time (in seconds) at the smallest SNTP time in era 1,
* i.e. UNIX time at 7 Feb 2036 6:28:16 UTC/
*
* Time Duration = 7 Feb 6:28:16 UTC (SNTP Era 1 Epoch) -
* 1 Jan 1970 0:0:0 UTC (UNIX epoch)
* = 66 years, 37 days, 6 hours, 28 minutes and 16 seconds
* = ((66 * 365) + 16 leap days) * 24 * 3600) + (6 * 3600)
* + (28 * 60) + 16
*/
#define UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME ( 2085978496U )
/**
* @ingroup sntp_constants
* @brief The fixed-length of any Kiss-o'-Death message ASCII code sent
* in an SNTP server response.
* @note An SNTP server sends a Kiss-o'-Death message to reject a time request
* from the client. For more information on the Kiss-o'-Death codes, refer to the
* [SNTPv4 specification Section 8](https://tools.ietf.org/html/rfc4330#section-8).
*/
#define SNTP_KISS_OF_DEATH_CODE_LENGTH ( 4U )
/**
* @ingroup sntp_constants
* @brief The value for the #SntpResponseData_t.rejectedResponseCode member
* when that the server response packet does not contain a Kiss-o'-Death
* message, and therefore, does not have a "kiss code".
* The server sends a "kiss-code" only when it rejects an SNTP request
* with a Kiss-o'-Death message.
*/
#define SNTP_KISS_OF_DEATH_CODE_NONE ( 0U )
/**
* @ingroup sntp_enum_types
* @brief Enumeration of status codes that can be returned
* by the coreSNTP Library API.
*/
typedef enum SntpStatus
{
/**
* @brief Successful operation of an SNTP API.
*/
SntpSuccess,
/**
* @brief Invalid parameter passed to an API function.
*/
SntpErrorBadParameter,
/**
* @brief Server sent a Kiss-o'-Death message to reject the request for time.
* This status can be returned by the @ref Sntp_ReceiveTimeResponse API.
*/
SntpRejectedResponse,
/**
* @brief Server sent a Kiss-o'-Death message with non-retryable code (i.e. DENY or RSTR).
*/
SntpRejectedResponseChangeServer,
/**
* @brief Server sent a Kiss-o'-Death message with a RATE code, which means that
* client should back-off before retrying.
*/
SntpRejectedResponseRetryWithBackoff,
/**
* @brief Server sent a Kiss-o'-Death message with a code, specific to the server.
* Application can inspect the ASCII kiss-code from @ref Sntp_DeserializeResponse API.
*/
SntpRejectedResponseOtherCode,
/**
* @brief Application provided insufficient buffer space for serializing
* or de-serializing an SNTP packet.
* The minimum size of an SNTP packet is #SNTP_PACKET_BASE_SIZE
* bytes. */
SntpErrorBufferTooSmall,
/**
* @brief Server response failed validation checks for expected data in SNTP packet.
*/
SntpInvalidResponse,
/**
* @brief Poll interval value is under 1 second which cannot be calculated
* by @ref Sntp_CalculatePollInterval.
*/
SntpZeroPollInterval,
/**
* @brief The user-defined DNS resolution interface, @ref SntpResolveDns_t, failed to resolve
* address for a time server. This status is returned by the @ref Sntp_SendTimeRequest API.
*/
SntpErrorDnsFailure,
/**
* @brief Networking operation of sending or receiving SNTP packet through the user-defined UDP
* transport interface, @ref UdpTransportInterface_t, failed.
* This status is returned by either of @ref Sntp_SendTimeRequest OR @ref Sntp_ReceiveTimeResponse
* APIs.
*/
SntpErrorNetworkFailure,
/**
* @brief Time server is not authenticated from the authentication data in its response.
* This status can be returned by the user-supplied definition of the
* @ref SntpValidateServerAuth_t authentication interface.
*/
SntpServerNotAuthenticated,
/**
* @brief Failure from the user-supplied authentication interface, @ref SntpAuthenticationInterface_t,
* in either generating authentication data for SNTP request OR validating the authentication
* data in SNTP response from server.
*/
SntpErrorAuthFailure,
/**
* @brief A timeout occurred in sending time request packet over the network to a server through the
* @ref Sntp_SendTimeRequest API.
*/
SntpErrorSendTimeout,
/**
* @brief A timeout has occurred in receiving server response with the @ref Sntp_ReceiveTimeResponse
* API.
*/
SntpErrorResponseTimeout,
/**
* @brief No SNTP packet for server response is received from the network by the
* @ref Sntp_ReceiveTimeResponse API.
*/
SntpNoResponseReceived,
/**
* @brief The SNTP context passed to @ref Sntp_SendTimeRequest or @ref Sntp_ReceiveTimeResponse APIs is
* is uninitialized.
*/
SntpErrorContextNotInitialized
} SntpStatus_t;
/**
* @ingroup sntp_enum_types
* @brief Enumeration for leap second information that an SNTP server can
* send its response to a time request. An SNTP server sends information about
* whether there is an upcoming leap second adjustment in the last day of the
* current month.
*
* @note A leap second is an adjustment made in atomic clock time because Earth's rotation
* can be inconsistent. Leap seconds are usually incorporated as an extra second insertion
* or second deletion in the last minute before midnight i.e. in the minute of 23h:59m UTC
* on the last day of June or December. For more information on leap seconds, refer to
* https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs.
*/
typedef enum SntpLeapSecondInfo
{
NoLeapSecond = 0x00, /**< @brief There is no upcoming leap second adjustment. */
LastMinuteHas61Seconds = 0x01, /**< @brief A leap second should be inserted in the last minute before midnight. */
LastMinuteHas59Seconds = 0x02, /**< @brief A leap second should be deleted from the last minute before midnight. */
AlarmServerNotSynchronized = 0x03 /**< @brief An alarm condition meaning that server's time is not synchronized
* to an upstream NTP (or SNTP) server. */
} SntpLeapSecondInfo_t;
/**
* @ingroup sntp_struct_types
* @brief Structure representing an SNTP timestamp.
*
* @note The SNTP timestamp uses 1st January 1900 0h 0m 0s Coordinated Universal Time (UTC)
* as the primary epoch i.e. the timestamp represents current time as the amount of time since
* the epoch time.
* Refer to the [SNTPv4 specification](https://tools.ietf.org/html/rfc4330#section-3) for more
* information of the SNTP timestamp format.
*/
typedef struct SntpTimestamp
{
uint32_t seconds; /**< @brief Number of seconds since epoch time. */
uint32_t fractions; /**< @brief The fractions part of the SNTP timestamp with resolution
* of 2^(-32) ~ 232 picoseconds. */
} SntpTimestamp_t;
/**
* @ingroup sntp_struct_types
* @brief Structure representing data parsed from an SNTP response from server
* as well as data of arithmetic calculations derived from the response.
*/
typedef struct SntpResponse
{
/**
* @brief The timestamp sent by the server.
*/
SntpTimestamp_t serverTime;
/**
* @brief The information of an upcoming leap second in the
* server response.
*/
SntpLeapSecondInfo_t leapSecondType;
/**
* @brief If a server responded with Kiss-o'-Death message to reject
* time request, this is the fixed length ASCII code sequence for the
* rejection.
*
* The Kiss-o'-Death code is always #SNTP_KISS_OF_DEATH_CODE_LENGTH
* bytes long.
*
* @note If the server does not send a Kiss-o'-Death message in its
* response, this value will be #SNTP_KISS_OF_DEATH_CODE_NONE.
*/
uint32_t rejectedResponseCode;
/**
* @brief The offset (in milliseconds) of the system clock relative to the server time
* calculated from timestamps in the client SNTP request and server SNTP response packets.
* If the the system time is BEHIND the server time, then the clock-offset value is > 0.
* If the system time is AHEAD of the server time, then the clock-offset value is < 0.
*
* @note This information can be used to synchronize the system clock with a "slew",
* "step" OR combination of the two clock correction methodologies depending on the degree
* of system clock drift (represented by the clock-offset) and the application's
* tolerance for system clock error.
*
* @note The library calculates the clock-offset value using the On-Wire
* protocol suggested by the NTPv4 specification. For more information,
* refer to https://tools.ietf.org/html/rfc5905#section-8.
*
* @note The library ASSUMES that the server and client systems are within
* ~68 years of each other clock, whether in the same NTP era or across adjacent
* NTP eras. Thus, the client and system times MUST be within ~68 years (or
* 2^31 seconds exactly) of each other for correct calculation of clock-offset.
*
* @note When the server and client times are exactly 2^31 (or INT32_MAX + 1 )
* seconds apart, the library ASSUMES that the server time is ahead of the client
* time, and return the clock-offset value of INT32_MAX.
*/
int64_t clockOffsetMs;
} SntpResponseData_t;
/**
* @brief Serializes an SNTP request packet to use for querying a
* time server.
*
* This function will fill only #SNTP_PACKET_BASE_SIZE bytes of data in the
* passed buffer.
*
* @param[in, out] pRequestTime The current time of the system, expressed as time
* since the SNTP epoch (i.e. 0h of 1st Jan 1900 ). This time will be serialized
* in the SNTP request packet. The function will use this parameter to return the
* timestamp serialized in the SNTP request. To protect against attacks spoofing
* server responses, the timestamp MUST NOT be zero in value.
* @param[in] randomNumber A random number (generated by a True Random Generator)
* for use in the SNTP request packet to protect against replay attacks as suggested
* by SNTPv4 specification. For more information, refer to
* [RFC 4330 Section 3](https://tools.ietf.org/html/rfc4330#section-3).
* @param[out] pBuffer The buffer that will be populated with the serialized
* SNTP request packet.
* @param[in] bufferSize The size of the @p pBuffer buffer. It should be at least
* #SNTP_PACKET_BASE_SIZE bytes in size.
*
* @note It is recommended to use a True Random Generator (TRNG) to generate
* the random number.
* @note The application MUST save the @p pRequestTime value for de-serializing
* the server response with @ref Sntp_DeserializeResponse API.
*
* @return This function returns one of the following:
* - #SntpSuccess when serialization operation is successful.
* - #SntpErrorBadParameter if an invalid parameter is passed.
* - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
* for serializing an SNTP request packet.
*/
/* @[define_sntp_serializerequest] */
SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
uint32_t randomNumber,
void * pBuffer,
size_t bufferSize );
/* @[define_sntp_serializerequest] */
/**
* @brief De-serializes an SNTP packet received from a server as a response
* to a SNTP request.
*
* This function will parse only the #SNTP_PACKET_BASE_SIZE bytes of data
* in the passed buffer.
*
* @note If the server has sent a Kiss-o'-Death message to reject the associated
* time request, the API function will return the appropriate return code and,
* also, provide the ASCII code (of fixed length, #SNTP_KISS_OF_DEATH_CODE_LENGTH bytes)
* in the #SntpResponseData_t.rejectedResponseCode member of @p pParsedResponse parameter,
* parsed from the response packet.
* The application SHOULD respect the server rejection and take appropriate action
* based on the rejection code.
* If the server response represents an accepted SNTP client request, then the API
* function will set the #SntpResponseData_t.rejectedResponseCode member of
* @p pParsedResponse parameter to #SNTP_KISS_OF_DEATH_CODE_NONE.
*
* @note If the server has positively responded with its clock time, then this API
* function will calculate the clock-offset. For the clock-offset to be correctly
* calculated, the system clock MUST be within ~68 years (or 2^31 seconds) of the server
* time mentioned. This function supports clock-offset calculation when server and client
* timestamps are in adjacent NTP eras, with one system is in NTP era 0 (i.e. before 7 Feb 2036
* 6h:28m:14s UTC) and another system in NTP era 1 (on or after 7 Feb 2036 6h:28m:14s UTC).
*
* @note In the special case when the server and client times are exactly 2^31 seconds apart,
* the library ASSUMES that the server time is ahead of the client time, and returns the
* positive clock-offset value of INT32_MAX seconds.
*
* @param[in] pRequestTime The system time used in the SNTP request packet
* that is associated with the server response. This MUST be the same as the
* time returned by the @ref Sntp_SerializeRequest API. To protect against attacks
* spoofing server responses, this timestamp MUST NOT be zero in value.
* @param[in] pResponseRxTime The time of the system, expressed as time since the
* SNTP epoch (i.e. 0h of 1st Jan 1900 ), at receiving SNTP response from server.
* This time will be used to calculate system clock offset relative to server.
* @param[in] pResponseBuffer The buffer containing the SNTP response from the
* server.
* @param[in] bufferSize The size of the @p pResponseBuffer containing the SNTP
* response. It MUST be at least #SNTP_PACKET_BASE_SIZE bytes
* long for a valid SNTP response.
* @param[out] pParsedResponse The information parsed from the SNTP response packet.
* If possible to calculate without overflow, it also contains the system clock
* offset relative to the server time.
*
* @return This function returns one of the following:
* - #SntpSuccess if the de-serialization operation is successful.
* - #SntpErrorBadParameter if an invalid parameter is passed.
* - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
* required for a valid SNTP response packet.
* - #SntpInvalidResponse if the response fails sanity checks expected in an
* SNTP response.
* - #SntpRejectedResponseChangeServer if the server rejected with a code
* indicating that client cannot be retry requests to it.
* - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
* indicating that client should back-off before retrying request.
* - #SntpRejectedResponseOtherCode if the server rejected with a code that
* application can inspect in the @p pParsedResponse parameter.
*/
/* @[define_sntp_deserializeresponse] */
SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
const SntpTimestamp_t * pResponseRxTime,
const void * pResponseBuffer,
size_t bufferSize,
SntpResponseData_t * pParsedResponse );
/* @[define_sntp_deserializeresponse] */
/**
* @brief Utility to calculate the poll interval of sending periodic time queries
* to servers to achieve a desired system clock accuracy for a given
* frequency tolerance of the system clock.
*
* For example, from the SNTPv4 specification, "if the frequency tolerance
* is 200 parts per million (PPM) and the required accuracy is one minute,
* the maximum timeout is about 3.5 days". In this example, the system
* clock frequency tolerance is 200 PPM and the desired accuracy is
* 60000 milliseconds (or 1 minute) for which this API function
* will return the maximum poll interval value as 2^18 seconds (or ~3 days).
*
* @note The poll interval returned is a power of 2, which is the
* standard way to represent the value. According to the SNTPv4 specification
* Best Practices, an SNTP client SHOULD NOT have a poll interval less than 15 seconds.
* https://tools.ietf.org/html/rfc4330#section-10. This API function DOES NOT
* support poll interval calculation less than 1 second.
*
* @param[in] clockFreqTolerance The frequency tolerance of system clock
* in parts per million (PPM) units. This parameter MUST be non-zero.
* @param[in] desiredAccuracy The acceptable maximum drift, in milliseconds,
* for the system clock. The maximum value (0xFFFF) represents ~1 minute of
* desired clock accuracy. This parameter MUST be non-zero.
* @param[out] pPollInterval This is filled with the poll interval, in seconds
* calculated as the closest power of 2 value that will achieve either the
* exact desired or higher clock accuracy @p desiredAccuracy, for the given clock
* frequency tolerance, @p clockFreqTolerance.
*
* @return Returns one of the following:
* - #SntpSuccess if calculation is successful.
* - #SntpErrorBadParameter for an invalid parameter passed to the function.
* - #SntpZeroPollInterval if calculated poll interval is less than 1 second.
*/
/* @[define_sntp_calculatepollinterval] */
SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
uint16_t desiredAccuracy,
uint32_t * pPollInterval );
/* @[define_sntp_calculatepollinterval] */
/**
* @brief Utility to convert SNTP timestamp (that uses 1st Jan 1900 as the epoch) to
* UNIX timestamp (that uses 1st Jan 1970 as the epoch).
*
* Refer to (this image)[docs/doxygen/images/Ntp_To_Unix_Time.png].
*
* @note The function also correctly handles SNTP era overflow (from 7 Feb 2036 6h 28m 16s,
* i.e., SNTP era 1) to ensure accurate conversion across SNTP eras.
*
* @param[in] pSntpTime The SNTP timestamp to convert to UNIX time.
* @param[out] pUnixTimeSecs This will be filled with the seconds part of the
* UNIX time equivalent of the SNTP time, @p pSntpTime.
* @param[out] pUnixTimeMicrosecs This will be filled with the microseconds part
* of the UNIX time equivalent of the SNTP time, @p pSntpTime.
*
* @return Returns one of the following:
* - #SntpSuccess if conversion to UNIX time is successful
* - #SntpErrorBadParameter if any of the passed parameters are NULL.
*/
/* @[define_sntp_converttounixtime] */
SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
uint32_t * pUnixTimeSecs,
uint32_t * pUnixTimeMicrosecs );
/* @[define_sntp_converttounixtime] */
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef CORE_SNTP_SERIALIZER_H_ */