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