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