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/locator_getters.hpp"
39 #include "instance/instance.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,Error aError)208 void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage, Error aError)
209 {
210     switch (mState)
211     {
212     case kStateIdle:
213         break;
214 
215     case kStateScanning:
216         if ((aError == kErrorNone) || (aError == kErrorChannelAccessFailure))
217         {
218             // Mark the Discovery Request message for direct tx to ensure it
219             // is not dequeued and freed by `MeshForwarder` and is ready for
220             // the next scan channel. Also pause message tx on `MeshForwarder`
221             // while listening to receive Discovery Responses.
222             aMessage.SetDirectTransmission();
223             aMessage.SetTimestampToNow();
224             Get<MeshForwarder>().PauseMessageTransmissions();
225             mTimer.Start(kDefaultScanDuration);
226             break;
227         }
228 
229         // If we encounter other error failures (e.g., `kErrorDrop` due
230         // to queue management dropping the message or if message being
231         // evicted), `aMessage` may be immediately freed. This prevents
232         // us from reusing it to request a scan on the next scan channel.
233         // As a result, we stop the scan operation in such cases.
234 
235         mState = kStateScanDone;
236 
237         OT_FALL_THROUGH;
238 
239     case kStateScanDone:
240         HandleDiscoverComplete();
241         break;
242     }
243 }
244 
HandleDiscoverComplete(void)245 void DiscoverScanner::HandleDiscoverComplete(void)
246 {
247     // Restore Data Polling or CSL for rx-off-when-idle device.
248     if (!Get<Mle>().IsDisabled() && !Get<Mle>().IsRxOnWhenIdle())
249     {
250         Get<MeshForwarder>().SetRxOnWhenIdle(false);
251     }
252 
253     switch (mState)
254     {
255     case kStateIdle:
256         break;
257 
258     case kStateScanning:
259         mTimer.Stop();
260         Get<MeshForwarder>().ResumeMessageTransmissions();
261 
262         OT_FALL_THROUGH;
263 
264     case kStateScanDone:
265         Get<Mac::Mac>().ClearTemporaryChannel();
266 
267         if (mShouldRestorePanId)
268         {
269             Get<Mac::Mac>().SetPanId(Mac::kPanIdBroadcast);
270             mShouldRestorePanId = false;
271         }
272 
273         // Post the tasklet to change `mState` and invoke handler
274         // callback. This allows users to safely call OT APIs from
275         // the callback.
276         mScanDoneTask.Post();
277         break;
278     }
279 }
280 
HandleScanDoneTask(void)281 void DiscoverScanner::HandleScanDoneTask(void)
282 {
283     mState = kStateIdle;
284     mCallback.InvokeIfSet(nullptr);
285 }
286 
HandleTimer(void)287 void DiscoverScanner::HandleTimer(void)
288 {
289     VerifyOrExit(mState == kStateScanning);
290 
291     // Move to next scan channel and resume message transmissions on
292     // `MeshForwarder` so that the queued MLE Discovery Request message
293     // is prepared again for the next scan channel. If no more channel
294     // to scan, change the state to `kStateScanDone` which ensures the
295     // frame tx is aborted  from `PrepareDiscoveryRequestFrame()` and
296     // then wraps up the scan (invoking handler callback).
297 
298     if (mScanChannels.GetNextChannel(mScanChannel) != kErrorNone)
299     {
300         mState = kStateScanDone;
301     }
302 
303     Get<MeshForwarder>().ResumeMessageTransmissions();
304 
305 exit:
306     return;
307 }
308 
HandleDiscoveryResponse(Mle::RxInfo & aRxInfo) const309 void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
310 {
311     Error                         error = kErrorNone;
312     MeshCoP::Tlv                  meshcopTlv;
313     MeshCoP::DiscoveryResponseTlv discoveryResponse;
314     MeshCoP::NetworkNameTlv       networkName;
315     ScanResult                    result;
316     uint16_t                      offset;
317     uint16_t                      end;
318     bool                          didCheckSteeringData = false;
319 
320     Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
321 
322     VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
323 
324     // Find MLE Discovery TLV
325     SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end));
326 
327     ClearAllBytes(result);
328     result.mDiscover = true;
329     result.mPanId    = aRxInfo.mMessage.GetPanId();
330     result.mChannel  = aRxInfo.mMessage.GetChannel();
331     result.mRssi     = aRxInfo.mMessage.GetAverageRss();
332     result.mLqi      = aRxInfo.mMessage.GetAverageLqi();
333 
334     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
335 
336     // Process MeshCoP TLVs
337     while (offset < end)
338     {
339         IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv));
340 
341         switch (meshcopTlv.GetType())
342         {
343         case MeshCoP::Tlv::kDiscoveryResponse:
344             IgnoreError(aRxInfo.mMessage.Read(offset, discoveryResponse));
345             VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
346             result.mVersion  = discoveryResponse.GetVersion();
347             result.mIsNative = discoveryResponse.IsNativeCommissioner();
348             break;
349 
350         case MeshCoP::Tlv::kExtendedPanId:
351             SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offset,
352                                                                        AsCoreType(&result.mExtendedPanId)));
353             break;
354 
355         case MeshCoP::Tlv::kNetworkName:
356             IgnoreError(aRxInfo.mMessage.Read(offset, networkName));
357             if (networkName.IsValid())
358             {
359                 IgnoreError(AsCoreType(&result.mNetworkName).Set(networkName.GetNetworkName()));
360             }
361             break;
362 
363         case MeshCoP::Tlv::kSteeringData:
364             if (meshcopTlv.GetLength() > 0)
365             {
366                 MeshCoP::SteeringData &steeringData = AsCoreType(&result.mSteeringData);
367                 uint8_t                dataLength   = MeshCoP::SteeringData::kMaxLength;
368 
369                 if (meshcopTlv.GetLength() < dataLength)
370                 {
371                     dataLength = meshcopTlv.GetLength();
372                 }
373 
374                 steeringData.Init(dataLength);
375 
376                 SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength));
377 
378                 if (mEnableFiltering)
379                 {
380                     VerifyOrExit(steeringData.Contains(mFilterIndexes));
381                 }
382 
383                 didCheckSteeringData = true;
384             }
385             break;
386 
387         case MeshCoP::Tlv::kJoinerUdpPort:
388             SuccessOrExit(error =
389                               Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offset, result.mJoinerUdpPort));
390             break;
391 
392         default:
393             break;
394         }
395 
396         offset += sizeof(meshcopTlv) + meshcopTlv.GetLength();
397     }
398 
399     VerifyOrExit(!mEnableFiltering || didCheckSteeringData);
400 
401     mCallback.InvokeIfSet(&result);
402 
403 exit:
404     Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error);
405 }
406 
407 } // namespace Mle
408 } // namespace ot
409