Index: projects/mesh11s/sys/net80211/ieee80211_mesh.c =================================================================== --- projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 191726) +++ projects/mesh11s/sys/net80211/ieee80211_mesh.c (revision 191727) @@ -1,815 +1,816 @@ /*- * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting * Copyright (c) 2009 The FreeBSD Foundation * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifdef __FreeBSD__ __FBSDID("$FreeBSD$"); #endif /* * IEEE 802.11s Mesh Point (MBSS) support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void mesh_vattach(struct ieee80211vap *); static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int, uint32_t); static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, int, int, uint32_t); static void mesh_recv_action(struct ieee80211_node *, struct mbuf *); void ieee80211_mesh_attach(struct ieee80211com *ic) { ic->ic_vattach[IEEE80211_M_MBSS] = mesh_vattach; } void ieee80211_mesh_detach(struct ieee80211com *ic) { } static void mesh_vdetach(struct ieee80211vap *vap) { } static void mesh_vattach(struct ieee80211vap *vap) { vap->iv_newstate = mesh_newstate; vap->iv_input = mesh_input; vap->iv_opdetach = mesh_vdetach; vap->iv_recv_mgmt = mesh_recv_mgmt; } /* * IEEE80211_M_MBSS vap state machine handler. */ static int mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; enum ieee80211_state ostate; IEEE80211_LOCK_ASSERT(ic); ostate = vap->iv_state; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); vap->iv_state = nstate; /* state transition */ if (ostate != IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); /* background scan */ ni = vap->iv_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ ieee80211_reset_bss(vap); } break; case IEEE80211_S_SCAN: switch (ostate) { case IEEE80211_S_INIT: if (vap->iv_des_chan != IEEE80211_CHAN_ANYC && !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { /* * Already have a channel; bypass the * scan and startup immediately. */ ieee80211_create_mbss(vap, vap->iv_des_chan); break; } /* * Initiate a scan. We can come here as a result * of an IEEE80211_IOC_SCAN_REQ too in which case * the vap will be marked with IEEE80211_FEXT_SCANREQ * and the scan request parameters will be present * in iv_scanreq. Otherwise we do the default. */ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) { ieee80211_check_scan(vap, vap->iv_scanreq_flags, vap->iv_scanreq_duration, vap->iv_scanreq_mindwell, vap->iv_scanreq_maxdwell, vap->iv_scanreq_nssid, vap->iv_scanreq_ssid); vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ; } else ieee80211_check_scan_current(vap); break; default: break; } case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: case IEEE80211_S_CAC: case IEEE80211_S_RUN: case IEEE80211_S_CSA: case IEEE80211_S_SLEEP: default: break; } return 0; } /* * Decide if a received management frame should be * printed when debugging is enabled. This filters some * of the less interesting frames that come frequently * (e.g. beacons). */ static __inline int doprint(struct ieee80211vap *vap, int subtype) { switch (subtype) { case IEEE80211_FC0_SUBTYPE_BEACON: return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN); case IEEE80211_FC0_SUBTYPE_PROBE_REQ: return 1; } return 1; } static int mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int noise, uint32_t rstamp) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; struct ieee80211_frame *wh; int need_tap; uint8_t dir, type, subtype; KASSERT(ni != NULL, ("null node")); ni->ni_inact = ni->ni_inact_reload; need_tap = 1; /* mbuf need to be tapped. */ type = -1; /* undefined */ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "too short (1): len %u", m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; goto out; } /* * Bit of a cheat here, we use a pointer for a 3-address * frame format but don't reference fields past outside * ieee80211_frame_min w/o first validating the data is * present. */ wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]); vap->iv_stats.is_rx_badversion++; goto err; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { } switch (type) { case IEEE80211_FC0_TYPE_DATA: case IEEE80211_FC0_TYPE_MGT: vap->iv_stats.is_rx_mgmt++; IEEE80211_NODE_STAT(ni, rx_mgmt); if (dir != IEEE80211_FC1_DIR_NODS) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, "data", "incorrect dir 0x%x", dir); vap->iv_stats.is_rx_wrongdir++; goto err; } if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "mgt", "too short: len %u", m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; goto out; } #ifdef IEEE80211_DEBUG if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) || ieee80211_msg_dumppkts(vap)) { if_printf(ifp, "received %s from %s rssi %d\n", ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], ether_sprintf(wh->i_addr2), rssi); } #endif if (wh->i_fc[1] & IEEE80211_FC1_WEP) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "WEP set but not permitted"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } if (bpf_peers_present(vap->iv_rawbpf)) bpf_mtap(vap->iv_rawbpf, m); vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp); m_freem(m); return IEEE80211_FC0_TYPE_MGT; case IEEE80211_FC0_TYPE_CTL: vap->iv_stats.is_rx_ctl++; IEEE80211_NODE_STAT(ni, rx_ctrl); goto out; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "bad", "frame type 0x%x", type); /* should not come here */ break; } err: ifp->if_ierrors++; out: if (m != NULL) { if (bpf_peers_present(vap->iv_rawbpf) && need_tap) bpf_mtap(vap->iv_rawbpf, m); m_freem(m); } return type; } static int is11bclient(const uint8_t *rates, const uint8_t *xrates) { static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11); int i; /* NB: the 11b clients we care about will not have xrates */ if (xrates != NULL || rates == NULL) return 0; for (i = 0; i < rates[1]; i++) { int r = rates[2+i] & IEEE80211_RATE_VAL; if (r > 2*11 || ((1<ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; uint8_t *frm, *efrm; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; efrm = mtod(m0, uint8_t *) + m0->m_len; switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { struct ieee80211_scanparams scan; /* * We process beacon/probe response * frames to discover neighbors. */ if (ieee80211_parse_beacon(ni, m0, &scan) != 0) return; /* * Count frame now that we know it's to be processed. */ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { vap->iv_stats.is_rx_beacon++; /* XXX remove */ IEEE80211_NODE_STAT(ni, rx_beacons); } else IEEE80211_NODE_STAT(ni, rx_proberesp); /* * If scanning, just pass information to the scan module. */ if (ic->ic_flags & IEEE80211_F_SCAN) { if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) { /* * Actively scanning a channel marked passive; * send a probe request now that we know there * is 802.11 traffic present. * * XXX check if the beacon we recv'd gives * us what we need and suppress the probe req */ ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } ieee80211_add_scan(vap, &scan, wh, subtype, rssi, noise, rstamp); return; } /* The rest of this code assumes we setup and running */ if (vap->iv_state != IEEE80211_S_RUN) return; /* * Ignore non-mesh STAs and STAs for other mesh networks. */ if (scan.meshid && memcmp(scan.meshid+2, vap->iv_meshid, vap->iv_meshidlen) != 0) { IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, "beacon not for our mesh (%s)", scan.meshid+2); return; } /* * More validation: make sure we are talking to a Mesh node. */ if ((scan.capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) == 0 && !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr) && IEEE80211_ADDR_EQ(wh->i_addr3, zerobssid)) { /* * Create a new entry in the neighbor table. */ ni = ieee80211_add_neighbor(vap, wh, &scan); } else { /* * Record tsf for potential resync. */ memcpy(ni->ni_tstamp.data, scan.tstamp, sizeof(ni->ni_tstamp)); } if (ni != NULL) { IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = noise; ni->ni_rstamp = rstamp; } /* * If it's a beacon for our mesh and we haven't already * peered with this node, send him a mgmt frame with * peer link IE. */ /* XXX */ break; } case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { uint8_t *ssid, *meshid, *rates, *xrates; uint8_t *sfrm; if (vap->iv_state != IEEE80211_S_RUN) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "wrong state %s", ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_rx_mgtdiscard++; return; } if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { /* frame must be directed */ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not unicast"); vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return; } /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates * [tlv] Mesh ID */ ssid = meshid = rates = xrates = NULL; sfrm = frm; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; case IEEE80211_ELEMID_MESHID: meshid = frm; break; } frm += frm[2] + 2; } IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); if (xrates != NULL) IEEE80211_VERIFY_ELEMENT(xrates, IEEE80211_RATE_MAXSIZE - rates[1], return); if (meshid != NULL) IEEE80211_VERIFY_ELEMENT(meshid, IEEE80211_MESHID_LEN, return); IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return); /* XXX add verify meshid ? */ /* XXX find a better class or define it's own */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, "%s", "recv probe req"); /* * Some legacy 11b clients cannot hack a complete * probe response frame. When the request includes * only a bare-bones rate set, communicate this to * the transmit side. */ ieee80211_send_proberesp(vap, wh->i_addr2, is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); break; } case IEEE80211_FC0_SUBTYPE_ACTION: if (vap->iv_state != IEEE80211_S_RUN) { vap->iv_stats.is_rx_mgtdiscard++; break; } /* XXX parse_action is a bit useless now */ if (ieee80211_parse_action(ni, m0) == 0) { mesh_recv_action(ni, m0); ic->ic_recv_action(ni, frm, efrm); } break; case IEEE80211_FC0_SUBTYPE_AUTH: case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: case IEEE80211_FC0_SUBTYPE_DEAUTH: case IEEE80211_FC0_SUBTYPE_DISASSOC: IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; return; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } } static void mesh_recv_action(struct ieee80211_node *ni, struct mbuf *m0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_action *ia; struct ieee80211_frame *wh; struct ieee80211_meshid_ie *meshid; struct ieee80211_meshconf_ie *meshconf; struct ieee80211_meshpeer_ie *meshpeer; uint8_t *frm, *efrm; uint16_t args[4]; wh = mtod(m0, struct ieee80211_frame *); ia = (struct ieee80211_action *) &wh[1]; frm = (uint8_t *)&wh[1]; frm += sizeof(ia); efrm = mtod(m0, uint8_t *) + m0->m_len; /* XXX explain frame format */ meshid = NULL; meshpeer = NULL; meshconf = NULL; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { case IEEE80211_ELEMID_MESHID: meshid = (struct ieee80211_meshid_ie *) frm; break; case IEEE80211_ELEMID_MESHCONF: meshconf = (struct ieee80211_meshconf_ie *) frm; break; case IEEE80211_ELEMID_MESHPEER: meshpeer = (struct ieee80211_meshpeer_ie *) frm; break; } frm += frm[1] + 2; } /* * Check if we agree on Mesh ID and Configuration. * XXX: TBD */ if (!meshid || !meshconf || !meshpeer) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "action frame not for our mesh"); vap->iv_stats.is_rx_mgtdiscard++; return; } switch (ia->ia_category) { case IEEE80211_ACTION_CAT_MESHPEERING: switch (ia->ia_action) { case IEEE80211_ACTION_MESHPEERING_OPEN: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, "%s", "recv peering open"); ni->ni_peerstate = IEEE80211_NODE_MESH_OPENRCV; ni->ni_llid = meshpeer->peer_llinkid; ni->ni_plid = 0xf4ef; args[0] = ni->ni_plid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_OPEN, args); break; case IEEE80211_ACTION_MESHPEERING_CONFIRM: IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, ni, "%s", "recv peering confirm"); if (ni->ni_peerstate != IEEE80211_NODE_MESH_OPNSENT && ni->ni_peerstate != IEEE80211_NODE_MESH_OPENRCV) { IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "received confirm in invalid " "state %d", ni->ni_peerstate); vap->iv_stats.is_rx_mgtdiscard++; return; } ni->ni_peerstate = IEEE80211_NODE_MESH_CONFIRMRECV; args[0] = 0xf4ef; args[1] = ni->ni_llid; ieee80211_send_action(ni, IEEE80211_ACTION_CAT_MESHPEERING, IEEE80211_ACTION_MESHPEERING_CONFIRM, args); } break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_MESH, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; } } /* * Parse a MESH ID ie on station join. */ void ieee80211_parse_meshid(struct ieee80211_node *ni, const uint8_t *ie) { struct ieee80211vap *vap = ni->ni_vap; if (vap->iv_caps & IEEE80211_C_MBSS) { /*const struct ieee80211_meshid_ie *meshid = (const struct ieee80211_meshid_ie *)ie;*/ /* * Mesh STAs are QoS stations, so QoS is not optional. */ ni->ni_flags |= IEEE80211_NODE_QOS; } } /* * Add a MESH ID element to a frame. */ uint8_t * ieee80211_add_meshid(uint8_t *frm, struct ieee80211vap *vap) { KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); *frm++ = IEEE80211_ELEMID_MESHID; *frm++ = vap->iv_meshidlen; memcpy(frm, vap->iv_meshid, vap->iv_meshidlen); return frm + vap->iv_meshidlen; } /* * Add a Mesh Configuration element to a frame. * For now just use HWMP routing, Airtime link metric, Null Congestion * Signaling, Null Sync Protocol and Null Authentication. */ uint8_t * ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) { static const struct ieee80211_meshconf_ie ie = { .conf_ie = IEEE80211_ELEMID_MESHCONF, .conf_len = sizeof(struct ieee80211_meshconf_ie) - 2, .conf_ver = IEEE80211_MESHCONF_VERSION, .conf_pselid = IEEE80211_MESHCONF_HWMP, .conf_pmetid = IEEE80211_MESHCONF_AIRTIME, .conf_ccid = IEEE80211_MESHCONF_CCSIG, .conf_syncid = { 0, 0, 0, 0 }, /* XXX */ .conf_authid = { 0x80, 0, 0, 0 }, /* XXX */ .conf_form = 0, /* XXX */ .conf_cap = 1, /* XXX */ }; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a mbss vap")); memcpy(frm, &ie, sizeof(ie)); return frm + sizeof(ie); } uint8_t * ieee80211_add_meshpeer(uint8_t *frm, uint8_t subtype, uint16_t localid, uint16_t peerid, uint16_t reason) { #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) *frm++ = IEEE80211_ELEMID_MESHPEER; switch (subtype) { case IEEE80211_MESH_PEER_LINK_OPEN: *frm++ = 3; /* length */ *frm++ = subtype; ADDSHORT(frm, localid); /* local ID */ break; case IEEE80211_MESH_PEER_LINK_CONFIRM: *frm++ = 5; /* length */ + *frm++ = subtype; ADDSHORT(frm, localid); /* local ID */ ADDSHORT(frm, peerid); /* peer ID */ break; case IEEE80211_MESH_PEER_LINK_CLOSE: if (peerid) *frm++ = 7; /* length */ else *frm++ = 5; /* length */ ADDSHORT(frm, localid); /* local ID */ if (peerid) ADDSHORT(frm, peerid); /* peer ID */ ADDSHORT(frm, reason); break; } return frm; #undef ADDSHORT } void ieee80211_create_mbss(struct ieee80211vap *vap, struct ieee80211_channel *chan) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, "%s: creating mbss on channel %u\n", __func__, ieee80211_chan2ieee(ic, chan)); ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); if (ni == NULL) { /* XXX recovery? */ return; } IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); ni->ni_meshidlen = vap->iv_meshidlen; memcpy(ni->ni_meshid, vap->iv_meshid, ni->ni_meshidlen); ni->ni_intval = ic->ic_bintval; /* * Fix the channel and related attributes. */ /* clear DFS CAC state on previous channel */ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && ic->ic_bsschan->ic_freq != chan->ic_freq && IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); ic->ic_bsschan = chan; ieee80211_node_set_chan(ni, chan); ic->ic_curmode = ieee80211_chan2mode(chan); /* * Do mode-specific setup. */ if (IEEE80211_IS_CHAN_FULL(chan)) { if (IEEE80211_IS_CHAN_ANYG(chan)) { /* * Use a mixed 11b/11g basic rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11G); if (vap->iv_flags & IEEE80211_F_PUREG) { /* * Also mark OFDM rates basic so 11b * stations do not join (WiFi compliance). */ ieee80211_addbasicrates(&ni->ni_rates, IEEE80211_MODE_11A); } } else if (IEEE80211_IS_CHAN_B(chan)) { /* * Force pure 11b rate set. */ ieee80211_setbasicrates(&ni->ni_rates, IEEE80211_MODE_11B); } } ieee80211_ref_node(ni); vap->iv_bss = ni; ieee80211_setcurchan(ic, ni->ni_chan); /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ ieee80211_reset_erp(ic); ieee80211_wme_initparams(vap); ieee80211_new_state(vap, IEEE80211_S_RUN, -1); } static int mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (vap->iv_opmode != IEEE80211_M_MBSS) return EINVAL; ireq->i_len = vap->iv_meshidlen; memcpy(tmpmeshid, vap->iv_meshid, ireq->i_len); error = copyout(tmpmeshid, ireq->i_data, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_GET(mesh, mesh_ioctl_get80211); static int mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) { int error; uint8_t tmpmeshid[IEEE80211_NWID_LEN]; error = 0; switch (ireq->i_type) { case IEEE80211_IOC_MESH_ID: if (ireq->i_val != 0 || ireq->i_len > IEEE80211_NWID_LEN) return EINVAL; error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); if (error) break; memset(vap->iv_meshid, 0, IEEE80211_NWID_LEN); vap->iv_meshidlen = ireq->i_len; memcpy(vap->iv_meshid, tmpmeshid, ireq->i_len); break; default: return ENOSYS; } return error; } IEEE80211_IOCTL_SET(mesh, mesh_ioctl_set80211);