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