Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/net80211/ieee80211_dfs.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
/*
29
* IEEE 802.11 DFS/Radar support.
30
*/
31
#include "opt_inet.h"
32
#include "opt_wlan.h"
33
34
#include <sys/param.h>
35
#include <sys/systm.h>
36
#include <sys/mbuf.h>
37
#include <sys/malloc.h>
38
#include <sys/kernel.h>
39
40
#include <sys/socket.h>
41
#include <sys/sockio.h>
42
#include <sys/endian.h>
43
#include <sys/errno.h>
44
#include <sys/proc.h>
45
#include <sys/sysctl.h>
46
47
#include <net/if.h>
48
#include <net/if_var.h>
49
#include <net/if_media.h>
50
#include <net/ethernet.h>
51
52
#include <net80211/ieee80211_var.h>
53
54
static MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
55
56
static int ieee80211_nol_timeout = 30*60; /* 30 minutes */
57
SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
58
&ieee80211_nol_timeout, 0, "NOL timeout (secs)");
59
#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
60
61
static int ieee80211_cac_timeout = 60; /* 60 seconds */
62
SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
63
&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
64
#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
65
66
/*
67
DFS* In order to facilitate debugging, a couple of operating
68
* modes aside from the default are needed.
69
*
70
* 0 - default CAC/NOL behaviour - ie, start CAC, place
71
* channel on NOL list.
72
* 1 - send CAC, but don't change channel or add the channel
73
* to the NOL list.
74
* 2 - just match on radar, don't send CAC or place channel in
75
* the NOL list.
76
*/
77
static int ieee80211_dfs_debug = DFS_DBG_NONE;
78
79
/*
80
* This option must not be included in the default kernel
81
* as it allows users to plainly disable CAC/NOL handling.
82
*/
83
#ifdef IEEE80211_DFS_DEBUG
84
SYSCTL_INT(_net_wlan, OID_AUTO, dfs_debug, CTLFLAG_RW,
85
&ieee80211_dfs_debug, 0, "DFS debug behaviour");
86
#endif
87
88
static int
89
null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
90
{
91
return ENOSYS;
92
}
93
94
void
95
ieee80211_dfs_attach(struct ieee80211com *ic)
96
{
97
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
98
99
callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
100
callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
101
102
ic->ic_set_quiet = null_set_quiet;
103
}
104
105
void
106
ieee80211_dfs_detach(struct ieee80211com *ic)
107
{
108
/* NB: we assume no locking is needed */
109
ieee80211_dfs_reset(ic);
110
}
111
112
void
113
ieee80211_dfs_reset(struct ieee80211com *ic)
114
{
115
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
116
int i;
117
118
/* NB: we assume no locking is needed */
119
/* NB: cac_timer should be cleared by the state machine */
120
callout_drain(&dfs->nol_timer);
121
for (i = 0; i < ic->ic_nchans; i++)
122
ic->ic_channels[i].ic_state = 0;
123
dfs->lastchan = NULL;
124
}
125
126
static void
127
cac_timeout(void *arg)
128
{
129
struct ieee80211vap *vap = arg;
130
struct ieee80211com *ic = vap->iv_ic;
131
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
132
int i;
133
134
IEEE80211_LOCK_ASSERT(ic);
135
136
if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
137
return;
138
/*
139
* When radar is detected during a CAC we are woken
140
* up prematurely to switch to a new channel.
141
* Check the channel to decide how to act.
142
*/
143
if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
144
ieee80211_notify_cac(ic, ic->ic_curchan,
145
IEEE80211_NOTIFY_CAC_RADAR);
146
147
net80211_vap_printf(vap,
148
"CAC timer on channel %u (%u MHz) stopped due to radar\n",
149
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
150
151
/* XXX clobbers any existing desired channel */
152
/* NB: dfs->newchan may be NULL, that's ok */
153
vap->iv_des_chan = dfs->newchan;
154
ieee80211_new_state_locked(vap, IEEE80211_S_SCAN, 0);
155
} else {
156
net80211_vap_printf(vap,
157
"CAC timer on channel %u (%u MHz) expired; "
158
"no radar detected\n",
159
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
160
/*
161
* Mark all channels with the current frequency
162
* as having completed CAC; this keeps us from
163
* doing it again until we change channels.
164
*/
165
for (i = 0; i < ic->ic_nchans; i++) {
166
struct ieee80211_channel *c = &ic->ic_channels[i];
167
if (c->ic_freq == ic->ic_curchan->ic_freq)
168
c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
169
}
170
ieee80211_notify_cac(ic, ic->ic_curchan,
171
IEEE80211_NOTIFY_CAC_EXPIRE);
172
ieee80211_cac_completeswitch(vap);
173
}
174
}
175
176
/*
177
* Initiate the CAC timer. The driver is responsible
178
* for setting up the hardware to scan for radar on the
179
* channnel, we just handle timing things out.
180
*/
181
void
182
ieee80211_dfs_cac_start(struct ieee80211vap *vap)
183
{
184
struct ieee80211com *ic = vap->iv_ic;
185
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
186
187
IEEE80211_LOCK_ASSERT(ic);
188
189
callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
190
net80211_vap_printf(vap,
191
"start %d second CAC timer on channel %u (%u MHz)\n",
192
ticks_to_secs(CAC_TIMEOUT),
193
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
194
ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
195
}
196
197
/*
198
* Clear the CAC timer.
199
*/
200
void
201
ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
202
{
203
struct ieee80211com *ic = vap->iv_ic;
204
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
205
206
IEEE80211_LOCK_ASSERT(ic);
207
208
/* NB: racey but not important */
209
if (callout_pending(&dfs->cac_timer)) {
210
net80211_vap_printf(vap,
211
"stop CAC timer on channel %u (%u MHz)\n",
212
ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
213
ieee80211_notify_cac(ic, ic->ic_curchan,
214
IEEE80211_NOTIFY_CAC_STOP);
215
}
216
callout_stop(&dfs->cac_timer);
217
}
218
219
void
220
ieee80211_dfs_cac_clear(struct ieee80211com *ic,
221
const struct ieee80211_channel *chan)
222
{
223
int i;
224
225
for (i = 0; i < ic->ic_nchans; i++) {
226
struct ieee80211_channel *c = &ic->ic_channels[i];
227
if (c->ic_freq == chan->ic_freq)
228
c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
229
}
230
}
231
232
static void
233
dfs_timeout(void *arg)
234
{
235
struct ieee80211com *ic = arg;
236
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
237
struct ieee80211_channel *c;
238
int i, oldest, now;
239
240
IEEE80211_LOCK_ASSERT(ic);
241
242
now = oldest = ticks;
243
for (i = 0; i < ic->ic_nchans; i++) {
244
c = &ic->ic_channels[i];
245
if (IEEE80211_IS_CHAN_RADAR(c)) {
246
if (ieee80211_time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
247
c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
248
if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
249
/*
250
* NB: do this here so we get only one
251
* msg instead of one for every channel
252
* table entry.
253
*/
254
ic_printf(ic, "radar on channel %u "
255
"(%u MHz) cleared after timeout\n",
256
c->ic_ieee, c->ic_freq);
257
/* notify user space */
258
c->ic_state &=
259
~IEEE80211_CHANSTATE_NORADAR;
260
ieee80211_notify_radar(ic, c);
261
}
262
} else if (dfs->nol_event[i] < oldest)
263
oldest = dfs->nol_event[i];
264
}
265
}
266
if (oldest != now) {
267
/* arrange to process next channel up for a status change */
268
callout_schedule(&dfs->nol_timer, oldest + NOL_TIMEOUT - now);
269
}
270
}
271
272
static void
273
announce_radar(struct ieee80211com *ic, const struct ieee80211_channel *curchan,
274
const struct ieee80211_channel *newchan)
275
{
276
if (newchan == NULL)
277
ic_printf(ic, "radar detected on channel %u (%u MHz)\n",
278
curchan->ic_ieee, curchan->ic_freq);
279
else
280
ic_printf(ic, "radar detected on channel %u (%u MHz), "
281
"moving to channel %u (%u MHz)\n",
282
curchan->ic_ieee, curchan->ic_freq,
283
newchan->ic_ieee, newchan->ic_freq);
284
}
285
286
/*
287
* Handle a radar detection event on a channel. The channel is
288
* added to the NOL list and we record the time of the event.
289
* Entries are aged out after NOL_TIMEOUT. If radar was
290
* detected while doing CAC we force a state/channel change.
291
* Otherwise radar triggers a channel switch using the CSA
292
* mechanism (when the channel is the bss channel).
293
*/
294
void
295
ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
296
{
297
struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
298
int i, now;
299
300
IEEE80211_LOCK_ASSERT(ic);
301
302
/*
303
* If doing DFS debugging (mode 2), don't bother
304
* running the rest of this function.
305
*
306
* Simply announce the presence of the radar and continue
307
* along merrily.
308
*/
309
if (ieee80211_dfs_debug == DFS_DBG_NOCSANOL) {
310
announce_radar(ic, chan, chan);
311
ieee80211_notify_radar(ic, chan);
312
return;
313
}
314
315
/*
316
* Don't mark the channel and don't put it into NOL
317
* if we're doing DFS debugging.
318
*/
319
if (ieee80211_dfs_debug == DFS_DBG_NONE) {
320
/*
321
* Mark all entries with this frequency. Notify user
322
* space and arrange for notification when the radar
323
* indication is cleared. Then kick the NOL processing
324
* thread if not already running.
325
*/
326
now = ticks;
327
for (i = 0; i < ic->ic_nchans; i++) {
328
struct ieee80211_channel *c = &ic->ic_channels[i];
329
if (c->ic_freq == chan->ic_freq) {
330
c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
331
c->ic_state |= IEEE80211_CHANSTATE_RADAR;
332
dfs->nol_event[i] = now;
333
}
334
}
335
ieee80211_notify_radar(ic, chan);
336
chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
337
if (!callout_pending(&dfs->nol_timer))
338
callout_reset(&dfs->nol_timer, NOL_TIMEOUT,
339
dfs_timeout, ic);
340
}
341
342
/*
343
* If radar is detected on the bss channel while
344
* doing CAC; force a state change by scheduling the
345
* callout to be dispatched asap. Otherwise, if this
346
* event is for the bss channel then we must quiet
347
* traffic and schedule a channel switch.
348
*
349
* Note this allows us to receive notification about
350
* channels other than the bss channel; not sure
351
* that can/will happen but it's simple to support.
352
*/
353
if (chan == ic->ic_bsschan) {
354
/* XXX need a way to defer to user app */
355
356
/*
357
* Don't flip over to a new channel if
358
* we are currently doing DFS debugging.
359
*/
360
if (ieee80211_dfs_debug == DFS_DBG_NONE)
361
dfs->newchan = ieee80211_dfs_pickchannel(ic);
362
else
363
dfs->newchan = chan;
364
365
announce_radar(ic, chan, dfs->newchan);
366
367
if (callout_pending(&dfs->cac_timer))
368
callout_schedule(&dfs->cac_timer, 0);
369
else if (dfs->newchan != NULL) {
370
/* XXX mode 1, switch count 2 */
371
/* XXX calculate switch count based on max
372
switch time and beacon interval? */
373
ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
374
} else {
375
/*
376
* Spec says to stop all transmissions and
377
* wait on the current channel for an entry
378
* on the NOL to expire.
379
*/
380
/*XXX*/
381
ic_printf(ic, "%s: No free channels; waiting for entry "
382
"on NOL to expire\n", __func__);
383
}
384
} else {
385
/*
386
* Issue rate-limited console msgs.
387
*/
388
if (dfs->lastchan != chan) {
389
dfs->lastchan = chan;
390
dfs->cureps = 0;
391
announce_radar(ic, chan, NULL);
392
} else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
393
announce_radar(ic, chan, NULL);
394
}
395
}
396
}
397
398
struct ieee80211_channel *
399
ieee80211_dfs_pickchannel(struct ieee80211com *ic)
400
{
401
struct ieee80211_channel *c;
402
int i, flags;
403
uint16_t v;
404
405
/*
406
* Consult the scan cache first.
407
*/
408
flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
409
/*
410
* XXX if curchan is HT this will never find a channel
411
* XXX 'cuz we scan only legacy channels
412
*/
413
c = ieee80211_scan_pickchannel(ic, flags);
414
if (c != NULL)
415
return c;
416
/*
417
* No channel found in scan cache; select a compatible
418
* one at random (skipping channels where radar has
419
* been detected).
420
*/
421
net80211_get_random_bytes(&v, sizeof(v));
422
v %= ic->ic_nchans;
423
for (i = v; i < ic->ic_nchans; i++) {
424
c = &ic->ic_channels[i];
425
if (!IEEE80211_IS_CHAN_RADAR(c) &&
426
(c->ic_flags & flags) == flags)
427
return c;
428
}
429
for (i = 0; i < v; i++) {
430
c = &ic->ic_channels[i];
431
if (!IEEE80211_IS_CHAN_RADAR(c) &&
432
(c->ic_flags & flags) == flags)
433
return c;
434
}
435
ic_printf(ic, "HELP, no channel located to switch to!\n");
436
return NULL;
437
}
438
439