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