RaspberrPi project source code
Guo Wenxue
2023-09-08 47098ac0fb3a6bed9bd5c2acfc64d84387302cda
commit | author | age
d6b4a7 1 /*
G 2  * coreSNTP v1.2.0
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 /**
26  * @file core_sntp_serializer.c
27  * @brief Implementation of the Serializer API of the coreSNTP library.
28  */
29
30 /* Standard includes. */
31 #include <string.h>
32 #include <stdbool.h>
33 #include <assert.h>
34
35 /* Include API header. */
36 #include "core_sntp_serializer.h"
37
38 #include "core_sntp_config_defaults.h"
39
40 /**
41  * @brief The version of SNTP supported by the coreSNTP library by complying
42  * with the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
43  */
44 #define SNTP_VERSION                        ( 4U )
45
46 /**
47  * @brief The bit mask for the "Mode" information in the first byte of an SNTP packet.
48  * The "Mode" field occupies bits 0-2 of the byte.
49  * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
50  * for more information.
51  */
52 #define SNTP_MODE_BITS_MASK                 ( 0x07U )
53
54 /**
55  * @brief The value indicating a "client" in the "Mode" field of an SNTP packet.
56  * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
57  * for more information.
58  */
59 #define SNTP_MODE_CLIENT                    ( 3U )
60
61 /**
62  * @brief The value indicating a "server" in the "Mode" field of an SNTP packet.
63  * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
64  * for more information.
65  */
66 #define SNTP_MODE_SERVER                    ( 4U )
67
68 /**
69  * @brief The position of the least significant bit of the "Leap Indicator" field
70  * in first byte of an SNTP packet. The "Leap Indicator" field occupies bits 6-7 of the byte.
71  * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
72  * for more information.
73  */
74 #define SNTP_LEAP_INDICATOR_LSB_POSITION    ( 6 )
75
76 /**
77  * @brief Value of Stratum field in SNTP packet representing a Kiss-o'-Death message
78  * from server.
79  */
80 #define SNTP_KISS_OF_DEATH_STRATUM          ( 0U )
81
82 /**
83  * @brief The position of least significant bit of the "Version" information
84  * in the first byte of an SNTP packet. "Version" field occupies bits 3-5 of
85  * the byte.
86  * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
87  * for more information.
88  */
89 #define SNTP_VERSION_LSB_POSITION           ( 3 )
90
91 /**
92  * @brief The integer value of the Kiss-o'-Death ASCII code, "DENY", used
93  * for comparison with data in an SNTP response.
94  * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
95  * for more information.
96  */
97 #define KOD_CODE_DENY_UINT_VALUE            ( 0x44454e59U )
98
99 /**
100  * @brief The integer value of the Kiss-o'-Death ASCII code, "RSTR", used
101  * for comparison with data in an SNTP response.
102  * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
103  * for more information.
104  */
105 #define KOD_CODE_RSTR_UINT_VALUE            ( 0x52535452U )
106
107 /**
108  * @brief The integer value of the Kiss-o'-Death ASCII code, "RATE", used
109  * for comparison with data in an SNTP response.
110  * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
111  * for more information.
112  */
113 #define KOD_CODE_RATE_UINT_VALUE            ( 0x52415445U )
114
115 /**
116  * @brief Macro to represent exactly half of the total number of seconds in an NTP era.
117  * As 32 bits are used to represent the "seconds" part of an SNTP timestamp, the half of
118  * the total range of seconds in an NTP era is 2^31 seconds, which represents ~68 years.
119  *
120  * @note This macro represents the edge case of calculating the client system clock-offset
121  * relative to server time as the library ASSUMES that the client and server times are within
122  * 2^31 seconds apart in duration. This is done to support calculating clock-offset for the
123  * cases when server and client systems are in adjacent NTP eras, which can occur when NTP time
124  * wraps around in 2036, and the relative NTP era presence of client and server times is
125  * determined based on comparing first order difference values between all possible NTP era
126  * configurations of the systems.
127  */
128 #define CLOCK_OFFSET_MAX_TIME_DIFFERENCE    ( ( ( ( int64_t ) INT32_MAX + 1 ) * 1000 ) )
129
130 /**
131  * @brief Macro to represent the total number of milliseconds that are represented in an
132  * NTP era period. This macro represents a duration of ~136 years.
133  *
134  * @note Rationale for calculation: The "seconds" part of an NTP timestamp is represented in
135  * unsigned 32 bit width, thus, the total range of seconds it represents is 2^32,
136  * i.e. (UINT32_MAX + 1).
137  */
138 #define TOTAL_MILLISECONDS_IN_NTP_ERA       ( ( ( int64_t ) UINT32_MAX + 1 ) * 1000 )
139
140 /**
141  * @brief Structure representing an SNTP packet header.
142  * For more information on SNTP packet format, refer to
143  * [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4).
144  *
145  * @note This does not include extension fields for authentication data
146  * for secure SNTP communication. Authentication data follows the
147  * packet header represented by this structure.
148  */
149 typedef struct SntpPacket
150 {
151     /**
152      * @brief Bits 6-7 leap indicator, bits 3-5 are version number, bits 0-2 are mode
153      */
154     uint8_t leapVersionMode;
155
156     /**
157      * @brief stratum
158      */
159     uint8_t stratum;
160
161     /**
162      * @brief poll interval
163      */
164     uint8_t poll;
165
166     /**
167      * @brief precision
168      */
169     uint8_t precision;
170
171     /**
172      * @brief root delay
173      */
174     uint32_t rootDelay;
175
176     /**
177      * @brief root dispersion
178      */
179     uint32_t rootDispersion;
180
181     /**
182      * @brief reference ID
183      */
184     uint32_t refId;
185
186     /**
187      * @brief reference time
188      */
189     SntpTimestamp_t refTime;
190
191     /**
192      * @brief origin timestamp
193      */
194     SntpTimestamp_t originTime;
195
196     /**
197      * @brief receive timestamp
198      */
199     SntpTimestamp_t receiveTime;
200
201     /**
202      * @brief transmit timestamp
203      */
204     SntpTimestamp_t transmitTime;
205 } SntpPacket_t;
206
207 /**
208  * @brief Utility macro to fill 32-bit integer in word-sized
209  * memory in network byte (or Big Endian) order.
210  *
211  * @param[out] pWordMemory Pointer to the word-sized memory in which
212  * the 32-bit integer will be filled.
213  * @param[in] data The 32-bit integer to fill in the @p wordMemory
214  * in network byte order.
215  *
216  * @note This utility ensures that data is filled in memory
217  * in expected network byte order, as an assignment operation
218  * (like *pWordMemory = word) can cause undesired side-effect
219  * of network-byte ordering getting reversed on Little Endian platforms.
220  */
221 static void fillWordMemoryInNetworkOrder( uint32_t * pWordMemory,
222                                           uint32_t data )
223 {
224     assert( pWordMemory != NULL );
225
226     *( ( uint8_t * ) pWordMemory ) = ( uint8_t ) ( data >> 24 );
227     *( ( uint8_t * ) pWordMemory + 1 ) = ( uint8_t ) ( ( data >> 16 ) & 0x000000FFU );
228     *( ( uint8_t * ) pWordMemory + 2 ) = ( uint8_t ) ( ( data >> 8 ) & 0x000000FFU );
229     *( ( uint8_t * ) pWordMemory + 3 ) = ( uint8_t ) ( ( data ) & 0x000000FFU );
230 }
231
232 /**
233  * @brief Utility macro to generate a 32-bit integer from memory containing
234  * integer in network (or Big Endian) byte order.
235  * @param[in] ptr Pointer to the memory containing 32-bit integer in network
236  * byte order.
237  *
238  * @return The host representation of the 32-bit integer in the passed word
239  * memory.
240  */
241 static uint32_t readWordFromNetworkByteOrderMemory( const uint32_t * ptr )
242 {
243     const uint8_t * pMemStartByte = ( const uint8_t * ) ptr;
244
245     assert( ptr != NULL );
246
247     return ( uint32_t ) ( ( ( uint32_t ) *( pMemStartByte ) << 24 ) |
248                           ( 0x00FF0000U & ( ( uint32_t ) *( pMemStartByte + 1 ) << 16 ) ) |
249                           ( 0x0000FF00U & ( ( uint32_t ) *( pMemStartByte + 2 ) << 8 ) ) |
250                           ( ( uint32_t ) *( pMemStartByte + 3 ) ) );
251 }
252
253 /**
254  * @brief Utility to return absolute (or positively signed) value of an signed
255  * 64 bit integer.
256  *
257  * @param[in] value The integer to return the absolute value of.
258  *
259  * @return The absolute value of @p value.
260  */
261 static int64_t absoluteOf( int64_t value )
262 {
263     return ( value >= 0 ) ? value : ( ( int64_t ) 0 - value );
264 }
265
266 /**
267  * @brief Utility to determine whether a timestamp represents a zero
268  * timestamp value.
269  *
270  * @note This utility is used to determine whether a timestamp value is
271  * invalid. According to the SNTPv4 specification, a zero timestamp value
272  * is considered invalid.
273  *
274  * @param[in] pTime The timestamp whose value is to be inspected for
275  * zero value.
276  *
277  * @return `true` if the timestamp is zero; otherwise `false`.
278  */
279 static bool isZeroTimestamp( const SntpTimestamp_t * pTime )
280 {
281     bool isSecondsZero = ( pTime->seconds == 0U ) ? true : false;
282     bool isFractionsZero = ( pTime->fractions == 0U ) ? true : false;
283
284     return( isSecondsZero && isFractionsZero );
285 }
286
287 /**
288  * @brief Utility to convert the "fractions" part of an SNTP timestamp to milliseconds
289  * duration of time.
290  * @param[in] fractions The fractions value.
291  *
292  * @return The milliseconds equivalent of the @p fractions value.
293  */
294 static uint32_t fractionsToMs( uint32_t fractions )
295 {
296     return( fractions / ( 1000U * SNTP_FRACTION_VALUE_PER_MICROSECOND ) );
297 }
298
299 /**
300  * @brief Utility to safely calculate difference between server and client timestamps and
301  * return the difference in the resolution of milliseconds as a signed 64 bit integer.
302  * The calculated value represents the effective subtraction as ( @p serverTimeSec - @p clientTimeSec ).
303  *
304  * @note This utility SUPPORTS the cases of server and client timestamps being in different NTP eras,
305  * and ASSUMES that the server and client systems are within 68 years of each other.
306  * To handle the case of different NTP eras, this function calculates difference values for all
307  * possible combinations of NTP eras of server and client times (i.e. 1. both timestamps in same era,
308  * 2. server timestamp one era ahead, and 3. client timestamp being one era ahead), and determines
309  * the NTP era configuration by choosing the difference value of the smallest absolute value.
310  *
311  * @param[in] pServerTime The server timestamp.
312  * @param[in] pClientTime The client timestamp.
313  *
314  * @return The calculated difference between server and client times as a signed 64 bit integer.
315  */
316 static int64_t safeTimeDifference( const SntpTimestamp_t * pServerTime,
317                                    const SntpTimestamp_t * pClientTime )
318 {
319     int64_t eraAdjustedDiff = 0;
320
321     /* Convert the timestamps into 64 bit signed integer values of milliseconds. */
322     int64_t serverTime = ( ( int64_t ) pServerTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pServerTime->fractions );
323     int64_t clientTime = ( ( int64_t ) pClientTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pClientTime->fractions );
324
325     /* The difference between the 2 timestamps is calculated by determining the whether the timestamps
326      * are present in the same NTP era or adjacent NTP eras (i.e. the NTP timestamp overflow case). */
327
328     /* First, calculate the first order time difference assuming that server and client times
329      * are in the same NTP era. */
330     int64_t diffWithNoEraAdjustment = serverTime - clientTime;
331
332     /* Store the absolute value of the time difference which will be used for comparison with
333      * different cases of relative NTP era configuration of client and server times. */
334     int64_t absSameEraDiff = absoluteOf( diffWithNoEraAdjustment );
335
336     /* If the absolute difference value is 2^31 seconds, it means that the server and client times are
337      * away by exactly half the range of SNTP timestamp "second" values representable in unsigned 32 bits.
338      * In such a case, irrespective of whether the 2 systems exist in the same or adjacent NTP eras, the
339      * time difference calculated between the systems will ALWAYS yield the same value when viewed from
340      * all NTP era configurations of the times.
341      * For such a case, we will ASSUME that the server time is AHEAD of client time, and thus, generate
342      * a positive clock-offset value.
343      */
344     if( absSameEraDiff == CLOCK_OFFSET_MAX_TIME_DIFFERENCE )
345     {
346         /* It does not matter whether server and client are in the same era for this
347          * special case as the difference value for both same and adjacent eras will yield
348          * the same absolute value of 2^31.*/
349         eraAdjustedDiff = CLOCK_OFFSET_MAX_TIME_DIFFERENCE;
350     }
351     else
352     {
353         /* Determine if server time belongs to an NTP era different than the client time, and accordingly
354          * choose the 64 bit representation of the first order difference to account for the era.
355          * The logic for determining the relative era presence of the timestamps is by calculating the
356          * first order difference (of "Server Time - Client Time") for all the 3 different era combinations
357          * (1. both timestamps in same era, 2. server time one era ahead, 3. client time one era ahead)
358          * and choosing the NTP era configuration that has the smallest first order difference value.
359          */
360         int64_t diffWithServerEraAdjustment = serverTime + TOTAL_MILLISECONDS_IN_NTP_ERA -
361                                               clientTime;                                     /* This helps determine whether server is an
362                                                                                                * era ahead of client time. */
363         int64_t diffWithClientEraAdjustment = serverTime -
364                                               ( TOTAL_MILLISECONDS_IN_NTP_ERA + clientTime ); /* This helps determine whether server is an
365                                                                                                * era behind of client time. */
366
367         /* Store the absolute value equivalents of all the time difference configurations
368          * for easier comparison to smallest value from them. */
369         int64_t absServerEraAheadDiff = absoluteOf( diffWithServerEraAdjustment );
370         int64_t absClientEraAheadDiff = absoluteOf( diffWithClientEraAdjustment );
371
372         /* Determine the correct relative era of client and server times by checking which era
373          * configuration of difference value represents the least difference. */
374         if( ( absSameEraDiff <= absServerEraAheadDiff ) && ( absSameEraDiff <= absClientEraAheadDiff ) )
375         {
376             /* Both server and client times are in the same era. */
377             eraAdjustedDiff = diffWithNoEraAdjustment;
378         }
379         /* Check if server time is an NTP era ahead of client time. */
380         else if( absServerEraAheadDiff < absSameEraDiff )
381         {
382             /* Server time is in NTP era 1 while client time is in NTP era 0. */
383             eraAdjustedDiff = diffWithServerEraAdjustment;
384         }
385         /* Now, we know that the client time is an era ahead of server time. */
386         else
387         {
388             /* Server time is in NTP era 0 while client time is in NTP era 1. */
389             eraAdjustedDiff = diffWithClientEraAdjustment;
390         }
391     }
392
393     return eraAdjustedDiff;
394 }
395
396 /**
397  * @brief Utility to calculate clock offset of system relative to the
398  * server using the on-wire protocol specified in the NTPv4 specification.
399  * For more information on on-wire protocol, refer to
400  * [RFC 5905 Section 8](https://tools.ietf.org/html/rfc5905#section-8).
401  *
402  * @note The following diagram explains the calculation of the clock
403  * offset:
404  *
405  *                 T2      T3
406  *      ---------------------------------   <-----   *SNTP/NTP server*
407  *               /\         \
408  *               /           \
409  *     Request* /             \ *Response*
410  *             /              \/
411  *      ---------------------------------   <-----   *SNTP client*
412  *           T1                T4
413  *
414  *  The four most recent timestamps, T1 through T4, are used to compute
415  *  the clock offset of SNTP client relative to the server where:
416  *
417  *     T1 = Client Request Transmit Time
418  *     T2 = Server Receive Time (of client request)
419  *     T3 = Server Response Transmit Time
420  *     T4 = Client Receive Time (of server response)
421  *
422  *  Clock Offset = T(NTP/SNTP server) - T(SNTP client)
423  *               = [( T2 - T1 ) + ( T3 - T4 )]
424  *                 ---------------------------
425  *                              2
426  *
427  * @note Both NTPv4 and SNTPv4 specifications suggest calculating the
428  * clock offset value, if possible. As the timestamp format uses 64 bit
429  * integer and there exist 2 orders of arithmetic calculations on the
430  * timestamp values (subtraction followed by addition as shown in the
431  * diagram above), the clock offset for the system can be calculated
432  * ONLY if the value can be represented in 62 significant bits and 2 sign
433  * bits i.e. if the system clock is within 34 years (in the future or past)
434  * of the server time.
435  *
436  * @param[in] pClientTxTime The system time of sending the SNTP request.
437  * This is the same as "T1" in the above diagram.
438  * @param[in] pServerRxTime The server time of receiving the SNTP request
439  * packet from the client. This is the same as "T2" in the above diagram.
440  * @param[in] pServerTxTime The server time of sending the SNTP response
441  * packet. This is the same as "T3" in the above diagram.
442  * @param[in] pClientRxTime The system time of receiving the SNTP response
443  * from the server. This is the same as "T4" in the above diagram.
444  * @param[out] pClockOffset This will be filled with the calculated offset value
445  * of the system clock relative to the server time with the assumption that the
446  * system clock is within 68 years of server time.
447  */
448 static void calculateClockOffset( const SntpTimestamp_t * pClientTxTime,
449                                   const SntpTimestamp_t * pServerRxTime,
450                                   const SntpTimestamp_t * pServerTxTime,
451                                   const SntpTimestamp_t * pClientRxTime,
452                                   int64_t * pClockOffset )
453 {
454     /* Variable for storing the first-order difference between timestamps. */
455     int64_t firstOrderDiffSend = 0;
456     int64_t firstOrderDiffRecv = 0;
457
458     assert( pClientTxTime != NULL );
459     assert( pServerRxTime != NULL );
460     assert( pServerTxTime != NULL );
461     assert( pClientRxTime != NULL );
462     assert( pClockOffset != NULL );
463
464     /* Perform first order difference of timestamps on the network send path i.e. T2 - T1.
465      * Note: The calculated difference value will always represent years in the range of
466      *[-68 years, +68 years]. */
467     firstOrderDiffSend = safeTimeDifference( pServerRxTime, pClientTxTime );
468
469     /* Perform first order difference of timestamps on the network receive path i.e. T3 - T4.
470      * Note: The calculated difference value will always represent years in the range of
471      *[-68 years, +68 years]. */
472     firstOrderDiffRecv = safeTimeDifference( pServerTxTime, pClientRxTime );
473
474     /* Now calculate the system clock-offset relative to server time as the average of the
475      * first order difference of timestamps in both directions of network path.
476      * Note: This will ALWAYS represent offset in the range of [-68 years, +68 years]. */
477     *pClockOffset = ( firstOrderDiffSend + firstOrderDiffRecv ) / 2;
478 }
479
480 /**
481  * @brief Parse a SNTP response packet by determining whether it is a rejected
482  * or accepted response to an SNTP request, and accordingly, populate the
483  * @p pParsedResponse parameter with the parsed data.
484  *
485  * @note If the server has rejected the request with the a Kiss-o'-Death message,
486  * then this function will set the associated rejection code in the output parameter
487  * while setting the remaining members to zero.
488  * If the server has accepted the time request, then the function will set the
489  * rejectedResponseCode member of the output parameter to #SNTP_KISS_OF_DEATH_CODE_NONE,
490  * and set the other the members with appropriate data extracted from the response
491  * packet.
492  *
493  * @param[in] pResponsePacket The SNTP response packet from server to parse.
494  * @param[in] pRequestTxTime The system time (in SNTP timestamp format) of
495  * sending the SNTP request to server.
496  * @param[in] pResponseRxTime The system time (in SNTP timestamp format) of
497  * receiving the SNTP response from server.
498  * @param[out] pParsedResponse The parameter that will be populated with data
499  * parsed from the response packet, @p pResponsePacket.
500  *
501  * @return This function returns one of the following:
502  * - #SntpSuccess if the server response does not represent a Kiss-o'-Death
503  * message.
504  * - #SntpRejectedResponseChangeServer if the server rejected with a code
505  * indicating that client cannot be retry requests to it.
506  * - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
507  * indicating that client should back-off before retrying request.
508  * - #SntpRejectedResponseOtherCode if the server rejected with a code
509  * other than "DENY", "RSTR" and "RATE".
510  */
511 static SntpStatus_t parseValidSntpResponse( const SntpPacket_t * pResponsePacket,
512                                             const SntpTimestamp_t * pRequestTxTime,
513                                             const SntpTimestamp_t * pResponseRxTime,
514                                             SntpResponseData_t * pParsedResponse )
515 {
516     SntpStatus_t status = SntpSuccess;
517
518     assert( pResponsePacket != NULL );
519     assert( pResponseRxTime != NULL );
520     assert( pParsedResponse != NULL );
521
522     /* Clear the output parameter memory to zero. */
523     ( void ) memset( pParsedResponse, 0, sizeof( *pParsedResponse ) );
524
525     /* Determine if the server has accepted or rejected the request for time. */
526     if( pResponsePacket->stratum == SNTP_KISS_OF_DEATH_STRATUM )
527     {
528         /* Server has sent a Kiss-o'-Death message i.e. rejected the request. */
529
530         /* Extract the kiss-code sent by the server from the "Reference ID" field
531          * of the SNTP packet. */
532         pParsedResponse->rejectedResponseCode =
533             readWordFromNetworkByteOrderMemory( &pResponsePacket->refId );
534
535         /* Determine the return code based on the Kiss-o'-Death code. */
536         switch( pParsedResponse->rejectedResponseCode )
537         {
538             case KOD_CODE_DENY_UINT_VALUE:
539             case KOD_CODE_RSTR_UINT_VALUE:
540                 status = SntpRejectedResponseChangeServer;
541                 break;
542
543             case KOD_CODE_RATE_UINT_VALUE:
544                 status = SntpRejectedResponseRetryWithBackoff;
545                 break;
546
547             default:
548                 status = SntpRejectedResponseOtherCode;
549                 break;
550         }
551     }
552     else
553     {
554         /* Server has responded successfully to the time request. */
555
556         SntpTimestamp_t serverRxTime;
557
558         /* Map of integer value to SntpLeapSecondInfo_t enumeration type for the
559          * "Leap Indicator" field in the first byte of an SNTP packet.
560          * Note: This map is used to not violate MISRA Rule 10.5 when directly
561          * converting an integer to enumeration type.
562          */
563         const SntpLeapSecondInfo_t leapIndicatorTypeMap[] =
564         {
565             NoLeapSecond,
566             LastMinuteHas61Seconds,
567             LastMinuteHas59Seconds,
568             AlarmServerNotSynchronized
569         };
570
571         /* Set the Kiss-o'-Death code value to NULL as server has responded favorably
572          * to the time request. */
573         pParsedResponse->rejectedResponseCode = SNTP_KISS_OF_DEATH_CODE_NONE;
574
575         /* Fill the output parameter with the server time which is the
576          * "transmit" time in the response packet. */
577         pParsedResponse->serverTime.seconds =
578             readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.seconds );
579         pParsedResponse->serverTime.fractions =
580             readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.fractions );
581
582         /* Extract information of any upcoming leap second from the response. */
583         pParsedResponse->leapSecondType = leapIndicatorTypeMap[
584             ( pResponsePacket->leapVersionMode >> SNTP_LEAP_INDICATOR_LSB_POSITION ) ];
585
586         /* Store the "receive" time in SNTP response packet in host order. */
587         serverRxTime.seconds =
588             readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.seconds );
589         serverRxTime.fractions =
590             readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.fractions );
591
592         /* Calculate system clock offset relative to server time, if possible, within
593          * the 64 bit integer width of the SNTP timestamp. */
594         calculateClockOffset( pRequestTxTime,
595                               &serverRxTime,
596                               &pParsedResponse->serverTime,
597                               pResponseRxTime,
598                               &pParsedResponse->clockOffsetMs );
599     }
600
601     return status;
602 }
603
604
605 SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
606                                     uint32_t randomNumber,
607                                     void * pBuffer,
608                                     size_t bufferSize )
609 {
610     SntpStatus_t status = SntpSuccess;
611
612     if( pRequestTime == NULL )
613     {
614         status = SntpErrorBadParameter;
615     }
616     else if( pBuffer == NULL )
617     {
618         status = SntpErrorBadParameter;
619     }
620     else if( bufferSize < SNTP_PACKET_BASE_SIZE )
621     {
622         status = SntpErrorBufferTooSmall;
623     }
624
625     /* Zero timestamps for client request time is not allowed to protect against
626      * attack spoofing server response containing zero value for "originate timestamp".
627      * Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
628      * matches the "transmit timestamp" in corresponding client request packet. */
629     else if( isZeroTimestamp( pRequestTime ) == true )
630     {
631         status = SntpErrorBadParameter;
632     }
633     else
634     {
635         /* MISRA Ref 11.5.1 [Void pointer assignment] */
636         /* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
637         /* coverity[misra_c_2012_rule_11_5_violation] */
638         SntpPacket_t * pRequestPacket = ( SntpPacket_t * ) pBuffer;
639
640         /* Fill the buffer with zero as most fields are zero for a standard SNTP
641          * request packet.*/
642         ( void ) memset( pBuffer, 0, sizeof( SntpPacket_t ) );
643
644         /* Set the first byte of the request packet for "Version" and "Mode" fields */
645         pRequestPacket->leapVersionMode = 0U /* Leap Indicator */ |
646                                           ( SNTP_VERSION << SNTP_VERSION_LSB_POSITION ) /* Version Number */ |
647                                           SNTP_MODE_CLIENT /* Mode */;
648
649
650         /* Add passed random number to non-significant bits of the fractions part
651          * of the transmit timestamp.
652          * This is suggested by the SNTPv4 (and NTPv4) specification(s)
653          * to protect against replay attacks. Refer to RFC 4330 Section 3 for
654          * more information.
655          * Adding random bits to the least significant 16 bits of the fractions
656          * part of the timestamp affects only ~15 microseconds of information
657          * (calculated as 0xFFFF * 232 picoseconds).
658          */
659         pRequestTime->fractions = ( pRequestTime->fractions
660                                     | ( randomNumber >> 16 ) );
661
662         /* Update the request buffer with request timestamp in network byte order. */
663         fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.seconds,
664                                       pRequestTime->seconds );
665         fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.fractions,
666                                       pRequestTime->fractions );
667     }
668
669     return status;
670 }
671
672
673 SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
674                                        const SntpTimestamp_t * pResponseRxTime,
675                                        const void * pResponseBuffer,
676                                        size_t bufferSize,
677                                        SntpResponseData_t * pParsedResponse )
678 {
679     SntpStatus_t status = SntpSuccess;
680     /* MISRA Ref 11.5.1 [Void pointer assignment] */
681     /* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
682     /* coverity[misra_c_2012_rule_11_5_violation] */
683     const SntpPacket_t * pResponsePacket = ( const SntpPacket_t * ) pResponseBuffer;
684
685     if( ( pRequestTime == NULL ) || ( pResponseRxTime == NULL ) ||
686         ( pResponseBuffer == NULL ) || ( pParsedResponse == NULL ) )
687     {
688         status = SntpErrorBadParameter;
689     }
690     else if( bufferSize < SNTP_PACKET_BASE_SIZE )
691     {
692         status = SntpErrorBufferTooSmall;
693     }
694
695     /* Zero timestamps for client request time is not allowed to protect against
696      * attack spoofing server response containing zero value for "originate timestamp".
697      * Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
698      * matches the "transmit timestamp" in corresponding client request packet. */
699     else if( isZeroTimestamp( pRequestTime ) == true )
700     {
701         status = SntpErrorBadParameter;
702     }
703     /* Check if the packet represents a server in the "Mode" field. */
704     else if( ( pResponsePacket->leapVersionMode & SNTP_MODE_BITS_MASK ) != SNTP_MODE_SERVER )
705     {
706         status = SntpInvalidResponse;
707     }
708
709     /* Check if any of the timestamps in the response packet are zero, which is invalid.
710      * Note: This is done to protect against a nuanced server spoofing attack where if the
711      * SNTP client resets its internal state of "Client transmit timestamp" (OR "originate
712      * timestamp" from server perspective) to zero as a protection against replay attack, an
713      * an attacker with this knowledge of the client operation can spoof a server response
714      * containing the "originate timestamp" as zero. Thus, to protect against such attack,
715      * a server response packet with any zero timestamp is rejected. */
716     else if( ( isZeroTimestamp( &pResponsePacket->originTime ) == true ) ||
717              ( isZeroTimestamp( &pResponsePacket->receiveTime ) == true ) ||
718              ( isZeroTimestamp( &pResponsePacket->transmitTime ) == true ) )
719     {
720         status = SntpInvalidResponse;
721     }
722
723
724     /* Validate that the server has sent the client's request timestamp in the
725      * "originate" timestamp field of the response. */
726     else if( ( pRequestTime->seconds !=
727                readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.seconds ) ) ||
728              ( pRequestTime->fractions !=
729                readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.fractions ) ) )
730
731     {
732         status = SntpInvalidResponse;
733     }
734     else
735     {
736         /* As the response packet is valid, parse more information from it and
737          * populate the output parameter. */
738
739         status = parseValidSntpResponse( pResponsePacket,
740                                          pRequestTime,
741                                          pResponseRxTime,
742                                          pParsedResponse );
743     }
744
745     return status;
746 }
747
748 SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
749                                          uint16_t desiredAccuracy,
750                                          uint32_t * pPollInterval )
751 {
752     SntpStatus_t status = SntpSuccess;
753
754     if( ( clockFreqTolerance == 0U ) || ( desiredAccuracy == 0U ) || ( pPollInterval == NULL ) )
755     {
756         status = SntpErrorBadParameter;
757     }
758     else
759     {
760         uint32_t exactIntervalForAccuracy = 0U;
761         uint8_t log2PollInterval = 0U;
762
763         /* Calculate the poll interval required for achieving the exact desired clock accuracy
764          * with the following formulae:
765          *
766          * System Clock Drift Rate ( microseconds / second ) = Clock Frequency Tolerance (in PPM )
767          * Maximum Clock Drift ( milliseconds ) = Desired Accuracy ( milliseconds )
768          *
769          * Poll Interval ( seconds ) =     Maximum Clock Drift
770          *                              ---------------------------
771          *                                System Clock Drift Rate
772          *
773          *                           =  Maximum Drift ( milliseconds ) * 1000 ( microseconds / millisecond )
774          *                             ------------------------------------------------------------------------
775          *                                        System Clock Drift Rate ( microseconds / second )
776          *
777          *                           =    Desired Accuracy * 1000
778          *                             ------------------------------
779          *                               Clock Frequency Tolerance
780          */
781         exactIntervalForAccuracy = ( ( uint32_t ) desiredAccuracy * 1000U ) / clockFreqTolerance;
782
783         /* Check if calculated poll interval value falls in the supported range of seconds. */
784         if( exactIntervalForAccuracy == 0U )
785         {
786             /* Poll interval value is less than 1 second, and is not supported by the function. */
787             status = SntpZeroPollInterval;
788         }
789         else
790         {
791             /* To calculate the log 2 value of the exact poll interval value, first determine the highest
792              * bit set in the value. */
793             while( exactIntervalForAccuracy != 0U )
794             {
795                 log2PollInterval++;
796                 exactIntervalForAccuracy /= 2U;
797             }
798
799             /* Convert the highest bit in the exact poll interval value to the nearest integer
800              * value lower or equal to the log2 of the exact poll interval value. */
801             log2PollInterval--;
802
803             /* Calculate the poll interval as the closest exponent of 2 value that achieves
804              * equal or higher accuracy than the desired accuracy. */
805             *pPollInterval = ( ( ( uint32_t ) 1U ) << log2PollInterval );
806         }
807     }
808
809     return status;
810 }
811
812 SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
813                                      uint32_t * pUnixTimeSecs,
814                                      uint32_t * pUnixTimeMicrosecs )
815 {
816     SntpStatus_t status = SntpSuccess;
817
818     if( ( pSntpTime == NULL ) || ( pUnixTimeSecs == NULL ) || ( pUnixTimeMicrosecs == NULL ) )
819     {
820         status = SntpErrorBadParameter;
821     }
822     /* Check if passed time does not lie in the [UNIX epoch in 1970, UNIX time overflow in 2038] time range. */
823     else if( ( pSntpTime->seconds > SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS ) &&
824              ( pSntpTime->seconds < SNTP_TIME_AT_UNIX_EPOCH_SECS ) )
825     {
826         /* The SNTP timestamp is outside the supported time range for conversion. */
827         status = SntpErrorTimeNotSupported;
828     }
829     else
830     {
831         /* Handle case when timestamp represents date in SNTP era 1
832          * (i.e. time from 7 Feb 2036 6:28:16 UTC onwards). */
833         if( pSntpTime->seconds <= SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS )
834         {
835             /* Unix Time ( seconds ) = Seconds Duration in
836              *                         [UNIX epoch, SNTP Era 1 Epoch Time]
837              *                                        +
838              *                           Sntp Time since Era 1 Epoch
839              */
840             *pUnixTimeSecs = UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME + pSntpTime->seconds;
841         }
842         /* Handle case when SNTP timestamp is in SNTP era 1 time range. */
843         else
844         {
845             *pUnixTimeSecs = pSntpTime->seconds - SNTP_TIME_AT_UNIX_EPOCH_SECS;
846         }
847
848         /* Convert SNTP fractions to microseconds for UNIX time. */
849         *pUnixTimeMicrosecs = pSntpTime->fractions / SNTP_FRACTION_VALUE_PER_MICROSECOND;
850     }
851
852     return status;
853 }