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