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/instance.hpp"
39 #include "common/locator_getters.hpp"
40 #include "common/logging.hpp"
41 #include "thread/thread_netif.hpp"
42 
43 namespace ot {
44 namespace Utils {
45 
46 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
47 
48 #if OPENTHREAD_FTD
49 
ChildSupervisor(Instance & aInstance)50 ChildSupervisor::ChildSupervisor(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mSupervisionInterval(kDefaultSupervisionInterval)
53 {
54 }
55 
SetSupervisionInterval(uint16_t aInterval)56 void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval)
57 {
58     mSupervisionInterval = aInterval;
59     CheckState();
60 }
61 
GetDestination(const Message & aMessage) const62 Child *ChildSupervisor::GetDestination(const Message &aMessage) const
63 {
64     Child *  child = nullptr;
65     uint16_t childIndex;
66 
67     VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
68 
69     IgnoreError(aMessage.Read(0, childIndex));
70     child = Get<ChildTable>().GetChildAtIndex(childIndex);
71 
72 exit:
73     return child;
74 }
75 
SendMessage(Child & aChild)76 void ChildSupervisor::SendMessage(Child &aChild)
77 {
78     Message *message = nullptr;
79     uint16_t childIndex;
80 
81     VerifyOrExit(aChild.GetIndirectMessageCount() == 0);
82 
83     message = Get<MessagePool>().New(Message::kTypeSupervision, sizeof(uint8_t));
84     VerifyOrExit(message != nullptr);
85 
86     // Supervision message is an empty payload 15.4 data frame.
87     // The child index is stored here in the message content to allow
88     // the destination of the message to be later retrieved using
89     // `ChildSupervisor::GetDestination(message)`.
90 
91     childIndex = Get<ChildTable>().GetChildIndex(aChild);
92     SuccessOrExit(message->Append(childIndex));
93 
94     SuccessOrExit(Get<ThreadNetif>().SendMessage(*message));
95     message = nullptr;
96 
97     otLogInfoUtil("Sending supervision message to child 0x%04x", aChild.GetRloc16());
98 
99 exit:
100     FreeMessage(message);
101 }
102 
UpdateOnSend(Child & aChild)103 void ChildSupervisor::UpdateOnSend(Child &aChild)
104 {
105     aChild.ResetSecondsSinceLastSupervision();
106 }
107 
HandleTimeTick(void)108 void ChildSupervisor::HandleTimeTick(void)
109 {
110     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
111     {
112         child.IncrementSecondsSinceLastSupervision();
113 
114         if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && !child.IsRxOnWhenIdle())
115         {
116             SendMessage(child);
117         }
118     }
119 }
120 
CheckState(void)121 void ChildSupervisor::CheckState(void)
122 {
123     bool shouldRun = false;
124 
125     // Child Supervision should run if `mSupervisionInterval` is not
126     // zero, Thread MLE operation is enabled, and there is at least one
127     // "valid" child in the child table.
128 
129     shouldRun = ((mSupervisionInterval != 0) && !Get<Mle::MleRouter>().IsDisabled() &&
130                  Get<ChildTable>().HasChildren(Child::kInStateValid));
131 
132     if (shouldRun && !Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
133     {
134         Get<TimeTicker>().RegisterReceiver(TimeTicker::kChildSupervisor);
135         otLogInfoUtil("Starting Child Supervision");
136     }
137 
138     if (!shouldRun && Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
139     {
140         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kChildSupervisor);
141         otLogInfoUtil("Stopping Child Supervision");
142     }
143 }
144 
HandleNotifierEvents(Events aEvents)145 void ChildSupervisor::HandleNotifierEvents(Events aEvents)
146 {
147     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildAdded | kEventThreadChildRemoved))
148     {
149         CheckState();
150     }
151 }
152 
153 #endif // #if OPENTHREAD_FTD
154 
SupervisionListener(Instance & aInstance)155 SupervisionListener::SupervisionListener(Instance &aInstance)
156     : InstanceLocator(aInstance)
157     , mTimeout(0)
158     , mTimer(aInstance, SupervisionListener::HandleTimer)
159 {
160     SetTimeout(kDefaultTimeout);
161 }
162 
Start(void)163 void SupervisionListener::Start(void)
164 {
165     RestartTimer();
166 }
167 
Stop(void)168 void SupervisionListener::Stop(void)
169 {
170     mTimer.Stop();
171 }
172 
SetTimeout(uint16_t aTimeout)173 void SupervisionListener::SetTimeout(uint16_t aTimeout)
174 {
175     if (mTimeout != aTimeout)
176     {
177         mTimeout = aTimeout;
178         RestartTimer();
179     }
180 }
181 
UpdateOnReceive(const Mac::Address & aSourceAddress,bool aIsSecure)182 void SupervisionListener::UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure)
183 {
184     // If listener is enabled and device is a child and it received a secure frame from its parent, restart the timer.
185 
186     VerifyOrExit(mTimer.IsRunning() && aIsSecure && Get<Mle::MleRouter>().IsChild() &&
187                  (Get<NeighborTable>().FindNeighbor(aSourceAddress) == &Get<Mle::MleRouter>().GetParent()));
188 
189     RestartTimer();
190 
191 exit:
192     return;
193 }
194 
RestartTimer(void)195 void SupervisionListener::RestartTimer(void)
196 {
197     if ((mTimeout != 0) && !Get<Mle::MleRouter>().IsDisabled() && !Get<MeshForwarder>().GetRxOnWhenIdle())
198     {
199         mTimer.Start(Time::SecToMsec(mTimeout));
200     }
201     else
202     {
203         mTimer.Stop();
204     }
205 }
206 
HandleTimer(Timer & aTimer)207 void SupervisionListener::HandleTimer(Timer &aTimer)
208 {
209     aTimer.Get<SupervisionListener>().HandleTimer();
210 }
211 
HandleTimer(void)212 void SupervisionListener::HandleTimer(void)
213 {
214     VerifyOrExit(Get<Mle::MleRouter>().IsChild() && !Get<MeshForwarder>().GetRxOnWhenIdle());
215 
216     otLogWarnUtil("Supervision timeout. No frame from parent in %d sec", mTimeout);
217 
218     IgnoreError(Get<Mle::MleRouter>().SendChildUpdateRequest());
219 
220 exit:
221     RestartTimer();
222 }
223 
224 #endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
225 
226 } // namespace Utils
227 } // namespace ot
228