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