• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, 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 the jam detector feature.
32  */
33 
34 #include "jam_detector.hpp"
35 
36 #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
37 
38 #include "common/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/random.hpp"
43 #include "thread/thread_netif.hpp"
44 
45 namespace ot {
46 namespace Utils {
47 
48 RegisterLogModule("JamDetector");
49 
JamDetector(Instance & aInstance)50 JamDetector::JamDetector(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mHandler(nullptr)
53     , mContext(nullptr)
54     , mTimer(aInstance, JamDetector::HandleTimer)
55     , mHistoryBitmap(0)
56     , mCurSecondStartTime(0)
57     , mSampleInterval(0)
58     , mWindow(kMaxWindow)
59     , mBusyPeriod(kMaxWindow)
60     , mEnabled(false)
61     , mAlwaysAboveThreshold(false)
62     , mJamState(false)
63     , mRssiThreshold(kDefaultRssiThreshold)
64 {
65 }
66 
Start(Handler aHandler,void * aContext)67 Error JamDetector::Start(Handler aHandler, void *aContext)
68 {
69     Error error = kErrorNone;
70 
71     VerifyOrExit(!mEnabled, error = kErrorAlready);
72     VerifyOrExit(aHandler != nullptr, error = kErrorInvalidArgs);
73 
74     mHandler = aHandler;
75     mContext = aContext;
76     mEnabled = true;
77 
78     LogInfo("Started");
79 
80     CheckState();
81 
82 exit:
83     return error;
84 }
85 
Stop(void)86 Error JamDetector::Stop(void)
87 {
88     Error error = kErrorNone;
89 
90     VerifyOrExit(mEnabled, error = kErrorAlready);
91 
92     mEnabled  = false;
93     mJamState = false;
94 
95     mTimer.Stop();
96 
97     LogInfo("Stopped");
98 
99 exit:
100     return error;
101 }
102 
CheckState(void)103 void JamDetector::CheckState(void)
104 {
105     VerifyOrExit(mEnabled);
106 
107     switch (Get<Mle::MleRouter>().GetRole())
108     {
109     case Mle::kRoleDisabled:
110         VerifyOrExit(mTimer.IsRunning());
111         mTimer.Stop();
112         SetJamState(false);
113         break;
114 
115     default:
116         VerifyOrExit(!mTimer.IsRunning());
117         mCurSecondStartTime   = TimerMilli::GetNow();
118         mAlwaysAboveThreshold = true;
119         mHistoryBitmap        = 0;
120         mJamState             = false;
121         mSampleInterval       = kMaxSampleInterval;
122         mTimer.Start(kMinSampleInterval);
123         break;
124     }
125 
126 exit:
127     return;
128 }
129 
SetRssiThreshold(int8_t aThreshold)130 void JamDetector::SetRssiThreshold(int8_t aThreshold)
131 {
132     mRssiThreshold = aThreshold;
133     LogInfo("RSSI threshold set to %d", mRssiThreshold);
134 }
135 
SetWindow(uint8_t aWindow)136 Error JamDetector::SetWindow(uint8_t aWindow)
137 {
138     Error error = kErrorNone;
139 
140     VerifyOrExit(aWindow != 0, error = kErrorInvalidArgs);
141     VerifyOrExit(aWindow <= kMaxWindow, error = kErrorInvalidArgs);
142 
143     mWindow = aWindow;
144     LogInfo("window set to %d", mWindow);
145 
146 exit:
147     return error;
148 }
149 
SetBusyPeriod(uint8_t aBusyPeriod)150 Error JamDetector::SetBusyPeriod(uint8_t aBusyPeriod)
151 {
152     Error error = kErrorNone;
153 
154     VerifyOrExit(aBusyPeriod != 0, error = kErrorInvalidArgs);
155     VerifyOrExit(aBusyPeriod <= mWindow, error = kErrorInvalidArgs);
156 
157     mBusyPeriod = aBusyPeriod;
158     LogInfo("busy period set to %d", mBusyPeriod);
159 
160 exit:
161     return error;
162 }
163 
HandleTimer(Timer & aTimer)164 void JamDetector::HandleTimer(Timer &aTimer)
165 {
166     aTimer.Get<JamDetector>().HandleTimer();
167 }
168 
HandleTimer(void)169 void JamDetector::HandleTimer(void)
170 {
171     int8_t rssi;
172     bool   didExceedThreshold = true;
173 
174     VerifyOrExit(mEnabled);
175 
176     rssi = Get<Radio>().GetRssi();
177 
178     // If the RSSI is valid, check if it exceeds the threshold
179     // and try to update the history bit map
180     if (rssi != OT_RADIO_RSSI_INVALID)
181     {
182         didExceedThreshold = (rssi >= mRssiThreshold);
183         UpdateHistory(didExceedThreshold);
184     }
185 
186     // If the RSSI sample does not exceed the threshold, go back to max sample interval
187     // Otherwise, divide the sample interval by half while ensuring it does not go lower
188     // than minimum sample interval.
189 
190     if (!didExceedThreshold)
191     {
192         mSampleInterval = kMaxSampleInterval;
193     }
194     else
195     {
196         mSampleInterval /= 2;
197 
198         if (mSampleInterval < kMinSampleInterval)
199         {
200             mSampleInterval = kMinSampleInterval;
201         }
202     }
203 
204     mTimer.Start(mSampleInterval + Random::NonCrypto::GetUint32InRange(0, kMaxRandomDelay));
205 
206 exit:
207     return;
208 }
209 
UpdateHistory(bool aDidExceedThreshold)210 void JamDetector::UpdateHistory(bool aDidExceedThreshold)
211 {
212     uint32_t interval = TimerMilli::GetNow() - mCurSecondStartTime;
213 
214     // If the RSSI is ever below the threshold, update mAlwaysAboveThreshold
215     // for current second interval.
216     if (!aDidExceedThreshold)
217     {
218         mAlwaysAboveThreshold = false;
219     }
220 
221     // If we reached end of current one second interval, update the history bitmap
222 
223     if (interval >= Time::kOneSecondInMsec)
224     {
225         mHistoryBitmap <<= 1;
226 
227         if (mAlwaysAboveThreshold)
228         {
229             mHistoryBitmap |= 0x1;
230         }
231 
232         mAlwaysAboveThreshold = true;
233 
234         mCurSecondStartTime += (interval / Time::kOneSecondInMsec) * Time::kOneSecondInMsec;
235 
236         UpdateJamState();
237     }
238 }
239 
UpdateJamState(void)240 void JamDetector::UpdateJamState(void)
241 {
242     uint8_t  numJammedSeconds = 0;
243     uint64_t bitmap           = mHistoryBitmap;
244 
245     // Clear all history bits beyond the current window size
246     bitmap &= (static_cast<uint64_t>(1) << mWindow) - 1;
247 
248     // Count the number of bits in the bitmap
249     while (bitmap != 0)
250     {
251         numJammedSeconds++;
252         bitmap &= (bitmap - 1);
253     }
254 
255     SetJamState(numJammedSeconds >= mBusyPeriod);
256 }
257 
SetJamState(bool aNewState)258 void JamDetector::SetJamState(bool aNewState)
259 {
260     bool shouldInvokeHandler = aNewState;
261 
262     if (aNewState != mJamState)
263     {
264         mJamState           = aNewState;
265         shouldInvokeHandler = true;
266         LogInfo("Jamming %s", mJamState ? "detected" : "cleared");
267     }
268 
269     if (shouldInvokeHandler)
270     {
271         mHandler(mJamState, mContext);
272     }
273 }
274 
HandleNotifierEvents(Events aEvents)275 void JamDetector::HandleNotifierEvents(Events aEvents)
276 {
277     if (aEvents.Contains(kEventThreadRoleChanged))
278     {
279         CheckState();
280     }
281 }
282 
283 } // namespace Utils
284 } // namespace ot
285 
286 #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
287