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: releng/10.2/sys/net80211/ieee80211_dfs.c 230793 2012-01-31 00:03:49Z adrian $");
   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                 /* XXX recursive lock need ieee80211_new_state_locked */
  156                 ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
  157         } else {
  158                 if_printf(vap->iv_ifp,
  159                     "CAC timer on channel %u (%u MHz) expired; "
  160                     "no radar detected\n",
  161                     ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
  162                 /*
  163                  * Mark all channels with the current frequency
  164                  * as having completed CAC; this keeps us from
  165                  * doing it again until we change channels.
  166                  */
  167                 for (i = 0; i < ic->ic_nchans; i++) {
  168                         struct ieee80211_channel *c = &ic->ic_channels[i];
  169                         if (c->ic_freq == ic->ic_curchan->ic_freq)
  170                                 c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
  171                 }
  172                 ieee80211_notify_cac(ic, ic->ic_curchan,
  173                     IEEE80211_NOTIFY_CAC_EXPIRE);
  174                 ieee80211_cac_completeswitch(vap);
  175         }
  176 }
  177 
  178 /*
  179  * Initiate the CAC timer.  The driver is responsible
  180  * for setting up the hardware to scan for radar on the
  181  * channnel, we just handle timing things out.
  182  */
  183 void
  184 ieee80211_dfs_cac_start(struct ieee80211vap *vap)
  185 {
  186         struct ieee80211com *ic = vap->iv_ic;
  187         struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
  188 
  189         IEEE80211_LOCK_ASSERT(ic);
  190 
  191         callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
  192         if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
  193             ticks_to_secs(CAC_TIMEOUT),
  194             ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
  195         ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
  196 }
  197 
  198 /*
  199  * Clear the CAC timer.
  200  */
  201 void
  202 ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
  203 {
  204         struct ieee80211com *ic = vap->iv_ic;
  205         struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
  206 
  207         IEEE80211_LOCK_ASSERT(ic);
  208 
  209         /* NB: racey but not important */
  210         if (callout_pending(&dfs->cac_timer)) {
  211                 if_printf(vap->iv_ifp, "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 (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                                         if_printf(ic->ic_ifp, "radar on channel"
  255                                             " %u (%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 ifnet *ifp, const struct ieee80211_channel *curchan,
  274         const struct ieee80211_channel *newchan)
  275 {
  276         if (newchan == NULL)
  277                 if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
  278                     curchan->ic_ieee, curchan->ic_freq);
  279         else
  280                 if_printf(ifp, "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->ic_ifp, 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->ic_ifp, 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                         if_printf(ic->ic_ifp, "%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->ic_ifp, chan, NULL);
  392                 } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
  393                         announce_radar(ic->ic_ifp, 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         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         if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
  436         return NULL;
  437 }

Cache object: 1c725fa3f384fa836322ae2f323c7c29


[ 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.