blob: ed4806eb46d54ccd12b1f470b79496174aa55643 [file] [log] [blame]
/*
* 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/config.h>
#include "child_supervision.hpp"
#include <openthread/openthread.h>
#include "openthread-core-config.h"
#include "openthread-instance.h"
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "thread/thread_netif.hpp"
namespace ot {
namespace Utils {
#if OPENTHREAD_ENABLE_CHILD_SUPERVISION
#if OPENTHREAD_FTD
ChildSupervisor::ChildSupervisor(ThreadNetif &aThreadNetif) :
ThreadNetifLocator(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 = GetNetif().GetMle().GetChildren(&numChildren);
VerifyOrExit(childIndex < numChildren, child = NULL);
child += childIndex;
exit:
return child;
}
void ChildSupervisor::SendMessage(Child &aChild)
{
ThreadNetif &netif = GetNetif();
Message *message = NULL;
otError error = OT_ERROR_NONE;
uint8_t childIndex;
VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
message = netif.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 = netif.GetMle().GetChildIndex(aChild);
SuccessOrExit(error = message->Append(&childIndex, sizeof(childIndex)));
SuccessOrExit(error = netif.SendMessage(*message));
message = NULL;
otLogInfoMle(GetInstance(), "Sending supervision message to child 0x%04x", aChild.GetRloc16());
exit:
if (message != NULL)
{
message->Free();
}
}
void ChildSupervisor::UpdateOnSend(Child &aChild)
{
aChild.ResetSecondsSinceLastSupervision();
}
void ChildSupervisor::HandleTimer(Timer &aTimer)
{
GetOwner(aTimer).HandleTimer();
}
void ChildSupervisor::HandleTimer(void)
{
Child *child;
uint8_t numChildren;
VerifyOrExit(mSupervisionInterval != 0);
child = GetNetif().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;
}
ChildSupervisor &ChildSupervisor::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
ChildSupervisor &supervisor = *static_cast<ChildSupervisor *>(aContext.GetContext());
#else
ChildSupervisor &supervisor = otGetThreadNetif().GetChildSupervisor();
OT_UNUSED_VARIABLE(aContext);
#endif
return supervisor;
}
#endif // #if OPENTHREAD_FTD
SupervisionListener::SupervisionListener(ThreadNetif &aThreadNetif) :
ThreadNetifLocator(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)
{
ThreadNetif &netif = GetNetif();
// 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 && (netif.GetMle().GetRole() == OT_DEVICE_ROLE_CHILD) &&
(netif.GetMle().GetNeighbor(aSourceAddress) == netif.GetMle().GetParent()));
RestartTimer();
exit:
return;
}
void SupervisionListener::RestartTimer(void)
{
ThreadNetif &netif = GetNetif();
// Restart the timer, if the timeout value is non-zero and the device is a sleepy child.
if ((mTimeout != 0) && (netif.GetMle().GetRole() == OT_DEVICE_ROLE_CHILD) &&
(netif.GetMeshForwarder().GetRxOnWhenIdle() == false))
{
mTimer.Start(Timer::SecToMsec(mTimeout));
}
else
{
mTimer.Stop();
}
}
void SupervisionListener::HandleTimer(Timer &aTimer)
{
GetOwner(aTimer).HandleTimer();
}
void SupervisionListener::HandleTimer(void)
{
ThreadNetif &netif = GetNetif();
VerifyOrExit((netif.GetMle().GetRole() == OT_DEVICE_ROLE_CHILD) &&
(netif.GetMeshForwarder().GetRxOnWhenIdle() == false));
otLogWarnMle(netif.GetInstance(), "Supervision timeout. No frame from parent in %d sec", mTimeout);
netif.GetMle().SendChildUpdateRequest();
exit:
RestartTimer();
}
SupervisionListener &SupervisionListener::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
SupervisionListener &listener = *static_cast<SupervisionListener *>(aContext.GetContext());
#else
SupervisionListener &listener = otGetThreadNetif().GetSupervisionListener();
OT_UNUSED_VARIABLE(aContext);
#endif
return listener;
}
#endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION
} // namespace Utils
} // namespace ot