1 /*
2  *  Copyright (c) 2017, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the child supervision feature.
32  */
33 
34 #include "child_supervision.hpp"
35 
36 #include "openthread-core-config.h"
37 #include "common/code_utils.hpp"
38 #include "common/locator_getters.hpp"
39 #include "common/log.hpp"
40 #include "instance/instance.hpp"
41 #include "thread/thread_netif.hpp"
42 
43 namespace ot {
44 
45 RegisterLogModule("ChildSupervsn");
46 
47 #if OPENTHREAD_FTD
48 
ChildSupervisor(Instance & aInstance)49 ChildSupervisor::ChildSupervisor(Instance &aInstance)
50     : InstanceLocator(aInstance)
51 {
52 }
53 
GetDestination(const Message & aMessage) const54 Child *ChildSupervisor::GetDestination(const Message &aMessage) const
55 {
56     Child   *child = nullptr;
57     uint16_t childIndex;
58 
59     VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
60 
61     IgnoreError(aMessage.Read(0, childIndex));
62     child = Get<ChildTable>().GetChildAtIndex(childIndex);
63 
64 exit:
65     return child;
66 }
67 
SendMessage(Child & aChild)68 void ChildSupervisor::SendMessage(Child &aChild)
69 {
70     OwnedPtr<Message> messagePtr;
71     uint16_t          childIndex;
72 
73     VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
74 
75     messagePtr.Reset(Get<MessagePool>().Allocate(Message::kTypeSupervision, sizeof(uint8_t)));
76     VerifyOrExit(messagePtr != nullptr);
77 
78     // Supervision message is an empty payload 15.4 data frame.
79     // The child index is stored here in the message content to allow
80     // the destination of the message to be later retrieved using
81     // `ChildSupervisor::GetDestination(message)`.
82 
83     childIndex = Get<ChildTable>().GetChildIndex(aChild);
84     SuccessOrExit(messagePtr->Append(childIndex));
85 
86     Get<MeshForwarder>().SendMessage(messagePtr.PassOwnership());
87 
88     LogInfo("Sending supervision message to child 0x%04x", aChild.GetRloc16());
89 
90 exit:
91     return;
92 }
93 
UpdateOnSend(Child & aChild)94 void ChildSupervisor::UpdateOnSend(Child &aChild) { aChild.ResetSecondsSinceLastSupervision(); }
95 
HandleTimeTick(void)96 void ChildSupervisor::HandleTimeTick(void)
97 {
98     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
99     {
100         if (child.IsRxOnWhenIdle() || (child.GetSupervisionInterval() == 0))
101         {
102             continue;
103         }
104 
105         child.IncrementSecondsSinceLastSupervision();
106 
107         if (child.GetSecondsSinceLastSupervision() >= child.GetSupervisionInterval())
108         {
109             SendMessage(child);
110         }
111     }
112 }
113 
CheckState(void)114 void ChildSupervisor::CheckState(void)
115 {
116     // Child Supervision should run if Thread MLE operation is
117     // enabled, and there is at least one "valid" child in the
118     // child table.
119 
120     bool shouldRun = (!Get<Mle::Mle>().IsDisabled() && Get<ChildTable>().HasChildren(Child::kInStateValid));
121 
122     if (shouldRun && !Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
123     {
124         Get<TimeTicker>().RegisterReceiver(TimeTicker::kChildSupervisor);
125         LogInfo("Starting Child Supervision");
126     }
127 
128     if (!shouldRun && Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
129     {
130         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kChildSupervisor);
131         LogInfo("Stopping Child Supervision");
132     }
133 }
134 
HandleNotifierEvents(Events aEvents)135 void ChildSupervisor::HandleNotifierEvents(Events aEvents)
136 {
137     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildAdded | kEventThreadChildRemoved))
138     {
139         CheckState();
140     }
141 }
142 
143 #endif // #if OPENTHREAD_FTD
144 
SupervisionListener(Instance & aInstance)145 SupervisionListener::SupervisionListener(Instance &aInstance)
146     : InstanceLocator(aInstance)
147     , mTimeout(0)
148     , mInterval(kDefaultInterval)
149     , mTimer(aInstance)
150 {
151     SetTimeout(kDefaultTimeout);
152 }
153 
Start(void)154 void SupervisionListener::Start(void) { RestartTimer(); }
155 
Stop(void)156 void SupervisionListener::Stop(void) { mTimer.Stop(); }
157 
SetInterval(uint16_t aInterval)158 void SupervisionListener::SetInterval(uint16_t aInterval)
159 {
160     VerifyOrExit(mInterval != aInterval);
161 
162     LogInfo("Interval: %u -> %u", mInterval, aInterval);
163 
164     mInterval = aInterval;
165 
166     if (Get<Mle::Mle>().IsChild())
167     {
168         IgnoreError(Get<Mle::Mle>().SendChildUpdateRequest());
169     }
170 
171 exit:
172     return;
173 }
174 
SetTimeout(uint16_t aTimeout)175 void SupervisionListener::SetTimeout(uint16_t aTimeout)
176 {
177     if (mTimeout != aTimeout)
178     {
179         LogInfo("Timeout: %u -> %u", mTimeout, aTimeout);
180 
181         mTimeout = aTimeout;
182         RestartTimer();
183     }
184 }
185 
UpdateOnReceive(const Mac::Address & aSourceAddress,bool aIsSecure)186 void SupervisionListener::UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure)
187 {
188     // If listener is enabled and device is a child and it received a secure frame from its parent, restart the timer.
189 
190     VerifyOrExit(mTimer.IsRunning() && aIsSecure && Get<Mle::MleRouter>().IsChild() &&
191                  (Get<NeighborTable>().FindNeighbor(aSourceAddress) == &Get<Mle::MleRouter>().GetParent()));
192 
193     RestartTimer();
194 
195 exit:
196     return;
197 }
198 
RestartTimer(void)199 void SupervisionListener::RestartTimer(void)
200 {
201     if ((mTimeout != 0) && !Get<Mle::MleRouter>().IsDisabled() && !Get<MeshForwarder>().GetRxOnWhenIdle())
202     {
203         mTimer.Start(Time::SecToMsec(mTimeout));
204     }
205     else
206     {
207         mTimer.Stop();
208     }
209 }
210 
HandleTimer(void)211 void SupervisionListener::HandleTimer(void)
212 {
213     VerifyOrExit(Get<Mle::MleRouter>().IsChild() && !Get<MeshForwarder>().GetRxOnWhenIdle());
214 
215     LogWarn("Supervision timeout. No frame from parent in %u sec", mTimeout);
216     mCounter++;
217 
218     IgnoreError(Get<Mle::MleRouter>().SendChildUpdateRequest());
219 
220 exit:
221     RestartTimer();
222 }
223 
224 } // namespace ot
225