The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net80211/ieee80211_dfs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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$");
   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 
   55 static MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
   56 
   57 static  int ieee80211_nol_timeout = 30*60;              /* 30 minutes */
   58 SYSCTL_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 
   62 static  int ieee80211_cac_timeout = 60;         /* 60 seconds */
   63 SYSCTL_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  */
   78 static  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
   85 SYSCTL_INT(_net_wlan, OID_AUTO, dfs_debug, CTLFLAG_RW,
   86         &ieee80211_dfs_debug, 0, "DFS debug behaviour");
   87 #endif
   88 
   89 static int
   90 null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
   91 {
   92         return ENOSYS;
   93 }
   94 
   95 void
   96 ieee80211_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 
  106 void
  107 ieee80211_dfs_detach(struct ieee80211com *ic)
  108 {
  109         /* NB: we assume no locking is needed */
  110         ieee80211_dfs_reset(ic);
  111 }
  112 
  113 void
  114 ieee80211_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 
  127 static void
  128 cac_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  */
  182 void
  183 ieee80211_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  */
  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                 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 
  218 void
  219 ieee80211_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 
  231 static void
  232 dfs_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 
  271 static void
  272 announce_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  */
  293 void
  294 ieee80211_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 
  397 struct ieee80211_channel *
  398 ieee80211_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 }

Cache object: 06a6084dbca7f80dbe3f03e7a15d9d9d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.