blob: 7f6368aece5b63ed3d0dda5edf55e0bf7373b6f5 [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 "child_supervision.hpp"
#include "openthread-core-config.h"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/logging.hpp"
#include "thread/thread_netif.hpp"
namespace ot {
namespace Utils {
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#if OPENTHREAD_FTD
ChildSupervisor::ChildSupervisor(Instance &aInstance)
: InstanceLocator(aInstance)
, mSupervisionInterval(kDefaultSupervisionInterval)
{
}
void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval)
{
mSupervisionInterval = aInterval;
CheckState();
}
Child *ChildSupervisor::GetDestination(const Message &aMessage) const
{
Child * child = nullptr;
uint16_t childIndex;
VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
IgnoreError(aMessage.Read(0, childIndex));
child = Get<ChildTable>().GetChildAtIndex(childIndex);
exit:
return child;
}
void ChildSupervisor::SendMessage(Child &aChild)
{
Message *message = nullptr;
uint16_t childIndex;
VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
message = Get<MessagePool>().Allocate(Message::kTypeSupervision, sizeof(uint8_t));
VerifyOrExit(message != nullptr);
// 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 = Get<ChildTable>().GetChildIndex(aChild);
SuccessOrExit(message->Append(childIndex));
SuccessOrExit(Get<ThreadNetif>().SendMessage(*message));
message = nullptr;
otLogInfoUtil("Sending supervision message to child 0x%04x", aChild.GetRloc16());
exit:
FreeMessage(message);
}
void ChildSupervisor::UpdateOnSend(Child &aChild)
{
aChild.ResetSecondsSinceLastSupervision();
}
void ChildSupervisor::HandleTimeTick(void)
{
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
child.IncrementSecondsSinceLastSupervision();
if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && !child.IsRxOnWhenIdle())
{
SendMessage(child);
}
}
}
void ChildSupervisor::CheckState(void)
{
bool shouldRun = false;
// Child Supervision should run if `mSupervisionInterval` is not
// zero, Thread MLE operation is enabled, and there is at least one
// "valid" child in the child table.
shouldRun = ((mSupervisionInterval != 0) && !Get<Mle::MleRouter>().IsDisabled() &&
Get<ChildTable>().HasChildren(Child::kInStateValid));
if (shouldRun && !Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
{
Get<TimeTicker>().RegisterReceiver(TimeTicker::kChildSupervisor);
otLogInfoUtil("Starting Child Supervision");
}
if (!shouldRun && Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
{
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kChildSupervisor);
otLogInfoUtil("Stopping Child Supervision");
}
}
void ChildSupervisor::HandleNotifierEvents(Events aEvents)
{
if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildAdded | kEventThreadChildRemoved))
{
CheckState();
}
}
#endif // #if OPENTHREAD_FTD
SupervisionListener::SupervisionListener(Instance &aInstance)
: InstanceLocator(aInstance)
, mTimeout(0)
, mTimer(aInstance, SupervisionListener::HandleTimer)
{
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 && Get<Mle::MleRouter>().IsChild() &&
(Get<NeighborTable>().FindNeighbor(aSourceAddress) == &Get<Mle::MleRouter>().GetParent()));
RestartTimer();
exit:
return;
}
void SupervisionListener::RestartTimer(void)
{
if ((mTimeout != 0) && !Get<Mle::MleRouter>().IsDisabled() && !Get<MeshForwarder>().GetRxOnWhenIdle())
{
mTimer.Start(Time::SecToMsec(mTimeout));
}
else
{
mTimer.Stop();
}
}
void SupervisionListener::HandleTimer(Timer &aTimer)
{
aTimer.Get<SupervisionListener>().HandleTimer();
}
void SupervisionListener::HandleTimer(void)
{
VerifyOrExit(Get<Mle::MleRouter>().IsChild() && !Get<MeshForwarder>().GetRxOnWhenIdle());
otLogWarnUtil("Supervision timeout. No frame from parent in %d sec", mTimeout);
IgnoreError(Get<Mle::MleRouter>().SendChildUpdateRequest());
exit:
RestartTimer();
}
#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
} // namespace Utils
} // namespace ot