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