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  -  FREEBSD11  -  FREEBSD10  -  FREEBSD9  -  FREEBSD92  -  FREEBSD91  -  FREEBSD90  -  FREEBSD8  -  FREEBSD82  -  FREEBSD81  -  FREEBSD80  -  FREEBSD7  -  FREEBSD74  -  FREEBSD73  -  FREEBSD72  -  FREEBSD71  -  FREEBSD70  -  FREEBSD6  -  FREEBSD64  -  FREEBSD63  -  FREEBSD62  -  FREEBSD61  -  FREEBSD60  -  FREEBSD5  -  FREEBSD55  -  FREEBSD54  -  FREEBSD53  -  FREEBSD52  -  FREEBSD51  -  FREEBSD50  -  FREEBSD4  -  FREEBSD3  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
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: stable/8/sys/net80211/ieee80211_hwmp.c 209572 2010-06-28 12:20:36Z 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_origseq;     /* 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         rann.rann_flags = 0;
  657         if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL)
  658                 rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR;
  659         rann.rann_hopcount = 0;
  660         rann.rann_ttl = ms->ms_ttl;
  661         IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr);
  662         rann.rann_seq = ++hs->hs_seq;
  663         rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  664 
  665         vap->iv_stats.is_hwmp_rootrann++;
  666         hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann);
  667         hwmp_rootmode_setup(vap);
  668 }
  669 
  670 #define PREQ_TFLAGS(n)  preq->preq_targets[n].target_flags
  671 #define PREQ_TADDR(n)   preq->preq_targets[n].target_addr
  672 #define PREQ_TSEQ(n)    preq->preq_targets[n].target_seq
  673 static void
  674 hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni,
  675     const struct ieee80211_frame *wh, const struct ieee80211_meshpreq_ie *preq)
  676 {
  677         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  678         struct ieee80211_mesh_route *rt = NULL;
  679         struct ieee80211_mesh_route *rtorig = NULL;
  680         struct ieee80211_hwmp_route *hrorig;
  681         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  682         struct ieee80211_meshprep_ie prep;
  683 
  684         if (ni == vap->iv_bss ||
  685             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
  686                 return;
  687         /*
  688          * Ignore PREQs from us. Could happen because someone forward it
  689          * back to us.
  690          */
  691         if (IEEE80211_ADDR_EQ(vap->iv_myaddr, preq->preq_origaddr))
  692                 return;
  693 
  694         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  695             "received PREQ, source %s", ether_sprintf(preq->preq_origaddr));
  696 
  697         /*
  698          * Acceptance criteria: if the PREQ is not for us and
  699          * forwarding is disabled, discard this PREQ.
  700          */
  701         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0)) &&
  702             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
  703                 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
  704                     preq->preq_origaddr, NULL, "%s", "not accepting PREQ");
  705                 return;
  706         }
  707         rtorig = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
  708         if (rtorig == NULL)
  709                 rtorig = ieee80211_mesh_rt_add(vap, preq->preq_origaddr);
  710         if (rtorig == NULL) {
  711                 /* XXX stat */
  712                 return;
  713         }
  714         hrorig = IEEE80211_MESH_ROUTE_PRIV(rtorig, struct ieee80211_hwmp_route);
  715         /*
  716          * Sequence number validation.
  717          */
  718         if (HWMP_SEQ_LEQ(preq->preq_id, hrorig->hr_preqid) &&
  719             HWMP_SEQ_LEQ(preq->preq_origseq, hrorig->hr_seq)) {
  720                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  721                     "discard PREQ from %s, old seq no %u <= %u",
  722                     ether_sprintf(preq->preq_origaddr),
  723                     preq->preq_origseq, hrorig->hr_seq);
  724                 return;
  725         }
  726         hrorig->hr_preqid = preq->preq_id;
  727         hrorig->hr_seq = preq->preq_origseq;
  728 
  729         /*
  730          * Check if the PREQ is addressed to us.
  731          */
  732         if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) {
  733                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  734                     "reply to %s", ether_sprintf(preq->preq_origaddr));
  735                 /*
  736                  * Build and send a PREP frame.
  737                  */
  738                 prep.prep_flags = 0;
  739                 prep.prep_hopcount = 0;
  740                 prep.prep_ttl = ms->ms_ttl;
  741                 IEEE80211_ADDR_COPY(prep.prep_targetaddr, vap->iv_myaddr);
  742                 prep.prep_targetseq = ++hs->hs_seq;
  743                 prep.prep_lifetime = preq->preq_lifetime;
  744                 prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  745                 IEEE80211_ADDR_COPY(prep.prep_origaddr, preq->preq_origaddr);
  746                 prep.prep_origseq = preq->preq_origseq;
  747                 hwmp_send_prep(ni, vap->iv_myaddr, wh->i_addr2, &prep);
  748                 /*
  749                  * Build the reverse path, if we don't have it already.
  750                  */
  751                 rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr);
  752                 if (rt == NULL)
  753                         hwmp_discover(vap, preq->preq_origaddr, NULL);
  754                 else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0)
  755                         hwmp_discover(vap, rt->rt_dest, NULL);
  756                 return;
  757         }
  758         /*
  759          * Proactive PREQ: reply with a proactive PREP to the
  760          * root STA if requested.
  761          */
  762         if (IEEE80211_ADDR_EQ(PREQ_TADDR(0), broadcastaddr) &&
  763             (PREQ_TFLAGS(0) &
  764             ((IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF) ==
  765             (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) {
  766                 uint8_t rootmac[IEEE80211_ADDR_LEN];
  767 
  768                 IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr);
  769                 rt = ieee80211_mesh_rt_find(vap, rootmac);
  770                 if (rt == NULL) {
  771                         rt = ieee80211_mesh_rt_add(vap, rootmac);
  772                         if (rt == NULL) {
  773                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  774                                     "unable to add root mesh path to %s",
  775                                     ether_sprintf(rootmac));
  776                                 vap->iv_stats.is_mesh_rtaddfailed++;
  777                                 return;
  778                         }
  779                 }
  780                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  781                     "root mesh station @ %s", ether_sprintf(rootmac));
  782 
  783                 /*
  784                  * Reply with a PREP if we don't have a path to the root
  785                  * or if the root sent us a proactive PREQ.
  786                  */
  787                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
  788                     (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) {
  789                         prep.prep_flags = 0;
  790                         prep.prep_hopcount = 0;
  791                         prep.prep_ttl = ms->ms_ttl;
  792                         IEEE80211_ADDR_COPY(prep.prep_origaddr, rootmac);
  793                         prep.prep_origseq = preq->preq_origseq;
  794                         prep.prep_lifetime = preq->preq_lifetime;
  795                         prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
  796                         IEEE80211_ADDR_COPY(prep.prep_targetaddr,
  797                             vap->iv_myaddr);
  798                         prep.prep_targetseq = ++hs->hs_seq;
  799                         hwmp_send_prep(vap->iv_bss, vap->iv_myaddr,
  800                             broadcastaddr, &prep);
  801                 }
  802                 hwmp_discover(vap, rootmac, NULL);
  803                 return;
  804         }
  805         rt = ieee80211_mesh_rt_find(vap, PREQ_TADDR(0));
  806 
  807         /*
  808          * Forwarding and Intermediate reply for PREQs with 1 target.
  809          */
  810         if (preq->preq_tcount == 1) {
  811                 struct ieee80211_meshpreq_ie ppreq; /* propagated PREQ */
  812 
  813                 memcpy(&ppreq, preq, sizeof(ppreq));
  814                 /*
  815                  * We have a valid route to this node.
  816                  */
  817                 if (rt != NULL &&
  818                     (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
  819                         if (preq->preq_ttl > 1 &&
  820                             preq->preq_hopcount < hs->hs_maxhops) {
  821                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  822                                     "forward PREQ from %s",
  823                                     ether_sprintf(preq->preq_origaddr));
  824                                 /*
  825                                  * Propagate the original PREQ.
  826                                  */
  827                                 ppreq.preq_hopcount += 1;
  828                                 ppreq.preq_ttl -= 1;
  829                                 ppreq.preq_metric +=
  830                                     ms->ms_pmetric->mpm_metric(ni);
  831                                 /*
  832                                  * Set TO and unset RF bits because we are going
  833                                  * to send a PREP next.
  834                                  */
  835                                 ppreq.preq_targets[0].target_flags |=
  836                                     IEEE80211_MESHPREQ_TFLAGS_TO;
  837                                 ppreq.preq_targets[0].target_flags &=
  838                                     ~IEEE80211_MESHPREQ_TFLAGS_RF;
  839                                 hwmp_send_preq(ni, vap->iv_myaddr,
  840                                     broadcastaddr, &ppreq);
  841                         }
  842                         /*
  843                          * Check if we can send an intermediate Path Reply,
  844                          * i.e., Target Only bit is not set.
  845                          */
  846                         if (!(PREQ_TFLAGS(0) & IEEE80211_MESHPREQ_TFLAGS_TO)) {
  847                                 struct ieee80211_meshprep_ie prep;
  848 
  849                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  850                                     "intermediate reply for PREQ from %s",
  851                                     ether_sprintf(preq->preq_origaddr));
  852                                 prep.prep_flags = 0;
  853                                 prep.prep_hopcount = rt->rt_nhops + 1;
  854                                 prep.prep_ttl = ms->ms_ttl;
  855                                 IEEE80211_ADDR_COPY(&prep.prep_targetaddr,
  856                                     PREQ_TADDR(0));
  857                                 prep.prep_targetseq = hrorig->hr_seq;
  858                                 prep.prep_lifetime = preq->preq_lifetime;
  859                                 prep.prep_metric = rt->rt_metric +
  860                                     ms->ms_pmetric->mpm_metric(ni);
  861                                 IEEE80211_ADDR_COPY(&prep.prep_origaddr,
  862                                     preq->preq_origaddr);
  863                                 prep.prep_origseq = hrorig->hr_seq;
  864                                 hwmp_send_prep(ni, vap->iv_myaddr,
  865                                     broadcastaddr, &prep);
  866                         }
  867                 /*
  868                  * We have no information about this path,
  869                  * propagate the PREQ.
  870                  */
  871                 } else if (preq->preq_ttl > 1 &&
  872                     preq->preq_hopcount < hs->hs_maxhops) {
  873                         if (rt == NULL) {
  874                                 rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0));
  875                                 if (rt == NULL) {
  876                                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
  877                                             ni, "unable to add PREQ path to %s",
  878                                             ether_sprintf(PREQ_TADDR(0)));
  879                                         vap->iv_stats.is_mesh_rtaddfailed++;
  880                                         return;
  881                                 }
  882                         }
  883                         rt->rt_metric = preq->preq_metric;
  884                         rt->rt_lifetime = preq->preq_lifetime;
  885                         hrorig = IEEE80211_MESH_ROUTE_PRIV(rt,
  886                             struct ieee80211_hwmp_route);
  887                         hrorig->hr_seq = preq->preq_origseq;
  888                         hrorig->hr_preqid = preq->preq_id;
  889 
  890                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  891                             "forward PREQ from %s",
  892                             ether_sprintf(preq->preq_origaddr));
  893                         ppreq.preq_hopcount += 1;
  894                         ppreq.preq_ttl -= 1;
  895                         ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
  896                         hwmp_send_preq(ni, vap->iv_myaddr, broadcastaddr,
  897                             &ppreq);
  898                 }
  899         }
  900 
  901 }
  902 #undef  PREQ_TFLAGS
  903 #undef  PREQ_TADDR
  904 #undef  PREQ_TSEQ
  905 
  906 static int
  907 hwmp_send_preq(struct ieee80211_node *ni,
  908     const uint8_t sa[IEEE80211_ADDR_LEN],
  909     const uint8_t da[IEEE80211_ADDR_LEN],
  910     struct ieee80211_meshpreq_ie *preq)
  911 {
  912         struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
  913 
  914         /*
  915          * Enforce PREQ interval.
  916          */
  917         if (ratecheck(&hs->hs_lastpreq, &ieee80211_hwmp_preqminint) == 0)
  918                 return EALREADY;
  919         getmicrouptime(&hs->hs_lastpreq);
  920 
  921         /*
  922          * mesh preq action frame format
  923          *     [6] da
  924          *     [6] sa 
  925          *     [6] addr3 = sa
  926          *     [1] action
  927          *     [1] category
  928          *     [tlv] mesh path request
  929          */
  930         preq->preq_ie = IEEE80211_ELEMID_MESHPREQ;
  931         return hwmp_send_action(ni, sa, da, (uint8_t *)preq,
  932             sizeof(struct ieee80211_meshpreq_ie));
  933 }
  934 
  935 static void
  936 hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni,
  937     const struct ieee80211_frame *wh, const struct ieee80211_meshprep_ie *prep)
  938 {
  939         struct ieee80211_mesh_state *ms = vap->iv_mesh;
  940         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
  941         struct ieee80211_mesh_route *rt = NULL;
  942         struct ieee80211_hwmp_route *hr;
  943         struct ieee80211com *ic = vap->iv_ic;
  944         struct ifnet *ifp = vap->iv_ifp;
  945         struct mbuf *m, *next;
  946 
  947         /*
  948          * Acceptance criteria: if the corresponding PREQ was not generated
  949          * by us and forwarding is disabled, discard this PREP.
  950          */
  951         if (ni == vap->iv_bss ||
  952             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED)
  953                 return;
  954         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
  955             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
  956                 return;
  957 
  958         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  959             "received PREP from %s", ether_sprintf(prep->prep_targetaddr));
  960 
  961         rt = ieee80211_mesh_rt_find(vap, prep->prep_targetaddr);
  962         if (rt == NULL) {
  963                 /*
  964                  * If we have no entry this could be a reply to a root PREQ.
  965                  */
  966                 if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) {
  967                         rt = ieee80211_mesh_rt_add(vap, prep->prep_targetaddr);
  968                         if (rt == NULL) {
  969                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
  970                                     ni, "unable to add PREP path to %s",
  971                                     ether_sprintf(prep->prep_targetaddr));
  972                                 vap->iv_stats.is_mesh_rtaddfailed++;
  973                                 return;
  974                         }
  975                         IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
  976                         rt->rt_nhops = prep->prep_hopcount;
  977                         rt->rt_lifetime = prep->prep_lifetime;
  978                         rt->rt_metric = prep->prep_metric;
  979                         rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
  980                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  981                             "add root path to %s nhops %d metric %d (PREP)",
  982                             ether_sprintf(prep->prep_targetaddr),
  983                             rt->rt_nhops, rt->rt_metric);
  984                         return;
  985                 } 
  986                 return;
  987         }
  988         /*
  989          * Sequence number validation.
  990          */
  991         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
  992         if (HWMP_SEQ_LEQ(prep->prep_targetseq, hr->hr_seq)) {
  993                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
  994                     "discard PREP from %s, old seq no %u <= %u",
  995                     ether_sprintf(prep->prep_targetaddr),
  996                     prep->prep_targetseq, hr->hr_seq);
  997                 return;
  998         }
  999         hr->hr_seq = prep->prep_targetseq;
 1000         /*
 1001          * If it's NOT for us, propagate the PREP.
 1002          */
 1003         if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, prep->prep_origaddr) &&
 1004             prep->prep_ttl > 1 && prep->prep_hopcount < hs->hs_maxhops) {
 1005                 struct ieee80211_meshprep_ie pprep; /* propagated PREP */
 1006 
 1007                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1008                     "propagate PREP from %s",
 1009                     ether_sprintf(prep->prep_targetaddr));
 1010 
 1011                 memcpy(&pprep, prep, sizeof(pprep));
 1012                 pprep.prep_hopcount += 1;
 1013                 pprep.prep_ttl -= 1;
 1014                 pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
 1015                 IEEE80211_ADDR_COPY(pprep.prep_targetaddr, vap->iv_myaddr);
 1016                 hwmp_send_prep(ni, vap->iv_myaddr, broadcastaddr, &pprep);
 1017         }
 1018         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1019         if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY) {
 1020                 /* NB: never clobber a proxy entry */;
 1021                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1022                     "discard PREP for %s, route is marked PROXY",
 1023                     ether_sprintf(prep->prep_targetaddr));
 1024                 vap->iv_stats.is_hwmp_proxy++;
 1025         } else if (prep->prep_origseq == hr->hr_origseq) {
 1026                 /*
 1027                  * Check if we already have a path to this node.
 1028                  * If we do, check if this path reply contains a
 1029                  * better route.
 1030                  */
 1031                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 ||
 1032                     (prep->prep_hopcount < rt->rt_nhops ||
 1033                      prep->prep_metric < rt->rt_metric)) {
 1034                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1035                             "%s path to %s, hopcount %d:%d metric %d:%d",
 1036                             rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID ?
 1037                                 "prefer" : "update",
 1038                             ether_sprintf(prep->prep_origaddr),
 1039                             rt->rt_nhops, prep->prep_hopcount,
 1040                             rt->rt_metric, prep->prep_metric);
 1041                         IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2);
 1042                         rt->rt_nhops = prep->prep_hopcount;
 1043                         rt->rt_lifetime = prep->prep_lifetime;
 1044                         rt->rt_metric = prep->prep_metric;
 1045                         rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID;
 1046                 } else {
 1047                         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1048                             "ignore PREP for %s, hopcount %d:%d metric %d:%d",
 1049                             ether_sprintf(prep->prep_targetaddr),
 1050                             rt->rt_nhops, prep->prep_hopcount,
 1051                             rt->rt_metric, prep->prep_metric);
 1052                 }
 1053         } else {
 1054                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1055                     "discard PREP for %s, wrong seqno %u != %u",
 1056                     ether_sprintf(prep->prep_targetaddr), prep->prep_origseq,
 1057                     hr->hr_seq);
 1058                 vap->iv_stats.is_hwmp_wrongseq++;
 1059         } 
 1060         /*
 1061          * Check for frames queued awaiting path discovery.
 1062          * XXX probably can tell exactly and avoid remove call
 1063          * NB: hash may have false matches, if so they will get
 1064          *     stuck back on the stageq because there won't be
 1065          *     a path.
 1066          */
 1067         m = ieee80211_ageq_remove(&ic->ic_stageq, 
 1068             (struct ieee80211_node *)(uintptr_t)
 1069                 ieee80211_mac_hash(ic, rt->rt_dest));
 1070         for (; m != NULL; m = next) {
 1071                 next = m->m_nextpkt;
 1072                 m->m_nextpkt = NULL;
 1073                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1074                     "flush queued frame %p len %d", m, m->m_pkthdr.len);
 1075                 ifp->if_transmit(ifp, m);
 1076         }
 1077 }
 1078 
 1079 static int
 1080 hwmp_send_prep(struct ieee80211_node *ni,
 1081     const uint8_t sa[IEEE80211_ADDR_LEN],
 1082     const uint8_t da[IEEE80211_ADDR_LEN],
 1083     struct ieee80211_meshprep_ie *prep)
 1084 {
 1085         /* NB: there's no PREP minimum interval. */
 1086 
 1087         /*
 1088          * mesh prep action frame format
 1089          *     [6] da
 1090          *     [6] sa 
 1091          *     [6] addr3 = sa
 1092          *     [1] action
 1093          *     [1] category
 1094          *     [tlv] mesh path reply
 1095          */
 1096         prep->prep_ie = IEEE80211_ELEMID_MESHPREP;
 1097         return hwmp_send_action(ni, sa, da, (uint8_t *)prep,
 1098             sizeof(struct ieee80211_meshprep_ie));
 1099 }
 1100 
 1101 #define PERR_DFLAGS(n)  perr.perr_dests[n].dest_flags
 1102 #define PERR_DADDR(n)   perr.perr_dests[n].dest_addr
 1103 #define PERR_DSEQ(n)    perr.perr_dests[n].dest_seq
 1104 #define PERR_DRCODE(n)  perr.perr_dests[n].dest_rcode
 1105 static void
 1106 hwmp_peerdown(struct ieee80211_node *ni)
 1107 {
 1108         struct ieee80211vap *vap = ni->ni_vap;
 1109         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1110         struct ieee80211_meshperr_ie perr;
 1111         struct ieee80211_mesh_route *rt;
 1112         struct ieee80211_hwmp_route *hr;
 1113 
 1114         rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr);
 1115         if (rt == NULL)
 1116                 return;
 1117         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1118         IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1119             "%s", "delete route entry");
 1120         perr.perr_ttl = ms->ms_ttl;
 1121         perr.perr_ndests = 1;
 1122         PERR_DFLAGS(0) = 0;
 1123         if (hr->hr_seq == 0)
 1124                 PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN;
 1125         PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC;
 1126         IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest);
 1127         PERR_DSEQ(0) = hr->hr_seq;
 1128         PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH;
 1129         /* NB: flush everything passing through peer */
 1130         ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr);
 1131         hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr);
 1132 }
 1133 #undef  PERR_DFLAGS
 1134 #undef  PERR_DADDR
 1135 #undef  PERR_DSEQ
 1136 #undef  PERR_DRCODE
 1137 
 1138 #define PERR_DFLAGS(n)  perr->perr_dests[n].dest_flags
 1139 #define PERR_DADDR(n)   perr->perr_dests[n].dest_addr
 1140 #define PERR_DSEQ(n)    perr->perr_dests[n].dest_seq
 1141 #define PERR_DRCODE(n)  perr->perr_dests[n].dest_rcode
 1142 static void
 1143 hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni,
 1144     const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr)
 1145 {
 1146         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1147         struct ieee80211_mesh_route *rt = NULL;
 1148         struct ieee80211_hwmp_route *hr;
 1149         struct ieee80211_meshperr_ie pperr;
 1150         int i, forward = 0;
 1151 
 1152         /*
 1153          * Acceptance criteria: check if we received a PERR from a
 1154          * neighbor and forwarding is enabled.
 1155          */
 1156         if (ni == vap->iv_bss ||
 1157             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
 1158             !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD))
 1159                 return;
 1160         /*
 1161          * Find all routing entries that match and delete them.
 1162          */
 1163         for (i = 0; i < perr->perr_ndests; i++) {
 1164                 rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i));
 1165                 if (rt == NULL)
 1166                         continue;
 1167                 hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1168                 if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && 
 1169                     HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) {
 1170                         ieee80211_mesh_rt_del(vap, rt->rt_dest);
 1171                         ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest);
 1172                         rt = NULL;
 1173                         forward = 1;
 1174                 }
 1175         }
 1176         /*
 1177          * Propagate the PERR if we previously found it on our routing table.
 1178          * XXX handle ndest > 1
 1179          */
 1180         if (forward && perr->perr_ttl > 1) {
 1181                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni,
 1182                     "propagate PERR from %s", ether_sprintf(wh->i_addr2));
 1183                 memcpy(&pperr, perr, sizeof(*perr));
 1184                 pperr.perr_ttl--;
 1185                 hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr,
 1186                     &pperr);
 1187         }
 1188 }
 1189 #undef  PEER_DADDR
 1190 #undef  PERR_DSEQ
 1191 
 1192 static int
 1193 hwmp_send_perr(struct ieee80211_node *ni,
 1194     const uint8_t sa[IEEE80211_ADDR_LEN],
 1195     const uint8_t da[IEEE80211_ADDR_LEN],
 1196     struct ieee80211_meshperr_ie *perr)
 1197 {
 1198         struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp;
 1199 
 1200         /*
 1201          * Enforce PERR interval.
 1202          */
 1203         if (ratecheck(&hs->hs_lastperr, &ieee80211_hwmp_perrminint) == 0)
 1204                 return EALREADY;
 1205         getmicrouptime(&hs->hs_lastperr);
 1206 
 1207         /*
 1208          * mesh perr action frame format
 1209          *     [6] da
 1210          *     [6] sa
 1211          *     [6] addr3 = sa
 1212          *     [1] action
 1213          *     [1] category
 1214          *     [tlv] mesh path error
 1215          */
 1216         perr->perr_ie = IEEE80211_ELEMID_MESHPERR;
 1217         return hwmp_send_action(ni, sa, da, (uint8_t *)perr,
 1218             sizeof(struct ieee80211_meshperr_ie));
 1219 }
 1220 
 1221 static void
 1222 hwmp_recv_rann(struct ieee80211vap *vap, struct ieee80211_node *ni,
 1223     const struct ieee80211_frame *wh, const struct ieee80211_meshrann_ie *rann)
 1224 {
 1225         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1226         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1227         struct ieee80211_mesh_route *rt = NULL;
 1228         struct ieee80211_hwmp_route *hr;
 1229         struct ieee80211_meshrann_ie prann;
 1230 
 1231         if (ni == vap->iv_bss ||
 1232             ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED ||
 1233             IEEE80211_ADDR_EQ(rann->rann_addr, vap->iv_myaddr))
 1234                 return;
 1235 
 1236         rt = ieee80211_mesh_rt_find(vap, rann->rann_addr);
 1237         /*
 1238          * Discover the path to the root mesh STA.
 1239          * If we already know it, propagate the RANN element.
 1240          */
 1241         if (rt == NULL) {
 1242                 hwmp_discover(vap, rann->rann_addr, NULL);
 1243                 return;
 1244         }
 1245         hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 1246         if (HWMP_SEQ_GT(rann->rann_seq, hr->hr_seq)) {
 1247                 hr->hr_seq = rann->rann_seq;
 1248                 if (rann->rann_ttl > 1 &&
 1249                     rann->rann_hopcount < hs->hs_maxhops &&
 1250                     (ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) {
 1251                         memcpy(&prann, rann, sizeof(prann));
 1252                         prann.rann_hopcount += 1;
 1253                         prann.rann_ttl -= 1;
 1254                         prann.rann_metric += ms->ms_pmetric->mpm_metric(ni);
 1255                         hwmp_send_rann(vap->iv_bss, vap->iv_myaddr,
 1256                             broadcastaddr, &prann);
 1257                 }
 1258         }
 1259 }
 1260 
 1261 static int
 1262 hwmp_send_rann(struct ieee80211_node *ni,
 1263     const uint8_t sa[IEEE80211_ADDR_LEN],
 1264     const uint8_t da[IEEE80211_ADDR_LEN],
 1265     struct ieee80211_meshrann_ie *rann)
 1266 {
 1267         /*
 1268          * mesh rann action frame format
 1269          *     [6] da
 1270          *     [6] sa 
 1271          *     [6] addr3 = sa
 1272          *     [1] action
 1273          *     [1] category
 1274          *     [tlv] root annoucement
 1275          */
 1276         rann->rann_ie = IEEE80211_ELEMID_MESHRANN;
 1277         return hwmp_send_action(ni, sa, da, (uint8_t *)rann,
 1278             sizeof(struct ieee80211_meshrann_ie));
 1279 }
 1280 
 1281 #define PREQ_TFLAGS(n)  preq.preq_targets[n].target_flags
 1282 #define PREQ_TADDR(n)   preq.preq_targets[n].target_addr
 1283 #define PREQ_TSEQ(n)    preq.preq_targets[n].target_seq
 1284 static struct ieee80211_node *
 1285 hwmp_discover(struct ieee80211vap *vap,
 1286     const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m)
 1287 {
 1288         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1289         struct ieee80211_mesh_state *ms = vap->iv_mesh;
 1290         struct ieee80211_mesh_route *rt = NULL;
 1291         struct ieee80211_hwmp_route *hr;
 1292         struct ieee80211_meshpreq_ie preq;
 1293         struct ieee80211_node *ni;
 1294         int sendpreq = 0;
 1295 
 1296         KASSERT(vap->iv_opmode == IEEE80211_M_MBSS,
 1297             ("not a mesh vap, opmode %d", vap->iv_opmode));
 1298 
 1299         KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest),
 1300             ("%s: discovering self!", __func__));
 1301 
 1302         ni = NULL;
 1303         if (!IEEE80211_IS_MULTICAST(dest)) {
 1304                 rt = ieee80211_mesh_rt_find(vap, dest);
 1305                 if (rt == NULL) {
 1306                         rt = ieee80211_mesh_rt_add(vap, dest);
 1307                         if (rt == NULL) {
 1308                                 IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP,
 1309                                     ni, "unable to add discovery path to %s",
 1310                                     ether_sprintf(dest));
 1311                                 vap->iv_stats.is_mesh_rtaddfailed++;
 1312                                 goto done;
 1313                         }
 1314                 }
 1315                 hr = IEEE80211_MESH_ROUTE_PRIV(rt,
 1316                     struct ieee80211_hwmp_route);
 1317                 if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) {
 1318                         if (hr->hr_origseq == 0)
 1319                                 hr->hr_origseq = ++hs->hs_seq;
 1320                         rt->rt_metric = IEEE80211_MESHLMETRIC_INITIALVAL;
 1321                         rt->rt_lifetime =
 1322                             ticks_to_msecs(ieee80211_hwmp_pathtimeout);
 1323                         /* XXX check preq retries */
 1324                         sendpreq = 1;
 1325                         IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
 1326                             "start path discovery (src %s)",
 1327                             m == NULL ? "<none>" : ether_sprintf(
 1328                                 mtod(m, struct ether_header *)->ether_shost));
 1329                         /*
 1330                          * Try to discover the path for this node.
 1331                          */
 1332                         preq.preq_flags = 0;
 1333                         preq.preq_hopcount = 0;
 1334                         preq.preq_ttl = ms->ms_ttl;
 1335                         preq.preq_id = ++hs->hs_preqid;
 1336                         IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr);
 1337                         preq.preq_origseq = hr->hr_origseq;
 1338                         preq.preq_lifetime = rt->rt_lifetime;
 1339                         preq.preq_metric = rt->rt_metric;
 1340                         preq.preq_tcount = 1;
 1341                         IEEE80211_ADDR_COPY(PREQ_TADDR(0), dest);
 1342                         PREQ_TFLAGS(0) = 0;
 1343                         if (ieee80211_hwmp_targetonly)
 1344                                 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_TO;
 1345                         if (ieee80211_hwmp_replyforward)
 1346                                 PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_RF;
 1347                         PREQ_TFLAGS(0) |= IEEE80211_MESHPREQ_TFLAGS_USN;
 1348                         PREQ_TSEQ(0) = 0;
 1349                         /* XXX check return value */
 1350                         hwmp_send_preq(vap->iv_bss, vap->iv_myaddr,
 1351                             broadcastaddr, &preq);
 1352                 }
 1353                 if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)
 1354                         ni = ieee80211_find_txnode(vap, rt->rt_nexthop);
 1355         } else {
 1356                 ni = ieee80211_find_txnode(vap, dest);
 1357                 /* NB: if null then we leak mbuf */
 1358                 KASSERT(ni != NULL, ("leak mcast frame"));
 1359                 return ni;
 1360         }
 1361 done:
 1362         if (ni == NULL && m != NULL) {
 1363                 if (sendpreq) {
 1364                         struct ieee80211com *ic = vap->iv_ic;
 1365                         /*
 1366                          * Queue packet for transmit when path discovery
 1367                          * completes.  If discovery never completes the
 1368                          * frame will be flushed by way of the aging timer.
 1369                          */
 1370                         IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest,
 1371                             "%s", "queue frame until path found");
 1372                         m->m_pkthdr.rcvif = (void *)(uintptr_t)
 1373                             ieee80211_mac_hash(ic, dest);
 1374                         /* XXX age chosen randomly */
 1375                         ieee80211_ageq_append(&ic->ic_stageq, m,
 1376                             IEEE80211_INACT_WAIT);
 1377                 } else {
 1378                         IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP,
 1379                             dest, NULL, "%s", "no valid path to this node");
 1380                         m_freem(m);
 1381                 }
 1382         }
 1383         return ni;
 1384 }
 1385 #undef  PREQ_TFLAGS
 1386 #undef  PREQ_TADDR
 1387 #undef  PREQ_TSEQ
 1388 
 1389 static int
 1390 hwmp_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
 1391 {
 1392         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1393         int error;
 1394  
 1395         if (vap->iv_opmode != IEEE80211_M_MBSS)
 1396                 return ENOSYS;
 1397         error = 0;
 1398         switch (ireq->i_type) {
 1399         case IEEE80211_IOC_HWMP_ROOTMODE:
 1400                 ireq->i_val = hs->hs_rootmode;
 1401                 break;
 1402         case IEEE80211_IOC_HWMP_MAXHOPS:
 1403                 ireq->i_val = hs->hs_maxhops;
 1404                 break;
 1405         default:
 1406                 return ENOSYS;
 1407         }
 1408         return error;
 1409 }
 1410 IEEE80211_IOCTL_GET(hwmp, hwmp_ioctl_get80211);
 1411 
 1412 static int
 1413 hwmp_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq)
 1414 {
 1415         struct ieee80211_hwmp_state *hs = vap->iv_hwmp;
 1416         int error;
 1417 
 1418         if (vap->iv_opmode != IEEE80211_M_MBSS)
 1419                 return ENOSYS;
 1420         error = 0;
 1421         switch (ireq->i_type) {
 1422         case IEEE80211_IOC_HWMP_ROOTMODE:
 1423                 if (ireq->i_val < 0 || ireq->i_val > 3)
 1424                         return EINVAL;
 1425                 hs->hs_rootmode = ireq->i_val;
 1426                 hwmp_rootmode_setup(vap);
 1427                 break;
 1428         case IEEE80211_IOC_HWMP_MAXHOPS:
 1429                 if (ireq->i_val <= 0 || ireq->i_val > 255)
 1430                         return EINVAL;
 1431                 hs->hs_maxhops = ireq->i_val;
 1432                 break;
 1433         default:
 1434                 return ENOSYS;
 1435         }
 1436         return error;
 1437 }
 1438 IEEE80211_IOCTL_SET(hwmp, hwmp_ioctl_set80211);

Cache object: 3ae943aa9608d9edaeb9dfaefa91bd87


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