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