1 /*
2 * Copyright (c) 2016-2020, 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 MLE Discover Scan process.
32 */
33
34 #include "discover_scanner.hpp"
35
36 #include "common/as_core_type.hpp"
37 #include "common/code_utils.hpp"
38 #include "common/instance.hpp"
39 #include "common/locator_getters.hpp"
40 #include "thread/mesh_forwarder.hpp"
41 #include "thread/mle.hpp"
42 #include "thread/mle_router.hpp"
43 #include "thread/version.hpp"
44
45 namespace ot {
46 namespace Mle {
47
DiscoverScanner(Instance & aInstance)48 DiscoverScanner::DiscoverScanner(Instance &aInstance)
49 : InstanceLocator(aInstance)
50 , mScanDoneTask(aInstance)
51 , mTimer(aInstance)
52 , mFilterIndexes()
53 , mState(kStateIdle)
54 , mScanChannel(0)
55 , mAdvDataLength(0)
56 , mEnableFiltering(false)
57 , mShouldRestorePanId(false)
58 {
59 }
60
Discover(const Mac::ChannelMask & aScanChannels,uint16_t aPanId,bool aJoiner,bool aEnableFiltering,const FilterIndexes * aFilterIndexes,Handler aCallback,void * aContext)61 Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels,
62 uint16_t aPanId,
63 bool aJoiner,
64 bool aEnableFiltering,
65 const FilterIndexes *aFilterIndexes,
66 Handler aCallback,
67 void *aContext)
68 {
69 Error error = kErrorNone;
70 Mle::TxMessage *message = nullptr;
71 Tlv tlv;
72 Ip6::Address destination;
73 MeshCoP::DiscoveryRequestTlv discoveryRequest;
74 MeshCoP::JoinerAdvertisementTlv joinerAdvertisement;
75
76 VerifyOrExit(Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
77
78 VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
79
80 mEnableFiltering = aEnableFiltering;
81
82 if (mEnableFiltering)
83 {
84 if (aFilterIndexes == nullptr)
85 {
86 Mac::ExtAddress extAddress;
87
88 Get<Radio>().GetIeeeEui64(extAddress);
89 MeshCoP::ComputeJoinerId(extAddress, extAddress);
90 MeshCoP::SteeringData::CalculateHashBitIndexes(extAddress, mFilterIndexes);
91 }
92 else
93 {
94 mFilterIndexes = *aFilterIndexes;
95 }
96 }
97
98 mCallback.Set(aCallback, aContext);
99 mShouldRestorePanId = false;
100 mScanChannels = Get<Mac::Mac>().GetSupportedChannelMask();
101
102 if (!aScanChannels.IsEmpty())
103 {
104 mScanChannels.Intersect(aScanChannels);
105 }
106
107 VerifyOrExit((message = Get<Mle>().NewMleMessage(Mle::kCommandDiscoveryRequest)) != nullptr, error = kErrorNoBufs);
108 message->SetPanId(aPanId);
109
110 // Prepare sub-TLV MeshCoP Discovery Request.
111 discoveryRequest.Init();
112 discoveryRequest.SetVersion(kThreadVersion);
113 discoveryRequest.SetJoiner(aJoiner);
114
115 if (mAdvDataLength != 0)
116 {
117 // Prepare sub-TLV MeshCoP Joiner Advertisement.
118 joinerAdvertisement.Init();
119 joinerAdvertisement.SetOui(mOui);
120 joinerAdvertisement.SetAdvData(mAdvData, mAdvDataLength);
121 }
122
123 // Append Discovery TLV with one or two sub-TLVs.
124 tlv.SetType(Tlv::kDiscovery);
125 tlv.SetLength(
126 static_cast<uint8_t>(discoveryRequest.GetSize() + ((mAdvDataLength != 0) ? joinerAdvertisement.GetSize() : 0)));
127
128 SuccessOrExit(error = message->Append(tlv));
129 SuccessOrExit(error = discoveryRequest.AppendTo(*message));
130
131 if (mAdvDataLength != 0)
132 {
133 SuccessOrExit(error = joinerAdvertisement.AppendTo(*message));
134 }
135
136 destination.SetToLinkLocalAllRoutersMulticast();
137
138 SuccessOrExit(error = message->SendTo(destination));
139
140 if ((aPanId == Mac::kPanIdBroadcast) && (Get<Mac::Mac>().GetPanId() == Mac::kPanIdBroadcast))
141 {
142 // In case a specific PAN ID of a Thread Network to be
143 // discovered is not known, Discovery Request messages MUST
144 // have the Destination PAN ID in the IEEE 802.15.4 MAC
145 // header set to be the Broadcast PAN ID (0xffff) and the
146 // Source PAN ID set to a randomly generated value.
147
148 Get<Mac::Mac>().SetPanId(Mac::GenerateRandomPanId());
149 mShouldRestorePanId = true;
150 }
151
152 mScanChannel = Mac::ChannelMask::kChannelIteratorFirst;
153 mState = (mScanChannels.GetNextChannel(mScanChannel) == kErrorNone) ? kStateScanning : kStateScanDone;
154
155 // For rx-off-when-idle device, temporarily enable receiver during discovery procedure.
156 if (!Get<Mle>().IsDisabled() && !Get<Mle>().IsRxOnWhenIdle())
157 {
158 Get<MeshForwarder>().SetRxOnWhenIdle(true);
159 }
160
161 Mle::Log(Mle::kMessageSend, Mle::kTypeDiscoveryRequest, destination);
162
163 exit:
164 FreeMessageOnError(message, error);
165 return error;
166 }
167
SetJoinerAdvertisement(uint32_t aOui,const uint8_t * aAdvData,uint8_t aAdvDataLength)168 Error DiscoverScanner::SetJoinerAdvertisement(uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength)
169 {
170 Error error = kErrorNone;
171
172 VerifyOrExit((aAdvData != nullptr) && (aAdvDataLength != 0) &&
173 (aAdvDataLength <= MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength) && (aOui <= kMaxOui),
174 error = kErrorInvalidArgs);
175
176 mOui = aOui;
177 mAdvDataLength = aAdvDataLength;
178
179 memcpy(mAdvData, aAdvData, aAdvDataLength);
180
181 exit:
182 return error;
183 }
184
PrepareDiscoveryRequestFrame(Mac::TxFrame & aFrame)185 Mac::TxFrame *DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame)
186 {
187 Mac::TxFrame *frame = &aFrame;
188
189 switch (mState)
190 {
191 case kStateIdle:
192 case kStateScanDone:
193 // If scan is finished (no more channels to scan), abort the
194 // Discovery Request frame tx. The handler callback is invoked &
195 // state is cleared from `HandleDiscoveryRequestFrameTxDone()`.
196 frame = nullptr;
197 break;
198
199 case kStateScanning:
200 frame->SetChannel(mScanChannel);
201 IgnoreError(Get<Mac::Mac>().SetTemporaryChannel(mScanChannel));
202 break;
203 }
204
205 return frame;
206 }
207
HandleDiscoveryRequestFrameTxDone(Message & aMessage)208 void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage)
209 {
210 switch (mState)
211 {
212 case kStateIdle:
213 break;
214
215 case kStateScanning:
216 // Mark the Discovery Request message for direct tx to ensure it
217 // is not dequeued and freed by `MeshForwarder` and is ready for
218 // the next scan channel. Also pause message tx on `MeshForwarder`
219 // while listening to receive Discovery Responses.
220 aMessage.SetDirectTransmission();
221 aMessage.SetTimestampToNow();
222 Get<MeshForwarder>().PauseMessageTransmissions();
223 mTimer.Start(kDefaultScanDuration);
224 break;
225
226 case kStateScanDone:
227 HandleDiscoverComplete();
228 break;
229 }
230 }
231
HandleDiscoverComplete(void)232 void DiscoverScanner::HandleDiscoverComplete(void)
233 {
234 // Restore Data Polling or CSL for rx-off-when-idle device.
235 if (!Get<Mle>().IsDisabled() && !Get<Mle>().IsRxOnWhenIdle())
236 {
237 Get<MeshForwarder>().SetRxOnWhenIdle(false);
238 }
239
240 switch (mState)
241 {
242 case kStateIdle:
243 break;
244
245 case kStateScanning:
246 mTimer.Stop();
247 Get<MeshForwarder>().ResumeMessageTransmissions();
248
249 OT_FALL_THROUGH;
250
251 case kStateScanDone:
252 Get<Mac::Mac>().ClearTemporaryChannel();
253
254 if (mShouldRestorePanId)
255 {
256 Get<Mac::Mac>().SetPanId(Mac::kPanIdBroadcast);
257 mShouldRestorePanId = false;
258 }
259
260 // Post the tasklet to change `mState` and invoke handler
261 // callback. This allows users to safely call OT APIs from
262 // the callback.
263 mScanDoneTask.Post();
264 break;
265 }
266 }
267
HandleScanDoneTask(void)268 void DiscoverScanner::HandleScanDoneTask(void)
269 {
270 mState = kStateIdle;
271 mCallback.InvokeIfSet(nullptr);
272 }
273
HandleTimer(void)274 void DiscoverScanner::HandleTimer(void)
275 {
276 VerifyOrExit(mState == kStateScanning);
277
278 // Move to next scan channel and resume message transmissions on
279 // `MeshForwarder` so that the queued MLE Discovery Request message
280 // is prepared again for the next scan channel. If no more channel
281 // to scan, change the state to `kStateScanDone` which ensures the
282 // frame tx is aborted from `PrepareDiscoveryRequestFrame()` and
283 // then wraps up the scan (invoking handler callback).
284
285 if (mScanChannels.GetNextChannel(mScanChannel) != kErrorNone)
286 {
287 mState = kStateScanDone;
288 }
289
290 Get<MeshForwarder>().ResumeMessageTransmissions();
291
292 exit:
293 return;
294 }
295
HandleDiscoveryResponse(Mle::RxInfo & aRxInfo) const296 void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
297 {
298 Error error = kErrorNone;
299 const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
300 MeshCoP::Tlv meshcopTlv;
301 MeshCoP::DiscoveryResponseTlv discoveryResponse;
302 MeshCoP::NetworkNameTlv networkName;
303 ScanResult result;
304 uint16_t offset;
305 uint16_t length;
306 uint16_t end;
307 bool didCheckSteeringData = false;
308
309 Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
310
311 VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
312
313 // Find MLE Discovery TLV
314 SuccessOrExit(error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset, length));
315 end = offset + length;
316
317 memset(&result, 0, sizeof(result));
318 result.mDiscover = true;
319 result.mPanId = linkInfo->mPanId;
320 result.mChannel = linkInfo->mChannel;
321 result.mRssi = linkInfo->mRss;
322 result.mLqi = linkInfo->mLqi;
323
324 aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
325
326 // Process MeshCoP TLVs
327 while (offset < end)
328 {
329 IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv));
330
331 switch (meshcopTlv.GetType())
332 {
333 case MeshCoP::Tlv::kDiscoveryResponse:
334 IgnoreError(aRxInfo.mMessage.Read(offset, discoveryResponse));
335 VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
336 result.mVersion = discoveryResponse.GetVersion();
337 result.mIsNative = discoveryResponse.IsNativeCommissioner();
338 break;
339
340 case MeshCoP::Tlv::kExtendedPanId:
341 SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offset,
342 AsCoreType(&result.mExtendedPanId)));
343 break;
344
345 case MeshCoP::Tlv::kNetworkName:
346 IgnoreError(aRxInfo.mMessage.Read(offset, networkName));
347 if (networkName.IsValid())
348 {
349 IgnoreError(AsCoreType(&result.mNetworkName).Set(networkName.GetNetworkName()));
350 }
351 break;
352
353 case MeshCoP::Tlv::kSteeringData:
354 if (meshcopTlv.GetLength() > 0)
355 {
356 MeshCoP::SteeringData &steeringData = AsCoreType(&result.mSteeringData);
357 uint8_t dataLength = MeshCoP::SteeringData::kMaxLength;
358
359 if (meshcopTlv.GetLength() < dataLength)
360 {
361 dataLength = meshcopTlv.GetLength();
362 }
363
364 steeringData.Init(dataLength);
365
366 SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength));
367
368 if (mEnableFiltering)
369 {
370 VerifyOrExit(steeringData.Contains(mFilterIndexes));
371 }
372
373 didCheckSteeringData = true;
374 }
375 break;
376
377 case MeshCoP::Tlv::kJoinerUdpPort:
378 SuccessOrExit(error =
379 Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offset, result.mJoinerUdpPort));
380 break;
381
382 default:
383 break;
384 }
385
386 offset += sizeof(meshcopTlv) + meshcopTlv.GetLength();
387 }
388
389 VerifyOrExit(!mEnableFiltering || didCheckSteeringData);
390
391 mCallback.InvokeIfSet(&result);
392
393 exit:
394 Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error);
395 }
396
397 } // namespace Mle
398 } // namespace ot
399