1 /*
2  *  Copyright (c) 2018, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef SNTP_CLIENT_HPP_
30 #define SNTP_CLIENT_HPP_
31 
32 #include "openthread-core-config.h"
33 
34 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
35 
36 #include <openthread/sntp.h>
37 
38 #include "common/clearable.hpp"
39 #include "common/message.hpp"
40 #include "common/non_copyable.hpp"
41 #include "common/timer.hpp"
42 #include "net/ip6.hpp"
43 #include "net/netif.hpp"
44 
45 /**
46  * @file
47  *   This file includes definitions for the SNTP client.
48  */
49 
50 namespace ot {
51 namespace Sntp {
52 
53 /**
54  * Implements SNTP client.
55  *
56  */
57 class Client : private NonCopyable
58 {
59 public:
60     typedef otSntpResponseHandler ResponseHandler; ///< Response handler callback.
61 
62     /**
63      * Initializes the object.
64      *
65      * @param[in]  aInstance     A reference to the OpenThread instance.
66      *
67      */
68     explicit Client(Instance &aInstance);
69 
70     /**
71      * Starts the SNTP client.
72      *
73      * @retval kErrorNone     Successfully started the SNTP client.
74      * @retval kErrorAlready  The socket is already open.
75      */
76     Error Start(void);
77 
78     /**
79      * Stops the SNTP client.
80      *
81      * @retval kErrorNone  Successfully stopped the SNTP client.
82      *
83      */
84     Error Stop(void);
85 
86     /**
87      * Returns the unix era number.
88      *
89      * @returns The unix era number.
90      *
91      */
GetUnixEra(void) const92     uint32_t GetUnixEra(void) const { return mUnixEra; }
93 
94     /**
95      * Sets the unix era number.
96      *
97      * @param[in]  aUnixEra  The unix era number.
98      *
99      */
SetUnixEra(uint32_t aUnixEra)100     void SetUnixEra(uint32_t aUnixEra) { mUnixEra = aUnixEra; }
101 
102     /**
103      * Sends an SNTP query.
104      *
105      * @param[in]  aQuery    A pointer to specify SNTP query parameters.
106      * @param[in]  aHandler  A function pointer that shall be called on response reception or time-out.
107      * @param[in]  aContext  A pointer to arbitrary context information.
108      *
109      * @retval kErrorNone         Successfully sent SNTP query.
110      * @retval kErrorNoBufs       Failed to allocate retransmission data.
111      * @retval kErrorInvalidArgs  Invalid arguments supplied.
112      *
113      */
114     Error Query(const otSntpQuery *aQuery, ResponseHandler aHandler, void *aContext);
115 
116 private:
117     static constexpr uint32_t kTimeAt1970 = 2208988800UL; // num seconds between 1st Jan 1900 and 1st Jan 1970.
118 
119     static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_SNTP_CLIENT_RESPONSE_TIMEOUT;
120     static constexpr uint8_t  kMaxRetransmit   = OPENTHREAD_CONFIG_SNTP_CLIENT_MAX_RETRANSMIT;
121 
122     OT_TOOL_PACKED_BEGIN
123     class Header : public Clearable<Header>
124     {
125     public:
126         enum Mode : uint8_t
127         {
128             kModeClient = 3,
129             kModeServer = 4,
130         };
131 
132         static constexpr uint8_t kKissCodeLength = 4;
133 
Init(void)134         void Init(void)
135         {
136             Clear();
137             mFlags = (kNtpVersion << kVersionOffset | kModeClient << kModeOffset);
138         }
139 
GetFlags(void) const140         uint8_t GetFlags(void) const { return mFlags; }
SetFlags(uint8_t aFlags)141         void    SetFlags(uint8_t aFlags) { mFlags = aFlags; }
142 
GetMode(void) const143         Mode GetMode(void) const { return static_cast<Mode>((mFlags & kModeMask) >> kModeOffset); }
144 
GetStratum(void) const145         uint8_t GetStratum(void) const { return mStratum; }
SetStratum(uint8_t aStratum)146         void    SetStratum(uint8_t aStratum) { mStratum = aStratum; }
147 
GetPoll(void) const148         uint8_t GetPoll(void) const { return mPoll; }
SetPoll(uint8_t aPoll)149         void    SetPoll(uint8_t aPoll) { mPoll = aPoll; }
150 
GetPrecision(void) const151         uint8_t GetPrecision(void) const { return mPrecision; }
SetPrecision(uint8_t aPrecision)152         void    SetPrecision(uint8_t aPrecision) { mPrecision = aPrecision; }
153 
GetRootDelay(void) const154         uint32_t GetRootDelay(void) const { return BigEndian::HostSwap32(mRootDelay); }
SetRootDelay(uint32_t aRootDelay)155         void     SetRootDelay(uint32_t aRootDelay) { mRootDelay = BigEndian::HostSwap32(aRootDelay); }
156 
GetRootDispersion(void) const157         uint32_t GetRootDispersion(void) const { return BigEndian::HostSwap32(mRootDispersion); }
SetRootDispersion(uint32_t aRootDispersion)158         void SetRootDispersion(uint32_t aRootDispersion) { mRootDispersion = BigEndian::HostSwap32(aRootDispersion); }
159 
GetReferenceId(void) const160         uint32_t GetReferenceId(void) const { return BigEndian::HostSwap32(mReferenceId); }
SetReferenceId(uint32_t aReferenceId)161         void     SetReferenceId(uint32_t aReferenceId) { mReferenceId = BigEndian::HostSwap32(aReferenceId); }
162 
GetKissCode(void)163         char *GetKissCode(void) { return reinterpret_cast<char *>(&mReferenceId); }
164 
GetReferenceTimestampSeconds(void) const165         uint32_t GetReferenceTimestampSeconds(void) const { return BigEndian::HostSwap32(mReferenceTimestampSeconds); }
SetReferenceTimestampSeconds(uint32_t aTimestamp)166         void     SetReferenceTimestampSeconds(uint32_t aTimestamp)
167         {
168             mReferenceTimestampSeconds = BigEndian::HostSwap32(aTimestamp);
169         }
170 
GetReferenceTimestampFraction(void) const171         uint32_t GetReferenceTimestampFraction(void) const
172         {
173             return BigEndian::HostSwap32(mReferenceTimestampFraction);
174         }
SetReferenceTimestampFraction(uint32_t aFraction)175         void SetReferenceTimestampFraction(uint32_t aFraction)
176         {
177             mReferenceTimestampFraction = BigEndian::HostSwap32(aFraction);
178         }
179 
GetOriginateTimestampSeconds(void) const180         uint32_t GetOriginateTimestampSeconds(void) const { return BigEndian::HostSwap32(mOriginateTimestampSeconds); }
SetOriginateTimestampSeconds(uint32_t aTimestamp)181         void     SetOriginateTimestampSeconds(uint32_t aTimestamp)
182         {
183             mOriginateTimestampSeconds = BigEndian::HostSwap32(aTimestamp);
184         }
185 
GetOriginateTimestampFraction(void) const186         uint32_t GetOriginateTimestampFraction(void) const
187         {
188             return BigEndian::HostSwap32(mOriginateTimestampFraction);
189         }
SetOriginateTimestampFraction(uint32_t aFraction)190         void SetOriginateTimestampFraction(uint32_t aFraction)
191         {
192             mOriginateTimestampFraction = BigEndian::HostSwap32(aFraction);
193         }
194 
GetReceiveTimestampSeconds(void) const195         uint32_t GetReceiveTimestampSeconds(void) const { return BigEndian::HostSwap32(mReceiveTimestampSeconds); }
SetReceiveTimestampSeconds(uint32_t aTimestamp)196         void     SetReceiveTimestampSeconds(uint32_t aTimestamp)
197         {
198             mReceiveTimestampSeconds = BigEndian::HostSwap32(aTimestamp);
199         }
200 
GetReceiveTimestampFraction(void) const201         uint32_t GetReceiveTimestampFraction(void) const { return BigEndian::HostSwap32(mReceiveTimestampFraction); }
SetReceiveTimestampFraction(uint32_t aFraction)202         void     SetReceiveTimestampFraction(uint32_t aFraction)
203         {
204             mReceiveTimestampFraction = BigEndian::HostSwap32(aFraction);
205         }
206 
GetTransmitTimestampSeconds(void) const207         uint32_t GetTransmitTimestampSeconds(void) const { return BigEndian::HostSwap32(mTransmitTimestampSeconds); }
SetTransmitTimestampSeconds(uint32_t aTimestamp)208         void     SetTransmitTimestampSeconds(uint32_t aTimestamp)
209         {
210             mTransmitTimestampSeconds = BigEndian::HostSwap32(aTimestamp);
211         }
212 
GetTransmitTimestampFraction(void) const213         uint32_t GetTransmitTimestampFraction(void) const { return BigEndian::HostSwap32(mTransmitTimestampFraction); }
SetTransmitTimestampFraction(uint32_t aFraction)214         void     SetTransmitTimestampFraction(uint32_t aFraction)
215         {
216             mTransmitTimestampFraction = BigEndian::HostSwap32(aFraction);
217         }
218 
219     private:
220         static constexpr uint8_t kNtpVersion    = 4;                      // Current NTP version.
221         static constexpr uint8_t kLeapOffset    = 6;                      // Leap Indicator field offset.
222         static constexpr uint8_t kLeapMask      = 0x03 << kLeapOffset;    // Leap Indicator field mask.
223         static constexpr uint8_t kVersionOffset = 3;                      // Version field offset.
224         static constexpr uint8_t kVersionMask   = 0x07 << kVersionOffset; // Version field mask.
225         static constexpr uint8_t kModeOffset    = 0;                      // Mode field offset.
226         static constexpr uint8_t kModeMask      = 0x07 << kModeOffset;    // Mode filed mask.
227 
228         uint8_t  mFlags;                      // SNTP flags: LI Leap Indicator, VN Version Number and Mode.
229         uint8_t  mStratum;                    // Packet Stratum.
230         uint8_t  mPoll;                       // Maximum interval between successive messages, in log2 seconds.
231         uint8_t  mPrecision;                  // The precision of the system clock, in log2 seconds.
232         uint32_t mRootDelay;                  // Total round-trip delay to the reference clock, in NTP short format.
233         uint32_t mRootDispersion;             // Total dispersion to the reference clock.
234         uint32_t mReferenceId;                // ID identifying the particular server or reference clock.
235         uint32_t mReferenceTimestampSeconds;  // Time the system clock was last set or corrected (NTP format).
236         uint32_t mReferenceTimestampFraction; // Fraction part of above value.
237         uint32_t mOriginateTimestampSeconds;  // Time at client when request departed for the server (NTP format).
238         uint32_t mOriginateTimestampFraction; // Fraction part of above value.
239         uint32_t mReceiveTimestampSeconds;    // Time at server when request arrived from the client (NTP format).
240         uint32_t mReceiveTimestampFraction;   // Fraction part of above value.
241         uint32_t mTransmitTimestampSeconds;   // Time at server when the response left for the client (NTP format).
242         uint32_t mTransmitTimestampFraction;  // Fraction part of above value.
243     } OT_TOOL_PACKED_END;
244 
245     class QueryMetadata
246     {
247     public:
AppendTo(Message & aMessage) const248         Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); }
ReadFrom(const Message & aMessage)249         void  ReadFrom(const Message &aMessage)
250         {
251             IgnoreError(aMessage.Read(aMessage.GetLength() - sizeof(*this), *this));
252         }
253 
UpdateIn(Message & aMessage) const254         void UpdateIn(Message &aMessage) const { aMessage.Write(aMessage.GetLength() - sizeof(*this), *this); }
255 
256         uint32_t                  mTransmitTimestamp;   // Time at client when request departed for server
257         Callback<ResponseHandler> mResponseHandler;     // Response handler callback
258         TimeMilli                 mTransmissionTime;    // Time when the timer should shoot for this message
259         Ip6::Address              mSourceAddress;       // Source IPv6 address
260         Ip6::Address              mDestinationAddress;  // Destination IPv6 address
261         uint16_t                  mDestinationPort;     // Destination UDP port
262         uint8_t                   mRetransmissionCount; // Number of retransmissions
263     };
264 
265     Message *NewMessage(const Header &aHeader);
266     Message *CopyAndEnqueueMessage(const Message &aMessage, const QueryMetadata &aQueryMetadata);
267     void     DequeueMessage(Message &aMessage);
268     Error    SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
269     void     SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
270 
271     Message *FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata);
272     void FinalizeSntpTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, uint64_t aTime, Error aResult);
273 
274     void HandleRetransmissionTimer(void);
275 
276     static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
277     void        HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
278 
279     using RetxTimer = TimerMilliIn<Client, &Client::HandleRetransmissionTimer>;
280 
281     Ip6::Udp::Socket mSocket;
282 
283     MessageQueue mPendingQueries;
284     RetxTimer    mRetransmissionTimer;
285 
286     uint32_t mUnixEra;
287 };
288 
289 } // namespace Sntp
290 } // namespace ot
291 
292 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
293 
294 #endif // SNTP_CLIENT_HPP_
295