• 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 "instance/instance.hpp"
37 
38 namespace ot {
39 namespace Mle {
40 
DiscoverScanner(Instance & aInstance)41 DiscoverScanner::DiscoverScanner(Instance &aInstance)
42     : InstanceLocator(aInstance)
43     , mScanDoneTask(aInstance)
44     , mTimer(aInstance)
45     , mFilterIndexes()
46     , mState(kStateIdle)
47     , mScanChannel(0)
48     , mAdvDataLength(0)
49     , mEnableFiltering(false)
50     , mShouldRestorePanId(false)
51 {
52 }
53 
Discover(const Mac::ChannelMask & aScanChannels,uint16_t aPanId,bool aJoiner,bool aEnableFiltering,const FilterIndexes * aFilterIndexes,Handler aCallback,void * aContext)54 Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels,
55                                 uint16_t                aPanId,
56                                 bool                    aJoiner,
57                                 bool                    aEnableFiltering,
58                                 const FilterIndexes    *aFilterIndexes,
59                                 Handler                 aCallback,
60                                 void                   *aContext)
61 {
62     Error                           error   = kErrorNone;
63     Mle::TxMessage                 *message = nullptr;
64     Tlv                             tlv;
65     Ip6::Address                    destination;
66     MeshCoP::DiscoveryRequestTlv    discoveryRequest;
67     MeshCoP::JoinerAdvertisementTlv joinerAdvertisement;
68 
69     VerifyOrExit(Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
70 
71     VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
72 
73     mEnableFiltering = aEnableFiltering;
74 
75     if (mEnableFiltering)
76     {
77         if (aFilterIndexes == nullptr)
78         {
79             Mac::ExtAddress extAddress;
80 
81             Get<Radio>().GetIeeeEui64(extAddress);
82             MeshCoP::ComputeJoinerId(extAddress, extAddress);
83             MeshCoP::SteeringData::CalculateHashBitIndexes(extAddress, mFilterIndexes);
84         }
85         else
86         {
87             mFilterIndexes = *aFilterIndexes;
88         }
89     }
90 
91     mCallback.Set(aCallback, aContext);
92     mShouldRestorePanId = false;
93     mScanChannels       = Get<Mac::Mac>().GetSupportedChannelMask();
94 
95     if (!aScanChannels.IsEmpty())
96     {
97         mScanChannels.Intersect(aScanChannels);
98     }
99 
100     VerifyOrExit((message = Get<Mle>().NewMleMessage(kCommandDiscoveryRequest)) != nullptr, error = kErrorNoBufs);
101     message->SetPanId(aPanId);
102 
103     // Prepare sub-TLV MeshCoP Discovery Request.
104     discoveryRequest.Init();
105     discoveryRequest.SetVersion(kThreadVersion);
106     discoveryRequest.SetJoiner(aJoiner);
107 
108     if (mAdvDataLength != 0)
109     {
110         // Prepare sub-TLV MeshCoP Joiner Advertisement.
111         joinerAdvertisement.Init();
112         joinerAdvertisement.SetOui(mOui);
113         joinerAdvertisement.SetAdvData(mAdvData, mAdvDataLength);
114     }
115 
116     // Append Discovery TLV with one or two sub-TLVs.
117     tlv.SetType(Tlv::kDiscovery);
118     tlv.SetLength(
119         static_cast<uint8_t>(discoveryRequest.GetSize() + ((mAdvDataLength != 0) ? joinerAdvertisement.GetSize() : 0)));
120 
121     SuccessOrExit(error = message->Append(tlv));
122     SuccessOrExit(error = discoveryRequest.AppendTo(*message));
123 
124     if (mAdvDataLength != 0)
125     {
126         SuccessOrExit(error = joinerAdvertisement.AppendTo(*message));
127     }
128 
129     destination.SetToLinkLocalAllRoutersMulticast();
130 
131     SuccessOrExit(error = message->SendTo(destination));
132 
133     if ((aPanId == Mac::kPanIdBroadcast) && (Get<Mac::Mac>().GetPanId() == Mac::kPanIdBroadcast))
134     {
135         // In case a specific PAN ID of a Thread Network to be
136         // discovered is not known, Discovery Request messages MUST
137         // have the Destination PAN ID in the IEEE 802.15.4 MAC
138         // header set to be the Broadcast PAN ID (0xffff) and the
139         // Source PAN ID set to a randomly generated value.
140 
141         Get<Mac::Mac>().SetPanId(Mac::GenerateRandomPanId());
142         mShouldRestorePanId = true;
143     }
144 
145     mScanChannel = Mac::ChannelMask::kChannelIteratorFirst;
146     mState       = (mScanChannels.GetNextChannel(mScanChannel) == kErrorNone) ? kStateScanning : kStateScanDone;
147 
148     // For rx-off-when-idle device, temporarily enable receiver during discovery procedure.
149     if (!Get<Mle>().IsDisabled() && !Get<Mle>().IsRxOnWhenIdle())
150     {
151         Get<MeshForwarder>().SetRxOnWhenIdle(true);
152     }
153 
154     Mle::Log(Mle::kMessageSend, Mle::kTypeDiscoveryRequest, destination);
155 
156 exit:
157     FreeMessageOnError(message, error);
158     return error;
159 }
160 
SetJoinerAdvertisement(uint32_t aOui,const uint8_t * aAdvData,uint8_t aAdvDataLength)161 Error DiscoverScanner::SetJoinerAdvertisement(uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength)
162 {
163     Error error = kErrorNone;
164 
165     VerifyOrExit((aAdvData != nullptr) && (aAdvDataLength != 0) &&
166                      (aAdvDataLength <= MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength) && (aOui <= kMaxOui),
167                  error = kErrorInvalidArgs);
168 
169     mOui           = aOui;
170     mAdvDataLength = aAdvDataLength;
171 
172     memcpy(mAdvData, aAdvData, aAdvDataLength);
173 
174 exit:
175     return error;
176 }
177 
PrepareDiscoveryRequestFrame(Mac::TxFrame & aFrame)178 Mac::TxFrame *DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame)
179 {
180     Mac::TxFrame *frame = &aFrame;
181 
182     switch (mState)
183     {
184     case kStateIdle:
185     case kStateScanDone:
186         // If scan is finished (no more channels to scan), abort the
187         // Discovery Request frame tx. The handler callback is invoked &
188         // state is cleared from `HandleDiscoveryRequestFrameTxDone()`.
189         frame = nullptr;
190         break;
191 
192     case kStateScanning:
193         frame->SetChannel(mScanChannel);
194         IgnoreError(Get<Mac::Mac>().SetTemporaryChannel(mScanChannel));
195         break;
196     }
197 
198     return frame;
199 }
200 
HandleDiscoveryRequestFrameTxDone(Message & aMessage,Error aError)201 void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage, Error aError)
202 {
203     switch (mState)
204     {
205     case kStateIdle:
206         break;
207 
208     case kStateScanning:
209         if ((aError == kErrorNone) || (aError == kErrorChannelAccessFailure))
210         {
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 
222         // If we encounter other error failures (e.g., `kErrorDrop` due
223         // to queue management dropping the message or if message being
224         // evicted), `aMessage` may be immediately freed. This prevents
225         // us from reusing it to request a scan on the next scan channel.
226         // As a result, we stop the scan operation in such cases.
227 
228         mState = kStateScanDone;
229 
230         OT_FALL_THROUGH;
231 
232     case kStateScanDone:
233         HandleDiscoverComplete();
234         break;
235     }
236 }
237 
HandleDiscoverComplete(void)238 void DiscoverScanner::HandleDiscoverComplete(void)
239 {
240     // Restore Data Polling or CSL for rx-off-when-idle device.
241     if (!Get<Mle>().IsDisabled() && !Get<Mle>().IsRxOnWhenIdle())
242     {
243         Get<MeshForwarder>().SetRxOnWhenIdle(false);
244     }
245 
246     switch (mState)
247     {
248     case kStateIdle:
249         break;
250 
251     case kStateScanning:
252         mTimer.Stop();
253         Get<MeshForwarder>().ResumeMessageTransmissions();
254 
255         OT_FALL_THROUGH;
256 
257     case kStateScanDone:
258         Get<Mac::Mac>().ClearTemporaryChannel();
259 
260         if (mShouldRestorePanId)
261         {
262             Get<Mac::Mac>().SetPanId(Mac::kPanIdBroadcast);
263             mShouldRestorePanId = false;
264         }
265 
266         // Post the tasklet to change `mState` and invoke handler
267         // callback. This allows users to safely call OT APIs from
268         // the callback.
269         mScanDoneTask.Post();
270         break;
271     }
272 }
273 
HandleScanDoneTask(void)274 void DiscoverScanner::HandleScanDoneTask(void)
275 {
276     mState = kStateIdle;
277     mCallback.InvokeIfSet(nullptr);
278 }
279 
HandleTimer(void)280 void DiscoverScanner::HandleTimer(void)
281 {
282     VerifyOrExit(mState == kStateScanning);
283 
284     // Move to next scan channel and resume message transmissions on
285     // `MeshForwarder` so that the queued MLE Discovery Request message
286     // is prepared again for the next scan channel. If no more channel
287     // to scan, change the state to `kStateScanDone` which ensures the
288     // frame tx is aborted  from `PrepareDiscoveryRequestFrame()` and
289     // then wraps up the scan (invoking handler callback).
290 
291     if (mScanChannels.GetNextChannel(mScanChannel) != kErrorNone)
292     {
293         mState = kStateScanDone;
294     }
295 
296     Get<MeshForwarder>().ResumeMessageTransmissions();
297 
298 exit:
299     return;
300 }
301 
HandleDiscoveryResponse(Mle::RxInfo & aRxInfo) const302 void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
303 {
304     Error                         error = kErrorNone;
305     MeshCoP::DiscoveryResponseTlv discoveryResponse;
306     ScanResult                    result;
307     OffsetRange                   offsetRange;
308     Tlv::ParsedInfo               tlvInfo;
309     bool                          didCheckSteeringData = false;
310 
311     Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
312 
313     VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
314 
315     // Find MLE Discovery TLV
316     SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aRxInfo.mMessage, Tlv::kDiscovery, offsetRange));
317 
318     ClearAllBytes(result);
319     result.mDiscover = true;
320     result.mPanId    = aRxInfo.mMessage.GetPanId();
321     result.mChannel  = aRxInfo.mMessage.GetChannel();
322     result.mRssi     = aRxInfo.mMessage.GetAverageRss();
323     result.mLqi      = aRxInfo.mMessage.GetAverageLqi();
324 
325     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
326 
327     for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(tlvInfo.GetSize()))
328     {
329         SuccessOrExit(error = tlvInfo.ParseFrom(aRxInfo.mMessage, offsetRange));
330 
331         if (tlvInfo.mIsExtended)
332         {
333             continue;
334         }
335 
336         switch (tlvInfo.mType)
337         {
338         case MeshCoP::Tlv::kDiscoveryResponse:
339             SuccessOrExit(error = aRxInfo.mMessage.Read(offsetRange, discoveryResponse));
340             VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
341             result.mVersion  = discoveryResponse.GetVersion();
342             result.mIsNative = discoveryResponse.IsNativeCommissioner();
343             break;
344 
345         case MeshCoP::Tlv::kExtendedPanId:
346             SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
347                                                                        AsCoreType(&result.mExtendedPanId)));
348             break;
349 
350         case MeshCoP::Tlv::kNetworkName:
351             SuccessOrExit(error = Tlv::Read<MeshCoP::NetworkNameTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
352                                                                      result.mNetworkName.m8));
353             break;
354 
355         case MeshCoP::Tlv::kSteeringData:
356             if (!tlvInfo.mValueOffsetRange.IsEmpty())
357             {
358                 MeshCoP::SteeringData &steeringData     = AsCoreType(&result.mSteeringData);
359                 OffsetRange            valueOffsetRange = tlvInfo.mValueOffsetRange;
360 
361                 valueOffsetRange.ShrinkLength(MeshCoP::SteeringData::kMaxLength);
362                 steeringData.Init(static_cast<uint8_t>(valueOffsetRange.GetLength()));
363                 aRxInfo.mMessage.ReadBytes(valueOffsetRange, steeringData.GetData());
364 
365                 if (mEnableFiltering)
366                 {
367                     VerifyOrExit(steeringData.Contains(mFilterIndexes));
368                 }
369 
370                 didCheckSteeringData = true;
371             }
372             break;
373 
374         case MeshCoP::Tlv::kJoinerUdpPort:
375             SuccessOrExit(error = Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offsetRange.GetOffset(),
376                                                                        result.mJoinerUdpPort));
377             break;
378 
379         default:
380             break;
381         }
382     }
383 
384     VerifyOrExit(!mEnableFiltering || didCheckSteeringData);
385 
386     mCallback.InvokeIfSet(&result);
387 
388 exit:
389     Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error);
390 }
391 
392 } // namespace Mle
393 } // namespace ot
394