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