diff --git a/configure.ac b/configure.ac index 83d0cca21..387d9335b 100644 --- a/configure.ac +++ b/configure.ac @@ -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} diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index 5444fff36..5d9541738 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -137,6 +137,7 @@ + @@ -224,6 +225,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index 193a486bb..b39290f9b 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -297,6 +297,9 @@ Source Files\crypto + + Source Files\utils + Source Files\utils @@ -554,6 +557,9 @@ Header Files\crypto + + Header Files\utils + Header Files\utils diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 82a21be8d..b04e77e9f 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -145,6 +145,7 @@ + @@ -258,6 +259,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 0d2f01f64..97cf5ad50 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -297,6 +297,9 @@ Source Files\crypto + + Source Files\utils + Source Files\utils @@ -554,6 +557,9 @@ Header Files\crypto + + Header Files\utils + Header Files\utils diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am index f19bf5b85..d8d45fb31 100644 --- a/include/openthread/Makefile.am +++ b/include/openthread/Makefile.am @@ -47,6 +47,7 @@ PRETTY_SUBDIRS = \ $(NULL) openthread_headers = \ + child_supervision.h \ cli.h \ border_agent_proxy.h \ coap.h \ diff --git a/include/openthread/child_supervision.h b/include/openthread/child_supervision.h new file mode 100644 index 000000000..549494830 --- /dev/null +++ b/include/openthread/child_supervision.h @@ -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 +#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_ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index e6d225f42..b43090a86 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -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 \ diff --git a/src/core/api/child_supervision_api.cpp b/src/core/api/child_supervision_api.cpp new file mode 100644 index 000000000..b31130ea6 --- /dev/null +++ b/src/core/api/child_supervision_api.cpp @@ -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 diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 7d4d3a347..af4f82e36 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -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 diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index d58eee153..32fc4e50c 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -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_ diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index d7757d872..0f5f4ac40 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -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()) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 309ff3612..19904818a 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -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, diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 5bfad985f..0bf31f1e8 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -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)) { diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp index 6f0392c60..ac4a455a9 100644 --- a/src/core/thread/mle_router_ftd.hpp +++ b/src/core/thread/mle_router_ftd.hpp @@ -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 diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp index eb292e6b5..6e67681f1 100644 --- a/src/core/thread/mle_router_mtd.hpp +++ b/src/core/thread/mle_router_mtd.hpp @@ -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 diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index cf0306b0f..e0e68653c 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -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); diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 0a9f23097..0a2bba6a1 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -66,6 +66,7 @@ #include #include #include +#include #if OPENTHREAD_ENABLE_JAM_DETECTION #include @@ -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; diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 6c92b7367..fab95b15a 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -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 + }; /** diff --git a/src/core/utils/child_supervision.cpp b/src/core/utils/child_supervision.cpp new file mode 100644 index 000000000..5346a72eb --- /dev/null +++ b/src/core/utils/child_supervision.cpp @@ -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 +#include +#include + +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(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(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 diff --git a/src/core/utils/child_supervision.hpp b/src/core/utils/child_supervision.hpp new file mode 100644 index 000000000..8e6f09758 --- /dev/null +++ b/src/core/utils/child_supervision.hpp @@ -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 +#endif + +#include +#include +#include +#include +#include + +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_