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