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