[sntp] initial implementation of sntp client (#3237)

This commit is contained in:
Łukasz Duda
2018-11-03 08:11:05 -07:00
committed by Jonathan Hui
parent 8f25db11cc
commit 4a89cc63b6
17 changed files with 1411 additions and 7 deletions
+5 -4
View File
@@ -75,6 +75,7 @@ python --version || die
--enable-radio-only \
--enable-raw-link-api \
--enable-service \
--enable-sntp-client \
--enable-udp-proxy \
--with-examples=posix || die
@@ -118,7 +119,7 @@ python --version || die
git checkout -- . || die
git clean -xfd || die
./bootstrap || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die
arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die
@@ -196,7 +197,7 @@ python --version || die
git checkout -- . || die
git clean -xfd || die
./bootstrap || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die
arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die
@@ -274,7 +275,7 @@ python --version || die
git checkout -- . || die
git clean -xfd || die
./bootstrap || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die
arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die
@@ -363,7 +364,7 @@ python --version || die
git checkout -- . || die
git clean -xfd || die
./bootstrap || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
BORDER_ROUTER=1 COAP=1 COMMISSIONER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 FULL_LOGS=1 JOINER=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_PROXY=1 make -f examples/Makefile-nrf52840 || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-ftd || die
arm-none-eabi-size output/nrf52840/bin/ot-cli-mtd || die
arm-none-eabi-size output/nrf52840/bin/ot-ncp-ftd || die
+31
View File
@@ -1336,6 +1336,36 @@ AC_SUBST(OPENTHREAD_ENABLE_DNS_CLIENT)
AM_CONDITIONAL([OPENTHREAD_ENABLE_DNS_CLIENT], [test "${enable_dns_client}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_DNS_CLIENT],[${OPENTHREAD_ENABLE_DNS_CLIENT}],[Define to 1 if you want to enable DNS Client])
#
# SNTP Client
#
AC_ARG_ENABLE(sntp_client,
[AS_HELP_STRING([--enable-sntp-client],[Enable SNTP client support @<:@default=no@:>@.])],
[
case "${enableval}" in
no|yes)
enable_sntp_client=${enableval}
;;
*)
AC_MSG_ERROR([Invalid value ${enable_sntp_client} for --enable-sntp-client])
;;
esac
],
[enable_sntp_client=no])
if test "$enable_sntp_client" = "yes"; then
OPENTHREAD_ENABLE_SNTP_CLIENT=1
else
OPENTHREAD_ENABLE_SNTP_CLIENT=0
fi
AC_SUBST(OPENTHREAD_ENABLE_SNTP_CLIENT)
AM_CONDITIONAL([OPENTHREAD_ENABLE_SNTP_CLIENT], [test "${enable_sntp_client}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_SNTP_CLIENT],[${OPENTHREAD_ENABLE_SNTP_CLIENT}],[Define to 1 if you want to enable SNTP Client])
#
# Application CoAP
#
@@ -1915,6 +1945,7 @@ AC_MSG_NOTICE([
OpenThread DHCPv6 Server support : ${enable_dhcp6_server}
OpenThread DHCPv6 Client support : ${enable_dhcp6_client}
OpenThread DNS Client support : ${enable_dns_client}
OpenThread SNTP Client support : ${enable_sntp_client}
OpenThread Application CoAP support : ${enable_application_coap}
OpenThread Application CoAP Secure support: ${enable_application_coap_secure}
OpenThread Raw Link-Layer support : ${enable_raw_link_api}
+4
View File
@@ -119,6 +119,10 @@ ifeq ($(SERVICE),1)
configure_OPTIONS += --enable-service
endif
ifeq ($(SNTP_CLIENT),1)
configure_OPTIONS += --enable-sntp-client
endif
ifeq ($(UDP_PROXY),1)
configure_OPTIONS += --enable-udp-proxy
endif
+1
View File
@@ -78,6 +78,7 @@ openthread_headers = \
netdata.h \
network_time.h \
server.h \
sntp.h \
tasklet.h \
thread.h \
thread_ftd.h \
+1 -1
View File
@@ -314,7 +314,7 @@ typedef enum otError
OT_ERROR_NOT_CAPABLE = 27,
/**
* Coap response or acknowledgment or DNS response not received.
* Coap response or acknowledgment or DNS, SNTP response not received.
*/
OT_ERROR_RESPONSE_TIMEOUT = 28,
+121
View File
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* @brief
* This file defines the top-level sntp functions for the OpenThread library.
*/
#ifndef OPENTHREAD_SNTP_H_
#define OPENTHREAD_SNTP_H_
#include <stdint.h>
#include <openthread/ip6.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup api-sntp
*
* @brief
* This module includes functions that control SNTP communication.
*
* @{
*
*/
#define OT_SNTP_DEFAULT_SERVER_IP "2001:4860:4806:8::" ///< Defines default SNTP Server address - Google NTP Server.
#define OT_SNTP_DEFAULT_SERVER_PORT 123 ///< Defines default SNTP Server port.
/**
* This structure implements SNTP Query parameters.
*
*/
typedef struct otSntpQuery
{
const otMessageInfo *mMessageInfo; ///< A reference to the message info related with SNTP Server.
} otSntpQuery;
/**
* This function pointer is called when a SNTP response is received.
*
* @param[in] aContext A pointer to application-specific context.
* @param[in] aTime Specifies the at the server when the response left for the client, in NTP timestamp format.
* @param[in] aResult A result of the SNTP transaction.
*
* @retval OT_ERROR_NONE A response was received successfully and time is provided
* in @p aTime.
* @retval OT_ERROR_ABORT A SNTP transaction was aborted by stack.
* @retval OT_ERROR_BUSY The Kiss-o'-death packet has been received.
* @retval OT_ERROR_RESPONSE_TIMEOUT No SNTP response has been received within timeout.
* @retval OT_ERROR_FAILED A response was received but contains incorrect data.
*
*/
typedef void (*otSntpResponseHandler)(void *aContext, uint64_t aTime, otError aResult);
/**
* This function sends a SNTP query.
*
* This function is available only if feature `OPENTHREAD_ENABLE_SNTP_CLIENT` is enabled.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aQuery A pointer to specify SNTP query parameters.
* @param[in] aHandler A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
*
*/
otError otSntpClientQuery(otInstance * aInstance,
const otSntpQuery * aQuery,
otSntpResponseHandler aHandler,
void * aContext);
/**
* This function sets the unix era number.
*
* The default value of unix era is set to 0. The subsequent eras start after year 2106.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aUnixEra Unix era number.
*
*/
void otSntpClientSetUnixEra(otInstance *aInstance, uint32_t aUnixEra);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_SNTP_H_
+19
View File
@@ -67,6 +67,7 @@ OpenThread test scripts use the CLI to execute test cases.
* [routerupgradethreshold](#routerupgradethreshold)
* [scan](#scan)
* [singleton](#singleton)
* [sntp](#sntp-query-sntp-server-ip-sntp-server-port)
* [state](#state)
* [thread](#thread-start)
* [txpower](#txpower)
@@ -1735,6 +1736,24 @@ true or false
Done
```
### sntp query \[SNTP server IP\] \[SNTP server port\]
Send SNTP Query to obtain current unix epoch time (from 1st January 1970).
The latter two parameters have following default values:
* NTP server IP: 2001:4860:4806:8:: (Google IPv6 NTP Server)
* NTP server port: 123
```bash
> sntp query
> SNTP response - Unix time: 1540894725 (era: 0)
```
You can use NAT64 of OpenThread Border Router to reach e.g. Google IPv4 NTP Server:
```bash
> sntp query 64:ff9b::d8ef:2308
> SNTP response - Unix time: 1540898611 (era: 0)
```
### state
Return state of current state.
+77
View File
@@ -226,6 +226,9 @@ const struct Command Interpreter::sCommands[] = {
{"service", &Interpreter::ProcessService},
#endif
{"singleton", &Interpreter::ProcessSingleton},
#if OPENTHREAD_ENABLE_SNTP_CLIENT
{"sntp", &Interpreter::ProcessSntp},
#endif
{"state", &Interpreter::ProcessState},
{"thread", &Interpreter::ProcessThread},
#ifndef OTDLL
@@ -2706,6 +2709,80 @@ void Interpreter::ProcessSingleton(int argc, char *argv[])
AppendResult(error);
}
#if OPENTHREAD_ENABLE_SNTP_CLIENT
void Interpreter::ProcessSntp(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long port = OT_SNTP_DEFAULT_SERVER_PORT;
Ip6::MessageInfo messageInfo;
otSntpQuery query;
VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(argv[0], "query") == 0)
{
VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD;
if (argc > 1)
{
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(argv[1]));
}
else
{
// Use IPv6 address of default SNTP server.
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
}
if (argc > 2)
{
SuccessOrExit(error = ParseLong(argv[2], port));
}
messageInfo.SetPeerPort(static_cast<uint16_t>(port));
query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
SuccessOrExit(error = otSntpClientQuery(mInstance, &query, &Interpreter::s_HandleSntpResponse, this));
mSntpQueryingInProgress = true;
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(error);
}
}
void Interpreter::s_HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult)
{
static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult);
}
void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult)
{
if (aResult == OT_ERROR_NONE)
{
// Some Embedded C libraries do not support printing of 64-bit unsigned integers.
// To simplify, unix epoch time and era number are printed separately.
mServer->OutputFormat("SNTP response - Unix time: %ld (era: %ld)\r\n",
static_cast<uint32_t>(aTime & UINT32_MAX), static_cast<uint32_t>(aTime >> 32));
}
else
{
mServer->OutputFormat("SNTP error - %s\r\n", otThreadErrorToString(aResult));
}
mSntpQueryingInProgress = false;
}
#endif
void Interpreter::ProcessState(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
+15 -2
View File
@@ -61,6 +61,7 @@
#ifndef OTDLL
#include <openthread/dhcp6_client.h>
#include <openthread/dns.h>
#include <openthread/sntp.h>
#include "common/timer.hpp"
#include "net/icmp6.hpp"
#endif
@@ -312,6 +313,9 @@ private:
void ProcessRloc16(int argc, char *argv[]);
void ProcessScan(int argc, char *argv[]);
void ProcessSingleton(int argc, char *argv[]);
#if OPENTHREAD_ENABLE_SNTP_CLIENT
void ProcessSntp(int argc, char *argv[]);
#endif
void ProcessState(int argc, char *argv[]);
void ProcessThread(int argc, char *argv[]);
#ifndef OTDLL
@@ -366,6 +370,10 @@ private:
otError aResult);
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
static void s_HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult);
#endif
#ifndef OTDLL
void HandleIcmpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const otIcmp6Header &aIcmpHeader);
void HandlePingTimer();
@@ -386,11 +394,12 @@ private:
void HandleDiagnosticGetResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
#endif
void HandleJoinerCallback(otError aError);
#if OPENTHREAD_ENABLE_DNS_CLIENT
void HandleDnsResponse(const char *aHostname, Ip6::Address &aAddress, uint32_t aTtl, otError aResult);
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
void HandleSntpResponse(uint64_t aTime, otError aResult);
#endif
static Interpreter &GetOwner(OwnerLocator &aOwnerLocator);
static const struct Command sCommands[];
@@ -434,6 +443,10 @@ private:
char mResolvingHostname[OT_DNS_MAX_HOSTNAME_LENGTH];
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
bool mSntpQueryingInProgress;
#endif
UdpExample mUdp;
#endif
+3
View File
@@ -126,6 +126,7 @@ SOURCES_COMMON = \
api/netdata_api.cpp \
api/network_time_api.cpp \
api/server_api.cpp \
api/sntp_api.cpp \
api/tasklet_api.cpp \
api/thread_api.cpp \
api/thread_ftd_api.cpp \
@@ -184,6 +185,7 @@ SOURCES_COMMON = \
net/ip6_mpl.cpp \
net/ip6_routes.cpp \
net/netif.cpp \
net/sntp_client.cpp \
net/udp6.cpp \
thread/address_resolver.cpp \
thread/announce_begin_server.cpp \
@@ -344,6 +346,7 @@ HEADERS_COMMON = \
net/ip6_mpl.hpp \
net/ip6_routes.hpp \
net/netif.hpp \
net/sntp_client.hpp \
net/socket.hpp \
net/tcp.hpp \
net/udp6.hpp \
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the OpenThread SNTP API.
*/
#include "openthread-core-config.h"
#include <openthread/sntp.h>
#include "common/instance.hpp"
using namespace ot;
#if OPENTHREAD_ENABLE_SNTP_CLIENT
otError otSntpClientQuery(otInstance * aInstance,
const otSntpQuery * aQuery,
otSntpResponseHandler aHandler,
void * aContext)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.GetThreadNetif().GetSntpClient().Query(aQuery, aHandler, aContext);
}
void otSntpClientSetUnixEra(otInstance *aInstance, uint32_t aUnixEra)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.GetThreadNetif().GetSntpClient().SetUnixEra(aUnixEra);
}
#endif
+7
View File
@@ -648,6 +648,13 @@ template <> inline Utils::JamDetector &Instance::Get(void)
}
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
template <> inline Sntp::Client &Instance::Get(void)
{
return GetThreadNetif().GetSntpClient();
}
#endif
template <> inline Utils::ChildSupervisor &Instance::Get(void)
{
return GetThreadNetif().GetChildSupervisor();
+396
View File
@@ -0,0 +1,396 @@
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sntp_client.hpp"
#include "utils/wrap_string.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/logging.hpp"
#include "common/owner-locator.hpp"
#include "net/udp6.hpp"
#include "thread/thread_netif.hpp"
#if OPENTHREAD_ENABLE_SNTP_CLIENT
/**
* @file
* This file implements the SNTP client.
*/
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace Sntp {
otError Client::Start(void)
{
otError error;
Ip6::SockAddr addr;
SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this));
SuccessOrExit(error = mSocket.Bind(addr));
exit:
return error;
}
otError Client::Stop(void)
{
Message * message = mPendingQueries.GetHead();
Message * messageToRemove;
QueryMetadata queryMetadata;
// Remove all pending queries.
while (message != NULL)
{
messageToRemove = message;
message = message->GetNext();
queryMetadata.ReadFrom(*messageToRemove);
FinalizeSntpTransaction(*messageToRemove, queryMetadata, 0, OT_ERROR_ABORT);
}
return mSocket.Close();
}
otError Client::Query(const otSntpQuery *aQuery, otSntpResponseHandler aHandler, void *aContext)
{
otError error;
QueryMetadata queryMetadata(aHandler, aContext);
Message * message = NULL;
Message * messageCopy = NULL;
Header header;
const Ip6::MessageInfo *messageInfo;
VerifyOrExit(aQuery->mMessageInfo != NULL, error = OT_ERROR_INVALID_ARGS);
// Originate timestamp is used only as a unique token.
header.SetTransmitTimestampSeconds(TimerMilli::GetNow() / 1000 + kTimeAt1970);
VerifyOrExit((message = NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS);
messageInfo = static_cast<const Ip6::MessageInfo *>(aQuery->mMessageInfo);
queryMetadata.mTransmitTimestamp = header.GetTransmitTimestampSeconds();
queryMetadata.mTransmissionTime = TimerMilli::GetNow() + kResponseTimeout;
queryMetadata.mSourceAddress = messageInfo->GetSockAddr();
queryMetadata.mDestinationPort = messageInfo->GetPeerPort();
queryMetadata.mDestinationAddress = messageInfo->GetPeerAddr();
queryMetadata.mRetransmissionCount = 0;
VerifyOrExit((messageCopy = CopyAndEnqueueMessage(*message, queryMetadata)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = SendMessage(*message, *messageInfo));
exit:
if (error != OT_ERROR_NONE)
{
if (message)
{
message->Free();
}
if (messageCopy)
{
DequeueMessage(*messageCopy);
}
}
return error;
}
Message *Client::NewMessage(const Header &aHeader)
{
Message *message = NULL;
VerifyOrExit((message = mSocket.NewMessage(sizeof(aHeader))) != NULL);
message->Prepend(&aHeader, sizeof(aHeader));
message->SetOffset(0);
exit:
return message;
}
Message *Client::CopyAndEnqueueMessage(const Message &aMessage, const QueryMetadata &aQueryMetadata)
{
otError error = OT_ERROR_NONE;
uint32_t now = TimerMilli::GetNow();
Message *messageCopy = NULL;
uint32_t nextTransmissionTime;
// Create a message copy for further retransmissions.
VerifyOrExit((messageCopy = aMessage.Clone()) != NULL, error = OT_ERROR_NO_BUFS);
// Append the copy with retransmission data and add it to the queue.
SuccessOrExit(error = aQueryMetadata.AppendTo(*messageCopy));
mPendingQueries.Enqueue(*messageCopy);
// Setup the timer.
if (mRetransmissionTimer.IsRunning())
{
// If timer is already running, check if it should be restarted with earlier fire time.
nextTransmissionTime = mRetransmissionTimer.GetFireTime();
if (aQueryMetadata.IsEarlier(nextTransmissionTime))
{
mRetransmissionTimer.Start(aQueryMetadata.mTransmissionTime - now);
}
}
else
{
mRetransmissionTimer.Start(aQueryMetadata.mTransmissionTime - now);
}
exit:
if (error != OT_ERROR_NONE && messageCopy != NULL)
{
messageCopy->Free();
messageCopy = NULL;
}
return messageCopy;
}
void Client::DequeueMessage(Message &aMessage)
{
mPendingQueries.Dequeue(aMessage);
if (mRetransmissionTimer.IsRunning() && (mPendingQueries.GetHead() == NULL))
{
// No more requests pending, stop the timer.
mRetransmissionTimer.Stop();
}
// Free the message memory.
aMessage.Free();
}
otError Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
return mSocket.SendTo(aMessage, aMessageInfo);
}
otError Client::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error;
Message *messageCopy = NULL;
// Create a message copy for lower layers.
VerifyOrExit((messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(QueryMetadata))) != NULL,
error = OT_ERROR_NO_BUFS);
// Send the copy.
SuccessOrExit(error = SendMessage(*messageCopy, aMessageInfo));
exit:
if (error != OT_ERROR_NONE && messageCopy != NULL)
{
messageCopy->Free();
}
return error;
}
Message *Client::FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata)
{
Header header;
Message *message = mPendingQueries.GetHead();
while (message != NULL)
{
// Read originate timestamp.
uint16_t count = aQueryMetadata.ReadFrom(*message);
assert(count == sizeof(aQueryMetadata));
if (aQueryMetadata.mTransmitTimestamp == aResponseHeader.GetOriginateTimestampSeconds())
{
aQueryMetadata.ReadFrom(*message);
ExitNow();
}
message = message->GetNext();
}
exit:
return message;
}
void Client::FinalizeSntpTransaction(Message & aQuery,
const QueryMetadata &aQueryMetadata,
uint64_t aTime,
otError aResult)
{
DequeueMessage(aQuery);
if (aQueryMetadata.mResponseHandler != NULL)
{
aQueryMetadata.mResponseHandler(aQueryMetadata.mResponseContext, aTime, aResult);
}
}
void Client::HandleRetransmissionTimer(Timer &aTimer)
{
aTimer.GetOwner<Client>().HandleRetransmissionTimer();
}
void Client::HandleRetransmissionTimer(void)
{
uint32_t now = TimerMilli::GetNow();
uint32_t nextDelta = 0xffffffff;
QueryMetadata queryMetadata;
Message * message = mPendingQueries.GetHead();
Message * nextMessage = NULL;
Ip6::MessageInfo messageInfo;
while (message != NULL)
{
nextMessage = message->GetNext();
queryMetadata.ReadFrom(*message);
if (queryMetadata.IsLater(now))
{
// Calculate the next delay and choose the lowest.
if (queryMetadata.mTransmissionTime - now < nextDelta)
{
nextDelta = queryMetadata.mTransmissionTime - now;
}
}
else if (queryMetadata.mRetransmissionCount < kMaxRetransmit)
{
// Increment retransmission counter and timer.
queryMetadata.mRetransmissionCount++;
queryMetadata.mTransmissionTime = now + kResponseTimeout;
queryMetadata.UpdateIn(*message);
// Check if retransmission time is lower than current lowest.
if (queryMetadata.mTransmissionTime - now < nextDelta)
{
nextDelta = queryMetadata.mTransmissionTime - now;
}
// Retransmit
messageInfo.SetPeerAddr(queryMetadata.mDestinationAddress);
messageInfo.SetPeerPort(queryMetadata.mDestinationPort);
messageInfo.SetSockAddr(queryMetadata.mSourceAddress);
SendCopy(*message, messageInfo);
}
else
{
// No expected response.
FinalizeSntpTransaction(*message, queryMetadata, 0, OT_ERROR_RESPONSE_TIMEOUT);
}
message = nextMessage;
}
if (nextDelta != 0xffffffff)
{
mRetransmissionTimer.Start(nextDelta);
}
}
void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<Client *>(aContext)->HandleUdpReceive(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
Header responseHeader;
QueryMetadata queryMetadata;
Message * message = NULL;
uint64_t unixTime = 0;
OT_UNUSED_VARIABLE(aMessageInfo);
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(responseHeader), &responseHeader) ==
sizeof(responseHeader));
VerifyOrExit((message = FindRelatedQuery(responseHeader, queryMetadata)) != NULL);
// Check if response came from the server.
VerifyOrExit(responseHeader.GetMode() == Header::kModeServer, error = OT_ERROR_FAILED);
// Check the Kiss-o'-death packet.
if (!responseHeader.GetStratum())
{
char kissCode[Header::GetKissCodeLength() + 1];
memcpy(kissCode, responseHeader.GetKissCode(), Header::GetKissCodeLength());
kissCode[Header::GetKissCodeLength()] = 0;
otLogInfoIp6("SNTP response contains the Kiss-o'-death packet with %s code", kissCode);
ExitNow(error = OT_ERROR_BUSY);
}
// Check if timestamp has been set.
VerifyOrExit(responseHeader.GetTransmitTimestampSeconds() != 0 &&
responseHeader.GetTransmitTimestampFraction() != 0,
error = OT_ERROR_FAILED);
// The NTP time starts at 1900 while the unix epoch starts at 1970.
// Due to NTP protocol limitation, this module stops working correctly after around year 2106, if
// unix era is not updated. This seems to be a reasonable limitation for now. Era number cannot be
// obtained using NTP protocol, and client of this module is responsible to set it properly.
unixTime = GetUnixEra() * (UINT32_MAX + 1);
if (responseHeader.GetTransmitTimestampSeconds() > kTimeAt1970)
{
unixTime += static_cast<uint64_t>(responseHeader.GetTransmitTimestampSeconds()) - kTimeAt1970;
}
else
{
unixTime +=
static_cast<uint64_t>(responseHeader.GetTransmitTimestampSeconds()) + (UINT32_MAX + 1) - kTimeAt1970;
}
// Return the time since 1970.
FinalizeSntpTransaction(*message, queryMetadata, unixTime, OT_ERROR_NONE);
exit:
if (message != NULL && error != OT_ERROR_NONE)
{
FinalizeSntpTransaction(*message, queryMetadata, 0, error);
}
return;
}
} // namespace Sntp
} // namespace ot
#endif // OPENTHREAD_ENABLE_SNTP_CLIENT
+629
View File
@@ -0,0 +1,629 @@
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SNTP_CLIENT_HPP_
#define SNTP_CLIENT_HPP_
#include "openthread-core-config.h"
#include <openthread/sntp.h>
#include "common/message.hpp"
#include "common/timer.hpp"
#include "net/ip6.hpp"
#include "net/netif.hpp"
/**
* @file
* This file includes definitions for the SNTP client.
*/
namespace ot {
namespace Sntp {
/**
* This class implements SNTP header generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class Header
{
public:
/**
* Default constructor for SNTP Header.
*
*/
Header(void)
{
memset(this, 0, sizeof(*this));
// Set default value of flags field.
SetFlags(kNtpVersion << kVersionOffset | kModeClient << kModeOffset);
}
/**
* Defines supported SNTP modes.
*
*/
enum Mode
{
kModeClient = 3,
kModeServer = 4,
};
/**
* This method returns the flags field value.
*
* @returns Value of the flags field (LI, VN and Mode).
*
*/
uint8_t GetFlags(void) const { return mFlags; }
/**
* This method sets the flags field.
*
* @param[in] aFlags The value of the flags field.
*
*/
void SetFlags(uint8_t aFlags) { mFlags = aFlags; }
/**
* This method returns the SNTP operational mode.
*
* @returns SNTP operational mode.
*
*/
Mode GetMode(void) const { return static_cast<Mode>((mFlags & kModeMask) >> kModeOffset); }
/**
* This method returns the packet stratum field value.
*
* @returns Value of the packet stratum.
*
*/
uint8_t GetStratum(void) const { return mStratum; }
/**
* This method sets the packet stratum field value.
*
* @param[in] aStratum The value of the packet stratum field.
*
*/
void SetStratum(uint8_t aStratum) { mStratum = aStratum; }
/**
* This method returns the poll field value.
*
* @returns Value of the poll field.
*
*/
uint8_t GetPoll(void) const { return mPoll; }
/**
* This method sets the poll field.
*
* @param[in] aPoll The value of the poll field.
*
*/
void SetPoll(uint8_t aPoll) { mPoll = aPoll; }
/**
* This method returns the precision field value.
*
* @returns Value of the precision field.
*
*/
uint8_t GetPrecision(void) const { return mPrecision; }
/**
* This method sets the precision field.
*
* @param[in] aPrecision The value of the precision field.
*
*/
void SetPrecision(uint8_t aPrecision) { mPrecision = aPrecision; }
/**
* This method returns the root delay field value.
*
* @returns Value of the the root delay field.
*
*/
uint32_t GetRootDelay(void) const { return HostSwap32(mRootDelay); }
/**
* This method sets the the root delay field.
*
* @param[in] aRootDelay The value of the the root delay field.
*
*/
void SetRootDelay(uint32_t aRootDelay) { mRootDelay = HostSwap32(aRootDelay); }
/**
* This method returns the root dispersion field value.
*
* @returns Value of the the root dispersion field.
*
*/
uint32_t GetRootDispersion(void) const { return HostSwap32(mRootDispersion); }
/**
* This method sets the the root dispersion field.
*
* @param[in] aRootDispersion The value of the the root dispersion field.
*
*/
void SetRootDispersion(uint32_t aRootDispersion) { mRootDispersion = HostSwap32(aRootDispersion); }
/**
* This method returns the reference identifier field value.
*
* @returns Value of the the reference identifier field.
*
*/
uint32_t GetReferenceId(void) const { return HostSwap32(mReferenceId); }
/**
* This method sets the the reference identifier field.
*
* @param[in] aReferenceId The value of the the reference identifier field.
*
*/
void SetReferenceId(uint32_t aReferenceId) { mReferenceId = HostSwap32(aReferenceId); }
/**
* This method returns the kiss code in ASCII format.
*
* @returns Value of the the reference identifier field in ASCII format.
*
*/
char *GetKissCode(void) { return reinterpret_cast<char *>(&mReferenceId); }
/**
* This method returns the length of the kiss code in ASCII format.
*
* @returns The length of the kiss code in ASCII format.
*
*/
static uint16_t GetKissCodeLength(void) { return sizeof(uint32_t); };
/**
* This method returns the reference timestamp seconds field.
*
* @returns Value of the reference timestamp seconds field.
*
*/
uint32_t GetReferenceTimestampSeconds(void) const { return HostSwap32(mReferenceTimestampSeconds); }
/**
* This method sets the reference timestamp seconds field.
*
* @param[in] aReferenceTimestampSeconds Value of the reference timestamp seconds field.
*
*/
void SetReferenceTimestampSeconds(uint32_t aReferenceTimestampSeconds)
{
mReferenceTimestampSeconds = HostSwap32(aReferenceTimestampSeconds);
}
/**
* This method returns the reference timestamp fraction field.
*
* @returns Value of the reference timestamp fraction field.
*
*/
uint32_t GetReferenceTimestampFraction(void) const { return HostSwap32(mReferenceTimestampFraction); }
/**
* This method sets the reference timestamp fraction field.
*
* @param[in] aReferenceTimestampFraction Value of the reference timestamp fraction field.
*
*/
void SetReferenceTimestampFraction(uint32_t aReferenceTimestampFraction)
{
mReferenceTimestampFraction = HostSwap32(aReferenceTimestampFraction);
}
/**
* This method returns the originate timestamp seconds field.
*
* @returns Value of the originate timestamp seconds field.
*
*/
uint32_t GetOriginateTimestampSeconds(void) const { return HostSwap32(mOriginateTimestampSeconds); }
/**
* This method sets the originate timestamp seconds field.
*
* @param[in] aOriginateTimestampSeconds Value of the originate timestamp seconds field.
*
*/
void SetOriginateTimestampSeconds(uint32_t aOriginateTimestampSeconds)
{
mOriginateTimestampSeconds = HostSwap32(aOriginateTimestampSeconds);
}
/**
* This method returns the originate timestamp fraction field.
*
* @returns Value of the originate timestamp fraction field.
*
*/
uint32_t GetOriginateTimestampFraction(void) const { return HostSwap32(mOriginateTimestampFraction); }
/**
* This method sets the originate timestamp fraction field.
*
* @param[in] aOriginateTimestampFraction Value of the originate timestamp fraction field.
*
*/
void SetOriginateTimestampFraction(uint32_t aOriginateTimestampFraction)
{
mOriginateTimestampFraction = HostSwap32(aOriginateTimestampFraction);
}
/**
* This method returns the receive timestamp seconds field.
*
* @returns Value of the receive timestamp seconds field.
*
*/
uint32_t GetReceiveTimestampSeconds(void) const { return HostSwap32(mReceiveTimestampSeconds); }
/**
* This method sets the receive timestamp seconds field.
*
* @param[in] aReceiveTimestampSeconds Value of the receive timestamp seconds field.
*
*/
void SetReceiveTimestampSeconds(uint32_t aReceiveTimestampSeconds)
{
mReceiveTimestampSeconds = HostSwap32(aReceiveTimestampSeconds);
}
/**
* This method returns the receive timestamp fraction field.
*
* @returns Value of the receive timestamp fraction field.
*
*/
uint32_t GetReceiveTimestampFraction(void) const { return HostSwap32(mReceiveTimestampFraction); }
/**
* This method sets the receive timestamp fraction field.
*
* @param[in] aReceiveTimestampFraction Value of the receive timestamp fraction field.
*
*/
void SetReceiveTimestampFraction(uint32_t aReceiveTimestampFraction)
{
mReceiveTimestampFraction = HostSwap32(aReceiveTimestampFraction);
}
/**
* This method returns the transmit timestamp seconds field.
*
* @returns Value of the transmit timestamp seconds field.
*
*/
uint32_t GetTransmitTimestampSeconds(void) const { return HostSwap32(mTransmitTimestampSeconds); }
/**
* This method sets the transmit timestamp seconds field.
*
* @param[in] aTransmitTimestampSeconds Value of the transmit timestamp seconds field.
*
*/
void SetTransmitTimestampSeconds(uint32_t aTransmitTimestampSeconds)
{
mTransmitTimestampSeconds = HostSwap32(aTransmitTimestampSeconds);
}
/**
* This method returns the transmit timestamp fraction field.
*
* @returns Value of the transmit timestamp fraction field.
*
*/
uint32_t GetTransmitTimestampFraction(void) const { return HostSwap32(mTransmitTimestampFraction); }
/**
* This method sets the transmit timestamp fraction field.
*
* @param[in] aTransmitTimestampFraction Value of the transmit timestamp fraction field.
*
*/
void SetTransmitTimestampFraction(uint32_t aTransmitTimestampFraction)
{
mTransmitTimestampFraction = HostSwap32(aTransmitTimestampFraction);
}
private:
/**
* Protocol Constants (RFC 5905).
*
*/
enum
{
/**
* Current NTP version.
*
*/
kNtpVersion = 4,
/**
* Flags offsets and masks.
*
*/
kLeapOffset = 6, ///< Leap Indicator field offset.
kLeapMask = 0x03 << kLeapOffset, ///< Leap Indicator field mask.
kVersionOffset = 3, ///< Version field offset.
kVersionMask = 0x07 << kVersionOffset, ///< Version field mask.
kModeOffset = 0, ///< Mode field offset.
kModeMask = 0x07 << kModeOffset, ///< Mode filed mask.
};
/**
* SNTP Header fields.
*
*/
uint8_t mFlags; ///< SNTP flags: LI Leap Indicator, VN Version Number and Mode.
uint8_t mStratum; ///< Packet Stratum.
uint8_t mPoll; ///< Maximum interval between successive messages, in log2 seconds.
uint8_t mPrecision; ///< The precision of the system clock, in log2 seconds.
uint32_t mRootDelay; ///< Total round-trip delay to the reference clock, in NTP short format.
uint32_t mRootDispersion; ///< Total dispersion to the reference clock.
uint32_t mReferenceId; ///< ID identifying the particular server or reference clock.
uint32_t mReferenceTimestampSeconds; ///< Time when the system clock was last set or corrected, in NTP
///< timestamp format.
uint32_t mReferenceTimestampFraction; ///< Fraction part of above value.
uint32_t mOriginateTimestampSeconds; ///< Time at the client when the request departed for the server, in NTP
///< timestamp format.
uint32_t mOriginateTimestampFraction; ///< Fraction part of above value.
uint32_t mReceiveTimestampSeconds; ///< Time at the server when the request arrived from the client, in NTP
///< timestamp format.
uint32_t mReceiveTimestampFraction; ///< Fraction part of above value.
uint32_t mTransmitTimestampSeconds; ///< Time at the server when the response left for the client, in NTP
///< timestamp format.
uint32_t mTransmitTimestampFraction; ///< Fraction part of above value.
} OT_TOOL_PACKED_END;
/**
* This class implements metadata required for SNTP retransmission.
*
*/
OT_TOOL_PACKED_BEGIN
class QueryMetadata
{
friend class Client;
public:
/**
* Default constructor for the object.
*
*/
QueryMetadata(void) { memset(this, 0, sizeof(*this)); };
/**
* This constructor initializes the object with specific values.
*
* @param[in] aHandler Pointer to a handler function for the response.
* @param[in] aContext Context for the handler function.
*
*/
QueryMetadata(otSntpResponseHandler aHandler, void *aContext)
{
memset(this, 0, sizeof(*this));
mResponseHandler = aHandler;
mResponseContext = aContext;
};
/**
* This method appends request data to the message.
*
* @param[in] aMessage A reference to the message.
*
* @retval OT_ERROR_NONE Successfully appended the bytes.
* @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message.
*
*/
otError AppendTo(Message &aMessage) const { return aMessage.Append(this, sizeof(*this)); };
/**
* This method reads request data from the message.
*
* @param[in] aMessage A reference to the message.
*
* @returns The number of bytes read.
*
*/
uint16_t ReadFrom(const Message &aMessage)
{
return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
};
/**
* This method updates request data in the message.
*
* @param[in] aMessage A reference to the message.
*
* @returns The number of bytes updated.
*
*/
int UpdateIn(Message &aMessage) const
{
return aMessage.Write(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
}
/**
* This method checks if the message shall be sent before the given time.
*
* @param[in] aTime A time to compare.
*
* @retval TRUE If the message shall be sent before the given time.
* @retval FALSE Otherwise.
*/
bool IsEarlier(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mTransmissionTime) > 0); };
/**
* This method checks if the message shall be sent after the given time.
*
* @param[in] aTime A time to compare.
*
* @retval TRUE If the message shall be sent after the given time.
* @retval FALSE Otherwise.
*/
bool IsLater(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mTransmissionTime) < 0); };
private:
uint32_t mTransmitTimestamp; ///< Time at the client when the request departed for the server.
otSntpResponseHandler mResponseHandler; ///< A function pointer that is called on response reception.
void * mResponseContext; ///< A pointer to arbitrary context information.
uint32_t mTransmissionTime; ///< Time when the timer should shoot for this message.
Ip6::Address mSourceAddress; ///< IPv6 address of the message source.
Ip6::Address mDestinationAddress; ///< IPv6 address of the message destination.
uint16_t mDestinationPort; ///< UDP port of the message destination.
uint8_t mRetransmissionCount; ///< Number of retransmissions.
} OT_TOOL_PACKED_END;
/**
* This class implements Sntp client.
*
*/
class Client
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aNetif A reference to the network interface that SNTP client should be assigned to.
*
*/
Client(Ip6::Netif &aNetif)
: mSocket(aNetif.GetIp6().GetUdp())
, mRetransmissionTimer(aNetif.GetInstance(), &Client::HandleRetransmissionTimer, this)
, mUnixEra(0){};
/**
* This method starts the SNTP client.
*
* @retval OT_ERROR_NONE Successfully started the SNTP client.
* @retval OT_ERROR_ALREADY The socket is already open.
*/
otError Start(void);
/**
* This method stops the SNTP client.
*
* @retval OT_ERROR_NONE Successfully stopped the SNTP client.
*
*/
otError Stop(void);
/**
* This method returns the unix era number.
*
* @returns The unix era number.
*
*/
uint32_t GetUnixEra(void) const { return mUnixEra; }
/**
* This method sets the unix era number.
*
* @param[in] aUnixEra The unix era number.
*
*/
void SetUnixEra(uint32_t aUnixEra) { mUnixEra = aUnixEra; }
/**
* This method sends an SNTP query.
*
* @param[in] aQuery A pointer to specify SNTP query parameters.
* @param[in] aHandler A function pointer that shall be called on response reception or time-out.
* @param[in] aContext A pointer to arbitrary context information.
*
* @retval OT_ERROR_NONE Successfully sent SNTP query.
* @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data.
* @retval OT_ERROR_INVALID_ARGS Invalid arguments supplied.
*
*/
otError Query(const otSntpQuery *aQuery, otSntpResponseHandler aHandler, void *aContext);
private:
/**
* Protocol Constants (RFC 5905).
*
*/
enum
{
/**
* Number of seconds between 1st January 1900 and 1st January 1970.
*
*/
kTimeAt1970 = 2208988800UL,
};
/**
* Retransmission parameters.
*
*/
enum
{
kResponseTimeout = OPENTHREAD_CONFIG_SNTP_RESPONSE_TIMEOUT,
kMaxRetransmit = OPENTHREAD_CONFIG_SNTP_MAX_RETRANSMIT,
};
Message *NewMessage(const Header &aHeader);
Message *CopyAndEnqueueMessage(const Message &aMessage, const QueryMetadata &aQueryMetadata);
void DequeueMessage(Message &aMessage);
otError SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
otError SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
Message *FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata);
void FinalizeSntpTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, uint64_t aTime, otError aResult);
static void HandleRetransmissionTimer(Timer &aTimer);
void HandleRetransmissionTimer(void);
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
Ip6::UdpSocket mSocket;
MessageQueue mPendingQueries;
TimerMilli mRetransmissionTimer;
uint32_t mUnixEra;
};
} // namespace Sntp
} // namespace ot
#endif // SNTP_CLIENT_HPP_
+20
View File
@@ -543,6 +543,26 @@
#define OPENTHREAD_CONFIG_DNS_MAX_RETRANSMIT 2
#endif
/**
* @def OPENTHREAD_CONFIG_SNTP_RESPONSE_TIMEOUT
*
* Maximum time that SNTP Client waits for response in milliseconds.
*
*/
#ifndef OPENTHREAD_CONFIG_SNTP_RESPONSE_TIMEOUT
#define OPENTHREAD_CONFIG_SNTP_RESPONSE_TIMEOUT 3000
#endif
/**
* @def OPENTHREAD_CONFIG_SNTP_MAX_RETRANSMIT
*
* Maximum number of retransmissions for SNTP client.
*
*/
#ifndef OPENTHREAD_CONFIG_SNTP_MAX_RETRANSMIT
#define OPENTHREAD_CONFIG_SNTP_MAX_RETRANSMIT 2
#endif
/**
* @def OPENTHREAD_CONFIG_JOIN_BEACON_VERSION
*
+9
View File
@@ -61,6 +61,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance)
#if OPENTHREAD_ENABLE_DNS_CLIENT
, mDnsClient(aInstance.GetThreadNetif())
#endif // OPENTHREAD_ENABLE_DNS_CLIENT
#if OPENTHREAD_ENABLE_SNTP_CLIENT
, mSntpClient(aInstance.GetThreadNetif())
#endif // OPENTHREAD_ENABLE_SNTP_CLIENT
, mActiveDataset(aInstance)
, mPendingDataset(aInstance)
, mKeyManager(aInstance)
@@ -121,6 +124,9 @@ otError ThreadNetif::Up(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Start();
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
mSntpClient.Start();
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MONITOR
GetInstance().GetChannelMonitor().Start();
#endif
@@ -137,6 +143,9 @@ otError ThreadNetif::Down(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Stop();
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
mSntpClient.Stop();
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MONITOR
GetInstance().GetChannelMonitor().Stop();
#endif
+14
View File
@@ -65,6 +65,7 @@
#include "net/dns_client.hpp"
#include "net/ip6_filter.hpp"
#include "net/netif.hpp"
#include "net/sntp_client.hpp"
#include "thread/address_resolver.hpp"
#include "thread/announce_begin_server.hpp"
#include "thread/energy_scan_server.hpp"
@@ -419,6 +420,16 @@ public:
TimeSync &GetTimeSync(void) { return mTimeSync; }
#endif
#if OPENTHREAD_ENABLE_SNTP_CLIENT
/**
* This method returns a reference to the SNTP client object.
*
* @returns A reference to the SNTP client object.
*
*/
Sntp::Client &GetSntpClient(void) { return mSntpClient; }
#endif // OPENTHREAD_ENABLE_SNTP_CLIENT
/**
* This method returns whether Thread Management Framework Addressing Rules are met.
*
@@ -441,6 +452,9 @@ private:
#if OPENTHREAD_ENABLE_DNS_CLIENT
Dns::Client mDnsClient;
#endif // OPENTHREAD_ENABLE_DNS_CLIENT
#if OPENTHREAD_ENABLE_SNTP_CLIENT
Sntp::Client mSntpClient;
#endif // OPENTHREAD_ENABLE_SNTP_CLIENT
MeshCoP::ActiveDataset mActiveDataset;
MeshCoP::PendingDataset mPendingDataset;
Ip6::Filter mIp6Filter;