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::DiscoveryResponseTlv discoveryResponse;
313     ScanResult                    result;
314     OffsetRange                   offsetRange;
315     Tlv::ParsedInfo               tlvInfo;
316     bool                          didCheckSteeringData = false;
317 
318     Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
319 
320     VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
321 
322     // Find MLE Discovery TLV
323     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange));
324 
325     ClearAllBytes(result);
326     result.mDiscover = true;
327     result.mPanId    = aRxInfo.mMessage.GetPanId();
328     result.mChannel  = aRxInfo.mMessage.GetChannel();
329     result.mRssi     = aRxInfo.mMessage.GetAverageRss();
330     result.mLqi      = aRxInfo.mMessage.GetAverageLqi();
331 
332     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
333 
334     for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize()))
335     {
336         SuccessOrExit(error = tlvInfo.ParseFrom(aRxInfo.mMessage, offsetRange));
337 
338         if (tlvInfo.mIsExtended)
339         {
340             continue;
341         }
342 
343         switch (tlvInfo.mType)
344         {
345         case MeshCoP::Tlv::kDiscoveryResponse:
346             SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryResponse));
347             VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
348             result.mVersion  = discoveryResponse.GetVersion();
349             result.mIsNative = discoveryResponse.IsNativeCommissioner();
350             break;
351 
352         case MeshCoP::Tlv::kExtendedPanId:
353             SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
354                                                                        AsCoreType(&result.mExtendedPanId)));
355             break;
356 
357         case MeshCoP::Tlv::kNetworkName:
358             SuccessOrExit(error = Tlv::Read<MeshCoP::NetworkNameTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
359                                                                      result.mNetworkName.m8));
360             break;
361 
362         case MeshCoP::Tlv::kSteeringData:
363             if (!tlvInfo.mValueOffsetRange.IsEmpty())
364             {
365                 MeshCoP::SteeringData &steeringData     = AsCoreType(&result.mSteeringData);
366                 OffsetRange            valueOffsetRange = tlvInfo.mValueOffsetRange;
367 
368                 valueOffsetRange.ShrinkLength(MeshCoP::SteeringData::kMaxLength);
369                 steeringData.Init(static_cast<uint8_t>(valueOffsetRange.GetLength()));
370                 aRxInfo.mMessage.ReadBytes(valueOffsetRange, steeringData.GetData());
371 
372                 if (mEnableFiltering)
373                 {
374                     VerifyOrExit(steeringData.Contains(mFilterIndexes));
375                 }
376 
377                 didCheckSteeringData = true;
378             }
379             break;
380 
381         case MeshCoP::Tlv::kJoinerUdpPort:
382             SuccessOrExit(error = Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
383                                                                        result.mJoinerUdpPort));
384             break;
385 
386         default:
387             break;
388         }
389     }
390 
391     VerifyOrExit(!mEnableFiltering || didCheckSteeringData);
392 
393     mCallback.InvokeIfSet(&result);
394 
395 exit:
396     Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error);
397 }
398 
399 } // namespace Mle
400 } // namespace ot
401