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 end;
306 bool didCheckSteeringData = false;
307
308 Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
309
310 VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
311
312 // Find MLE Discovery TLV
313 SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end));
314
315 memset(&result, 0, sizeof(result));
316 result.mDiscover = true;
317 result.mPanId = linkInfo->mPanId;
318 result.mChannel = linkInfo->mChannel;
319 result.mRssi = linkInfo->mRss;
320 result.mLqi = linkInfo->mLqi;
321
322 aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
323
324 // Process MeshCoP TLVs
325 while (offset < end)
326 {
327 IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv));
328
329 switch (meshcopTlv.GetType())
330 {
331 case MeshCoP::Tlv::kDiscoveryResponse:
332 IgnoreError(aRxInfo.mMessage.Read(offset, discoveryResponse));
333 VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
334 result.mVersion = discoveryResponse.GetVersion();
335 result.mIsNative = discoveryResponse.IsNativeCommissioner();
336 break;
337
338 case MeshCoP::Tlv::kExtendedPanId:
339 SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offset,
340 AsCoreType(&result.mExtendedPanId)));
341 break;
342
343 case MeshCoP::Tlv::kNetworkName:
344 IgnoreError(aRxInfo.mMessage.Read(offset, networkName));
345 if (networkName.IsValid())
346 {
347 IgnoreError(AsCoreType(&result.mNetworkName).Set(networkName.GetNetworkName()));
348 }
349 break;
350
351 case MeshCoP::Tlv::kSteeringData:
352 if (meshcopTlv.GetLength() > 0)
353 {
354 MeshCoP::SteeringData &steeringData = AsCoreType(&result.mSteeringData);
355 uint8_t dataLength = MeshCoP::SteeringData::kMaxLength;
356
357 if (meshcopTlv.GetLength() < dataLength)
358 {
359 dataLength = meshcopTlv.GetLength();
360 }
361
362 steeringData.Init(dataLength);
363
364 SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength));
365
366 if (mEnableFiltering)
367 {
368 VerifyOrExit(steeringData.Contains(mFilterIndexes));
369 }
370
371 didCheckSteeringData = true;
372 }
373 break;
374
375 case MeshCoP::Tlv::kJoinerUdpPort:
376 SuccessOrExit(error =
377 Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offset, result.mJoinerUdpPort));
378 break;
379
380 default:
381 break;
382 }
383
384 offset += sizeof(meshcopTlv) + meshcopTlv.GetLength();
385 }
386
387 VerifyOrExit(!mEnableFiltering || didCheckSteeringData);
388
389 mCallback.InvokeIfSet(&result);
390
391 exit:
392 Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error);
393 }
394
395 } // namespace Mle
396 } // namespace ot
397