Adding "Child Supervision" feature (#1608)

Child supervision feature provides a mechanism for parent to ensure
that a message is sent to each sleepy child within a fixed interval,
namely the supervision interval. If there is no transmission to the
child within the supervision interval, child supervisor enqueues and
sends a supervision message (a data message with empty payload) to the
child.

On the child side, this is used to check the connectivity to the
parent. If the child does not hear from its parent for a supervision
check timeout interval it assumes that it may be disconnected and
tries to re-attach to the parent.
This commit is contained in:
Abtin Keshavarzian
2017-05-04 20:20:05 -07:00
committed by Jonathan Hui
parent 8a6371ccdc
commit bb4ffe373c
21 changed files with 935 additions and 27 deletions
+32
View File
@@ -806,6 +806,37 @@ AC_SUBST(OPENTHREAD_ENABLE_LEGACY)
AM_CONDITIONAL([OPENTHREAD_ENABLE_LEGACY], [test "${enable_legacy}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_LEGACY],[${OPENTHREAD_ENABLE_LEGACY}],[Define to 1 if you want to use legacy network support])
#
# Child Supervision
#
AC_ARG_ENABLE(child_supervision,
[AS_HELP_STRING([--enable-child-supervision],[Enable child supervision feature @<:@default=no@:>@.])],
[
case "${enableval}" in
no|yes)
enable_child_supervision=${enableval}
;;
*)
AC_MSG_ERROR([Invalid value ${enable_child_supervision} for --enable-child-supervision])
;;
esac
],
[enable_child_supervision=no])
if test "$enable_child_supervision" = "yes"; then
OPENTHREAD_ENABLE_CHILD_SUPERVISION=1
else
OPENTHREAD_ENABLE_CHILD_SUPERVISION=0
fi
AC_MSG_RESULT(${enable_child_supervision})
AC_SUBST(OPENTHREAD_ENABLE_CHILD_SUPERVISION)
AM_CONDITIONAL([OPENTHREAD_ENABLE_CHILD_SUPERVISION], [test "${enable_child_supervision}" = "yes"])
AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CHILD_SUPERVISION],[${OPENTHREAD_ENABLE_CHILD_SUPERVISION}],[Define to 1 if you want to use child supervision feature])
#
# Default Logging
#
@@ -1344,6 +1375,7 @@ AC_MSG_NOTICE([
OpenThread Jam Detection support : ${enable_jam_detection}
OpenThread MAC Whitelist support : ${enable_mac_whitelist}
OpenThread Diagnostics support : ${enable_diag}
OpenThread Child Supervision support : ${enable_child_supervision}
OpenThread Legacy network support : ${enable_legacy}
OpenThread Default logging support : ${enable_default_logging}
OpenThread Certification log support : ${enable_cert_log}
+2
View File
@@ -137,6 +137,7 @@
<ClCompile Include="..\..\src\core\thread\src_match_controller.cpp" />
<ClCompile Include="..\..\src\core\thread\thread_netif.cpp" />
<ClCompile Include="..\..\src\core\thread\topology.cpp" />
<ClCompile Include="..\..\src\core\utils\child_supervision.cpp" />
<ClCompile Include="..\..\src\core\utils\slaac_address.cpp" />
<ClCompile Include="..\..\src\core\utils\jam_detector.cpp" />
<ClCompile Include="..\..\src\core\utils\missing_strnlen.c" />
@@ -224,6 +225,7 @@
<ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp" />
<ClInclude Include="..\..\src\core\thread\thread_uris.hpp" />
<ClInclude Include="..\..\src\core\thread\topology.hpp" />
<ClInclude Include="..\..\src\core\utils\child_supervision.hpp" />
<ClInclude Include="..\..\src\core\utils\slaac_address.hpp" />
<ClInclude Include="..\..\src\core\utils\jam_detector.hpp" />
<ClInclude Include="..\..\src\core\utils\wrap_string.h" />
@@ -297,6 +297,9 @@
<ClCompile Include="..\..\src\core\crypto\sha256.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\utils\child_supervision.cpp">
<Filter>Source Files\utils</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\utils\slaac_address.cpp">
<Filter>Source Files\utils</Filter>
</ClCompile>
@@ -554,6 +557,9 @@
<ClInclude Include="..\..\src\core\crypto\sha256.hpp">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\utils\child_supervision.hpp">
<Filter>Header Files\utils</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\utils\slaac_address.hpp">
<Filter>Header Files\utils</Filter>
</ClInclude>
@@ -145,6 +145,7 @@
<ClCompile Include="..\..\src\core\thread\src_match_controller.cpp" />
<ClCompile Include="..\..\src\core\thread\thread_netif.cpp" />
<ClCompile Include="..\..\src\core\thread\topology.cpp" />
<ClCompile Include="..\..\src\core\utils\child_supervision.cpp" />
<ClCompile Include="..\..\src\core\utils\slaac_address.cpp" />
<ClCompile Include="..\..\src\core\utils\jam_detector.cpp" />
<ClCompile Include="..\..\src\core\utils\missing_strlcat.c" />
@@ -258,6 +259,7 @@
<ClInclude Include="..\..\src\core\thread\thread_tlvs.hpp" />
<ClInclude Include="..\..\src\core\thread\thread_uris.hpp" />
<ClInclude Include="..\..\src\core\thread\topology.hpp" />
<ClInclude Include="..\..\src\core\utils\child_supervision.hpp" />
<ClInclude Include="..\..\src\core\utils\slaac_address.hpp" />
<ClInclude Include="..\..\src\core\utils\jam_detector.hpp" />
<ClInclude Include="..\..\src\core\utils\wrap_string.h" />
@@ -297,6 +297,9 @@
<ClCompile Include="..\..\src\core\crypto\sha256.cpp">
<Filter>Source Files\crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\utils\child_supervision.cpp">
<Filter>Source Files\utils</Filter>
</ClCompile>
<ClCompile Include="..\..\src\core\utils\slaac_address.cpp">
<Filter>Source Files\utils</Filter>
</ClCompile>
@@ -554,6 +557,9 @@
<ClInclude Include="..\..\src\core\crypto\sha256.hpp">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\utils\child_supervision.hpp">
<Filter>Header Files\utils</Filter>
</ClInclude>
<ClInclude Include="..\..\src\core\utils\slaac_address.hpp">
<Filter>Header Files\utils</Filter>
</ClInclude>
+1
View File
@@ -47,6 +47,7 @@ PRETTY_SUBDIRS = \
$(NULL)
openthread_headers = \
child_supervision.h \
cli.h \
border_agent_proxy.h \
coap.h \
+118
View File
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2017, 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 includes the OenThread API for child supervision feature
*/
#ifndef OPENTHREAD_CHILD_SUPERVISION_H_
#define OPENTHREAD_CHILD_SUPERVISION_H_
#ifdef OPENTHREAD_CONFIG_FILE
#include OPENTHREAD_CONFIG_FILE
#else
#include <openthread-config.h>
#endif
#include "openthread/types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
/**
* @addtogroup child-supervision Child Supervision
*
* @brief
* This module includes functions for child supervision feature.
*
* @{
*
*/
/**
* Get the child supervision interval (in seconds).
*
* Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child
* within the supervision interval. If there is no transmission to the child within the supervision interval,
* OpenThread will enqueue and send a supervision message (a data message with empty payload) to the child.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The child supervision interval. Zero indicates that child supervision is disabled.
*
*/
uint16_t otChildSupervisionGetInterval(otInstance *aInstance);
/**
* Set the child supervision interval (in seconds).
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision on parent.
*
*/
void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval);
/**
* Get the supervision check timeout interval (in seconds).
*
* If the device is a sleepy child and it does not hear from its parent within the specified check timeout, it initiates
* the re-attach process (MLE Child Update Request/Response exchange with its parent).
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The supervision check timeout. Zero indicates that supervision check on the child is disabled.
*
*/
uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance);
/**
* Set the supervision check timeout interval (in seconds).
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aTimeout The check timeout (in seconds). Zero to disable supervision check on the child.
*
*/
void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout);
/**
* @}
*
*/
#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_CHILD_SUPERVISION_H_
+3
View File
@@ -93,6 +93,7 @@ SOURCES_COMMON = \
api/border_agent_proxy_api.cpp \
api/coap_api.cpp \
api/commissioner_api.cpp \
api/child_supervision_api.cpp \
api/crypto_api.cpp \
api/dataset_api.cpp \
api/dataset_ftd_api.cpp \
@@ -179,6 +180,7 @@ SOURCES_COMMON = \
thread/src_match_controller.cpp \
thread/thread_netif.cpp \
thread/topology.cpp \
utils/child_supervision.cpp \
utils/jam_detector.cpp \
utils/missing_strlcpy.c \
utils/missing_strlcat.c \
@@ -294,6 +296,7 @@ HEADERS_COMMON = \
thread/thread_tlvs.hpp \
thread/thread_uris.hpp \
thread/topology.hpp \
utils/child_supervision.hpp \
utils/slaac_address.hpp \
utils/jam_detector.hpp \
utils/wrap_stdbool.h \
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2017, 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 child supervision API.
*/
#include "openthread/child_supervision.h"
#include "openthread-instance.h"
using namespace ot;
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
uint16_t otChildSupervisionGetInterval(otInstance *aInstance)
{
return aInstance->mThreadNetif.GetChildSupervisor().GetSupervisionInterval();
}
void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval)
{
aInstance->mThreadNetif.GetChildSupervisor().SetSupervisionInterval(aInterval);
}
uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance)
{
return aInstance->mThreadNetif.GetSupervisionListener().GetTimeout();
}
void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout)
{
aInstance->mThreadNetif.GetSupervisionListener().SetTimeout(aTimeout);
}
#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
+3 -2
View File
@@ -203,9 +203,10 @@ class Message: public Buffer
public:
enum
{
kTypeIp6 = 0, ///< A full uncompress IPv6 packet
kTypeIp6 = 0, ///< A full uncompressed IPv6 packet
kType6lowpan = 1, ///< A 6lowpan frame
kTypeMacDataPoll = 2, ///< A MAC data poll message
kTypeSupervision = 3, ///< A child supervision frame.
};
enum
@@ -215,7 +216,7 @@ public:
kSubTypeMleDiscoverRequest = 2, ///< MLE Discover Request
kSubTypeMleDiscoverResponse = 3, ///< MLE Discover Response
kSubTypeJoinerEntrust = 4, ///< Joiner Entrust
kSubTypeMplRetransmission = 5, ///< MPL next retranmission message
kSubTypeMplRetransmission = 5, ///< MPL next retransmission message
kSubTypeMleGeneral = 6, ///< General MLE
kSubTypeJoinerFinalizeResponse = 7, ///< Joiner Finalize Response
kSubTypeMleChildUpdateRequest = 8, ///< MLE Child Update Request
+45
View File
@@ -735,4 +735,49 @@
#define OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB 0
#endif
/*
* @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL
*
* The default supervision interval in seconds used by parent. Set to zero to disable the supervision process on the
* parent.
*
* Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION ` is set).
*
* Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child
* within the supervision interval. If there is no transmission to the child within the supervision interval, child
* supervisor will enqueue and send a supervision message (a data message with empty payload) to the child.
*
*/
#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL
#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL 129
#endif
/**
* @def OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT
*
* The default supervision check timeout interval (in seconds) used by a device in child state. Set to zero to disable
* the supervision check process on the child.
*
* Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION` is set).
*
* If the sleepy child does not hear from its parent within the specified timeout interval, it initiates the re-attach
* process (MLE Child Update Request/Response exchange with its parent).
*
*/
#ifndef OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT
#define OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT 190
#endif
/**
* @def OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST
*
* Define as 1 to clear/disable 15.4 ack request in the MAC header of a supervision message.
*
* Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_ENABLE_CHILD_SUPERVISION` is set).
*
*/
#ifndef OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST
#define OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST 0
#endif
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
+56 -20
View File
@@ -300,18 +300,14 @@ void MeshForwarder::ScheduleTransmissionTask(void)
{
mMacSource.mLength = sizeof(mMacSource.mShortAddress);
mMacSource.mShortAddress = mNetif.GetMac().GetShortAddress();
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = child.GetRloc16();
}
else
{
mMacSource.mLength = sizeof(mMacSource.mExtAddress);
memcpy(mMacSource.mExtAddress.m8, mNetif.GetMac().GetExtAddress(), sizeof(mMacDest.mExtAddress));
mMacDest.mLength = sizeof(mMacDest.mExtAddress);
mMacDest.mExtAddress = child.GetExtAddress();
}
child.GetMacAddress(mMacDest);
}
// To ensure fairness in handling of data requests from sleepy
@@ -421,6 +417,15 @@ ThreadError MeshForwarder::SendMessage(Message &aMessage)
case Message::kTypeMacDataPoll:
aMessage.SetDirectTransmission();
break;
case Message::kTypeSupervision:
child = mNetif.GetChildSupervisor().GetDestination(aMessage);
VerifyOrExit(child != NULL, error = kThreadError_Drop);
VerifyOrExit(!child->IsRxOnWhenIdle(), error = kThreadError_Drop);
aMessage.SetChildMask(mNetif.GetMle().GetChildIndex(*child));
mSourceMatchController.IncrementMessageCount(*child);
break;
}
aMessage.SetOffset(0);
@@ -493,6 +498,10 @@ Message *MeshForwarder::GetDirectTransmission(void)
case Message::kTypeMacDataPoll:
ExitNow();
case Message::kTypeSupervision:
error = kThreadError_Drop;
break;
}
switch (error)
@@ -524,12 +533,26 @@ exit:
Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
{
Message *message = NULL;
Message *next;
uint8_t childIndex = mNetif.GetMle().GetChildIndex(aChild);
for (message = mSendQueue.GetHead(); message; message = message->GetNext())
for (message = mSendQueue.GetHead(); message; message = next)
{
next = message->GetNext();
if (message->GetChildMask(childIndex))
{
// Skip and remove the supervision message if there are other messages queued for the child.
if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1))
{
message->ClearChildMask(childIndex);
mSourceMatchController.DecrementMessageCount(aChild);
mSendQueue.Dequeue(*message);
message->Free();
continue;
}
break;
}
}
@@ -577,16 +600,7 @@ void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &
}
else
{
if (aChild.IsIndirectSourceMatchShort())
{
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = aChild.GetRloc16();
}
else
{
mMacDest.mLength = sizeof(mMacDest.mExtAddress);
mMacDest.mExtAddress = aChild.GetExtAddress();
}
aChild.GetMacAddress(mMacDest);
}
break;
@@ -607,6 +621,10 @@ void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &
break;
}
case Message::kTypeSupervision:
aChild.GetMacAddress(mMacDest);
break;
default:
assert(false);
break;
@@ -825,6 +843,7 @@ void MeshForwarder::SetRxOff(void)
{
mNetif.GetMac().SetRxOnWhenIdle(false);
mDataPollManager.StopPolling();
mNetif.GetSupervisionListener().Stop();
}
bool MeshForwarder::GetRxOnWhenIdle(void)
@@ -839,10 +858,12 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
if (aRxOnWhenIdle)
{
mDataPollManager.StopPolling();
mNetif.GetSupervisionListener().Stop();
}
else
{
mDataPollManager.StartPolling();
mNetif.GetSupervisionListener().Start();
}
}
@@ -909,7 +930,7 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
if (mSendMessage == NULL)
{
SendEmptyFrame(aFrame);
SendEmptyFrame(aFrame, false);
aFrame.SetIsARetransmission(false);
aFrame.SetMaxTxAttempts(Mac::kDirectFrameMacTxAttempts);
ExitNow();
@@ -963,6 +984,11 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
case Message::kTypeMacDataPoll:
error = SendPoll(*mSendMessage, aFrame);
break;
case Message::kTypeSupervision:
error = SendEmptyFrame(aFrame, kSupervisionMsgAckRequest);
mMessageNextOffset = mSendMessage->GetLength();
break;
}
assert(error == kThreadError_None);
@@ -1328,7 +1354,7 @@ exit:
return error;
}
ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame)
ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame, bool aAckRequest)
{
uint16_t fcf;
uint8_t secCtl;
@@ -1350,7 +1376,10 @@ ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame)
fcf |= (mMacDest.mLength == 2) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (macSource.mLength == 2) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
// Not requesting acknowledgment for null/empty frame.
if (aAckRequest)
{
fcf |= Mac::Frame::kFcfAckRequest;
}
fcf |= Mac::Frame::kFcfSecurityEnabled;
secCtl = Mac::Frame::kKeyIdMode1;
@@ -1532,6 +1561,11 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError)
mSourceMatchController.DecrementMessageCount(*child);
}
}
if (aError == kThreadError_None)
{
mNetif.GetChildSupervisor().UpdateOnSend(*child);
}
}
VerifyOrExit(mSendMessage != NULL);
@@ -1663,6 +1697,8 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
payload = aFrame.GetPayload();
payloadLength = aFrame.GetPayloadLength();
mNetif.GetSupervisionListener().UpdateOnReceive(macSource, messageInfo.mLinkSecurity);
mDataPollManager.CheckFramePending(aFrame);
switch (aFrame.GetType())
+10 -2
View File
@@ -111,7 +111,9 @@ public:
*
* @param[in] aMessage A reference to the message.
*
* @retval kThreadError_None Successfully enqueued the message.
* @retval kThreadError_None Successfully enqueued the message.
* @retval kThreadError_Already The message was already enqueued.
* @retval kThreadError_Drop The message could not be sent and should be dropped.
*
*/
ThreadError SendMessage(Message &aMessage);
@@ -232,6 +234,12 @@ private:
*
*/
kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS,
/**
* Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message.
*
*/
kSupervisionMsgAckRequest = (OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST == 0) ? true : false,
};
enum MessageAction ///< Defines the action parameter in `LogMessageInfo()` method.
@@ -263,7 +271,7 @@ private:
ThreadError SendPoll(Message &aMessage, Mac::Frame &aFrame);
ThreadError SendMesh(Message &aMessage, Mac::Frame &aFrame);
ThreadError SendFragment(Message &aMessage, Mac::Frame &aFrame);
ThreadError SendEmptyFrame(Mac::Frame &aFrame);
ThreadError SendEmptyFrame(Mac::Frame &aFrame, bool aAckRequest);
ThreadError UpdateIp6Route(Message &aMessage);
ThreadError UpdateMeshRoute(Message &aMessage);
ThreadError HandleDatagram(Message &aMessage, const ThreadMessageInfo &aMessageInfo,
+1 -1
View File
@@ -3234,7 +3234,7 @@ bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const
return memcmp(&mMeshLocal16, &aAddress, kRlocPrefixLength) == 0 && aAddress.mFields.m8[14] == Ip6::Address::kAloc16Mask;
}
Router *Mle::GetParent()
Router *Mle::GetParent(void)
{
if ((!mParent.IsStateValidOrRestoring()) && (mParentCandidate.GetState() == Neighbor::kStateValid))
{
+9
View File
@@ -644,6 +644,15 @@ public:
*/
void FillRouteTlv(RouteTlv &aTlv);
/**
* This method generates an MLE Child Update Request message to be sent to the parent.
*
* @retval kThreadError_None Successfully generated an MLE Child Update Request message.
* @retval kThreadError_NoBufs Insufficient buffers to generate the MLE Child Update Request message.
*
*/
ThreadError SendChildUpdateRequest(void) { return Mle::SendChildUpdateRequest(); }
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
/**
* This method sets steering data out of band
+2
View File
@@ -122,6 +122,8 @@ public:
void FillConnectivityTlv(ConnectivityTlv &) { }
void FillRouteTlv(RouteTlv &) { }
ThreadError SendChildUpdateRequest(void) { return Mle::SendChildUpdateRequest(); }
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
ThreadError SetSteeringData(otExtAddress *) { return kThreadError_NotImplemented; };
#endif // OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
+4
View File
@@ -107,6 +107,8 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6):
mLeader(*this),
mAddressResolver(*this),
#endif // OPENTHREAD_FTD
mChildSupervisor(*this),
mSupervisionListener(*this),
mAnnounceBegin(*this),
mPanIdQuery(*this),
mEnergyScan(*this)
@@ -130,6 +132,7 @@ ThreadError ThreadNetif::Up(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Start();
#endif
mChildSupervisor.Start();
mMleRouter.Enable();
mIsUp = true;
}
@@ -147,6 +150,7 @@ ThreadError ThreadNetif::Down(void)
#if OPENTHREAD_ENABLE_DNS_CLIENT
mDnsClient.Stop();
#endif
mChildSupervisor.Stop();
mMleRouter.Disable();
mMeshForwarder.Stop();
mIp6.RemoveNetif(*this);
+19
View File
@@ -66,6 +66,7 @@
#include <thread/mle_router.hpp>
#include <thread/network_data_local.hpp>
#include <thread/panid_query_server.hpp>
#include <utils/child_supervision.hpp>
#if OPENTHREAD_ENABLE_JAM_DETECTION
#include <utils/jam_detector.hpp>
@@ -397,6 +398,22 @@ public:
MeshCoP::BorderAgentProxy &GetBorderAgentProxy(void) { return mBorderAgentProxy; }
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
/**
* This method returns a reference to the child supervisor object.
*
* @returns A reference to the child supervisor object.
*
*/
Utils::ChildSupervisor &GetChildSupervisor(void) { return mChildSupervisor; }
/**
* This method returns a reference to the supervision listener object.
*
* @returns A reference to the supervision listener object.
*
*/
Utils::SupervisionListener &GetSupervisionListener(void) { return mSupervisionListener; }
/**
* This method returns the pointer to the parent otInstance structure.
*
@@ -462,6 +479,8 @@ private:
AddressResolver mAddressResolver;
#endif // OPENTHREAD_FTD
Utils::ChildSupervisor mChildSupervisor;
Utils::SupervisionListener mSupervisionListener;
AnnounceBeginServer mAnnounceBegin;
PanIdQueryServer mPanIdQuery;
EnergyScanServer mEnergyScan;
+31 -2
View File
@@ -525,7 +525,7 @@ public:
void SetIndirectDataSequenceNumber(uint8_t aDsn) { mIndirectDsn = aDsn; }
/**
* This method indicates whether or not to source match on the source address.
* This method indicates whether or not to source match on the short address.
*
* @returns TRUE if using the short address, FALSE if using the extended address.
*
@@ -533,7 +533,7 @@ public:
bool IsIndirectSourceMatchShort(void) const { return mUseShortAddress; }
/**
* This method sets whether or not to source match on the source address.
* This method sets whether or not to source match on the short address.
*
* @param[in] aShort TRUE if using the short address, FALSE if using the extended address.
*
@@ -617,6 +617,30 @@ public:
*/
const Mac::Address &GetMacAddress(Mac::Address &aMacAddress) const;
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
/**
* This method increments the number of seconds since last supervision of the child.
*
*/
void IncrementSecondsSinceLastSupervision(void) { mSecondsSinceSupervision++; }
/**
* This method returns the number of seconds since last supervision of the child (last message to the child)
*
* @returns Number of seconds since last supervision of the child.
*
*/
uint16_t GetSecondsSinceLastSupervision(void) const { return mSecondsSinceSupervision; }
/**
* This method resets the number of seconds since last supervision of the child to zero.
*
*/
void ResetSecondsSinceLastSupervision(void) { mSecondsSinceSupervision = 0; }
#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
private:
Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses
uint32_t mTimeout; ///< Child timeout
@@ -637,6 +661,11 @@ private:
uint16_t mQueuedMessageCount : 13; ///< Number of queued indirect messages for the child.
bool mUseShortAddress : 1; ///< Indicates whether to use short or extended address.
bool mSourceMatchPending : 1; ///< Indicates whether or not pending to add to src match table.
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
uint16_t mSecondsSinceSupervision; ///< Number of seconds since last supervision of the child.
#endif // OPENTHREAD_ENABLE_CHILD_SUPERVISION
};
/**
+239
View File
@@ -0,0 +1,239 @@
/*
* Copyright (c) 2017, 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 child supervision feature.
*/
#include "openthread/openthread.h"
#include <common/code_utils.hpp>
#include <utils/child_supervision.hpp>
#include <thread/thread_netif.hpp>
namespace ot {
namespace Utils {
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
#if OPENTHREAD_FTD
ChildSupervisor::ChildSupervisor(ThreadNetif &aThreadNetif) :
mNetif(aThreadNetif),
mTimer(aThreadNetif.GetIp6().mTimerScheduler, &ChildSupervisor::HandleTimer, this),
mSupervisionInterval(kDefaultSupervisionInterval)
{
}
void ChildSupervisor::Start(void)
{
VerifyOrExit(mSupervisionInterval != 0);
VerifyOrExit(!mTimer.IsRunning());
mTimer.Start(kOneSecond);
exit:
return;
}
void ChildSupervisor::Stop(void)
{
mTimer.Stop();
}
void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval)
{
mSupervisionInterval = aInterval;
Start();
}
Child *ChildSupervisor::GetDestination(const Message &aMessage) const
{
Child *child = NULL;
uint8_t childIndex;
uint8_t numChildren;
VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
aMessage.Read(0, sizeof(childIndex), &childIndex);
child = mNetif.GetMle().GetChildren(&numChildren);
VerifyOrExit(childIndex < numChildren, child = NULL);
child += childIndex;
exit:
return child;
}
void ChildSupervisor::SendMessage(Child &aChild)
{
Message *message = NULL;
ThreadError error = kThreadError_None;
uint8_t childIndex;
VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
message = mNetif.GetIp6().mMessagePool.New(Message::kTypeSupervision, sizeof(uint8_t));
VerifyOrExit(message != NULL);
// Supervision message is an empty payload 15.4 data frame.
// The child index is stored here in the message content to allow
// the destination of the message to be later retrieved using
// `ChildSupervisor::GetDestination(message)`.
childIndex = mNetif.GetMle().GetChildIndex(aChild);
SuccessOrExit(error = message->Append(&childIndex, sizeof(childIndex)));
SuccessOrExit(error = mNetif.SendMessage(*message));
message = NULL;
exit:
if (message != NULL)
{
message->Free();
}
}
void ChildSupervisor::UpdateOnSend(Child &aChild)
{
aChild.ResetSecondsSinceLastSupervision();
}
void ChildSupervisor::HandleTimer(void *aContext)
{
static_cast<ChildSupervisor *>(aContext)->HandleTimer();
}
void ChildSupervisor::HandleTimer(void)
{
Child *child;
uint8_t numChildren;
VerifyOrExit(mSupervisionInterval != 0);
child = mNetif.GetMle().GetChildren(&numChildren);
for (uint8_t i = 0; i < numChildren; i++, child++)
{
if (!child->IsStateValidOrRestoring())
{
continue;
}
child->IncrementSecondsSinceLastSupervision();
if ((child->GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child->IsRxOnWhenIdle() == false))
{
SendMessage(*child);
}
}
mTimer.Start(kOneSecond);
exit:
return;
}
#endif // #if OPENTHREAD_FTD
SupervisionListener::SupervisionListener(ThreadNetif &aThreadNetif) :
mNetif(aThreadNetif),
mTimer(aThreadNetif.GetIp6().mTimerScheduler, &SupervisionListener::HandleTimer, this),
mTimeout(0)
{
SetTimeout(kDefaultTimeout);
}
void SupervisionListener::Start(void)
{
RestartTimer();
}
void SupervisionListener::Stop(void)
{
mTimer.Stop();
}
void SupervisionListener::SetTimeout(uint16_t aTimeout)
{
if (mTimeout != aTimeout)
{
mTimeout = aTimeout;
RestartTimer();
}
}
void SupervisionListener::UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure)
{
// If listener is enabled and device is a child and it received a secure frame from its parent, restart the timer.
VerifyOrExit(mTimer.IsRunning() && aIsSecure && (mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
(mNetif.GetMle().GetNeighbor(aSourceAddress) == mNetif.GetMle().GetParent()));
RestartTimer();
exit:
return;
}
void SupervisionListener::RestartTimer(void)
{
// Restart the timer, if the timeout value is non-zero and the device is a sleepy child.
if ((mTimeout != 0) && (mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
(mNetif.GetMeshForwarder().GetRxOnWhenIdle() == false))
{
mTimer.Start(Timer::SecToMsec(mTimeout));
}
else
{
mTimer.Stop();
}
}
void SupervisionListener::HandleTimer(void *aContext)
{
static_cast<SupervisionListener *>(aContext)->HandleTimer();
}
void SupervisionListener::HandleTimer(void)
{
VerifyOrExit((mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild) &&
(mNetif.GetMeshForwarder().GetRxOnWhenIdle() == false));
mNetif.GetMle().SendChildUpdateRequest();
exit:
RestartTimer();
}
#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
} // namespace Utils
} // namespace ot
+284
View File
@@ -0,0 +1,284 @@
/*
* Copyright (c) 2017, 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 includes definitions for child supervision feature.
*
*/
#ifndef CHILD_SUPERVISION_HPP_
#define CHILD_SUPERVISION_HPP_
#ifdef OPENTHREAD_CONFIG_FILE
#include OPENTHREAD_CONFIG_FILE
#else
#include <openthread-config.h>
#endif
#include <stdint.h>
#include <common/timer.hpp>
#include <common/message.hpp>
#include <mac/mac_frame.hpp>
#include <thread/topology.hpp>
namespace ot {
class ThreadNetif;
namespace Utils {
/**
*
* Child supervision feature provides a mechanism for parent
* to ensure that a message is sent to each sleepy child within
* a fixed interval, namely the supervision interval. If there
* is no transmission to the child within the supervision
* interval, child supervisor enqueues and sends a supervision
* message (a data message with empty payload) to the child.
*
* On the child side, this is used to check the connectivity
* to the parent. If the child does not hear from its parent
* for a pre-specified timeout interval it assumes that it may
* be disconnected and tries to re-attach to the parent.
*
* The child supervision provides an alternative, more
* energy-efficient solution compared to requiring the sleepy child
* to periodically perform an MLE Child Update Request/Response
* exchange with the parent (as a way of verifying that it
* is still connected to the parent). The child supervision
* solution puts the burden of message transmissions on the
* parent instead of the typically more energy-constrained child.
*
* Note that most radios generate an auto-ack in hardware in
* response to a received frame, so the child cannot solely rely
* on the 15.4 acknowledgments it receives from parent as an
* indicator that it is still connected and is in parent's
* child table.
*
*/
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
/**
* This class implements a child supervisor.
*
*/
class ChildSupervisor
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aThreadNetif A reference to the Thread network interface.
*
*/
explicit ChildSupervisor(ThreadNetif &aThreadNetif);
/**
* This method starts the child supervision process on parent.
*
*/
void Start(void);
/**
* This method stops the child supervision process on parent.
*
*/
void Stop(void);
/**
* This method sets the supervision interval.
*
* Setting the supervision interval to a non-zero value will ensure to start the supervision process (if not
* already started).
*
* @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision.
*
*/
void SetSupervisionInterval(uint16_t aInterval);
/**
* This method returns the supervision interval.
*
* @returns The current supervision interval (seconds), or zero if supervision is disabled.
*
*/
uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; }
/**
* This method returns the destination for a supervision message.
*
* @param[in] aMessage The message for which to get the destination.
*
* @returns A pointer to the destination child of the message, or NULL if @p aMessage is not of supervision type.
*
*/
Child *GetDestination(const Message &aMessage) const;
/**
* This method updates the supervision state for a child. It informs the child supervisor that a message was
* successfully sent to the child.
*
* @param[in] aChild The child to which a message was successfully sent.
*
*/
void UpdateOnSend(Child &aChild);
private:
enum
{
kDefaultSupervisionInterval = OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL, // (seconds)
kOneSecond = 1000, // One second interval (in ms).
};
void SendMessage(Child &aChild);
static void HandleTimer(void *aContext);
void HandleTimer(void);
ThreadNetif &mNetif;
Timer mTimer;
uint16_t mSupervisionInterval;
};
#else // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
class ChildSupervisor
{
public:
explicit ChildSupervisor(ThreadNetif &) { }
void Start(void) { }
void Stop(void) { }
void SetSupervisionInterval(uint16_t) { }
uint16_t GetSupervisionInterval(void) const { return 0; }
Child *GetDestination(const Message &) const { return NULL; }
void UpdateOnSend(Child &) { }
};
#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
/**
* This class implements a child supervision listener.
*
*/
class SupervisionListener
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aThreadNetif A reference to the Thread network interface.
*
*/
explicit SupervisionListener(ThreadNetif &aThreadNetif);
/**
* This method starts the supervision listener operation.
*
*/
void Start(void);
/**
* This method stops the supervision listener operation.
*
*/
void Stop(void);
/**
* This method sets the supervision check timeout (in seconds).
*
* If the child does not hear from its parent within the given check timeout interval, it initiates the re-attach
* process (MLE Child Update Request/Response exchange with its parent). Setting the timeout to zero, disables the
* supervision check on the child.
*
* It is recommended to select a supervision check timeout value larger than the parent's child supervision
* interval plus the maximum time between the child's data poll transmissions.
*
* @param[in] aTimeout The timeout interval (in seconds), zero to disable the supervision check on the child.
*
*/
void SetTimeout(uint16_t aTimeout);
/**
* This method returns the supervision check timeout interval (in seconds).
*
* @returns The check timeout interval (in seconds) or zero if the supervision check on the child is disabled.
*
*/
uint16_t GetTimeout(void) const { return mTimeout; }
/**
* This method updates the supervision listener state. It informs the listener of a received frame.
*
* @param[in] aSource The source MAC address of the received frame
* @param[in] aIsSecure TRUE to indicate that the received frame is secure, FALSE otherwise.
*
*/
void UpdateOnReceive(const Mac::Address &aSource, bool aIsSeucre);
private:
enum
{
kDefaultTimeout = OPENTHREAD_CONFIG_SUPERVISION_CHECK_TIMEOUT, // (seconds)
};
void RestartTimer(void);
static void HandleTimer(void *aContext);
void HandleTimer(void);
ThreadNetif &mNetif;
Timer mTimer;
uint16_t mTimeout;
};
#else // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
class SupervisionListener
{
public:
SupervisionListener(ThreadNetif &) { }
void Start(void) { }
void Stop(void) { }
void SetTimeout(uint16_t) { }
uint16_t GetTimeout(void) const { return 0; }
void UpdateOnReceive(const Mac::Address &, bool) { }
};
#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
/**
* @}
*
*/
} // namespace Utils
} // namespace ot
#endif // CHILD_SUPERVISION_HPP_