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