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