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_hwmp.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) 2009 The FreeBSD Foundation 
    3  * All rights reserved. 
    4  * 
    5  * This software was developed by Rui Paulo under sponsorship from the
    6  * FreeBSD Foundation. 
    7  *  
    8  * Redistribution and use in source and binary forms, with or without 
    9  * modification, are permitted provided that the following conditions 
   10  * are met: 
   11  * 1. Redistributions of source code must retain the above copyright 
   12  *    notice, this list of conditions and the following disclaimer. 
   13  * 2. Redistributions in binary form must reproduce the above copyright 
   14  *    notice, this list of conditions and the following disclaimer in the 
   15  *    documentation and/or other materials provided with the distribution. 
   16  * 
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
   27  * SUCH DAMAGE. 
   28  */ 
   29 #include <sys/cdefs.h>
   30 #ifdef __FreeBSD__
   31 __FBSDID("$FreeBSD: releng/8.0/sys/net80211/ieee80211_hwmp.c 198188 2009-10-17 13:42:23Z rpaulo $");
   32 #endif
   33 
   34 /*
   35  * IEEE 802.11s Hybrid Wireless Mesh Protocol, HWMP.
   36  * 
   37  * Based on March 2009, D3.0 802.11s draft spec.
   38  */
   39 #include "opt_inet.h"
   40 #include "opt_wlan.h"
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h> 
   44 #include <sys/mbuf.h>   
   45 #include <sys/malloc.h>
   46 #include <sys/kernel.h>
   47 
   48 #include <sys/socket.h>
   49 #include <sys/sockio.h>
   50 #include <sys/endian.h>
   51 #include <sys/errno.h>
   52 #include <sys/proc.h>
   53 #include <sys/sysctl.h>
   54 
   55 #include <net/if.h>
   56 #include <net/if_media.h>
   57 #include <net/if_llc.h>
   58 #include <net/ethernet.h>
   59 
   60 #include <net/bpf.h>
   61 
   62 #include <net80211/ieee80211_var.h>
   63 #include <net80211/ieee80211_action.h>
   64 #include <net80211/ieee80211_input.h>
   65 #include <net80211/ieee80211_mesh.h>
   66 
   67 static void     hwmp_vattach(struct ieee80211vap *);
   68 static void     hwmp_vdetach(struct ieee80211vap *);
   69 static int      hwmp_newstate(struct ieee80211vap *,
   70                     enum ieee80211_state, int);
   71 static int      hwmp_send_action(struct ieee80211_node *,
   72                     const uint8_t [IEEE80211_ADDR_LEN],
   73                     const uint8_t [IEEE80211_ADDR_LEN],
   74                     uint8_t *, size_t);
   75 static uint8_t * hwmp_add_meshpreq(uint8_t *,
   76                     const struct ieee80211_meshpreq_ie *);
   77 static uint8_t * hwmp_add_meshprep(uint8_t *,
   78                     const struct ieee80211_meshprep_ie *);
   79 static uint8_t * hwmp_add_meshperr(uint8_t *,
   80                     const struct ieee80211_meshperr_ie *);
   81 static uint8_t * hwmp_add_meshrann(uint8_t *,
   82                     const struct ieee80211_meshrann_ie *);
   83 static void     hwmp_rootmode_setup(struct ieee80211vap *);
   84 static void     hwmp_rootmode_cb(void *);
   85 static void     hwmp_rootmode_rann_cb(void *);
   86 static void     hwmp_recv_preq(struct ieee80211vap *, struct ieee80211_node *,
   87                     const struct ieee80211_frame *,
   88                     const struct ieee80211_meshpreq_ie *);
   89 static int      hwmp_send_preq(struct ieee80211_node *,
   90                     const uint8_t [IEEE80211_ADDR_LEN],
   91                     const uint8_t [IEEE80211_ADDR_LEN],
   92                     struct ieee80211_meshpreq_ie *);
   93 static void     hwmp_recv_prep(struct ieee80211vap *, struct ieee80211_node *,
   94                     const struct ieee80211_frame *,
   95                     const struct ieee80211_meshprep_ie *);
   96 static int      hwmp_send_prep(struct ieee80211_node *,
   97                     const uint8_t [IEEE80211_ADDR_LEN],
   98                     const uint8_t [IEEE80211_ADDR_LEN],
   99                     struct ieee80211_meshprep_ie *);
  100 static void     hwmp_recv_perr(struct ieee80211vap *, struct ieee80211_node *,
  101                     const struct ieee80211_frame *,
  102                     const struct ieee80211_meshperr_ie *);
  103 static int      hwmp_send_perr(struct ieee80211_node *,
  104                     const uint8_t [IEEE80211_ADDR_LEN],
  105                     const uint8_t [IEEE80211_ADDR_LEN],
  106                     struct ieee80211_meshperr_ie *);
  107 static void     hwmp_recv_rann(struct ieee80211vap *, struct ieee80211_node *,
  108                    const struct ieee80211_frame *,
  109                    const struct ieee80211_meshrann_ie *);
  110 static int      hwmp_send_rann(struct ieee80211_node *,
  111                     const uint8_t [IEEE80211_ADDR_LEN],
  112                     const uint8_t [IEEE80211_ADDR_LEN],
  113                     struct ieee80211_meshrann_ie *);
  114 static struct ieee80211_node *
  115                 hwmp_discover(struct ieee80211vap *,
  116                     const uint8_t [IEEE80211_ADDR_LEN], struct mbuf *);
  117 static void     hwmp_peerdown(struct ieee80211_node *);
  118 
  119 static struct timeval ieee80211_hwmp_preqminint = { 0, 100000 };
  120 static struct timeval ieee80211_hwmp_perrminint = { 0, 100000 };
  121 
  122 /* unalligned little endian access */
  123 #define LE_WRITE_2(p, v) do {                           \
  124         ((uint8_t *)(p))[0] = (v) & 0xff;               \
  125         ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;        \
  126 } while (0)
  127 #define LE_WRITE_4(p, v) do {                           \
  128         ((uint8_t *)(p))[0] = (v) & 0xff;               \
  129         ((uint8_t *)(p))[1] = ((v) >> 8) & 0xff;        \
  130         ((uint8_t *)(p))[2] = ((v) >> 16) & 0xff;       \
  131         ((uint8_t *)(p))[3] = ((v) >> 24) & 0xff;       \
  132 } while (0)
  133 
  134 
  135 /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */
  136 static const uint8_t    broadcastaddr[IEEE80211_ADDR_LEN] =
  137         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  138 
  139 typedef uint32_t ieee80211_hwmp_seq;
  140 #define HWMP_SEQ_LT(a, b)       ((int32_t)((a)-(b)) < 0)
  141 #define HWMP_SEQ_LEQ(a, b)      ((int32_t)((a)-(b)) <= 0)
  142 #define HWMP_SEQ_GT(a, b)       ((int32_t)((a)-(b)) > 0)
  143 #define HWMP_SEQ_GEQ(a, b)      ((int32_t)((a)-(b)) >= 0)
  144 
  145 /*
  146  * Private extension of ieee80211_mesh_route.
  147  */
  148 struct ieee80211_hwmp_route {
  149         ieee80211_hwmp_seq      hr_seq;         /* last HWMP seq seen from dst*/
  150         ieee80211_hwmp_seq      hr_preqid;      /* last PREQ ID seen from dst */
  151         ieee80211_hwmp_seq      hr_targetseq;   /* seq. no. on our latest PREQ*/
  152         int                     hr_preqretries;
  153 };
  154 struct ieee80211_hwmp_state {
  155         ieee80211_hwmp_seq      hs_seq;         /* next seq to be used */
  156         ieee80211_hwmp_seq      hs_preqid;      /* next PREQ ID to be used */
  157         struct timeval          hs_lastpreq;    /* last time we sent a PREQ */
  158         struct timeval          hs_lastperr;    /* last time we sent a PERR */
  159         int                     hs_rootmode;    /* proactive HWMP */
  160         struct callout          hs_roottimer;
  161         uint8_t                 hs_maxhops;     /* max hop count */
  162 };
  163 
  164 SYSCTL_NODE(_net_wlan, OID_AUTO, hwmp, CTLFLAG_RD, 0,
  165     "IEEE 802.11s HWMP parameters");
  166 static int      ieee80211_hwmp_targetonly = 0;
  167 SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, targetonly, CTLTYPE_INT | CTLFLAG_RW,
  168     &ieee80211_hwmp_targetonly, 0, "Set TO bit on generated PREQs");
  169 static int      ieee80211_hwmp_replyforward = 1;
  170 SYSCTL_INT(_net_wlan_hwmp, OID_AUTO, replyforward, CTLTYPE_INT | CTLFLAG_RW,
  171     &ieee80211_hwmp_replyforward, 0, "Set RF bit on generated PREQs");
  172 static int      ieee80211_hwmp_pathtimeout = -1;
  173 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, pathlifetime, CTLTYPE_INT | CTLFLAG_RW,
  174     &ieee80211_hwmp_pathtimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
  175     "path entry lifetime (ms)");
  176 static int      ieee80211_hwmp_roottimeout = -1;
  177 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, roottimeout, CTLTYPE_INT | CTLFLAG_RW,
  178     &ieee80211_hwmp_roottimeout, 0, ieee80211_sysctl_msecs_ticks, "I",
  179     "root PREQ timeout (ms)");
  180 static int      ieee80211_hwmp_rootint = -1;
  181 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rootint, CTLTYPE_INT | CTLFLAG_RW,
  182     &ieee80211_hwmp_rootint, 0, ieee80211_sysctl_msecs_ticks, "I",
  183     "root interval (ms)");
  184 static int      ieee80211_hwmp_rannint = -1;
  185 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, rannint, CTLTYPE_INT | CTLFLAG_RW,
  186     &ieee80211_hwmp_rannint, 0, ieee80211_sysctl_msecs_ticks, "I",
  187     "root announcement interval (ms)");
  188 
  189 #define IEEE80211_HWMP_DEFAULT_MAXHOPS  31
  190 
  191 static  ieee80211_recv_action_func hwmp_recv_action_meshpath;
  192 
  193 static struct ieee80211_mesh_proto_path mesh_proto_hwmp = {
  194         .mpp_descr      = "HWMP",
  195         .mpp_ie         = IEEE80211_MESHCONF_PATH_HWMP,
  196         .mpp_discover   = hwmp_discover,
  197         .mpp_peerdown   = hwmp_peerdown,
  198         .mpp_vattach    = hwmp_vattach,
  199         .mpp_vdetach    = hwmp_vdetach,
  200         .mpp_newstate   = hwmp_newstate,
  201         .mpp_privlen    = sizeof(struct ieee80211_hwmp_route),
  202 };
  203 SYSCTL_PROC(_net_wlan_hwmp, OID_AUTO, inact, CTLTYPE_INT | CTLFLAG_RW,
  204         &mesh_proto_hwmp.mpp_inact, 0, ieee80211_sysctl_msecs_ticks, "I",
  205         "mesh route inactivity timeout (ms)");
  206 
  207 
  208 static void
  209 ieee80211_hwmp_init(void)
  210 {
  211         ieee80211_hwmp_pathtimeout = msecs_to_ticks(5*1000);
  212         ieee80211_hwmp_roottimeout = msecs_to_ticks(5*1000);
  213         ieee80211_hwmp_rootint = msecs_to_ticks(2*1000);
  214         ieee80211_hwmp_rannint = msecs_to_ticks(1*1000);
  215 
  216         /*
  217          * Register action frame handler.
  218          */
  219         ieee80211_recv_action_register(IEEE80211_ACTION_CAT_MESHPATH,
  220             IEEE80211_ACTION_MESHPATH_SEL, hwmp_recv_action_meshpath);
  221 
  222         /* NB: default is 5 secs per spec */
  223         mesh_proto_hwmp.mpp_inact = msecs_to_ticks(5*1000);
  224 
  225         /*
  226          * Register HWMP.
  227          */
  228         ieee80211_mesh_register_proto_path(&mesh_proto_hwmp);
  229 }
  230 SYSINIT(wlan_hwmp, SI_SUB_DRIVERS, SI_ORDER_SECOND, ieee80211_hwmp_init, NULL);
  231 
  232 void
  233 hwmp_vattach(struct ieee80211vap *vap)
  234 {
  235         struct ieee80211_hwmp_state *hs;
  236 
  237         KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
  238             ("not a mesh vap, opmode %d", vap->iv_opmode));
  239 
  240         hs = malloc(sizeof(struct ieee80211_hwmp_state), M_80211_VAP,
  241             M_NOWAIT | M_ZERO);
  242         if (hs == NULL) {
  243                 printf("%s: couldn't alloc HWMP state\n", __func__);
  244                 return;
  245         }
  246         hs->hs_maxhops = IEEE80211_HWMP_DEFAULT_MAXHOPS;
  247         callout_init(&hs->hs_roottimer, CALLOUT_MPSAFE);
  248         vap->iv_hwmp = hs;
  249 }
  250 
  251 void
  252 hwmp_vdetach(struct ieee80211vap *vap)
  253 {
  254         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  255 
  256         callout_drain(&hs->hs_roottimer);
  257         free(vap->iv_hwmp, M_80211_VAP);
  258         vap->iv_hwmp = NULL;
  259 } 
  260 
  261 int
  262 hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg)
  263 {
  264         enum ieee80211_state nstate = vap->iv_state;
  265         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  266 
  267         IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
  268             __func__, ieee80211_state_name[ostate],
  269             ieee80211_state_name[nstate], arg);
  270 
  271         if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN)
  272                 callout_drain(&hs->hs_roottimer);
  273         if (nstate == IEEE80211_S_RUN)
  274                 hwmp_rootmode_setup(vap);
  275         return 0;
  276 }
  277 
  278 static int
  279 hwmp_recv_action_meshpath(struct ieee80211_node *ni,
  280         const struct ieee80211_frame *wh,
  281         const uint8_t *frm, const uint8_t *efrm)
  282 {
  283         struct ieee80211vap *vap = ni->ni_vap;
  284         struct ieee80211_meshpreq_ie preq;
  285         struct ieee80211_meshprep_ie prep;
  286         struct ieee80211_meshperr_ie perr;
  287         struct ieee80211_meshrann_ie rann;
  288         const uint8_t *iefrm = frm + 2; /* action + code */
  289         int found = 0;
  290 
  291         while (efrm - iefrm > 1) {
  292                 IEEE80211_VERIFY_LENGTH(efrm - iefrm, iefrm[1] + 2, return 0);
  293                 switch (*iefrm) {
  294                 case IEEE80211_ELEMID_MESHPREQ:
  295                 {
  296                         const struct ieee80211_meshpreq_ie *mpreq =
  297                             (const struct ieee80211_meshpreq_ie *) iefrm;
  298                         /* XXX > 1 target */
  299                         if (mpreq->preq_len !=
  300                             sizeof(struct ieee80211_meshpreq_ie) - 2) {
  301                                 IEEE80211_DISCARD(vap,
  302                                     IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
  303                                     wh, NULL, "%s", "PREQ with wrong len");
  304                                 vap->iv_stats.is_rx_mgtdiscard++;
  305                                 break;
  306                         }
  307                         memcpy(&preq, mpreq, sizeof(preq));
  308                         preq.preq_id = LE_READ_4(&mpreq->preq_id);
  309                         preq.preq_origseq = LE_READ_4(&mpreq->preq_origseq);
  310                         preq.preq_lifetime = LE_READ_4(&mpreq->preq_lifetime);
  311                         preq.preq_metric = LE_READ_4(&mpreq->preq_metric);
  312                         preq.preq_targets[0].target_seq =
  313                             LE_READ_4(&mpreq->preq_targets[0].target_seq);
  314                         hwmp_recv_preq(vap, ni, wh, &preq);
  315                         found++;
  316                         break;  
  317                 }
  318                 case IEEE80211_ELEMID_MESHPREP:
  319                 {
  320                         const struct ieee80211_meshprep_ie *mprep =
  321                             (const struct ieee80211_meshprep_ie *) iefrm;
  322                         if (mprep->prep_len !=
  323                             sizeof(struct ieee80211_meshprep_ie) - 2) {
  324                                 IEEE80211_DISCARD(vap,
  325                                     IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
  326                                     wh, NULL, "%s", "PREP with wrong len");
  327                                 vap->iv_stats.is_rx_mgtdiscard++;
  328                                 break;
  329                         }
  330                         memcpy(&prep, mprep, sizeof(prep));
  331                         prep.prep_targetseq = LE_READ_4(&mprep->prep_targetseq);
  332                         prep.prep_lifetime = LE_READ_4(&mprep->prep_lifetime);
  333                         prep.prep_metric = LE_READ_4(&mprep->prep_metric);
  334                         prep.prep_origseq = LE_READ_4(&mprep->prep_origseq);
  335                         hwmp_recv_prep(vap, ni, wh, &prep);
  336                         found++;
  337                         break;
  338                 }
  339                 case IEEE80211_ELEMID_MESHPERR:
  340                 {
  341                         const struct ieee80211_meshperr_ie *mperr =
  342                             (const struct ieee80211_meshperr_ie *) iefrm;
  343                         /* XXX > 1 target */
  344                         if (mperr->perr_len !=
  345                             sizeof(struct ieee80211_meshperr_ie) - 2) {
  346                                 IEEE80211_DISCARD(vap,
  347                                     IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
  348                                     wh, NULL, "%s", "PERR with wrong len");
  349                                 vap->iv_stats.is_rx_mgtdiscard++;
  350                                 break;
  351                         }
  352                         memcpy(&perr, mperr, sizeof(perr));
  353                         perr.perr_dests[0].dest_seq =
  354                             LE_READ_4(&mperr->perr_dests[0].dest_seq);
  355                         hwmp_recv_perr(vap, ni, wh, &perr);
  356                         found++;
  357                         break;
  358                 }
  359                 case IEEE80211_ELEMID_MESHRANN:
  360                 {
  361                         const struct ieee80211_meshrann_ie *mrann =
  362                             (const struct ieee80211_meshrann_ie *) iefrm;
  363                         if (mrann->rann_len !=
  364                             sizeof(struct ieee80211_meshrann_ie) - 2) {
  365                                 IEEE80211_DISCARD(vap,
  366                                     IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
  367                                     wh, NULL, "%s", "RAN with wrong len");
  368                                 vap->iv_stats.is_rx_mgtdiscard++;
  369                                 return 1;
  370                         }
  371                         memcpy(&rann, mrann, sizeof(rann));
  372                         rann.rann_seq = LE_READ_4(&mrann->rann_seq);
  373                         rann.rann_metric = LE_READ_4(&mrann->rann_metric);
  374                         hwmp_recv_rann(vap, ni, wh, &rann);
  375                         found++;
  376                         break;
  377                 }
  378                 }
  379                 iefrm += iefrm[1] + 2;
  380         }
  381         if (!found) {
  382                 IEEE80211_DISCARD(vap,
  383                     IEEE80211_MSG_ACTION | IEEE80211_MSG_HWMP,
  384                     wh, NULL, "%s", "PATH SEL action without IE");
  385                 vap->iv_stats.is_rx_mgtdiscard++;
  386         }
  387         return 0;
  388 }
  389 
  390 static int
  391 hwmp_send_action(struct ieee80211_node *ni,
  392     const uint8_t sa[IEEE80211_ADDR_LEN],
  393     const uint8_t da[IEEE80211_ADDR_LEN],
  394     uint8_t *ie, size_t len)
  395 {
  396         struct ieee80211vap *vap = ni->ni_vap;
  397         struct ieee80211com *ic = ni->ni_ic;
  398         struct ieee80211_bpf_params params;
  399         struct mbuf *m;
  400         uint8_t *frm;
  401 
  402         if (vap->iv_state == IEEE80211_S_CAC) {
  403                 IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
  404                     "block %s frame in CAC state", "HWMP action");
  405                 vap->iv_stats.is_tx_badstate++;
  406                 return EIO;     /* XXX */
  407         }
  408 
  409         KASSERT(ni != NULL, ("null node"));
  410         /*
  411          * Hold a reference on the node so it doesn't go away until after
  412          * the xmit is complete all the way in the driver.  On error we
  413          * will remove our reference.
  414          */
  415 #ifdef IEEE80211_DEBUG_REFCNT
  416         IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
  417             "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
  418             __func__, __LINE__,
  419             ni, ether_sprintf(ni->ni_macaddr),
  420             ieee80211_node_refcnt(ni)+1);
  421 #endif
  422         ieee80211_ref_node(ni);
  423 
  424         m = ieee80211_getmgtframe(&frm,
  425             ic->ic_headroom + sizeof(struct ieee80211_frame),
  426             sizeof(struct ieee80211_action) + len
  427         );
  428         if (m == NULL) {
  429                 ieee80211_free_node(ni);
  430                 vap->iv_stats.is_tx_nobuf++;
  431                 return ENOMEM;
  432         }
  433         *frm++ = IEEE80211_ACTION_CAT_MESHPATH;
  434         *frm++ = IEEE80211_ACTION_MESHPATH_SEL;
  435         switch (*ie) {
  436         case IEEE80211_ELEMID_MESHPREQ:
  437                 frm = hwmp_add_meshpreq(frm,
  438                     (struct ieee80211_meshpreq_ie *)ie);
  439                 break;
  440         case IEEE80211_ELEMID_MESHPREP:
  441                 frm = hwmp_add_meshprep(frm,
  442                     (struct ieee80211_meshprep_ie *)ie);
  443                 break;
  444         case IEEE80211_ELEMID_MESHPERR:
  445                 frm = hwmp_add_meshperr(frm,
  446                     (struct ieee80211_meshperr_ie *)ie);
  447                 break;
  448         case IEEE80211_ELEMID_MESHRANN:
  449                 frm = hwmp_add_meshrann(frm,
  450                     (struct ieee80211_meshrann_ie *)ie);
  451                 break;
  452         }
  453 
  454         m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
  455         M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
  456         if (m == NULL) {
  457                 ieee80211_free_node(ni);
  458                 vap->iv_stats.is_tx_nobuf++;
  459                 return ENOMEM;
  460         }
  461         ieee80211_send_setup(ni, m,
  462             IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
  463             IEEE80211_NONQOS_TID, sa, da, sa);
  464 
  465         m->m_flags |= M_ENCAP;          /* mark encapsulated */
  466         IEEE80211_NODE_STAT(ni, tx_mgmt);
  467 
  468         memset(&params, 0, sizeof(params));
  469         params.ibp_pri = WME_AC_VO;
  470         params.ibp_rate0 = ni->ni_txparms->mgmtrate;
  471         if (IEEE80211_IS_MULTICAST(da))
  472                 params.ibp_try0 = 1;
  473         else
  474                 params.ibp_try0 = ni->ni_txparms->maxretry;
  475         params.ibp_power = ni->ni_txpower;
  476         return ic->ic_raw_xmit(ni, m, &params);
  477 }
  478 
  479 #define ADDSHORT(frm, v) do {           \
  480         frm[0] = (v) & 0xff;            \
  481         frm[1] = (v) >> 8;              \
  482         frm += 2;                       \
  483 } while (0)
  484 #define ADDWORD(frm, v) do {            \
  485         LE_WRITE_4(frm, v);             \
  486         frm += 4;                       \
  487 } while (0)
  488 /*
  489  * Add a Mesh Path Request IE to a frame.
  490  */
  491 static uint8_t *
  492 hwmp_add_meshpreq(uint8_t *frm, const struct ieee80211_meshpreq_ie *preq)
  493 {
  494         int i;
  495 
  496         *frm++ = IEEE80211_ELEMID_MESHPREQ;
  497         *frm++ = sizeof(struct ieee80211_meshpreq_ie) - 2 +
  498             (preq->preq_tcount - 1) * sizeof(*preq->preq_targets);
  499         *frm++ = preq->preq_flags;
  500         *frm++ = preq->preq_hopcount;
  501         *frm++ = preq->preq_ttl;
  502         ADDWORD(frm, preq->preq_id);
  503         IEEE80211_ADDR_COPY(frm, preq->preq_origaddr); frm += 6;
  504         ADDWORD(frm, preq->preq_origseq);
  505         ADDWORD(frm, preq->preq_lifetime);
  506         ADDWORD(frm, preq->preq_metric);
  507         *frm++ = preq->preq_tcount;
  508         for (i = 0; i < preq->preq_tcount; i++) {
  509                 *frm++ = preq->preq_targets[i].target_flags;
  510                 IEEE80211_ADDR_COPY(frm, preq->preq_targets[i].target_addr);
  511                 frm += 6;
  512                 ADDWORD(frm, preq->preq_targets[i].target_seq);
  513         }
  514         return frm;
  515 }
  516 
  517 /*
  518  * Add a Mesh Path Reply IE to a frame.
  519  */
  520 static uint8_t *
  521 hwmp_add_meshprep(uint8_t *frm, const struct ieee80211_meshprep_ie *prep)
  522 {
  523         *frm++ = IEEE80211_ELEMID_MESHPREP;
  524         *frm++ = sizeof(struct ieee80211_meshprep_ie) - 2;
  525         *frm++ = prep->prep_flags;
  526         *frm++ = prep->prep_hopcount;
  527         *frm++ = prep->prep_ttl;
  528         IEEE80211_ADDR_COPY(frm, prep->prep_targetaddr); frm += 6;
  529         ADDWORD(frm, prep->prep_targetseq);
  530         ADDWORD(frm, prep->prep_lifetime);
  531         ADDWORD(frm, prep->prep_metric);
  532         IEEE80211_ADDR_COPY(frm, prep->prep_origaddr); frm += 6;
  533         ADDWORD(frm, prep->prep_origseq);
  534         return frm;
  535 }
  536 
  537 /*
  538  * Add a Mesh Path Error IE to a frame.
  539  */
  540 static uint8_t *
  541 hwmp_add_meshperr(uint8_t *frm, const struct ieee80211_meshperr_ie *perr)
  542 {
  543         int i;
  544 
  545         *frm++ = IEEE80211_ELEMID_MESHPERR;
  546         *frm++ = sizeof(struct ieee80211_meshperr_ie) - 2 +
  547             (perr->perr_ndests - 1) * sizeof(*perr->perr_dests);
  548         *frm++ = perr->perr_ttl;
  549         *frm++ = perr->perr_ndests;
  550         for (i = 0; i < perr->perr_ndests; i++) {
  551                 *frm += perr->perr_dests[i].dest_flags;
  552                 IEEE80211_ADDR_COPY(frm, perr->perr_dests[i].dest_addr);
  553                 frm += 6;
  554                 ADDWORD(frm, perr->perr_dests[i].dest_seq);
  555                 ADDSHORT(frm, perr->perr_dests[i].dest_rcode);
  556         }
  557         return frm;
  558 }
  559 
  560 /*
  561  * Add a Root Annoucement IE to a frame.
  562  */
  563 static uint8_t *
  564 hwmp_add_meshrann(uint8_t *frm, const struct ieee80211_meshrann_ie *rann)
  565 {
  566         *frm++ = IEEE80211_ELEMID_MESHRANN;
  567         *frm++ = sizeof(struct ieee80211_meshrann_ie) - 2;
  568         *frm++ = rann->rann_flags;
  569         *frm++ = rann->rann_hopcount;
  570         *frm++ = rann->rann_ttl;
  571         IEEE80211_ADDR_COPY(frm, rann->rann_addr); frm += 6;
  572         ADDWORD(frm, rann->rann_seq);
  573         ADDWORD(frm, rann->rann_metric);
  574         return frm;
  575 }
  576 
  577 static void
  578 hwmp_rootmode_setup(struct ieee80211vap *vap)
  579 {
  580         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  581 
  582         switch (hs->hs_rootmode) {
  583         case IEEE80211_HWMP_ROOTMODE_DISABLED:
  584                 callout_drain(&hs->hs_roottimer);
  585                 break;
  586         case IEEE80211_HWMP_ROOTMODE_NORMAL:
  587         case IEEE80211_HWMP_ROOTMODE_PROACTIVE:
  588                 callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rootint,
  589                     hwmp_rootmode_cb, vap);
  590                 break;
  591         case IEEE80211_HWMP_ROOTMODE_RANN:
  592                 callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint,
  593                     hwmp_rootmode_rann_cb, vap);
  594                 break;
  595         }
  596 }
  597 
  598 /*
  599  * Send a broadcast Path Request to find all nodes on the mesh. We are
  600  * called when the vap is configured as a HWMP root node.
  601  */
  602 #define PREQ_TFLAGS(n)  preq.preq_targets[n].target_flags
  603 #define PREQ_TADDR(n)   preq.preq_targets[n].target_addr
  604 #define PREQ_TSEQ(n)    preq.preq_targets[n].target_seq
  605 static void
  606 hwmp_rootmode_cb(void *arg)
  607 {
  608         struct ieee80211vap *vap = (struct ieee80211vap *)arg;
  609         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  610         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  611         struct ieee80211_meshpreq_ie preq;
  612 
  613         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
  614             "%s", "send broadcast PREQ");
  615 
  616         preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM;
  617         if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
  618                 preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR;
  619         if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE)
  620                 preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP;
  621         preq.preq_hopcount = 0;
  622         preq.preq_ttl = ms->ms_ttl;
  623         preq.preq_id = ++hs->hs_preqid;
  624         IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
  625         preq.preq_origseq = ++hs->hs_seq;
  626         preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout);
  627         preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  628         preq.preq_tcount = 1;
  629         IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr);
  630         PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO |
  631             IEEE80211_MESHPREQ_TFLAGS_RF;
  632         PREQ_TSEQ(0) = 0;
  633         vap->iv_stats.is_hwmp_rootreqs++;
  634         hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq);
  635         hwmp_rootmode_setup(vap);
  636 }
  637 #undef  PREQ_TFLAGS
  638 #undef  PREQ_TADDR
  639 #undef  PREQ_TSEQ
  640 
  641 /*
  642  * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are
  643  * called when the vap is configured as a HWMP RANN root node.
  644  */
  645 static void
  646 hwmp_rootmode_rann_cb(void *arg)
  647 {
  648         struct ieee80211vap *vap = (struct ieee80211vap *)arg;
  649         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  650         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  651         struct ieee80211_meshrann_ie rann;
  652 
  653         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss,
  654             "%s", "send broadcast RANN");
  655 
  656         if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
  657                 rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
  658         rann.rann_hopcount = 0;
  659         rann.rann_ttl = ms->ms_ttl;
  660         IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
  661         rann.rann_seq = ++hs->hs_seq;
  662         rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  663 
  664         vap->iv_stats.is_hwmp_rootrann++;
  665         hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
  666         hwmp_rootmode_setup(vap);
  667 }
  668 
  669 #define PREQ_TFLAGS(n)  preq->preq_targets[n].target_flags
  670 #define PREQ_TADDR(n)   preq->preq_targets[n].target_addr
  671 #define PREQ_TSEQ(n)    preq->preq_targets[n].target_seq
  672 static void
  673 hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
  674     const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
  675 {
  676         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  677         struct ieee80211_mesh_route *rt = NULL;
  678         struct ieee80211_mesh_route *rtorig = NULL;
  679         struct ieee80211_hwmp_route *hrorig;
  680         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  681         struct ieee80211_meshprep_ie prep;
  682 
  683         if (ni == vap->iv_bss ||
  684             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
  685                 return;
  686         /*
  687          * Ignore PREQs from us. Could happen because someone forward it
  688          * back to us.
  689          */
  690         if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
  691                 return;
  692 
  693         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  694             "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
  695 
  696         /*
  697          * Acceptance criteria: if the PREQ is not for us and
  698          * forwarding is disabled, discard this PREQ.
  699          */
  700         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
  701             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
  702                 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
  703                     preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
  704                 return;
  705         }
  706         rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
  707         if (rtorig == NULL)
  708                 rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
  709         hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
  710         /*
  711          * Sequence number validation.
  712          */
  713         if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
  714             HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
  715                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  716                     "discard PREQ from %s, old seq no %u <= %u",
  717                     ether_sprintf(preq->preq_origaddr),
  718                     preq->preq_origseq, hrorig->hr_seq);
  719                 return;
  720         }
  721         hrorig->hr_preqid = preq->preq_id;
  722         hrorig->hr_seq = preq->preq_origseq;
  723 
  724         /*
  725          * Check if the PREQ is addressed to us.
  726          */
  727         if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
  728                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  729                     "reply to %s", ether_sprintf(preq->preq_origaddr));
  730                 /*
  731                  * Build and send a PREP frame.
  732                  */
  733                 prep.prep_flags = 0;
  734                 prep.prep_hopcount = 0;
  735                 prep.prep_ttl = ms->ms_ttl;
  736                 IEEE80211_ADDR_COPY(prep.prep_targetaddr, preq->preq_origaddr);
  737                 prep.prep_targetseq = preq->preq_origseq;
  738                 prep.prep_lifetime = preq->preq_lifetime;
  739                 prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  740                 IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
  741                 prep.prep_origseq = ++hs->hs_seq;
  742                 hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
  743                 /*
  744                  * Build the reverse path, if we don't have it already.
  745                  */
  746                 rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
  747                 if (rt == NULL)
  748                         hwmp_discover(vap, preq->preq_origaddr, NULL);
  749                 else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
  750                         hwmp_discover(vap, rt->rt_dest, NULL);
  751                 return;
  752         }
  753         /*
  754          * Proactive PREQ: reply with a proactive PREP to the
  755          * root STA if requested.
  756          */
  757         if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
  758             (PREQ_TFLAGS(0) &
  759             ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
  760             (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
  761                 uint8_t rootmac[IEEE80211_ADDR_LEN];
  762 
  763                 IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
  764                 rt = ieee80211_mesh_rt_find(vap, rootmac);
  765                 if (rt == NULL) {
  766                         rt = ieee80211_mesh_rt_add(vap, rootmac);
  767                         if (rt == NULL) {
  768                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  769                                     "unable to add root mesh path to %s",
  770                                     ether_sprintf(rootmac));
  771                                 vap->iv_stats.is_mesh_rtaddfailed++;
  772                                 return;
  773                         }
  774                 }
  775                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  776                     "root mesh station @ %s", ether_sprintf(rootmac));
  777 
  778                 /*
  779                  * Reply with a PREP if we don't have a path to the root
  780                  * or if the root sent us a proactive PREQ.
  781                  */
  782                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
  783                     (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
  784                         prep.prep_flags = 0;
  785                         prep.prep_hopcount = 0;
  786                         prep.prep_ttl = ms->ms_ttl;
  787                         IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr);
  788                         prep.prep_origseq = preq->preq_origseq;
  789                         prep.prep_targetseq = ++hs->hs_seq;
  790                         prep.prep_lifetime = preq->preq_lifetime;
  791                         prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  792                         IEEE80211_ADDR_COPY(prep.prep_targetaddr, rootmac);
  793                         prep.prep_targetseq = PREQ_TSEQ(0);
  794                         hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
  795                             broadcastaddr, &prep);
  796                 }
  797                 hwmp_discover(vap, rootmac, NULL);
  798                 return;
  799         }
  800         rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
  801 
  802         /*
  803          * Forwarding and Intermediate reply for PREQs with 1 target.
  804          */
  805         if (preq->preq_tcount == 1) {
  806                 struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
  807 
  808                 memcpy(&ppreq, preq, sizeof(ppreq));
  809                 /*
  810                  * We have a valid route to this node.
  811                  */
  812                 if (rt != NULL &&
  813                     (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
  814                         if (preq->preq_ttl > 1 &&
  815                             preq->preq_hopcount < hs->hs_maxhops) {
  816                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  817                                     "forward PREQ from %s",
  818                                     ether_sprintf(preq->preq_origaddr));
  819                                 /*
  820                                  * Propagate the original PREQ.
  821                                  */
  822                                 ppreq.preq_hopcount += 1;
  823                                 ppreq.preq_ttl -= 1;
  824                                 ppreq.preq_metric +=
  825                                     ms->ms_pmetric->mpm_metric(ni);
  826                                 /*
  827                                  * Set TO and unset RF bits because we are going
  828                                  * to send a PREP next.
  829                                  */
  830                                 ppreq.preq_targets[0].target_flags |=
  831                                     IEEE80211_MESHPREQ_TFLAGS_TO;
  832                                 ppreq.preq_targets[0].target_flags &=
  833                                     ~IEEE80211_MESHPREQ_TFLAGS_RF;
  834                                 hwmp_send_preq(ni, vap->iv_myaddr,
  835                                     broadcastaddr, &ppreq);
  836                         }
  837                         /*
  838                          * Check if we can send an intermediate Path Reply,
  839                          * i.e., Target Only bit is not set.
  840                          */
  841                         if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
  842                                 struct ieee80211_meshprep_ie prep;
  843 
  844                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  845                                     "intermediate reply for PREQ from %s",
  846                                     ether_sprintf(preq->preq_origaddr));
  847                                 prep.prep_flags = 0;
  848                                 prep.prep_hopcount = rt->rt_nhops + 1;
  849                                 prep.prep_ttl = ms->ms_ttl;
  850                                 IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
  851                                     preq->preq_origaddr);
  852                                 prep.prep_targetseq = hrorig->hr_seq;
  853                                 prep.prep_lifetime = preq->preq_lifetime;
  854                                 prep.prep_metric = rt->rt_metric +
  855                                     ms->ms_pmetric->mpm_metric(ni);
  856                                 IEEE80211_ADDR_COPY(&prep.prep_origaddr,
  857                                     PREQ_TADDR(0));
  858                                 prep.prep_origseq = hrorig->hr_seq;
  859                                 hwmp_send_prep(ni, vap->iv_myaddr,
  860                                     broadcastaddr, &prep);
  861                         }
  862                 /*
  863                  * We have no information about this path,
  864                  * propagate the PREQ.
  865                  */
  866                 } else if (preq->preq_ttl > 1 &&
  867                     preq->preq_hopcount < hs->hs_maxhops) {
  868                         if (rt == NULL) {
  869                                 rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
  870                                 if (rt == NULL) {
  871                                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
  872                                             ni, "unable to add PREQ path to %s",
  873                                             ether_sprintf(PREQ_TADDR(0)));
  874                                         vap->iv_stats.is_mesh_rtaddfailed++;
  875                                         return;
  876                                 }
  877                         }
  878                         rt->rt_metric = preq->preq_metric;
  879                         rt->rt_lifetime = preq->preq_lifetime;
  880                         hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
  881                             struct ieee80211_hwmp_route);
  882                         hrorig->hr_seq = preq->preq_origseq;
  883                         hrorig->hr_preqid = preq->preq_id;
  884 
  885                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  886                             "forward PREQ from %s",
  887                             ether_sprintf(preq->preq_origaddr));
  888                         ppreq.preq_hopcount += 1;
  889                         ppreq.preq_ttl -= 1;
  890                         ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
  891                         hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
  892                             &ppreq);
  893                 }
  894         }
  895 
  896 }
  897 #undef  PREQ_TFLAGS
  898 #undef  PREQ_TADDR
  899 #undef  PREQ_TSEQ
  900 
  901 static int
  902 hwmp_send_preq(struct ieee80211_node *ni,
  903     const uint8_t sa[IEEE80211_ADDR_LEN],
  904     const uint8_t da[IEEE80211_ADDR_LEN],
  905     struct ieee80211_meshpreq_ie *preq)
  906 {
  907         struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
  908 
  909         /*
  910          * Enforce PREQ interval.
  911          */
  912         if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
  913                 return EALREADY;
  914         getmicrouptime(&hs->hs_lastpreq);
  915 
  916         /*
  917          * mesh preq action frame format
  918          *     [6] da
  919          *     [6] sa 
  920          *     [6] addr3 = sa
  921          *     [1] action
  922          *     [1] category
  923          *     [tlv] mesh path request
  924          */
  925         preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
  926         return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
  927             sizeof(struct ieee80211_meshpreq_ie));
  928 }
  929 
  930 static void
  931 hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
  932     const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
  933 {
  934         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  935         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  936         struct ieee80211_mesh_route *rt = NULL;
  937         struct ieee80211_hwmp_route *hr;
  938         struct ieee80211com *ic = vap->iv_ic;
  939         struct ifnet *ifp = vap->iv_ifp;
  940         struct mbuf *m, *next;
  941 
  942         /*
  943          * Acceptance criteria: if the corresponding PREQ was not generated
  944          * by us and forwarding is disabled, discard this PREP.
  945          */
  946         if (ni == vap->iv_bss ||
  947             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
  948                 return;
  949         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
  950             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
  951                 return;
  952 
  953         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  954             "received PREP from %s", ether_sprintf(prep->prep_origaddr));
  955 
  956         rt = ieee80211_mesh_rt_find(vap, prep->prep_origaddr);
  957         if (rt == NULL) {
  958                 /*
  959                  * If we have no entry this could be a reply to a root PREQ.
  960                  */
  961                 if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
  962                         rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr);
  963                         if (rt == NULL) {
  964                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
  965                                     ni, "unable to add PREP path to %s",
  966                                     ether_sprintf(prep->prep_origaddr));
  967                                 vap->iv_stats.is_mesh_rtaddfailed++;
  968                                 return;
  969                         }
  970                         IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
  971                         rt->rt_nhops = prep->prep_hopcount;
  972                         rt->rt_lifetime = prep->prep_lifetime;
  973                         rt->rt_metric = prep->prep_metric;
  974                         rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
  975                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  976                             "add root path to %s nhops %d metric %d (PREP)",
  977                             ether_sprintf(prep->prep_origaddr),
  978                             rt->rt_nhops, rt->rt_metric);
  979                         return;
  980                 } 
  981                 return;
  982         }
  983         /*
  984          * Sequence number validation.
  985          */
  986         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
  987         if (HWMP_SEQ_LEQ(prep->prep_origseq, hr->hr_seq)) {
  988                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  989                     "discard PREP from %s, old seq no %u <= %u",
  990                     ether_sprintf(prep->prep_origaddr),
  991                     prep->prep_origseq, hr->hr_seq);
  992                 return;
  993         }
  994         hr->hr_seq = prep->prep_origseq;
  995         /*
  996          * If it's NOT for us, propagate the PREP.
  997          */
  998         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_targetaddr) &&
  999             prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
 1000                 struct ieee80211_meshprep_ie pprep; /* propagated PREP */
 1001 
 1002                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1003                     "propagate PREP from %s",
 1004                     ether_sprintf(prep->prep_origaddr));
 1005 
 1006                 memcpy(&pprep, prep, sizeof(pprep));
 1007                 pprep.prep_hopcount += 1;
 1008                 pprep.prep_ttl -= 1;
 1009                 pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
 1010                 IEEE80211_ADDR_COPY(pprep.prep_origaddr, vap->iv_myaddr);
 1011                 hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
 1012         }
 1013         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1014         if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
 1015                 /* NB: never clobber a proxy entry */;
 1016                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1017                     "discard PREP for %s, route is marked PROXY",
 1018                     ether_sprintf(prep->prep_origaddr));
 1019                 vap->iv_stats.is_hwmp_proxy++;
 1020         } else if (prep->prep_targetseq == hr->hr_targetseq) {
 1021                 /*
 1022                  * Check if we already have a path to this node.
 1023                  * If we do, check if this path reply contains a
 1024                  * better route.
 1025                  */
 1026                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
 1027                     (prep->prep_hopcount < rt->rt_nhops ||
 1028                      prep->prep_metric < rt->rt_metric)) {
 1029                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1030                             "%s path to %s, hopcount %d:%d metric %d:%d",
 1031                             rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
 1032                                 "prefer" : "update",
 1033                             ether_sprintf(prep->prep_origaddr),
 1034                             rt->rt_nhops, prep->prep_hopcount,
 1035                             rt->rt_metric, prep->prep_metric);
 1036                         IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
 1037                         rt->rt_nhops = prep->prep_hopcount;
 1038                         rt->rt_lifetime = prep->prep_lifetime;
 1039                         rt->rt_metric = prep->prep_metric;
 1040                         rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
 1041                 } else {
 1042                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1043                             "ignore PREP for %s, hopcount %d:%d metric %d:%d",
 1044                             ether_sprintf(prep->prep_origaddr),
 1045                             rt->rt_nhops, prep->prep_hopcount,
 1046                             rt->rt_metric, prep->prep_metric);
 1047                 }
 1048         } else {
 1049                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1050                     "discard PREP for %s, wrong seqno %u != %u",
 1051                     ether_sprintf(prep->prep_origaddr), prep->prep_targetseq,
 1052                     hr->hr_seq);
 1053                 vap->iv_stats.is_hwmp_wrongseq++;
 1054         } 
 1055         /*
 1056          * Check for frames queued awaiting path discovery.
 1057          * XXX probably can tell exactly and avoid remove call
 1058          * NB: hash may have false matches, if so they will get
 1059          *     stuck back on the stageq because there won't be
 1060          *     a path.
 1061          */
 1062         m = ieee80211_ageq_remove(&ic->ic_stageq, 
 1063             (struct ieee80211_node *)(uintptr_t)
 1064                 ieee80211_mac_hash(ic, rt->rt_dest));
 1065         for (; m != NULL; m = next) {
 1066                 next = m->m_nextpkt;
 1067                 m->m_nextpkt = NULL;
 1068                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1069                     "flush queued frame %p len %d", m, m->m_pkthdr.len);
 1070                 ifp->if_transmit(ifp, m);
 1071         }
 1072 }
 1073 
 1074 static int
 1075 hwmp_send_prep(struct ieee80211_node *ni,
 1076     const uint8_t sa[IEEE80211_ADDR_LEN],
 1077     const uint8_t da[IEEE80211_ADDR_LEN],
 1078     struct ieee80211_meshprep_ie *prep)
 1079 {
 1080         /* NB: there's no PREP minimum interval. */
 1081 
 1082         /*
 1083          * mesh prep action frame format
 1084          *     [6] da
 1085          *     [6] sa 
 1086          *     [6] addr3 = sa
 1087          *     [1] action
 1088          *     [1] category
 1089          *     [tlv] mesh path reply
 1090          */
 1091         prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
 1092         return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
 1093             sizeof(struct ieee80211_meshprep_ie));
 1094 }
 1095 
 1096 #define PERR_DFLAGS(n)  perr.perr_dests[n].dest_flags
 1097 #define PERR_DADDR(n)   perr.perr_dests[n].dest_addr
 1098 #define PERR_DSEQ(n)    perr.perr_dests[n].dest_seq
 1099 #define PERR_DRCODE(n)  perr.perr_dests[n].dest_rcode
 1100 static void
 1101 hwmp_peerdown(struct ieee80211_node *ni)
 1102 {
 1103         struct ieee80211vap *vap = ni->ni_vap;
 1104         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1105         struct ieee80211_meshperr_ie perr;
 1106         struct ieee80211_mesh_route *rt;
 1107         struct ieee80211_hwmp_route *hr;
 1108 
 1109         rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
 1110         if (rt == NULL)
 1111                 return;
 1112         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1113         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1114             "%s", "delete route entry");
 1115         perr.perr_ttl = ms->ms_ttl;
 1116         perr.perr_ndests = 1;
 1117         if (hr->hr_seq == 0)
 1118                 PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
 1119         PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
 1120         IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
 1121         PERR_DSEQ(0) = hr->hr_seq;
 1122         PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
 1123         /* NB: flush everything passing through peer */
 1124         ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
 1125         hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
 1126 }
 1127 #undef  PERR_DFLAGS
 1128 #undef  PERR_DADDR
 1129 #undef  PERR_DSEQ
 1130 #undef  PERR_DRCODE
 1131 
 1132 #define PERR_DFLAGS(n)  perr->perr_dests[n].dest_flags
 1133 #define PERR_DADDR(n)   perr->perr_dests[n].dest_addr
 1134 #define PERR_DSEQ(n)    perr->perr_dests[n].dest_seq
 1135 #define PERR_DRCODE(n)  perr->perr_dests[n].dest_rcode
 1136 static void
 1137 hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
 1138     const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
 1139 {
 1140         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1141         struct ieee80211_mesh_route *rt = NULL;
 1142         struct ieee80211_hwmp_route *hr;
 1143         struct ieee80211_meshperr_ie pperr;
 1144         int i, forward = 0;
 1145 
 1146         /*
 1147          * Acceptance criteria: check if we received a PERR from a
 1148          * neighbor and forwarding is enabled.
 1149          */
 1150         if (ni == vap->iv_bss ||
 1151             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
 1152             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
 1153                 return;
 1154         /*
 1155          * Find all routing entries that match and delete them.
 1156          */
 1157         for (i = 0; i < perr->perr_ndests; i++) {
 1158                 rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
 1159                 if (rt == NULL)
 1160                         continue;
 1161                 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1162                 if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 
 1163                     HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
 1164                         ieee80211_mesh_rt_del(vap, rt->rt_dest);
 1165                         ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
 1166                         rt = NULL;
 1167                         forward = 1;
 1168                 }
 1169         }
 1170         /*
 1171          * Propagate the PERR if we previously found it on our routing table.
 1172          * XXX handle ndest > 1
 1173          */
 1174         if (forward && perr->perr_ttl > 1) {
 1175                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1176                     "propagate PERR from %s", ether_sprintf(wh->i_addr2));
 1177                 memcpy(&pperr, perr, sizeof(*perr));
 1178                 pperr.perr_ttl--;
 1179                 hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
 1180                     &pperr);
 1181         }
 1182 }
 1183 #undef  PEER_DADDR
 1184 #undef  PERR_DSEQ
 1185 
 1186 static int
 1187 hwmp_send_perr(struct ieee80211_node *ni,
 1188     const uint8_t sa[IEEE80211_ADDR_LEN],
 1189     const uint8_t da[IEEE80211_ADDR_LEN],
 1190     struct ieee80211_meshperr_ie *perr)
 1191 {
 1192         struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
 1193 
 1194         /*
 1195          * Enforce PERR interval.
 1196          */
 1197         if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
 1198                 return EALREADY;
 1199         getmicrouptime(&hs->hs_lastperr);
 1200 
 1201         /*
 1202          * mesh perr action frame format
 1203          *     [6] da
 1204          *     [6] sa
 1205          *     [6] addr3 = sa
 1206          *     [1] action
 1207          *     [1] category
 1208          *     [tlv] mesh path error
 1209          */
 1210         perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
 1211         return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
 1212             sizeof(struct ieee80211_meshperr_ie));
 1213 }
 1214 
 1215 static void
 1216 hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
 1217     const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
 1218 {
 1219         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1220         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1221         struct ieee80211_mesh_route *rt = NULL;
 1222         struct ieee80211_hwmp_route *hr;
 1223         struct ieee80211_meshrann_ie prann;
 1224 
 1225         if (ni == vap->iv_bss ||
 1226             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
 1227                 return;
 1228 
 1229         rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
 1230         /*
 1231          * Discover the path to the root mesh STA.
 1232          * If we already know it, propagate the RANN element.
 1233          */
 1234         if (rt == NULL) {
 1235                 hwmp_discover(vap, rann->rann_addr, NULL);
 1236                 return;
 1237         }
 1238         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1239         if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq) && rann->rann_ttl > 1 &&
 1240             rann->rann_hopcount < hs->hs_maxhops &&
 1241             (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
 1242                 memcpy(&prann, rann, sizeof(prann));
 1243                 prann.rann_hopcount += 1;
 1244                 prann.rann_ttl -= 1;
 1245                 prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
 1246                 hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
 1247                     &prann);
 1248         }
 1249 }
 1250 
 1251 static int
 1252 hwmp_send_rann(struct ieee80211_node *ni,
 1253     const uint8_t sa[IEEE80211_ADDR_LEN],
 1254     const uint8_t da[IEEE80211_ADDR_LEN],
 1255     struct ieee80211_meshrann_ie *rann)
 1256 {
 1257         /*
 1258          * mesh rann action frame format
 1259          *     [6] da
 1260          *     [6] sa 
 1261          *     [6] addr3 = sa
 1262          *     [1] action
 1263          *     [1] category
 1264          *     [tlv] root annoucement
 1265          */
 1266         rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
 1267         return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
 1268             sizeof(struct ieee80211_meshrann_ie));
 1269 }
 1270 
 1271 #define PREQ_TFLAGS(n)  preq.preq_targets[n].target_flags
 1272 #define PREQ_TADDR(n)   preq.preq_targets[n].target_addr
 1273 #define PREQ_TSEQ(n)    preq.preq_targets[n].target_seq
 1274 static struct ieee80211_node *
 1275 hwmp_discover(struct ieee80211vap *vap,
 1276     const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
 1277 {
 1278         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1279         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1280         struct ieee80211_mesh_route *rt = NULL;
 1281         struct ieee80211_hwmp_route *hr;
 1282         struct ieee80211_meshpreq_ie preq;
 1283         struct ieee80211_node *ni;
 1284         int sendpreq = 0;
 1285 
 1286         KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
 1287             ("not a mesh vap, opmode %d", vap->iv_opmode));
 1288 
 1289         KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
 1290             ("%s: discovering self!", __func__));
 1291 
 1292         ni = NULL;
 1293         if (!IEEE80211_IS_MULTICAST(dest)) {
 1294                 rt = ieee80211_mesh_rt_find(vap, dest);
 1295                 if (rt == NULL) {
 1296                         rt = ieee80211_mesh_rt_add(vap, dest);
 1297                         if (rt == NULL) {
 1298                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
 1299                                     ni, "unable to add discovery path to %s",
 1300                                     ether_sprintf(dest));
 1301                                 vap->iv_stats.is_mesh_rtaddfailed++;
 1302                                 goto done;
 1303                         }
 1304                 }
 1305                 hr = IEEE80211_MESH_ROUTE_PRIV(rt,
 1306                     struct ieee80211_hwmp_route);
 1307                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
 1308                         if (hr->hr_targetseq == 0)
 1309                                 hr->hr_targetseq = ++hs->hs_seq;
 1310                         rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
 1311                         rt->rt_lifetime =
 1312                             ticks_to_msecs(ieee80211_hwmp_pathtimeout);
 1313                         /* XXX check preq retries */
 1314                         sendpreq = 1;
 1315                         IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
 1316                             "start path discovery (src %s)",
 1317                             m == NULL ? "<none>" : ether_sprintf(
 1318                                 mtod(m, struct ether_header *)->ether_shost));
 1319                         /*
 1320                          * Try to discover the path for this node.
 1321                          */
 1322                         preq.preq_flags = 0;
 1323                         preq.preq_hopcount = 0;
 1324                         preq.preq_ttl = ms->ms_ttl;
 1325                         preq.preq_id = ++hs->hs_preqid;
 1326                         IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
 1327                         preq.preq_origseq = hr->hr_targetseq;
 1328                         preq.preq_lifetime = rt->rt_lifetime;
 1329                         preq.preq_metric = rt->rt_metric;
 1330                         preq.preq_tcount = 1;
 1331                         IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
 1332                         PREQ_TFLAGS(0) = 0;
 1333                         if (ieee80211_hwmp_targetonly)
 1334                                 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
 1335                         if (ieee80211_hwmp_replyforward)
 1336                                 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
 1337                         PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
 1338                         PREQ_TSEQ(0) = 0;
 1339                         /* XXX check return value */
 1340                         hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
 1341                             broadcastaddr, &preq);
 1342                 }
 1343                 if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
 1344                         ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
 1345         } else {
 1346                 ni = ieee80211_find_txnode(vap, dest);
 1347                 /* NB: if null then we leak mbuf */
 1348                 KASSERT(ni != NULL, ("leak mcast frame"));
 1349                 return ni;
 1350         }
 1351 done:
 1352         if (ni == NULL && m != NULL) {
 1353                 if (sendpreq) {
 1354                         struct ieee80211com *ic = vap->iv_ic;
 1355                         /*
 1356                          * Queue packet for transmit when path discovery
 1357                          * completes.  If discovery never completes the
 1358                          * frame will be flushed by way of the aging timer.
 1359                          */
 1360                         IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
 1361                             "%s", "queue frame until path found");
 1362                         m->m_pkthdr.rcvif = (void *)(uintptr_t)
 1363                             ieee80211_mac_hash(ic, dest);
 1364                         /* XXX age chosen randomly */
 1365                         ieee80211_ageq_append(&ic->ic_stageq, m,
 1366                             IEEE80211_INACT_WAIT);
 1367                 } else {
 1368                         IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
 1369                             dest, NULL, "%s", "no valid path to this node");
 1370                         m_freem(m);
 1371                 }
 1372         }
 1373         return ni;
 1374 }
 1375 #undef  PREQ_TFLAGS
 1376 #undef  PREQ_TADDR
 1377 #undef  PREQ_TSEQ
 1378 
 1379 static int
 1380 hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
 1381 {
 1382         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1383         int error;
 1384  
 1385         if (vap->iv_opmode != IEEE80211_M_MBSS)
 1386                 return ENOSYS;
 1387         error = 0;
 1388         switch (ireq->i_type) {
 1389         case IEEE80211_IOC_HWMP_ROOTMODE:
 1390                 ireq->i_val = hs->hs_rootmode;
 1391                 break;
 1392         case IEEE80211_IOC_HWMP_MAXHOPS:
 1393                 ireq->i_val = hs->hs_maxhops;
 1394                 break;
 1395         default:
 1396                 return ENOSYS;
 1397         }
 1398         return error;
 1399 }
 1400 IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
 1401 
 1402 static int
 1403 hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
 1404 {
 1405         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1406         int error;
 1407 
 1408         if (vap->iv_opmode != IEEE80211_M_MBSS)
 1409                 return ENOSYS;
 1410         error = 0;
 1411         switch (ireq->i_type) {
 1412         case IEEE80211_IOC_HWMP_ROOTMODE:
 1413                 if (ireq->i_val < 0 || ireq->i_val > 3)
 1414                         return EINVAL;
 1415                 hs->hs_rootmode = ireq->i_val;
 1416                 hwmp_rootmode_setup(vap);
 1417                 break;
 1418         case IEEE80211_IOC_HWMP_MAXHOPS:
 1419                 if (ireq->i_val <= 0 || ireq->i_val > 255)
 1420                         return EINVAL;
 1421                 hs->hs_maxhops = ireq->i_val;
 1422                 break;
 1423         default:
 1424                 return ENOSYS;
 1425         }
 1426         return error;
 1427 }
 1428 IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);

Cache object: e8a9a1a3215d250692a9cde28fdafd58


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