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