• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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