| Commit | Line | Data |
|---|---|---|
| 32176cfd | 1 | /*- |
| f186073c | 2 | * Copyright (c) 2001 Atsushi Onoe |
| 32176cfd | 3 | * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting |
| f186073c JS |
4 | * All rights reserved. |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| f186073c JS |
14 | * |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 | * | |
| 4028af95 | 26 | * $FreeBSD: head/sys/net80211/ieee80211_node.c 206358 2010-04-07 15:29:13Z rpaulo $ |
| 32176cfd | 27 | * $DragonFly$ |
| f186073c JS |
28 | */ |
| 29 | ||
| 32176cfd RP |
30 | #include "opt_wlan.h" |
| 31 | ||
| f186073c JS |
32 | #include <sys/param.h> |
| 33 | #include <sys/systm.h> | |
| 34 | #include <sys/mbuf.h> | |
| 35 | #include <sys/malloc.h> | |
| 36 | #include <sys/kernel.h> | |
| 841ab66c | 37 | |
| f186073c | 38 | #include <sys/socket.h> |
| f186073c JS |
39 | |
| 40 | #include <net/if.h> | |
| 841ab66c | 41 | #include <net/if_media.h> |
| f186073c | 42 | #include <net/ethernet.h> |
| 32176cfd | 43 | #include <net/route.h> |
| f186073c JS |
44 | |
| 45 | #include <netproto/802_11/ieee80211_var.h> | |
| 32176cfd RP |
46 | #include <netproto/802_11/ieee80211_input.h> |
| 47 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 48 | #include <netproto/802_11/ieee80211_superg.h> | |
| 49 | #endif | |
| 50 | #ifdef IEEE80211_SUPPORT_TDMA | |
| 51 | #include <netproto/802_11/ieee80211_tdma.h> | |
| 52 | #endif | |
| 53 | #include <netproto/802_11/ieee80211_wds.h> | |
| 54 | #include <netproto/802_11/ieee80211_mesh.h> | |
| 4028af95 | 55 | #include <netproto/802_11/ieee80211_ratectl.h> |
| f186073c JS |
56 | |
| 57 | #include <net/bpf.h> | |
| 58 | ||
| 841ab66c | 59 | /* |
| 32176cfd RP |
60 | * IEEE80211_NODE_HASHSIZE must be a power of 2. |
| 61 | */ | |
| 62 | CTASSERT((IEEE80211_NODE_HASHSIZE & (IEEE80211_NODE_HASHSIZE-1)) == 0); | |
| 63 | ||
| 64 | /* | |
| 841ab66c SZ |
65 | * Association id's are managed with a bit vector. |
| 66 | */ | |
| 32176cfd RP |
67 | #define IEEE80211_AID_SET(_vap, b) \ |
| 68 | ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \ | |
| 69 | (1 << (IEEE80211_AID(b) % 32))) | |
| 70 | #define IEEE80211_AID_CLR(_vap, b) \ | |
| 71 | ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \ | |
| 72 | ~(1 << (IEEE80211_AID(b) % 32))) | |
| 73 | #define IEEE80211_AID_ISSET(_vap, b) \ | |
| 74 | ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32))) | |
| 841ab66c | 75 | |
| 5d004897 SZ |
76 | #ifdef IEEE80211_DEBUG_REFCNT |
| 77 | #define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line | |
| 78 | #else | |
| 79 | #define REFCNT_LOC "%s %p<%s> refcnt %d\n", __func__ | |
| 80 | #endif | |
| 81 | ||
| 32176cfd RP |
82 | static int ieee80211_sta_join1(struct ieee80211_node *); |
| 83 | ||
| 84 | static struct ieee80211_node *node_alloc(struct ieee80211vap *, | |
| 85 | const uint8_t [IEEE80211_ADDR_LEN]); | |
| 841ab66c SZ |
86 | static void node_cleanup(struct ieee80211_node *); |
| 87 | static void node_free(struct ieee80211_node *); | |
| 32176cfd RP |
88 | static void node_age(struct ieee80211_node *); |
| 89 | static int8_t node_getrssi(const struct ieee80211_node *); | |
| 90 | static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); | |
| 91 | static void node_getmimoinfo(const struct ieee80211_node *, | |
| 92 | struct ieee80211_mimo_info *); | |
| f186073c | 93 | |
| 841ab66c | 94 | static void _ieee80211_free_node(struct ieee80211_node *); |
| 841ab66c SZ |
95 | |
| 96 | static void ieee80211_node_table_init(struct ieee80211com *ic, | |
| 97 | struct ieee80211_node_table *nt, const char *name, | |
| 32176cfd RP |
98 | int inact, int keymaxix); |
| 99 | static void ieee80211_node_table_reset(struct ieee80211_node_table *, | |
| 100 | struct ieee80211vap *); | |
| 841ab66c | 101 | static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt); |
| 32176cfd | 102 | static void ieee80211_erp_timeout(struct ieee80211com *); |
| f186073c JS |
103 | |
| 104 | MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); | |
| 32176cfd | 105 | MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie"); |
| f186073c JS |
106 | |
| 107 | void | |
| 841ab66c | 108 | ieee80211_node_attach(struct ieee80211com *ic) |
| f186073c | 109 | { |
| 32176cfd RP |
110 | /* XXX really want maxlen enforced per-sta */ |
| 111 | ieee80211_ageq_init(&ic->ic_stageq, ic->ic_max_keyix * 8, | |
| 112 | "802.11 staging q"); | |
| 113 | ieee80211_node_table_init(ic, &ic->ic_sta, "station", | |
| 114 | IEEE80211_INACT_INIT, ic->ic_max_keyix); | |
| 34a60cf6 | 115 | callout_init_mp(&ic->ic_inact); |
| 32176cfd RP |
116 | callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, |
| 117 | ieee80211_node_timeout, ic); | |
| 118 | ||
| 841ab66c SZ |
119 | ic->ic_node_alloc = node_alloc; |
| 120 | ic->ic_node_free = node_free; | |
| 121 | ic->ic_node_cleanup = node_cleanup; | |
| 32176cfd RP |
122 | ic->ic_node_age = node_age; |
| 123 | ic->ic_node_drain = node_age; /* NB: same as age */ | |
| 841ab66c | 124 | ic->ic_node_getrssi = node_getrssi; |
| 32176cfd RP |
125 | ic->ic_node_getsignal = node_getsignal; |
| 126 | ic->ic_node_getmimoinfo = node_getmimoinfo; | |
| f186073c | 127 | |
| 32176cfd RP |
128 | /* |
| 129 | * Set flags to be propagated to all vap's; | |
| 130 | * these define default behaviour/configuration. | |
| 131 | */ | |
| 132 | ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */ | |
| f186073c JS |
133 | } |
| 134 | ||
| 135 | void | |
| 32176cfd | 136 | ieee80211_node_detach(struct ieee80211com *ic) |
| f186073c | 137 | { |
| f186073c | 138 | |
| dfcf81fd | 139 | callout_stop(&ic->ic_inact); |
| 32176cfd RP |
140 | ieee80211_node_table_cleanup(&ic->ic_sta); |
| 141 | ieee80211_ageq_cleanup(&ic->ic_stageq); | |
| 142 | } | |
| 841ab66c | 143 | |
| 32176cfd RP |
144 | void |
| 145 | ieee80211_node_vattach(struct ieee80211vap *vap) | |
| 146 | { | |
| 147 | /* NB: driver can override */ | |
| 148 | vap->iv_max_aid = IEEE80211_AID_DEF; | |
| 841ab66c | 149 | |
| 32176cfd RP |
150 | /* default station inactivity timer setings */ |
| 151 | vap->iv_inact_init = IEEE80211_INACT_INIT; | |
| 152 | vap->iv_inact_auth = IEEE80211_INACT_AUTH; | |
| 153 | vap->iv_inact_run = IEEE80211_INACT_RUN; | |
| 154 | vap->iv_inact_probe = IEEE80211_INACT_PROBE; | |
| 841ab66c | 155 | |
| 32176cfd RP |
156 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_INACT, |
| 157 | "%s: init %u auth %u run %u probe %u\n", __func__, | |
| 158 | vap->iv_inact_init, vap->iv_inact_auth, | |
| 159 | vap->iv_inact_run, vap->iv_inact_probe); | |
| 160 | } | |
| 841ab66c | 161 | |
| 32176cfd RP |
162 | void |
| 163 | ieee80211_node_latevattach(struct ieee80211vap *vap) | |
| 164 | { | |
| 165 | if (vap->iv_opmode == IEEE80211_M_HOSTAP) { | |
| 166 | /* XXX should we allow max aid to be zero? */ | |
| 167 | if (vap->iv_max_aid < IEEE80211_AID_MIN) { | |
| 168 | vap->iv_max_aid = IEEE80211_AID_MIN; | |
| 169 | if_printf(vap->iv_ifp, | |
| 170 | "WARNING: max aid too small, changed to %d\n", | |
| 171 | vap->iv_max_aid); | |
| 172 | } | |
| 173 | vap->iv_aid_bitmap = (uint32_t *) kmalloc( | |
| 174 | howmany(vap->iv_max_aid, 32) * sizeof(uint32_t), | |
| fcaa651d | 175 | M_80211_NODE, M_INTWAIT | M_ZERO); |
| 32176cfd RP |
176 | if (vap->iv_aid_bitmap == NULL) { |
| 177 | /* XXX no way to recover */ | |
| 178 | kprintf("%s: no memory for AID bitmap, max aid %d!\n", | |
| 179 | __func__, vap->iv_max_aid); | |
| 180 | vap->iv_max_aid = 0; | |
| 181 | } | |
| 182 | } | |
| 841ab66c | 183 | |
| 32176cfd | 184 | ieee80211_reset_bss(vap); |
| 841ab66c | 185 | |
| 32176cfd | 186 | vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode); |
| 841ab66c SZ |
187 | } |
| 188 | ||
| 189 | void | |
| 32176cfd | 190 | ieee80211_node_vdetach(struct ieee80211vap *vap) |
| 841ab66c | 191 | { |
| 32176cfd RP |
192 | struct ieee80211com *ic = vap->iv_ic; |
| 193 | ||
| 194 | ieee80211_node_table_reset(&ic->ic_sta, vap); | |
| 195 | if (vap->iv_bss != NULL) { | |
| 196 | ieee80211_free_node(vap->iv_bss); | |
| 197 | vap->iv_bss = NULL; | |
| 841ab66c | 198 | } |
| 32176cfd RP |
199 | if (vap->iv_aid_bitmap != NULL) { |
| 200 | kfree(vap->iv_aid_bitmap, M_80211_NODE); | |
| 201 | vap->iv_aid_bitmap = NULL; | |
| 841ab66c SZ |
202 | } |
| 203 | } | |
| 204 | ||
| 205 | /* | |
| 206 | * Port authorize/unauthorize interfaces for use by an authenticator. | |
| 207 | */ | |
| 208 | ||
| 209 | void | |
| 210 | ieee80211_node_authorize(struct ieee80211_node *ni) | |
| 211 | { | |
| 32176cfd | 212 | struct ieee80211vap *vap = ni->ni_vap; |
| 841ab66c SZ |
213 | |
| 214 | ni->ni_flags |= IEEE80211_NODE_AUTH; | |
| 32176cfd RP |
215 | ni->ni_inact_reload = vap->iv_inact_run; |
| 216 | ni->ni_inact = ni->ni_inact_reload; | |
| 217 | ||
| 218 | IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, | |
| 219 | "%s: inact_reload %u", __func__, ni->ni_inact_reload); | |
| f186073c JS |
220 | } |
| 221 | ||
| 222 | void | |
| 841ab66c | 223 | ieee80211_node_unauthorize(struct ieee80211_node *ni) |
| f186073c | 224 | { |
| 32176cfd | 225 | struct ieee80211vap *vap = ni->ni_vap; |
| f186073c | 226 | |
| 32176cfd RP |
227 | ni->ni_flags &= ~IEEE80211_NODE_AUTH; |
| 228 | ni->ni_inact_reload = vap->iv_inact_auth; | |
| 229 | if (ni->ni_inact > ni->ni_inact_reload) | |
| 230 | ni->ni_inact = ni->ni_inact_reload; | |
| 841ab66c | 231 | |
| 32176cfd RP |
232 | IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, |
| 233 | "%s: inact_reload %u inact %u", __func__, | |
| 234 | ni->ni_inact_reload, ni->ni_inact); | |
| 841ab66c | 235 | } |
| 841ab66c | 236 | |
| f186073c | 237 | /* |
| 32176cfd | 238 | * Fix tx parameters for a node according to ``association state''. |
| f186073c | 239 | */ |
| 32176cfd RP |
240 | void |
| 241 | ieee80211_node_setuptxparms(struct ieee80211_node *ni) | |
| f186073c | 242 | { |
| 32176cfd RP |
243 | struct ieee80211vap *vap = ni->ni_vap; |
| 244 | enum ieee80211_phymode mode; | |
| f186073c | 245 | |
| 32176cfd RP |
246 | if (ni->ni_flags & IEEE80211_NODE_HT) { |
| 247 | if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) | |
| 248 | mode = IEEE80211_MODE_11NA; | |
| 249 | else | |
| 250 | mode = IEEE80211_MODE_11NG; | |
| 251 | } else { /* legacy rate handling */ | |
| 252 | if (IEEE80211_IS_CHAN_ST(ni->ni_chan)) | |
| 253 | mode = IEEE80211_MODE_STURBO_A; | |
| 254 | else if (IEEE80211_IS_CHAN_HALF(ni->ni_chan)) | |
| 255 | mode = IEEE80211_MODE_HALF; | |
| 256 | else if (IEEE80211_IS_CHAN_QUARTER(ni->ni_chan)) | |
| 257 | mode = IEEE80211_MODE_QUARTER; | |
| 258 | /* NB: 108A should be handled as 11a */ | |
| 259 | else if (IEEE80211_IS_CHAN_A(ni->ni_chan)) | |
| 260 | mode = IEEE80211_MODE_11A; | |
| 261 | else if (IEEE80211_IS_CHAN_108G(ni->ni_chan) || | |
| 262 | (ni->ni_flags & IEEE80211_NODE_ERP)) | |
| 263 | mode = IEEE80211_MODE_11G; | |
| 264 | else | |
| 265 | mode = IEEE80211_MODE_11B; | |
| 841ab66c | 266 | } |
| 32176cfd | 267 | ni->ni_txparms = &vap->iv_txparms[mode]; |
| f186073c JS |
268 | } |
| 269 | ||
| 270 | /* | |
| 32176cfd RP |
271 | * Set/change the channel. The rate set is also updated as |
| 272 | * to insure a consistent view by drivers. | |
| 273 | * XXX should be private but hostap needs it to deal with CSA | |
| f186073c JS |
274 | */ |
| 275 | void | |
| 32176cfd RP |
276 | ieee80211_node_set_chan(struct ieee80211_node *ni, |
| 277 | struct ieee80211_channel *chan) | |
| f186073c | 278 | { |
| 32176cfd RP |
279 | struct ieee80211com *ic = ni->ni_ic; |
| 280 | struct ieee80211vap *vap = ni->ni_vap; | |
| 281 | enum ieee80211_phymode mode; | |
| 841ab66c | 282 | |
| 32176cfd | 283 | KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel")); |
| 841ab66c | 284 | |
| 32176cfd RP |
285 | ni->ni_chan = chan; |
| 286 | mode = ieee80211_chan2mode(chan); | |
| 287 | if (IEEE80211_IS_CHAN_HT(chan)) { | |
| 841ab66c | 288 | /* |
| 32176cfd RP |
289 | * XXX Gotta be careful here; the rate set returned by |
| 290 | * ieee80211_get_suprates is actually any HT rate | |
| 291 | * set so blindly copying it will be bad. We must | |
| 292 | * install the legacy rate est in ni_rates and the | |
| 293 | * HT rate set in ni_htrates. | |
| 841ab66c | 294 | */ |
| 32176cfd RP |
295 | ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan); |
| 296 | /* | |
| 297 | * Setup bss tx parameters based on operating mode. We | |
| 298 | * use legacy rates when operating in a mixed HT+non-HT bss | |
| 299 | * and non-ERP rates in 11g for mixed ERP+non-ERP bss. | |
| 300 | */ | |
| 301 | if (mode == IEEE80211_MODE_11NA && | |
| 302 | (vap->iv_flags_ht & IEEE80211_FHT_PUREN) == 0) | |
| 303 | mode = IEEE80211_MODE_11A; | |
| 304 | else if (mode == IEEE80211_MODE_11NG && | |
| 305 | (vap->iv_flags_ht & IEEE80211_FHT_PUREN) == 0) | |
| 306 | mode = IEEE80211_MODE_11G; | |
| 307 | if (mode == IEEE80211_MODE_11G && | |
| 308 | (vap->iv_flags & IEEE80211_F_PUREG) == 0) | |
| 309 | mode = IEEE80211_MODE_11B; | |
| 310 | } | |
| 311 | ni->ni_txparms = &vap->iv_txparms[mode]; | |
| 312 | ni->ni_rates = *ieee80211_get_suprates(ic, chan); | |
| 841ab66c SZ |
313 | } |
| 314 | ||
| 315 | static __inline void | |
| 316 | copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss) | |
| 317 | { | |
| 318 | /* propagate useful state */ | |
| 319 | nbss->ni_authmode = obss->ni_authmode; | |
| 320 | nbss->ni_txpower = obss->ni_txpower; | |
| 321 | nbss->ni_vlan = obss->ni_vlan; | |
| 841ab66c | 322 | /* XXX statistics? */ |
| 32176cfd | 323 | /* XXX legacy WDS bssid? */ |
| f186073c JS |
324 | } |
| 325 | ||
| 326 | void | |
| 32176cfd | 327 | ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan) |
| f186073c | 328 | { |
| 32176cfd | 329 | struct ieee80211com *ic = vap->iv_ic; |
| f186073c | 330 | struct ieee80211_node *ni; |
| f186073c | 331 | |
| 32176cfd RP |
332 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| 333 | "%s: creating %s on channel %u\n", __func__, | |
| 334 | ieee80211_opmode_name[vap->iv_opmode], | |
| 335 | ieee80211_chan2ieee(ic, chan)); | |
| 841ab66c | 336 | |
| 32176cfd | 337 | ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); |
| 841ab66c SZ |
338 | if (ni == NULL) { |
| 339 | /* XXX recovery? */ | |
| 340 | return; | |
| 341 | } | |
| 32176cfd RP |
342 | IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); |
| 343 | ni->ni_esslen = vap->iv_des_ssid[0].len; | |
| 344 | memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); | |
| 345 | if (vap->iv_bss != NULL) | |
| 346 | copy_bss(ni, vap->iv_bss); | |
| 841ab66c | 347 | ni->ni_intval = ic->ic_bintval; |
| 32176cfd | 348 | if (vap->iv_flags & IEEE80211_F_PRIVACY) |
| f186073c JS |
349 | ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; |
| 350 | if (ic->ic_phytype == IEEE80211_T_FH) { | |
| 351 | ni->ni_fhdwell = 200; /* XXX */ | |
| 352 | ni->ni_fhindex = 1; | |
| 353 | } | |
| 32176cfd RP |
354 | if (vap->iv_opmode == IEEE80211_M_IBSS) { |
| 355 | vap->iv_flags |= IEEE80211_F_SIBSS; | |
| 841ab66c | 356 | ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */ |
| 32176cfd RP |
357 | if (vap->iv_flags & IEEE80211_F_DESBSSID) |
| 358 | IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); | |
| 359 | else { | |
| 215bf50c | 360 | get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN); |
| 32176cfd | 361 | /* clear group bit, add local bit */ |
| 215bf50c SZ |
362 | ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02; |
| 363 | } | |
| 32176cfd RP |
364 | } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) { |
| 365 | if (vap->iv_flags & IEEE80211_F_DESBSSID) | |
| 366 | IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid); | |
| 841ab66c | 367 | else |
| 32176cfd RP |
368 | #ifdef IEEE80211_SUPPORT_TDMA |
| 369 | if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) | |
| 370 | #endif | |
| 841ab66c | 371 | memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN); |
| 32176cfd RP |
372 | #ifdef IEEE80211_SUPPORT_MESH |
| 373 | } else if (vap->iv_opmode == IEEE80211_M_MBSS) { | |
| 374 | ni->ni_meshidlen = vap->iv_mesh->ms_idlen; | |
| 375 | memcpy(ni->ni_meshid, vap->iv_mesh->ms_id, ni->ni_meshidlen); | |
| 376 | #endif | |
| 841ab66c SZ |
377 | } |
| 378 | /* | |
| 379 | * Fix the channel and related attributes. | |
| 380 | */ | |
| 32176cfd RP |
381 | /* clear DFS CAC state on previous channel */ |
| 382 | if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && | |
| 383 | ic->ic_bsschan->ic_freq != chan->ic_freq && | |
| 384 | IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) | |
| 385 | ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); | |
| 386 | ic->ic_bsschan = chan; | |
| 387 | ieee80211_node_set_chan(ni, chan); | |
| 388 | ic->ic_curmode = ieee80211_chan2mode(chan); | |
| 841ab66c | 389 | /* |
| 32176cfd | 390 | * Do mode-specific setup. |
| 841ab66c | 391 | */ |
| 32176cfd RP |
392 | if (IEEE80211_IS_CHAN_FULL(chan)) { |
| 393 | if (IEEE80211_IS_CHAN_ANYG(chan)) { | |
| 394 | /* | |
| 395 | * Use a mixed 11b/11g basic rate set. | |
| 396 | */ | |
| 397 | ieee80211_setbasicrates(&ni->ni_rates, | |
| 398 | IEEE80211_MODE_11G); | |
| 399 | if (vap->iv_flags & IEEE80211_F_PUREG) { | |
| 400 | /* | |
| 401 | * Also mark OFDM rates basic so 11b | |
| 402 | * stations do not join (WiFi compliance). | |
| 403 | */ | |
| 404 | ieee80211_addbasicrates(&ni->ni_rates, | |
| 405 | IEEE80211_MODE_11A); | |
| 406 | } | |
| 407 | } else if (IEEE80211_IS_CHAN_B(chan)) { | |
| 408 | /* | |
| 409 | * Force pure 11b rate set. | |
| 410 | */ | |
| 411 | ieee80211_setbasicrates(&ni->ni_rates, | |
| 412 | IEEE80211_MODE_11B); | |
| 413 | } | |
| 414 | } | |
| 841ab66c | 415 | |
| 32176cfd | 416 | (void) ieee80211_sta_join1(ieee80211_ref_node(ni)); |
| 841ab66c SZ |
417 | } |
| 418 | ||
| 32176cfd RP |
419 | /* |
| 420 | * Reset bss state on transition to the INIT state. | |
| 421 | * Clear any stations from the table (they have been | |
| 422 | * deauth'd) and reset the bss node (clears key, rate | |
| 423 | * etc. state). | |
| 424 | */ | |
| 841ab66c | 425 | void |
| 32176cfd | 426 | ieee80211_reset_bss(struct ieee80211vap *vap) |
| 841ab66c | 427 | { |
| 32176cfd | 428 | struct ieee80211com *ic = vap->iv_ic; |
| 841ab66c SZ |
429 | struct ieee80211_node *ni, *obss; |
| 430 | ||
| 32176cfd RP |
431 | ieee80211_node_table_reset(&ic->ic_sta, vap); |
| 432 | /* XXX multi-bss: wrong */ | |
| 433 | ieee80211_reset_erp(ic); | |
| 841ab66c | 434 | |
| 32176cfd | 435 | ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); |
| 4d770dcf | 436 | KASSERT(ni != NULL, ("unable to setup initial BSS node")); |
| 32176cfd RP |
437 | obss = vap->iv_bss; |
| 438 | vap->iv_bss = ieee80211_ref_node(ni); | |
| 841ab66c SZ |
439 | if (obss != NULL) { |
| 440 | copy_bss(ni, obss); | |
| 441 | ni->ni_intval = ic->ic_bintval; | |
| 442 | ieee80211_free_node(obss); | |
| 32176cfd RP |
443 | } else |
| 444 | IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr); | |
| f186073c JS |
445 | } |
| 446 | ||
| 32176cfd RP |
447 | static int |
| 448 | match_ssid(const struct ieee80211_node *ni, | |
| 449 | int nssid, const struct ieee80211_scan_ssid ssids[]) | |
| 450 | { | |
| 451 | int i; | |
| 841ab66c | 452 | |
| 32176cfd RP |
453 | for (i = 0; i < nssid; i++) { |
| 454 | if (ni->ni_esslen == ssids[i].len && | |
| 455 | memcmp(ni->ni_essid, ssids[i].ssid, ni->ni_esslen) == 0) | |
| 456 | return 1; | |
| 457 | } | |
| 458 | return 0; | |
| 459 | } | |
| 460 | ||
| 461 | /* | |
| 462 | * Test a node for suitability/compatibility. | |
| 463 | */ | |
| f186073c | 464 | static int |
| 32176cfd | 465 | check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni) |
| f186073c | 466 | { |
| 32176cfd | 467 | struct ieee80211com *ic = ni->ni_ic; |
| f186073c | 468 | uint8_t rate; |
| f186073c | 469 | |
| 32176cfd RP |
470 | if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) |
| 471 | return 0; | |
| 472 | if (vap->iv_opmode == IEEE80211_M_IBSS) { | |
| 473 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) | |
| 474 | return 0; | |
| 475 | } else { | |
| 476 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) | |
| 477 | return 0; | |
| 478 | } | |
| 479 | if (vap->iv_flags & IEEE80211_F_PRIVACY) { | |
| 480 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) | |
| 481 | return 0; | |
| 482 | } else { | |
| 483 | /* XXX does this mean privacy is supported or required? */ | |
| 484 | if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) | |
| 485 | return 0; | |
| 486 | } | |
| 487 | rate = ieee80211_fix_rate(ni, &ni->ni_rates, | |
| 488 | IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); | |
| 489 | if (rate & IEEE80211_RATE_BASIC) | |
| 490 | return 0; | |
| 491 | if (vap->iv_des_nssid != 0 && | |
| 492 | !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) | |
| 493 | return 0; | |
| 494 | if ((vap->iv_flags & IEEE80211_F_DESBSSID) && | |
| 495 | !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) | |
| 496 | return 0; | |
| 497 | return 1; | |
| 498 | } | |
| 499 | ||
| 500 | #ifdef IEEE80211_DEBUG | |
| 501 | /* | |
| 502 | * Display node suitability/compatibility. | |
| 503 | */ | |
| 504 | static void | |
| 505 | check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni) | |
| 506 | { | |
| 507 | struct ieee80211com *ic = ni->ni_ic; | |
| 508 | uint8_t rate; | |
| 509 | int fail; | |
| 208a1285 | 510 | |
| f186073c JS |
511 | fail = 0; |
| 512 | if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) | |
| 513 | fail |= 0x01; | |
| 32176cfd | 514 | if (vap->iv_opmode == IEEE80211_M_IBSS) { |
| f186073c JS |
515 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) |
| 516 | fail |= 0x02; | |
| 517 | } else { | |
| 518 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) | |
| 519 | fail |= 0x02; | |
| 520 | } | |
| 32176cfd | 521 | if (vap->iv_flags & IEEE80211_F_PRIVACY) { |
| f186073c JS |
522 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) |
| 523 | fail |= 0x04; | |
| 524 | } else { | |
| 525 | /* XXX does this mean privacy is supported or required? */ | |
| 526 | if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) | |
| 527 | fail |= 0x04; | |
| 528 | } | |
| 32176cfd RP |
529 | rate = ieee80211_fix_rate(ni, &ni->ni_rates, |
| 530 | IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); | |
| f186073c JS |
531 | if (rate & IEEE80211_RATE_BASIC) |
| 532 | fail |= 0x08; | |
| 32176cfd RP |
533 | if (vap->iv_des_nssid != 0 && |
| 534 | !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid)) | |
| f186073c | 535 | fail |= 0x10; |
| 32176cfd RP |
536 | if ((vap->iv_flags & IEEE80211_F_DESBSSID) && |
| 537 | !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid)) | |
| f186073c | 538 | fail |= 0x20; |
| f186073c | 539 | |
| 6168f72e RP |
540 | kprintf(" %c %6D", fail ? '-' : '+', ni->ni_macaddr, ":"); |
| 541 | kprintf(" %6D%c", ni->ni_bssid, ":", fail & 0x20 ? '!' : ' '); | |
| 32176cfd RP |
542 | kprintf(" %3d%c", |
| 543 | ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); | |
| 544 | kprintf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, | |
| 545 | fail & 0x08 ? '!' : ' '); | |
| 546 | kprintf(" %4s%c", | |
| 547 | (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : | |
| 548 | (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : | |
| 549 | "????", | |
| 550 | fail & 0x02 ? '!' : ' '); | |
| 551 | kprintf(" %3s%c ", | |
| 552 | (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", | |
| 553 | fail & 0x04 ? '!' : ' '); | |
| 554 | ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); | |
| 555 | kprintf("%s\n", fail & 0x10 ? "!" : ""); | |
| 841ab66c | 556 | } |
| 32176cfd | 557 | #endif /* IEEE80211_DEBUG */ |
| 841ab66c SZ |
558 | |
| 559 | /* | |
| 560 | * Handle 802.11 ad hoc network merge. The | |
| 561 | * convention, set by the Wireless Ethernet Compatibility Alliance | |
| 562 | * (WECA), is that an 802.11 station will change its BSSID to match | |
| 563 | * the "oldest" 802.11 ad hoc network, on the same channel, that | |
| 564 | * has the station's desired SSID. The "oldest" 802.11 network | |
| 565 | * sends beacons with the greatest TSF timestamp. | |
| 566 | * | |
| 567 | * The caller is assumed to validate TSF's before attempting a merge. | |
| 568 | * | |
| 569 | * Return !0 if the BSSID changed, 0 otherwise. | |
| 570 | */ | |
| 571 | int | |
| 572 | ieee80211_ibss_merge(struct ieee80211_node *ni) | |
| 573 | { | |
| 32176cfd RP |
574 | struct ieee80211vap *vap = ni->ni_vap; |
| 575 | #ifdef IEEE80211_DEBUG | |
| 841ab66c | 576 | struct ieee80211com *ic = ni->ni_ic; |
| 32176cfd | 577 | #endif |
| 841ab66c | 578 | |
| 32176cfd RP |
579 | if (ni == vap->iv_bss || |
| 580 | IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) { | |
| 841ab66c SZ |
581 | /* unchanged, nothing to do */ |
| 582 | return 0; | |
| 583 | } | |
| 32176cfd RP |
584 | if (!check_bss(vap, ni)) { |
| 585 | /* capabilities mismatch */ | |
| 586 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, | |
| 841ab66c | 587 | "%s: merge failed, capabilities mismatch\n", __func__); |
| 32176cfd RP |
588 | #ifdef IEEE80211_DEBUG |
| 589 | if (ieee80211_msg_assoc(vap)) | |
| 590 | check_bss_debug(vap, ni); | |
| 591 | #endif | |
| 592 | vap->iv_stats.is_ibss_capmismatch++; | |
| 841ab66c SZ |
593 | return 0; |
| 594 | } | |
| 32176cfd | 595 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, |
| 6168f72e RP |
596 | "%s: new bssid %6D: %s preamble, %s slot time%s\n", __func__, |
| 597 | ni->ni_bssid, ":", | |
| 841ab66c SZ |
598 | ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", |
| 599 | ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", | |
| 600 | ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" | |
| 601 | ); | |
| 32176cfd | 602 | return ieee80211_sta_join1(ieee80211_ref_node(ni)); |
| 841ab66c SZ |
603 | } |
| 604 | ||
| 605 | /* | |
| 32176cfd RP |
606 | * Calculate HT channel promotion flags for all vaps. |
| 607 | * This assumes ni_chan have been setup for each vap. | |
| 841ab66c | 608 | */ |
| 32176cfd RP |
609 | static int |
| 610 | gethtadjustflags(struct ieee80211com *ic) | |
| 841ab66c | 611 | { |
| 32176cfd RP |
612 | struct ieee80211vap *vap; |
| 613 | int flags; | |
| 614 | ||
| 615 | flags = 0; | |
| 616 | /* XXX locking */ | |
| 617 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |
| 618 | if (vap->iv_state < IEEE80211_S_RUN) | |
| 619 | continue; | |
| 620 | switch (vap->iv_opmode) { | |
| 621 | case IEEE80211_M_WDS: | |
| 622 | case IEEE80211_M_STA: | |
| 623 | case IEEE80211_M_AHDEMO: | |
| 624 | case IEEE80211_M_HOSTAP: | |
| 625 | case IEEE80211_M_IBSS: | |
| 626 | case IEEE80211_M_MBSS: | |
| 627 | flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan); | |
| 628 | break; | |
| 629 | default: | |
| 630 | break; | |
| 631 | } | |
| 632 | } | |
| 633 | return flags; | |
| 634 | } | |
| 841ab66c | 635 | |
| 32176cfd RP |
636 | /* |
| 637 | * Check if the current channel needs to change based on whether | |
| 638 | * any vap's are using HT20/HT40. This is used to sync the state | |
| 639 | * of ic_curchan after a channel width change on a running vap. | |
| 640 | */ | |
| 641 | void | |
| 642 | ieee80211_sync_curchan(struct ieee80211com *ic) | |
| 643 | { | |
| 644 | struct ieee80211_channel *c; | |
| 645 | ||
| 646 | c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic)); | |
| 647 | if (c != ic->ic_curchan) { | |
| 648 | ic->ic_curchan = c; | |
| 649 | ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); | |
| 650 | ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | |
| 32176cfd RP |
651 | ic->ic_set_channel(ic); |
| 652 | ieee80211_radiotap_chan_change(ic); | |
| 32176cfd RP |
653 | } |
| 654 | } | |
| 841ab66c | 655 | |
| 32176cfd RP |
656 | /* |
| 657 | * Setup the current channel. The request channel may be | |
| 658 | * promoted if other vap's are operating with HT20/HT40. | |
| 659 | */ | |
| 660 | void | |
| 661 | ieee80211_setupcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) | |
| 662 | { | |
| 663 | if (ic->ic_htcaps & IEEE80211_HTC_HT) { | |
| 664 | int flags = gethtadjustflags(ic); | |
| f186073c | 665 | /* |
| 32176cfd RP |
666 | * Check for channel promotion required to support the |
| 667 | * set of running vap's. This assumes we are called | |
| 668 | * after ni_chan is setup for each vap. | |
| f186073c | 669 | */ |
| 32176cfd RP |
670 | /* NB: this assumes IEEE80211_FHT_USEHT40 > IEEE80211_FHT_HT */ |
| 671 | if (flags > ieee80211_htchanflags(c)) | |
| 672 | c = ieee80211_ht_adjust_channel(ic, c, flags); | |
| 673 | } | |
| 674 | ic->ic_bsschan = ic->ic_curchan = c; | |
| 675 | ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); | |
| 676 | ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | |
| 677 | } | |
| 208a1285 | 678 | |
| 32176cfd RP |
679 | /* |
| 680 | * Change the current channel. The channel change is guaranteed to have | |
| 681 | * happened before the next state change. | |
| 682 | */ | |
| 683 | void | |
| 684 | ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) | |
| 685 | { | |
| 686 | ieee80211_setupcurchan(ic, c); | |
| 687 | ieee80211_runtask(ic, &ic->ic_chan_task); | |
| 688 | } | |
| 208a1285 | 689 | |
| 32176cfd RP |
690 | /* |
| 691 | * Join the specified IBSS/BSS network. The node is assumed to | |
| 692 | * be passed in with a held reference. | |
| 693 | */ | |
| 694 | static int | |
| 695 | ieee80211_sta_join1(struct ieee80211_node *selbs) | |
| 696 | { | |
| 697 | struct ieee80211vap *vap = selbs->ni_vap; | |
| 698 | struct ieee80211com *ic = selbs->ni_ic; | |
| 699 | struct ieee80211_node *obss; | |
| 700 | int canreassoc; | |
| 841ab66c SZ |
701 | |
| 702 | /* | |
| 703 | * Committed to selbs, setup state. | |
| 704 | */ | |
| 32176cfd RP |
705 | obss = vap->iv_bss; |
| 706 | /* | |
| 707 | * Check if old+new node have the same address in which | |
| 708 | * case we can reassociate when operating in sta mode. | |
| 709 | */ | |
| 710 | canreassoc = (obss != NULL && | |
| 711 | vap->iv_state == IEEE80211_S_RUN && | |
| 712 | IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr)); | |
| 713 | vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */ | |
| 841ab66c SZ |
714 | if (obss != NULL) { |
| 715 | copy_bss(selbs, obss); | |
| 32176cfd RP |
716 | ieee80211_node_decref(obss); /* iv_bss reference */ |
| 717 | ieee80211_free_node(obss); /* station table reference */ | |
| 718 | obss = NULL; /* NB: guard against later use */ | |
| f186073c | 719 | } |
| 32176cfd RP |
720 | |
| 721 | /* | |
| 722 | * Delete unusable rates; we've already checked | |
| 723 | * that the negotiated rate set is acceptable. | |
| 724 | */ | |
| 725 | ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates, | |
| 726 | IEEE80211_F_DODEL | IEEE80211_F_JOIN); | |
| 727 | ||
| 728 | ieee80211_setcurchan(ic, selbs->ni_chan); | |
| 841ab66c SZ |
729 | /* |
| 730 | * Set the erp state (mostly the slot time) to deal with | |
| 731 | * the auto-select case; this should be redundant if the | |
| 732 | * mode is locked. | |
| 733 | */ | |
| 841ab66c | 734 | ieee80211_reset_erp(ic); |
| 32176cfd | 735 | ieee80211_wme_initparams(vap); |
| 841ab66c | 736 | |
| 32176cfd RP |
737 | if (vap->iv_opmode == IEEE80211_M_STA) { |
| 738 | if (canreassoc) { | |
| 739 | /* Reassociate */ | |
| 740 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1); | |
| 741 | } else { | |
| 742 | /* | |
| 743 | * Act as if we received a DEAUTH frame in case we | |
| 744 | * are invoked from the RUN state. This will cause | |
| 745 | * us to try to re-authenticate if we are operating | |
| 746 | * as a station. | |
| 747 | */ | |
| 748 | ieee80211_new_state(vap, IEEE80211_S_AUTH, | |
| 749 | IEEE80211_FC0_SUBTYPE_DEAUTH); | |
| 750 | } | |
| 751 | } else | |
| 752 | ieee80211_new_state(vap, IEEE80211_S_RUN, -1); | |
| 753 | return 1; | |
| 754 | } | |
| 755 | ||
| 756 | int | |
| 757 | ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, | |
| 758 | const struct ieee80211_scan_entry *se) | |
| 759 | { | |
| 760 | struct ieee80211com *ic = vap->iv_ic; | |
| 761 | struct ieee80211_node *ni; | |
| 762 | ||
| 763 | ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr); | |
| 764 | if (ni == NULL) { | |
| 765 | /* XXX msg */ | |
| 766 | return 0; | |
| 767 | } | |
| 208a1285 | 768 | /* |
| 32176cfd RP |
769 | * Expand scan state into node's format. |
| 770 | * XXX may not need all this stuff | |
| 208a1285 | 771 | */ |
| 32176cfd RP |
772 | IEEE80211_ADDR_COPY(ni->ni_bssid, se->se_bssid); |
| 773 | ni->ni_esslen = se->se_ssid[1]; | |
| 774 | memcpy(ni->ni_essid, se->se_ssid+2, ni->ni_esslen); | |
| 775 | ni->ni_tstamp.tsf = se->se_tstamp.tsf; | |
| 776 | ni->ni_intval = se->se_intval; | |
| 777 | ni->ni_capinfo = se->se_capinfo; | |
| 778 | ni->ni_chan = chan; | |
| 779 | ni->ni_timoff = se->se_timoff; | |
| 780 | ni->ni_fhdwell = se->se_fhdwell; | |
| 781 | ni->ni_fhindex = se->se_fhindex; | |
| 782 | ni->ni_erp = se->se_erp; | |
| 783 | IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi); | |
| 784 | ni->ni_noise = se->se_noise; | |
| 785 | if (vap->iv_opmode == IEEE80211_M_STA) { | |
| 786 | /* NB: only infrastructure mode requires an associd */ | |
| 787 | ni->ni_flags |= IEEE80211_NODE_ASSOCID; | |
| 208a1285 | 788 | } |
| 32176cfd RP |
789 | |
| 790 | if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) { | |
| 791 | ieee80211_ies_expand(&ni->ni_ies); | |
| 792 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 793 | if (ni->ni_ies.ath_ie != NULL) | |
| 794 | ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); | |
| 795 | #endif | |
| 796 | if (ni->ni_ies.htcap_ie != NULL) | |
| 797 | ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie); | |
| 798 | if (ni->ni_ies.htinfo_ie != NULL) | |
| 799 | ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie); | |
| 800 | #ifdef IEEE80211_SUPPORT_MESH | |
| 801 | if (ni->ni_ies.meshid_ie != NULL) | |
| 802 | ieee80211_parse_meshid(ni, ni->ni_ies.meshid_ie); | |
| 803 | #endif | |
| 804 | #ifdef IEEE80211_SUPPORT_TDMA | |
| 805 | if (ni->ni_ies.tdma_ie != NULL) | |
| 806 | ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie); | |
| 208a1285 | 807 | #endif |
| 32176cfd | 808 | } |
| 208a1285 | 809 | |
| 32176cfd RP |
810 | vap->iv_dtim_period = se->se_dtimperiod; |
| 811 | vap->iv_dtim_count = 0; | |
| 812 | ||
| 813 | /* NB: must be after ni_chan is setup */ | |
| 814 | ieee80211_setup_rates(ni, se->se_rates, se->se_xrates, | |
| 815 | IEEE80211_F_DOSORT); | |
| 816 | if (ieee80211_iserp_rateset(&ni->ni_rates)) | |
| 817 | ni->ni_flags |= IEEE80211_NODE_ERP; | |
| 818 | ieee80211_node_setuptxparms(ni); | |
| 819 | ||
| 820 | return ieee80211_sta_join1(ieee80211_ref_node(ni)); | |
| 841ab66c SZ |
821 | } |
| 822 | ||
| 823 | /* | |
| 824 | * Leave the specified IBSS/BSS network. The node is assumed to | |
| 825 | * be passed in with a held reference. | |
| 826 | */ | |
| 827 | void | |
| 32176cfd | 828 | ieee80211_sta_leave(struct ieee80211_node *ni) |
| 841ab66c | 829 | { |
| 32176cfd RP |
830 | struct ieee80211com *ic = ni->ni_ic; |
| 831 | ||
| 841ab66c | 832 | ic->ic_node_cleanup(ni); |
| 32176cfd RP |
833 | ieee80211_notify_node_leave(ni); |
| 834 | } | |
| 835 | ||
| 836 | /* | |
| 837 | * Send a deauthenticate frame and drop the station. | |
| 838 | */ | |
| 839 | void | |
| 840 | ieee80211_node_deauth(struct ieee80211_node *ni, int reason) | |
| 841 | { | |
| 842 | /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */ | |
| 843 | ieee80211_ref_node(ni); | |
| 844 | if (ni->ni_associd != 0) | |
| 845 | IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); | |
| 846 | ieee80211_node_leave(ni); | |
| 847 | ieee80211_free_node(ni); | |
| f186073c JS |
848 | } |
| 849 | ||
| 850 | static struct ieee80211_node * | |
| 32176cfd | 851 | node_alloc(struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) |
| f186073c JS |
852 | { |
| 853 | struct ieee80211_node *ni; | |
| 841ab66c | 854 | |
| 32176cfd | 855 | ni = (struct ieee80211_node *) kmalloc(sizeof(struct ieee80211_node), |
| fcaa651d | 856 | M_80211_NODE, M_INTWAIT | M_ZERO); |
| f186073c JS |
857 | return ni; |
| 858 | } | |
| 859 | ||
| 841ab66c | 860 | /* |
| 32176cfd RP |
861 | * Initialize an ie blob with the specified data. If previous |
| 862 | * data exists re-use the data block. As a side effect we clear | |
| 863 | * all references to specific ie's; the caller is required to | |
| 864 | * recalculate them. | |
| 865 | */ | |
| 866 | int | |
| 867 | ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len) | |
| 868 | { | |
| 869 | /* NB: assumes data+len are the last fields */ | |
| 870 | memset(ies, 0, offsetof(struct ieee80211_ies, data)); | |
| 871 | if (ies->data != NULL && ies->len != len) { | |
| 872 | /* data size changed */ | |
| 873 | kfree(ies->data, M_80211_NODE_IE); | |
| 874 | ies->data = NULL; | |
| 875 | } | |
| 876 | if (ies->data == NULL) { | |
| fcaa651d | 877 | ies->data = (uint8_t *) kmalloc(len, M_80211_NODE_IE, M_INTWAIT); |
| 32176cfd RP |
878 | if (ies->data == NULL) { |
| 879 | ies->len = 0; | |
| 880 | /* NB: pointers have already been zero'd above */ | |
| 881 | return 0; | |
| 882 | } | |
| 883 | } | |
| 884 | memcpy(ies->data, data, len); | |
| 885 | ies->len = len; | |
| 886 | return 1; | |
| 887 | } | |
| 888 | ||
| 889 | /* | |
| 890 | * Reclaim storage for an ie blob. | |
| 891 | */ | |
| 892 | void | |
| 893 | ieee80211_ies_cleanup(struct ieee80211_ies *ies) | |
| 894 | { | |
| 895 | if (ies->data != NULL) | |
| 896 | kfree(ies->data, M_80211_NODE_IE); | |
| 897 | } | |
| 898 | ||
| 899 | /* | |
| 900 | * Expand an ie blob data contents and to fillin individual | |
| 901 | * ie pointers. The data blob is assumed to be well-formed; | |
| 902 | * we don't do any validity checking of ie lengths. | |
| 903 | */ | |
| 904 | void | |
| 905 | ieee80211_ies_expand(struct ieee80211_ies *ies) | |
| 906 | { | |
| 907 | uint8_t *ie; | |
| 908 | int ielen; | |
| 909 | ||
| 910 | ie = ies->data; | |
| 911 | ielen = ies->len; | |
| 912 | while (ielen > 0) { | |
| 913 | switch (ie[0]) { | |
| 914 | case IEEE80211_ELEMID_VENDOR: | |
| 915 | if (iswpaoui(ie)) | |
| 916 | ies->wpa_ie = ie; | |
| 917 | else if (iswmeoui(ie)) | |
| 918 | ies->wme_ie = ie; | |
| 919 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 920 | else if (isatherosoui(ie)) | |
| 921 | ies->ath_ie = ie; | |
| 922 | #endif | |
| 923 | #ifdef IEEE80211_SUPPORT_TDMA | |
| 924 | else if (istdmaoui(ie)) | |
| 925 | ies->tdma_ie = ie; | |
| 926 | #endif | |
| 927 | break; | |
| 928 | case IEEE80211_ELEMID_RSN: | |
| 929 | ies->rsn_ie = ie; | |
| 930 | break; | |
| 931 | case IEEE80211_ELEMID_HTCAP: | |
| 932 | ies->htcap_ie = ie; | |
| 933 | break; | |
| 934 | #ifdef IEEE80211_SUPPORT_MESH | |
| 935 | case IEEE80211_ELEMID_MESHID: | |
| 936 | ies->meshid_ie = ie; | |
| 937 | break; | |
| 938 | #endif | |
| 939 | } | |
| 940 | ielen -= 2 + ie[1]; | |
| 941 | ie += 2 + ie[1]; | |
| 942 | } | |
| 943 | } | |
| 944 | ||
| 945 | /* | |
| 841ab66c SZ |
946 | * Reclaim any resources in a node and reset any critical |
| 947 | * state. Typically nodes are free'd immediately after, | |
| 948 | * but in some cases the storage may be reused so we need | |
| 949 | * to insure consistent state (should probably fix that). | |
| 950 | */ | |
| f186073c | 951 | static void |
| 841ab66c | 952 | node_cleanup(struct ieee80211_node *ni) |
| f186073c | 953 | { |
| 841ab66c | 954 | #define N(a) (sizeof(a)/sizeof(a[0])) |
| 32176cfd | 955 | struct ieee80211vap *vap = ni->ni_vap; |
| 841ab66c | 956 | struct ieee80211com *ic = ni->ni_ic; |
| 32176cfd | 957 | int i; |
| 841ab66c SZ |
958 | |
| 959 | /* NB: preserve ni_table */ | |
| 960 | if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { | |
| 32176cfd RP |
961 | if (vap->iv_opmode != IEEE80211_M_STA) |
| 962 | vap->iv_ps_sta--; | |
| 841ab66c | 963 | ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; |
| 32176cfd RP |
964 | IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, |
| 965 | "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); | |
| 841ab66c SZ |
966 | } |
| 967 | /* | |
| 32176cfd RP |
968 | * Cleanup any HT-related state. |
| 969 | */ | |
| 970 | if (ni->ni_flags & IEEE80211_NODE_HT) | |
| 971 | ieee80211_ht_node_cleanup(ni); | |
| 972 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 973 | else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) | |
| 974 | ieee80211_ff_node_cleanup(ni); | |
| 975 | #endif | |
| 976 | #ifdef IEEE80211_SUPPORT_MESH | |
| 977 | /* | |
| 978 | * Cleanup any mesh-related state. | |
| 979 | */ | |
| 980 | if (vap->iv_opmode == IEEE80211_M_MBSS) | |
| 981 | ieee80211_mesh_node_cleanup(ni); | |
| 982 | #endif | |
| 983 | /* | |
| 984 | * Clear any staging queue entries. | |
| 985 | */ | |
| 986 | ieee80211_ageq_drain_node(&ic->ic_stageq, ni); | |
| 987 | ||
| 988 | /* | |
| 841ab66c SZ |
989 | * Clear AREF flag that marks the authorization refcnt bump |
| 990 | * has happened. This is probably not needed as the node | |
| 991 | * should always be removed from the table so not found but | |
| 992 | * do it just in case. | |
| 32176cfd RP |
993 | * Likewise clear the ASSOCID flag as these flags are intended |
| 994 | * to be managed in tandem. | |
| 841ab66c | 995 | */ |
| 32176cfd | 996 | ni->ni_flags &= ~(IEEE80211_NODE_AREF | IEEE80211_NODE_ASSOCID); |
| 841ab66c SZ |
997 | |
| 998 | /* | |
| 999 | * Drain power save queue and, if needed, clear TIM. | |
| 1000 | */ | |
| 32176cfd RP |
1001 | if (ieee80211_node_psq_drain(ni) != 0 && vap->iv_set_tim != NULL) |
| 1002 | vap->iv_set_tim(ni, 0); | |
| 841ab66c SZ |
1003 | |
| 1004 | ni->ni_associd = 0; | |
| 1005 | if (ni->ni_challenge != NULL) { | |
| 32176cfd | 1006 | kfree(ni->ni_challenge, M_80211_NODE); |
| 841ab66c SZ |
1007 | ni->ni_challenge = NULL; |
| 1008 | } | |
| 1009 | /* | |
| 1010 | * Preserve SSID, WPA, and WME ie's so the bss node is | |
| 1011 | * reusable during a re-auth/re-assoc state transition. | |
| 1012 | * If we remove these data they will not be recreated | |
| 1013 | * because they come from a probe-response or beacon frame | |
| 1014 | * which cannot be expected prior to the association-response. | |
| 1015 | * This should not be an issue when operating in other modes | |
| 1016 | * as stations leaving always go through a full state transition | |
| 1017 | * which will rebuild this state. | |
| 1018 | * | |
| 1019 | * XXX does this leave us open to inheriting old state? | |
| 1020 | */ | |
| 1021 | for (i = 0; i < N(ni->ni_rxfrag); i++) | |
| 1022 | if (ni->ni_rxfrag[i] != NULL) { | |
| 1023 | m_freem(ni->ni_rxfrag[i]); | |
| 1024 | ni->ni_rxfrag[i] = NULL; | |
| 1025 | } | |
| 1026 | /* | |
| 32176cfd RP |
1027 | * Must be careful here to remove any key map entry w/o a LOR. |
| 1028 | */ | |
| 1029 | ieee80211_node_delucastkey(ni); | |
| 1030 | #undef N | |
| 1031 | } | |
| 1032 | ||
| 1033 | static void | |
| 1034 | node_free(struct ieee80211_node *ni) | |
| 1035 | { | |
| 1036 | struct ieee80211com *ic = ni->ni_ic; | |
| 1037 | ||
| 4028af95 | 1038 | ieee80211_ratectl_node_deinit(ni); |
| 32176cfd RP |
1039 | ic->ic_node_cleanup(ni); |
| 1040 | ieee80211_ies_cleanup(&ni->ni_ies); | |
| 1041 | ieee80211_psq_cleanup(&ni->ni_psq); | |
| 1042 | kfree(ni, M_80211_NODE); | |
| 1043 | } | |
| 1044 | ||
| 1045 | static void | |
| 1046 | node_age(struct ieee80211_node *ni) | |
| 1047 | { | |
| 1048 | struct ieee80211vap *vap = ni->ni_vap; | |
| 1049 | ||
| 32176cfd RP |
1050 | /* |
| 1051 | * Age frames on the power save queue. | |
| 841ab66c | 1052 | */ |
| 32176cfd RP |
1053 | if (ieee80211_node_psq_age(ni) != 0 && |
| 1054 | ni->ni_psq.psq_len == 0 && vap->iv_set_tim != NULL) | |
| 1055 | vap->iv_set_tim(ni, 0); | |
| 1056 | /* | |
| 1057 | * Age out HT resources (e.g. frames on the | |
| 1058 | * A-MPDU reorder queues). | |
| 1059 | */ | |
| 1060 | if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT)) | |
| 1061 | ieee80211_ht_node_age(ni); | |
| f186073c JS |
1062 | } |
| 1063 | ||
| 32176cfd RP |
1064 | static int8_t |
| 1065 | node_getrssi(const struct ieee80211_node *ni) | |
| f186073c | 1066 | { |
| 32176cfd RP |
1067 | uint32_t avgrssi = ni->ni_avgrssi; |
| 1068 | int32_t rssi; | |
| 841ab66c | 1069 | |
| 32176cfd RP |
1070 | if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER) |
| 1071 | return 0; | |
| 1072 | rssi = IEEE80211_RSSI_GET(avgrssi); | |
| 1073 | return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi; | |
| f186073c JS |
1074 | } |
| 1075 | ||
| 32176cfd RP |
1076 | static void |
| 1077 | node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) | |
| f186073c | 1078 | { |
| 32176cfd RP |
1079 | *rssi = node_getrssi(ni); |
| 1080 | *noise = ni->ni_noise; | |
| f186073c JS |
1081 | } |
| 1082 | ||
| 1083 | static void | |
| 32176cfd RP |
1084 | node_getmimoinfo(const struct ieee80211_node *ni, |
| 1085 | struct ieee80211_mimo_info *info) | |
| 1086 | { | |
| 1087 | /* XXX zero data? */ | |
| 1088 | } | |
| 1089 | ||
| 1090 | struct ieee80211_node * | |
| 1091 | ieee80211_alloc_node(struct ieee80211_node_table *nt, | |
| 1092 | struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| f186073c | 1093 | { |
| 841ab66c | 1094 | struct ieee80211com *ic = nt->nt_ic; |
| 32176cfd | 1095 | struct ieee80211_node *ni; |
| f186073c JS |
1096 | int hash; |
| 1097 | ||
| 32176cfd RP |
1098 | ni = ic->ic_node_alloc(vap, macaddr); |
| 1099 | if (ni == NULL) { | |
| 1100 | vap->iv_stats.is_rx_nodealloc++; | |
| 1101 | return NULL; | |
| 1102 | } | |
| 841ab66c | 1103 | |
| 32176cfd | 1104 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, |
| 6168f72e RP |
1105 | "%s %p<%6D> in %s table\n", __func__, ni, |
| 1106 | macaddr, ":", nt->nt_name); | |
| 841ab66c | 1107 | |
| f186073c | 1108 | IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); |
| 32176cfd | 1109 | hash = IEEE80211_NODE_HASH(ic, macaddr); |
| 841ab66c SZ |
1110 | ieee80211_node_initref(ni); /* mark referenced */ |
| 1111 | ni->ni_chan = IEEE80211_CHAN_ANYC; | |
| 1112 | ni->ni_authmode = IEEE80211_AUTH_OPEN; | |
| 1113 | ni->ni_txpower = ic->ic_txpowlimit; /* max power */ | |
| 32176cfd RP |
1114 | ni->ni_txparms = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; |
| 1115 | ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); | |
| 1116 | ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER; | |
| 841ab66c SZ |
1117 | ni->ni_inact_reload = nt->nt_inact_init; |
| 1118 | ni->ni_inact = ni->ni_inact_reload; | |
| 32176cfd RP |
1119 | ni->ni_ath_defkeyix = 0x7fff; |
| 1120 | ieee80211_psq_init(&ni->ni_psq, "unknown"); | |
| 1121 | #ifdef IEEE80211_SUPPORT_MESH | |
| 1122 | if (vap->iv_opmode == IEEE80211_M_MBSS) | |
| 1123 | ieee80211_mesh_node_init(vap, ni); | |
| 1124 | #endif | |
| 841ab66c SZ |
1125 | TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list); |
| 1126 | LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash); | |
| 1127 | ni->ni_table = nt; | |
| 32176cfd | 1128 | ni->ni_vap = vap; |
| 841ab66c | 1129 | ni->ni_ic = ic; |
| b9334f94 | 1130 | |
| 32176cfd RP |
1131 | IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, |
| 1132 | "%s: inact_reload %u", __func__, ni->ni_inact_reload); | |
| 841ab66c | 1133 | |
| f186073c JS |
1134 | return ni; |
| 1135 | } | |
| 1136 | ||
| 841ab66c SZ |
1137 | /* |
| 1138 | * Craft a temporary node suitable for sending a management frame | |
| 1139 | * to the specified station. We craft only as much state as we | |
| 1140 | * need to do the work since the node will be immediately reclaimed | |
| 1141 | * once the send completes. | |
| 1142 | */ | |
| 1143 | struct ieee80211_node * | |
| 32176cfd RP |
1144 | ieee80211_tmp_node(struct ieee80211vap *vap, |
| 1145 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c | 1146 | { |
| 32176cfd | 1147 | struct ieee80211com *ic = vap->iv_ic; |
| 841ab66c SZ |
1148 | struct ieee80211_node *ni; |
| 1149 | ||
| 32176cfd | 1150 | ni = ic->ic_node_alloc(vap, macaddr); |
| 841ab66c | 1151 | if (ni != NULL) { |
| 32176cfd RP |
1152 | struct ieee80211_node *bss = vap->iv_bss; |
| 1153 | ||
| 1154 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, | |
| 6168f72e | 1155 | "%s %p<%6D>\n", __func__, ni, macaddr, ":"); |
| 32176cfd RP |
1156 | |
| 1157 | ni->ni_table = NULL; /* NB: pedantic */ | |
| 1158 | ni->ni_ic = ic; /* NB: needed to set channel */ | |
| 1159 | ni->ni_vap = vap; | |
| 841ab66c SZ |
1160 | |
| 1161 | IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); | |
| 32176cfd | 1162 | IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); |
| 841ab66c | 1163 | ieee80211_node_initref(ni); /* mark referenced */ |
| 841ab66c | 1164 | /* NB: required by ieee80211_fix_rate */ |
| 32176cfd RP |
1165 | ieee80211_node_set_chan(ni, bss->ni_chan); |
| 1166 | ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, | |
| 841ab66c | 1167 | IEEE80211_KEYIX_NONE); |
| 32176cfd | 1168 | ni->ni_txpower = bss->ni_txpower; |
| 841ab66c | 1169 | /* XXX optimize away */ |
| 32176cfd | 1170 | ieee80211_psq_init(&ni->ni_psq, "unknown"); |
| 841ab66c SZ |
1171 | } else { |
| 1172 | /* XXX msg */ | |
| 32176cfd | 1173 | vap->iv_stats.is_rx_nodealloc++; |
| 841ab66c SZ |
1174 | } |
| 1175 | return ni; | |
| 1176 | } | |
| 1177 | ||
| f186073c | 1178 | struct ieee80211_node * |
| 32176cfd RP |
1179 | ieee80211_dup_bss(struct ieee80211vap *vap, |
| 1180 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| f186073c | 1181 | { |
| 32176cfd | 1182 | struct ieee80211com *ic = vap->iv_ic; |
| 841ab66c SZ |
1183 | struct ieee80211_node *ni; |
| 1184 | ||
| 32176cfd | 1185 | ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr); |
| f186073c | 1186 | if (ni != NULL) { |
| 32176cfd | 1187 | struct ieee80211_node *bss = vap->iv_bss; |
| f186073c | 1188 | /* |
| 32176cfd | 1189 | * Inherit from iv_bss. |
| f186073c | 1190 | */ |
| 32176cfd RP |
1191 | copy_bss(ni, bss); |
| 1192 | IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); | |
| 1193 | ieee80211_node_set_chan(ni, bss->ni_chan); | |
| 1194 | } | |
| f186073c JS |
1195 | return ni; |
| 1196 | } | |
| 1197 | ||
| 32176cfd RP |
1198 | /* |
| 1199 | * Create a bss node for a legacy WDS vap. The far end does | |
| 1200 | * not associate so we just create create a new node and | |
| 1201 | * simulate an association. The caller is responsible for | |
| 1202 | * installing the node as the bss node and handling any further | |
| 1203 | * setup work like authorizing the port. | |
| 1204 | */ | |
| 1205 | struct ieee80211_node * | |
| 1206 | ieee80211_node_create_wds(struct ieee80211vap *vap, | |
| 1207 | const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan) | |
| 1208 | { | |
| 1209 | struct ieee80211com *ic = vap->iv_ic; | |
| 1210 | struct ieee80211_node *ni; | |
| 1211 | ||
| 1212 | /* XXX check if node already in sta table? */ | |
| 1213 | ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid); | |
| 1214 | if (ni != NULL) { | |
| 1215 | ni->ni_wdsvap = vap; | |
| 1216 | IEEE80211_ADDR_COPY(ni->ni_bssid, bssid); | |
| 1217 | /* | |
| 1218 | * Inherit any manually configured settings. | |
| 1219 | */ | |
| 1220 | copy_bss(ni, vap->iv_bss); | |
| 1221 | ieee80211_node_set_chan(ni, chan); | |
| 1222 | /* NB: propagate ssid so available to WPA supplicant */ | |
| 1223 | ni->ni_esslen = vap->iv_des_ssid[0].len; | |
| 1224 | memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); | |
| 1225 | /* NB: no associd for peer */ | |
| 1226 | /* | |
| 1227 | * There are no management frames to use to | |
| 1228 | * discover neighbor capabilities, so blindly | |
| 1229 | * propagate the local configuration. | |
| 1230 | */ | |
| 1231 | if (vap->iv_flags & IEEE80211_F_WME) | |
| 1232 | ni->ni_flags |= IEEE80211_NODE_QOS; | |
| 1233 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 1234 | if (vap->iv_flags & IEEE80211_F_FF) | |
| 1235 | ni->ni_flags |= IEEE80211_NODE_FF; | |
| 1236 | #endif | |
| 1237 | if ((ic->ic_htcaps & IEEE80211_HTC_HT) && | |
| 1238 | (vap->iv_flags_ht & IEEE80211_FHT_HT)) { | |
| 1239 | /* | |
| 1240 | * Device is HT-capable and HT is enabled for | |
| 1241 | * the vap; setup HT operation. On return | |
| 1242 | * ni_chan will be adjusted to an HT channel. | |
| 1243 | */ | |
| 1244 | ieee80211_ht_wds_init(ni); | |
| 1245 | } else { | |
| 1246 | struct ieee80211_channel *c = ni->ni_chan; | |
| 1247 | /* | |
| 1248 | * Force a legacy channel to be used. | |
| 1249 | */ | |
| 1250 | c = ieee80211_find_channel(ic, | |
| 1251 | c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT); | |
| 1252 | KASSERT(c != NULL, ("no legacy channel, %u/%x", | |
| 1253 | ni->ni_chan->ic_freq, ni->ni_chan->ic_flags)); | |
| 1254 | ni->ni_chan = c; | |
| 1255 | } | |
| 1256 | } | |
| 1257 | return ni; | |
| 1258 | } | |
| 1259 | ||
| 1260 | struct ieee80211_node * | |
| 841ab66c | 1261 | #ifdef IEEE80211_DEBUG_REFCNT |
| 32176cfd RP |
1262 | ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt, |
| 1263 | const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) | |
| 841ab66c | 1264 | #else |
| 32176cfd RP |
1265 | ieee80211_find_node_locked(struct ieee80211_node_table *nt, |
| 1266 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c | 1267 | #endif |
| f186073c JS |
1268 | { |
| 1269 | struct ieee80211_node *ni; | |
| 1270 | int hash; | |
| 1271 | ||
| 32176cfd | 1272 | hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr); |
| 841ab66c | 1273 | LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { |
| f186073c | 1274 | if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { |
| 841ab66c SZ |
1275 | ieee80211_ref_node(ni); /* mark referenced */ |
| 1276 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 32176cfd | 1277 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, |
| 6168f72e | 1278 | "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, |
| 841ab66c | 1279 | func, line, |
| 6168f72e | 1280 | ni, ni->ni_macaddr, ":", |
| 841ab66c SZ |
1281 | ieee80211_node_refcnt(ni)); |
| 1282 | #endif | |
| f186073c JS |
1283 | return ni; |
| 1284 | } | |
| 1285 | } | |
| 1286 | return NULL; | |
| 1287 | } | |
| 1288 | ||
| 1289 | struct ieee80211_node * | |
| 841ab66c SZ |
1290 | #ifdef IEEE80211_DEBUG_REFCNT |
| 1291 | ieee80211_find_node_debug(struct ieee80211_node_table *nt, | |
| 32176cfd | 1292 | const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) |
| 841ab66c | 1293 | #else |
| 32176cfd RP |
1294 | ieee80211_find_node(struct ieee80211_node_table *nt, |
| 1295 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c | 1296 | #endif |
| f186073c JS |
1297 | { |
| 1298 | struct ieee80211_node *ni; | |
| f186073c | 1299 | |
| 32176cfd | 1300 | ni = ieee80211_find_node_locked(nt, macaddr); |
| f186073c JS |
1301 | return ni; |
| 1302 | } | |
| 1303 | ||
| f186073c | 1304 | struct ieee80211_node * |
| 32176cfd RP |
1305 | #ifdef IEEE80211_DEBUG_REFCNT |
| 1306 | ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt, | |
| 1307 | const struct ieee80211vap *vap, | |
| 1308 | const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) | |
| 1309 | #else | |
| 1310 | ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt, | |
| 1311 | const struct ieee80211vap *vap, | |
| 841ab66c | 1312 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) |
| 32176cfd | 1313 | #endif |
| f186073c JS |
1314 | { |
| 1315 | struct ieee80211_node *ni; | |
| 32176cfd | 1316 | int hash; |
| f186073c | 1317 | |
| 32176cfd RP |
1318 | hash = IEEE80211_NODE_HASH(nt->nt_ic, macaddr); |
| 1319 | LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { | |
| 1320 | if (ni->ni_vap == vap && | |
| 1321 | IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { | |
| 1322 | ieee80211_ref_node(ni); /* mark referenced */ | |
| 1323 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 1324 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, | |
| 6168f72e | 1325 | "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, |
| 32176cfd | 1326 | func, line, |
| 6168f72e | 1327 | ni, ni->ni_macaddr, ":", |
| 32176cfd RP |
1328 | ieee80211_node_refcnt(ni)); |
| 1329 | #endif | |
| 1330 | return ni; | |
| 841ab66c SZ |
1331 | } |
| 1332 | } | |
| 32176cfd | 1333 | return NULL; |
| 841ab66c SZ |
1334 | } |
| 1335 | ||
| 32176cfd RP |
1336 | struct ieee80211_node * |
| 1337 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 1338 | ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt, | |
| 1339 | const struct ieee80211vap *vap, | |
| 1340 | const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line) | |
| 841ab66c | 1341 | #else |
| 32176cfd RP |
1342 | ieee80211_find_vap_node(struct ieee80211_node_table *nt, |
| 1343 | const struct ieee80211vap *vap, | |
| 1344 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c | 1345 | #endif |
| 841ab66c | 1346 | { |
| 32176cfd | 1347 | struct ieee80211_node *ni; |
| 841ab66c | 1348 | |
| 32176cfd | 1349 | ni = ieee80211_find_vap_node_locked(nt, vap, macaddr); |
| 32176cfd | 1350 | return ni; |
| 841ab66c SZ |
1351 | } |
| 1352 | ||
| 1353 | /* | |
| 32176cfd RP |
1354 | * Fake up a node; this handles node discovery in adhoc mode. |
| 1355 | * Note that for the driver's benefit we we treat this like | |
| 1356 | * an association so the driver has an opportunity to setup | |
| 1357 | * it's private state. | |
| 841ab66c | 1358 | */ |
| 32176cfd RP |
1359 | struct ieee80211_node * |
| 1360 | ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap, | |
| 1361 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c | 1362 | { |
| 841ab66c | 1363 | struct ieee80211_node *ni; |
| 841ab66c | 1364 | |
| 32176cfd | 1365 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, |
| 6168f72e | 1366 | "%s: mac<%6D>\n", __func__, macaddr, ":"); |
| 32176cfd RP |
1367 | ni = ieee80211_dup_bss(vap, macaddr); |
| 1368 | if (ni != NULL) { | |
| 1369 | struct ieee80211com *ic = vap->iv_ic; | |
| 841ab66c | 1370 | |
| 32176cfd RP |
1371 | /* XXX no rate negotiation; just dup */ |
| 1372 | ni->ni_rates = vap->iv_bss->ni_rates; | |
| 1373 | if (ieee80211_iserp_rateset(&ni->ni_rates)) | |
| 1374 | ni->ni_flags |= IEEE80211_NODE_ERP; | |
| 1375 | if (vap->iv_opmode == IEEE80211_M_AHDEMO) { | |
| 1376 | /* | |
| 1377 | * In adhoc demo mode there are no management | |
| 1378 | * frames to use to discover neighbor capabilities, | |
| 1379 | * so blindly propagate the local configuration | |
| 1380 | * so we can do interesting things (e.g. use | |
| 1381 | * WME to disable ACK's). | |
| 1382 | */ | |
| 1383 | if (vap->iv_flags & IEEE80211_F_WME) | |
| 1384 | ni->ni_flags |= IEEE80211_NODE_QOS; | |
| 1385 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 1386 | if (vap->iv_flags & IEEE80211_F_FF) | |
| 1387 | ni->ni_flags |= IEEE80211_NODE_FF; | |
| 1388 | #endif | |
| 1389 | } | |
| 1390 | ieee80211_node_setuptxparms(ni); | |
| 1391 | if (ic->ic_newassoc != NULL) | |
| 1392 | ic->ic_newassoc(ni, 1); | |
| 1393 | /* XXX not right for 802.1x/WPA */ | |
| 1394 | ieee80211_node_authorize(ni); | |
| 841ab66c | 1395 | } |
| 32176cfd | 1396 | return ni; |
| 841ab66c SZ |
1397 | } |
| 1398 | ||
| 1399 | void | |
| 1400 | ieee80211_init_neighbor(struct ieee80211_node *ni, | |
| 1401 | const struct ieee80211_frame *wh, | |
| 1402 | const struct ieee80211_scanparams *sp) | |
| 1403 | { | |
| 841ab66c SZ |
1404 | ni->ni_esslen = sp->ssid[1]; |
| 1405 | memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); | |
| 1406 | IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); | |
| 1407 | memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp)); | |
| 1408 | ni->ni_intval = sp->bintval; | |
| 1409 | ni->ni_capinfo = sp->capinfo; | |
| 1410 | ni->ni_chan = ni->ni_ic->ic_curchan; | |
| 1411 | ni->ni_fhdwell = sp->fhdwell; | |
| 1412 | ni->ni_fhindex = sp->fhindex; | |
| 1413 | ni->ni_erp = sp->erp; | |
| 1414 | ni->ni_timoff = sp->timoff; | |
| 32176cfd RP |
1415 | #ifdef IEEE80211_SUPPORT_MESH |
| 1416 | if (ni->ni_vap->iv_opmode == IEEE80211_M_MBSS) | |
| 1417 | ieee80211_mesh_init_neighbor(ni, wh, sp); | |
| 1418 | #endif | |
| 1419 | if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) { | |
| 1420 | ieee80211_ies_expand(&ni->ni_ies); | |
| 1421 | if (ni->ni_ies.wme_ie != NULL) | |
| 1422 | ni->ni_flags |= IEEE80211_NODE_QOS; | |
| 1423 | else | |
| 1424 | ni->ni_flags &= ~IEEE80211_NODE_QOS; | |
| 1425 | #ifdef IEEE80211_SUPPORT_SUPERG | |
| 1426 | if (ni->ni_ies.ath_ie != NULL) | |
| 1427 | ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); | |
| 1428 | #endif | |
| 1429 | } | |
| 841ab66c SZ |
1430 | |
| 1431 | /* NB: must be after ni_chan is setup */ | |
| 208a1285 SZ |
1432 | ieee80211_setup_rates(ni, sp->rates, sp->xrates, |
| 1433 | IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | | |
| 32176cfd | 1434 | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); |
| 841ab66c SZ |
1435 | } |
| 1436 | ||
| 1437 | /* | |
| 1438 | * Do node discovery in adhoc mode on receipt of a beacon | |
| 1439 | * or probe response frame. Note that for the driver's | |
| 1440 | * benefit we we treat this like an association so the | |
| 1441 | * driver has an opportunity to setup it's private state. | |
| 1442 | */ | |
| 1443 | struct ieee80211_node * | |
| 32176cfd | 1444 | ieee80211_add_neighbor(struct ieee80211vap *vap, |
| 841ab66c SZ |
1445 | const struct ieee80211_frame *wh, |
| 1446 | const struct ieee80211_scanparams *sp) | |
| 1447 | { | |
| 1448 | struct ieee80211_node *ni; | |
| 1449 | ||
| 32176cfd | 1450 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, |
| 6168f72e | 1451 | "%s: mac<%6D>\n", __func__, wh->i_addr2, ":"); |
| 32176cfd | 1452 | ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */ |
| 841ab66c | 1453 | if (ni != NULL) { |
| 32176cfd | 1454 | struct ieee80211com *ic = vap->iv_ic; |
| b9334f94 | 1455 | |
| 32176cfd RP |
1456 | ieee80211_init_neighbor(ni, wh, sp); |
| 1457 | if (ieee80211_iserp_rateset(&ni->ni_rates)) | |
| 1458 | ni->ni_flags |= IEEE80211_NODE_ERP; | |
| 1459 | ieee80211_node_setuptxparms(ni); | |
| 841ab66c SZ |
1460 | if (ic->ic_newassoc != NULL) |
| 1461 | ic->ic_newassoc(ni, 1); | |
| 1462 | /* XXX not right for 802.1x/WPA */ | |
| 1463 | ieee80211_node_authorize(ni); | |
| 1464 | } | |
| 1465 | return ni; | |
| 1466 | } | |
| 1467 | ||
| 32176cfd RP |
1468 | #define IS_PROBEREQ(wh) \ |
| 1469 | ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK|IEEE80211_FC0_SUBTYPE_MASK)) \ | |
| 1470 | == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ)) | |
| 1471 | #define IS_BCAST_PROBEREQ(wh) \ | |
| 1472 | (IS_PROBEREQ(wh) && IEEE80211_IS_MULTICAST( \ | |
| 1473 | ((const struct ieee80211_frame *)(wh))->i_addr3)) | |
| 1474 | ||
| 1475 | static __inline struct ieee80211_node * | |
| 1476 | _find_rxnode(struct ieee80211_node_table *nt, | |
| 1477 | const struct ieee80211_frame_min *wh) | |
| 1478 | { | |
| 1479 | if (IS_BCAST_PROBEREQ(wh)) | |
| 1480 | return NULL; /* spam bcast probe req to all vap's */ | |
| 1481 | return ieee80211_find_node_locked(nt, wh->i_addr2); | |
| 1482 | } | |
| 1483 | ||
| 841ab66c SZ |
1484 | /* |
| 1485 | * Locate the node for sender, track state, and then pass the | |
| 32176cfd RP |
1486 | * (referenced) node up to the 802.11 layer for its use. Note |
| 1487 | * we can return NULL if the sender is not in the table. | |
| 841ab66c SZ |
1488 | */ |
| 1489 | struct ieee80211_node * | |
| 1490 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 1491 | ieee80211_find_rxnode_debug(struct ieee80211com *ic, | |
| 1492 | const struct ieee80211_frame_min *wh, const char *func, int line) | |
| 1493 | #else | |
| 1494 | ieee80211_find_rxnode(struct ieee80211com *ic, | |
| 1495 | const struct ieee80211_frame_min *wh) | |
| 1496 | #endif | |
| 1497 | { | |
| 1498 | struct ieee80211_node_table *nt; | |
| 1499 | struct ieee80211_node *ni; | |
| 1500 | ||
| 32176cfd | 1501 | nt = &ic->ic_sta; |
| 32176cfd | 1502 | ni = _find_rxnode(nt, wh); |
| 841ab66c SZ |
1503 | |
| 1504 | return ni; | |
| 1505 | } | |
| 1506 | ||
| 1507 | /* | |
| 1508 | * Like ieee80211_find_rxnode but use the supplied h/w | |
| 1509 | * key index as a hint to locate the node in the key | |
| 1510 | * mapping table. If an entry is present at the key | |
| 1511 | * index we return it; otherwise do a normal lookup and | |
| 1512 | * update the mapping table if the station has a unicast | |
| 1513 | * key assigned to it. | |
| 1514 | */ | |
| 1515 | struct ieee80211_node * | |
| 1516 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 1517 | ieee80211_find_rxnode_withkey_debug(struct ieee80211com *ic, | |
| 1518 | const struct ieee80211_frame_min *wh, ieee80211_keyix keyix, | |
| 1519 | const char *func, int line) | |
| 1520 | #else | |
| 1521 | ieee80211_find_rxnode_withkey(struct ieee80211com *ic, | |
| 1522 | const struct ieee80211_frame_min *wh, ieee80211_keyix keyix) | |
| 1523 | #endif | |
| 1524 | { | |
| 1525 | struct ieee80211_node_table *nt; | |
| 1526 | struct ieee80211_node *ni; | |
| 1527 | ||
| 32176cfd | 1528 | nt = &ic->ic_sta; |
| 841ab66c SZ |
1529 | if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) |
| 1530 | ni = nt->nt_keyixmap[keyix]; | |
| 1531 | else | |
| 1532 | ni = NULL; | |
| 1533 | if (ni == NULL) { | |
| 32176cfd RP |
1534 | ni = _find_rxnode(nt, wh); |
| 1535 | if (ni != NULL && nt->nt_keyixmap != NULL) { | |
| 841ab66c SZ |
1536 | /* |
| 1537 | * If the station has a unicast key cache slot | |
| 1538 | * assigned update the key->node mapping table. | |
| 1539 | */ | |
| 1540 | keyix = ni->ni_ucastkey.wk_rxkeyix; | |
| 1541 | /* XXX can keyixmap[keyix] != NULL? */ | |
| 1542 | if (keyix < nt->nt_keyixmax && | |
| 1543 | nt->nt_keyixmap[keyix] == NULL) { | |
| 32176cfd RP |
1544 | IEEE80211_DPRINTF(ni->ni_vap, |
| 1545 | IEEE80211_MSG_NODE, | |
| 6168f72e RP |
1546 | "%s: add key map entry %p<%6D> refcnt %d\n", |
| 1547 | __func__, ni, ni->ni_macaddr, ":", | |
| 841ab66c SZ |
1548 | ieee80211_node_refcnt(ni)+1); |
| 1549 | nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni); | |
| 1550 | } | |
| 1551 | } | |
| 1552 | } else { | |
| 32176cfd RP |
1553 | if (IS_BCAST_PROBEREQ(wh)) |
| 1554 | ni = NULL; /* spam bcast probe req to all vap's */ | |
| 1555 | else | |
| 1556 | ieee80211_ref_node(ni); | |
| 841ab66c | 1557 | } |
| 841ab66c SZ |
1558 | return ni; |
| 1559 | } | |
| 32176cfd RP |
1560 | #undef IS_BCAST_PROBEREQ |
| 1561 | #undef IS_PROBEREQ | |
| 841ab66c SZ |
1562 | |
| 1563 | /* | |
| 1564 | * Return a reference to the appropriate node for sending | |
| 1565 | * a data frame. This handles node discovery in adhoc networks. | |
| 1566 | */ | |
| 1567 | struct ieee80211_node * | |
| 1568 | #ifdef IEEE80211_DEBUG_REFCNT | |
| 32176cfd RP |
1569 | ieee80211_find_txnode_debug(struct ieee80211vap *vap, |
| 1570 | const uint8_t macaddr[IEEE80211_ADDR_LEN], | |
| 841ab66c SZ |
1571 | const char *func, int line) |
| 1572 | #else | |
| 32176cfd RP |
1573 | ieee80211_find_txnode(struct ieee80211vap *vap, |
| 1574 | const uint8_t macaddr[IEEE80211_ADDR_LEN]) | |
| 841ab66c SZ |
1575 | #endif |
| 1576 | { | |
| 32176cfd | 1577 | struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta; |
| 841ab66c SZ |
1578 | struct ieee80211_node *ni; |
| 1579 | ||
| 841ab66c SZ |
1580 | /* |
| 1581 | * The destination address should be in the node table | |
| 1582 | * unless this is a multicast/broadcast frame. We can | |
| 1583 | * also optimize station mode operation, all frames go | |
| 1584 | * to the bss node. | |
| 1585 | */ | |
| 32176cfd | 1586 | /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ |
| 32176cfd RP |
1587 | if (vap->iv_opmode == IEEE80211_M_STA || |
| 1588 | vap->iv_opmode == IEEE80211_M_WDS || | |
| 1589 | IEEE80211_IS_MULTICAST(macaddr)) | |
| 1590 | ni = ieee80211_ref_node(vap->iv_bss); | |
| 1591 | else | |
| 1592 | ni = ieee80211_find_node_locked(nt, macaddr); | |
| 841ab66c SZ |
1593 | |
| 1594 | if (ni == NULL) { | |
| 32176cfd RP |
1595 | if (vap->iv_opmode == IEEE80211_M_IBSS || |
| 1596 | vap->iv_opmode == IEEE80211_M_AHDEMO) { | |
| 841ab66c SZ |
1597 | /* |
| 1598 | * In adhoc mode cons up a node for the destination. | |
| 1599 | * Note that we need an additional reference for the | |
| 32176cfd RP |
1600 | * caller to be consistent with |
| 1601 | * ieee80211_find_node_locked. | |
| 841ab66c | 1602 | */ |
| 32176cfd | 1603 | ni = ieee80211_fakeup_adhoc_node(vap, macaddr); |
| 841ab66c | 1604 | if (ni != NULL) |
| 32176cfd | 1605 | (void) ieee80211_ref_node(ni); |
| 841ab66c | 1606 | } else { |
| 32176cfd RP |
1607 | IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr, |
| 1608 | "no node, discard frame (%s)", __func__); | |
| 1609 | vap->iv_stats.is_tx_nonode++; | |
| 841ab66c SZ |
1610 | } |
| 1611 | } | |
| 841ab66c | 1612 | return ni; |
| 841ab66c SZ |
1613 | } |
| 1614 | ||
| f186073c | 1615 | static void |
| 841ab66c | 1616 | _ieee80211_free_node(struct ieee80211_node *ni) |
| f186073c | 1617 | { |
| 841ab66c SZ |
1618 | struct ieee80211_node_table *nt = ni->ni_table; |
| 1619 | ||
| 32176cfd RP |
1620 | /* |
| 1621 | * NB: careful about referencing the vap as it may be | |
| 1622 | * gone if the last reference was held by a driver. | |
| 1623 | * We know the com will always be present so it's safe | |
| 1624 | * to use ni_ic below to reclaim resources. | |
| 1625 | */ | |
| 1626 | #if 0 | |
| 1627 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, | |
| 6168f72e RP |
1628 | "%s %p<%6D> in %s table\n", __func__, ni, |
| 1629 | ni->ni_macaddr, ":", | |
| 841ab66c | 1630 | nt != NULL ? nt->nt_name : "<gone>"); |
| 32176cfd RP |
1631 | #endif |
| 1632 | if (ni->ni_associd != 0) { | |
| 1633 | struct ieee80211vap *vap = ni->ni_vap; | |
| 1634 | if (vap->iv_aid_bitmap != NULL) | |
| 1635 | IEEE80211_AID_CLR(vap, ni->ni_associd); | |
| 1636 | } | |
| 841ab66c SZ |
1637 | if (nt != NULL) { |
| 1638 | TAILQ_REMOVE(&nt->nt_node, ni, ni_list); | |
| 1639 | LIST_REMOVE(ni, ni_hash); | |
| 1640 | } | |
| 32176cfd | 1641 | ni->ni_ic->ic_node_free(ni); |
| f186073c JS |
1642 | } |
| 1643 | ||
| 1644 | void | |
| 841ab66c SZ |
1645 | #ifdef IEEE80211_DEBUG_REFCNT |
| 1646 | ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line) | |
| 1647 | #else | |
| 1648 | ieee80211_free_node(struct ieee80211_node *ni) | |
| 1649 | #endif | |
| f186073c | 1650 | { |
| 841ab66c | 1651 | struct ieee80211_node_table *nt = ni->ni_table; |
| f186073c | 1652 | |
| 841ab66c | 1653 | #ifdef IEEE80211_DEBUG_REFCNT |
| 32176cfd | 1654 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, |
| 6168f72e RP |
1655 | "%s (%s:%u) %p<%6D> refcnt %d\n", __func__, func, line, ni, |
| 1656 | ni->ni_macaddr, ":", ieee80211_node_refcnt(ni)-1); | |
| 841ab66c SZ |
1657 | #endif |
| 1658 | if (nt != NULL) { | |
| 1659 | if (ieee80211_node_dectestref(ni)) { | |
| 1660 | /* | |
| 1661 | * Last reference, reclaim state. | |
| 1662 | */ | |
| 1663 | _ieee80211_free_node(ni); | |
| 1664 | } else if (ieee80211_node_refcnt(ni) == 1 && | |
| 1665 | nt->nt_keyixmap != NULL) { | |
| 1666 | ieee80211_keyix keyix; | |
| 1667 | /* | |
| 1668 | * Check for a last reference in the key mapping table. | |
| 1669 | */ | |
| 1670 | keyix = ni->ni_ucastkey.wk_rxkeyix; | |
| 1671 | if (keyix < nt->nt_keyixmax && | |
| 1672 | nt->nt_keyixmap[keyix] == ni) { | |
| 32176cfd RP |
1673 | IEEE80211_DPRINTF(ni->ni_vap, |
| 1674 | IEEE80211_MSG_NODE, | |
| 6168f72e RP |
1675 | "%s: %p<%6D> clear key map entry", __func__, |
| 1676 | ni, ni->ni_macaddr, ":"); | |
| 841ab66c SZ |
1677 | nt->nt_keyixmap[keyix] = NULL; |
| 1678 | ieee80211_node_decref(ni); /* XXX needed? */ | |
| 1679 | _ieee80211_free_node(ni); | |
| 1680 | } | |
| 1681 | } | |
| f186073c | 1682 | } else { |
| 841ab66c SZ |
1683 | if (ieee80211_node_dectestref(ni)) |
| 1684 | _ieee80211_free_node(ni); | |
| f186073c JS |
1685 | } |
| 1686 | } | |
| 1687 | ||
| 841ab66c SZ |
1688 | /* |
| 1689 | * Reclaim a unicast key and clear any key cache state. | |
| 1690 | */ | |
| 1691 | int | |
| 1692 | ieee80211_node_delucastkey(struct ieee80211_node *ni) | |
| f186073c | 1693 | { |
| 841ab66c SZ |
1694 | struct ieee80211com *ic = ni->ni_ic; |
| 1695 | struct ieee80211_node_table *nt = &ic->ic_sta; | |
| 1696 | struct ieee80211_node *nikey; | |
| 32176cfd | 1697 | ieee80211_keyix keyix; |
| 26c6f223 | 1698 | int status; |
| 32176cfd RP |
1699 | |
| 1700 | /* | |
| 1701 | * NB: We must beware of LOR here; deleting the key | |
| 1702 | * can cause the crypto layer to block traffic updates | |
| 1703 | * which can generate a LOR against the node table lock; | |
| 1704 | * grab it here and stash the key index for our use below. | |
| 1705 | * | |
| 1706 | * Must also beware of recursion on the node table lock. | |
| 1707 | * When called from node_cleanup we may already have | |
| 1708 | * the node table lock held. Unfortunately there's no | |
| 1709 | * way to separate out this path so we must do this | |
| 1710 | * conditionally. | |
| 1711 | */ | |
| 32176cfd RP |
1712 | nikey = NULL; |
| 1713 | status = 1; /* NB: success */ | |
| 1714 | if (ni->ni_ucastkey.wk_keyix != IEEE80211_KEYIX_NONE) { | |
| 1715 | keyix = ni->ni_ucastkey.wk_rxkeyix; | |
| 1716 | status = ieee80211_crypto_delkey(ni->ni_vap, &ni->ni_ucastkey); | |
| 1717 | if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) { | |
| 1718 | nikey = nt->nt_keyixmap[keyix]; | |
| 1719 | nt->nt_keyixmap[keyix] = NULL; | |
| 1720 | } | |
| 1721 | } | |
| 841ab66c SZ |
1722 | |
| 1723 | if (nikey != NULL) { | |
| 1724 | KASSERT(nikey == ni, | |
| 1725 | ("key map out of sync, ni %p nikey %p", ni, nikey)); | |
| 32176cfd | 1726 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, |
| 6168f72e RP |
1727 | "%s: delete key map entry %p<%6D> refcnt %d\n", |
| 1728 | __func__, ni, ni->ni_macaddr, ":", | |
| 841ab66c SZ |
1729 | ieee80211_node_refcnt(ni)-1); |
| 1730 | ieee80211_free_node(ni); | |
| 1731 | } | |
| 1732 | return status; | |
| 1733 | } | |
| 1734 | ||
| 1735 | /* | |
| 1736 | * Reclaim a node. If this is the last reference count then | |
| 1737 | * do the normal free work. Otherwise remove it from the node | |
| 1738 | * table and mark it gone by clearing the back-reference. | |
| 1739 | */ | |
| 1740 | static void | |
| 1741 | node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) | |
| 1742 | { | |
| 1743 | ieee80211_keyix keyix; | |
| 1744 | ||
| 32176cfd | 1745 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, |
| 6168f72e RP |
1746 | "%s: remove %p<%6D> from %s table, refcnt %d\n", |
| 1747 | __func__, ni, ni->ni_macaddr, ":", | |
| 841ab66c SZ |
1748 | nt->nt_name, ieee80211_node_refcnt(ni)-1); |
| 1749 | /* | |
| 1750 | * Clear any entry in the unicast key mapping table. | |
| 1751 | * We need to do it here so rx lookups don't find it | |
| 1752 | * in the mapping table even if it's not in the hash | |
| 1753 | * table. We cannot depend on the mapping table entry | |
| 1754 | * being cleared because the node may not be free'd. | |
| 1755 | */ | |
| 1756 | keyix = ni->ni_ucastkey.wk_rxkeyix; | |
| 1757 | if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax && | |
| 1758 | nt->nt_keyixmap[keyix] == ni) { | |
| 32176cfd | 1759 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, |
| 6168f72e RP |
1760 | "%s: %p<%6D> clear key map entry %u\n", |
| 1761 | __func__, ni, ni->ni_macaddr, ":", keyix); | |
| 841ab66c SZ |
1762 | nt->nt_keyixmap[keyix] = NULL; |
| 1763 | ieee80211_node_decref(ni); /* NB: don't need free */ | |
| 1764 | } | |
| 1765 | if (!ieee80211_node_dectestref(ni)) { | |
| 1766 | /* | |
| 1767 | * Other references are present, just remove the | |
| 1768 | * node from the table so it cannot be found. When | |
| 1769 | * the references are dropped storage will be | |
| 1770 | * reclaimed. | |
| 1771 | */ | |
| 1772 | TAILQ_REMOVE(&nt->nt_node, ni, ni_list); | |
| 1773 | LIST_REMOVE(ni, ni_hash); | |
| 1774 | ni->ni_table = NULL; /* clear reference */ | |
| 1775 | } else | |
| 1776 | _ieee80211_free_node(ni); | |
| 1777 | } | |
| 1778 | ||
| 32176cfd RP |
1779 | /* |
| 1780 | * Node table support. | |
| 1781 | */ | |
| 1782 | ||
| 841ab66c | 1783 | static void |
| 32176cfd RP |
1784 | ieee80211_node_table_init(struct ieee80211com *ic, |
| 1785 | struct ieee80211_node_table *nt, | |
| 1786 | const char *name, int inact, int keyixmax) | |
| 841ab66c | 1787 | { |
| 32176cfd | 1788 | nt->nt_ic = ic; |
| 32176cfd RP |
1789 | TAILQ_INIT(&nt->nt_node); |
| 1790 | nt->nt_name = name; | |
| 1791 | nt->nt_scangen = 1; | |
| 1792 | nt->nt_inact_init = inact; | |
| 1793 | nt->nt_keyixmax = keyixmax; | |
| 1794 | if (nt->nt_keyixmax > 0) { | |
| 1795 | nt->nt_keyixmap = (struct ieee80211_node **) kmalloc( | |
| 1796 | keyixmax * sizeof(struct ieee80211_node *), | |
| fcaa651d | 1797 | M_80211_NODE, M_INTWAIT | M_ZERO); |
| 32176cfd RP |
1798 | if (nt->nt_keyixmap == NULL) |
| 1799 | if_printf(ic->ic_ifp, | |
| 1800 | "Cannot allocate key index map with %u entries\n", | |
| 1801 | keyixmax); | |
| 1802 | } else | |
| 1803 | nt->nt_keyixmap = NULL; | |
| 1804 | } | |
| f186073c | 1805 | |
| 32176cfd RP |
1806 | static void |
| 1807 | ieee80211_node_table_reset(struct ieee80211_node_table *nt, | |
| 1808 | struct ieee80211vap *match) | |
| 1809 | { | |
| 1810 | struct ieee80211_node *ni, *next; | |
| 841ab66c | 1811 | |
| 32176cfd RP |
1812 | TAILQ_FOREACH_MUTABLE(ni, &nt->nt_node, ni_list, next) { |
| 1813 | if (match != NULL && ni->ni_vap != match) | |
| 1814 | continue; | |
| 1815 | /* XXX can this happen? if so need's work */ | |
| 841ab66c | 1816 | if (ni->ni_associd != 0) { |
| 32176cfd RP |
1817 | struct ieee80211vap *vap = ni->ni_vap; |
| 1818 | ||
| 1819 | if (vap->iv_auth->ia_node_leave != NULL) | |
| 1820 | vap->iv_auth->ia_node_leave(ni); | |
| 1821 | if (vap->iv_aid_bitmap != NULL) | |
| 1822 | IEEE80211_AID_CLR(vap, ni->ni_associd); | |
| 841ab66c | 1823 | } |
| 32176cfd | 1824 | ni->ni_wdsvap = NULL; /* clear reference */ |
| 841ab66c SZ |
1825 | node_reclaim(nt, ni); |
| 1826 | } | |
| 32176cfd RP |
1827 | if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) { |
| 1828 | /* | |
| 1829 | * Make a separate pass to clear references to this vap | |
| 1830 | * held by DWDS entries. They will not be matched above | |
| 1831 | * because ni_vap will point to the ap vap but we still | |
| 1832 | * need to clear ni_wdsvap when the WDS vap is destroyed | |
| 1833 | * and/or reset. | |
| 1834 | */ | |
| 1835 | TAILQ_FOREACH_MUTABLE(ni, &nt->nt_node, ni_list, next) | |
| 1836 | if (ni->ni_wdsvap == match) | |
| 1837 | ni->ni_wdsvap = NULL; | |
| 1838 | } | |
| f186073c JS |
1839 | } |
| 1840 | ||
| 841ab66c | 1841 | static void |
| 32176cfd | 1842 | ieee80211_node_table_cleanup(struct ieee80211_node_table *nt) |
| 841ab66c | 1843 | { |
| 32176cfd RP |
1844 | ieee80211_node_table_reset(nt, NULL); |
| 1845 | if (nt->nt_keyixmap != NULL) { | |
| 1846 | #ifdef DIAGNOSTIC | |
| 1847 | /* XXX verify all entries are NULL */ | |
| 1848 | int i; | |
| 1849 | for (i = 0; i < nt->nt_keyixmax; i++) | |
| 1850 | if (nt->nt_keyixmap[i] != NULL) | |
| 2fafbbf5 | 1851 | kprintf("%s: %s[%u] still active\n", __func__, |
| 32176cfd RP |
1852 | nt->nt_name, i); |
| 1853 | #endif | |
| 1854 | kfree(nt->nt_keyixmap, M_80211_NODE); | |
| 1855 | nt->nt_keyixmap = NULL; | |
| 841ab66c | 1856 | } |
| 841ab66c SZ |
1857 | } |
| 1858 | ||
| 1859 | /* | |
| 1860 | * Timeout inactive stations and do related housekeeping. | |
| 1861 | * Note that we cannot hold the node lock while sending a | |
| 1862 | * frame as this would lead to a LOR. Instead we use a | |
| 1863 | * generation number to mark nodes that we've scanned and | |
| 1864 | * drop the lock and restart a scan if we have to time out | |
| 1865 | * a node. Since we are single-threaded by virtue of | |
| f186073c JS |
1866 | * controlling the inactivity timer we can be sure this will |
| 1867 | * process each node only once. | |
| 1868 | */ | |
| 841ab66c | 1869 | static void |
| 32176cfd | 1870 | ieee80211_timeout_stations(struct ieee80211com *ic) |
| f186073c | 1871 | { |
| 32176cfd RP |
1872 | struct ieee80211_node_table *nt = &ic->ic_sta; |
| 1873 | struct ieee80211vap *vap; | |
| 1874 | struct ieee80211_node *ni; | |
| 1875 | int gen = 0; | |
| f186073c | 1876 | |
| 32176cfd RP |
1877 | gen = ++nt->nt_scangen; |
| 1878 | restart: | |
| 32176cfd RP |
1879 | TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| 1880 | if (ni->ni_scangen == gen) /* previously handled */ | |
| 1881 | continue; | |
| 1882 | ni->ni_scangen = gen; | |
| 841ab66c SZ |
1883 | /* |
| 1884 | * Ignore entries for which have yet to receive an | |
| 1885 | * authentication frame. These are transient and | |
| 1886 | * will be reclaimed when the last reference to them | |
| 1887 | * goes away (when frame xmits complete). | |
| 1888 | */ | |
| 32176cfd RP |
1889 | vap = ni->ni_vap; |
| 1890 | /* | |
| 1891 | * Only process stations when in RUN state. This | |
| 1892 | * insures, for example, that we don't timeout an | |
| 1893 | * inactive station during CAC. Note that CSA state | |
| 1894 | * is actually handled in ieee80211_node_timeout as | |
| 1895 | * it applies to more than timeout processing. | |
| 1896 | */ | |
| 1897 | if (vap->iv_state != IEEE80211_S_RUN) | |
| 1898 | continue; | |
| 1899 | /* XXX can vap be NULL? */ | |
| 1900 | if ((vap->iv_opmode == IEEE80211_M_HOSTAP || | |
| 1901 | vap->iv_opmode == IEEE80211_M_STA) && | |
| 841ab66c | 1902 | (ni->ni_flags & IEEE80211_NODE_AREF) == 0) |
| f186073c | 1903 | continue; |
| 841ab66c SZ |
1904 | /* |
| 1905 | * Free fragment if not needed anymore | |
| 1906 | * (last fragment older than 1s). | |
| 32176cfd | 1907 | * XXX doesn't belong here, move to node_age |
| 841ab66c SZ |
1908 | */ |
| 1909 | if (ni->ni_rxfrag[0] != NULL && | |
| 1910 | ticks > ni->ni_rxfragstamp + hz) { | |
| 1911 | m_freem(ni->ni_rxfrag[0]); | |
| 1912 | ni->ni_rxfrag[0] = NULL; | |
| 1913 | } | |
| 32176cfd RP |
1914 | if (ni->ni_inact > 0) { |
| 1915 | ni->ni_inact--; | |
| 1916 | IEEE80211_NOTE(vap, IEEE80211_MSG_INACT, ni, | |
| 1917 | "%s: inact %u inact_reload %u nrates %u", | |
| 1918 | __func__, ni->ni_inact, ni->ni_inact_reload, | |
| 1919 | ni->ni_rates.rs_nrates); | |
| 1920 | } | |
| 841ab66c SZ |
1921 | /* |
| 1922 | * Special case ourself; we may be idle for extended periods | |
| 1923 | * of time and regardless reclaiming our state is wrong. | |
| 32176cfd | 1924 | * XXX run ic_node_age |
| 841ab66c | 1925 | */ |
| 32176cfd | 1926 | if (ni == vap->iv_bss) |
| 841ab66c | 1927 | continue; |
| 32176cfd RP |
1928 | if (ni->ni_associd != 0 || |
| 1929 | (vap->iv_opmode == IEEE80211_M_IBSS || | |
| 1930 | vap->iv_opmode == IEEE80211_M_AHDEMO)) { | |
| 841ab66c | 1931 | /* |
| 32176cfd | 1932 | * Age/drain resources held by the station. |
| 841ab66c | 1933 | */ |
| 32176cfd | 1934 | ic->ic_node_age(ni); |
| f186073c | 1935 | /* |
| 841ab66c SZ |
1936 | * Probe the station before time it out. We |
| 1937 | * send a null data frame which may not be | |
| 1938 | * universally supported by drivers (need it | |
| 1939 | * for ps-poll support so it should be...). | |
| 32176cfd RP |
1940 | * |
| 1941 | * XXX don't probe the station unless we've | |
| 1942 | * received a frame from them (and have | |
| 1943 | * some idea of the rates they are capable | |
| 1944 | * of); this will get fixed more properly | |
| 1945 | * soon with better handling of the rate set. | |
| f186073c | 1946 | */ |
| 32176cfd RP |
1947 | if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && |
| 1948 | (0 < ni->ni_inact && | |
| 1949 | ni->ni_inact <= vap->iv_inact_probe) && | |
| 1950 | ni->ni_rates.rs_nrates != 0) { | |
| 1951 | IEEE80211_NOTE(vap, | |
| 841ab66c SZ |
1952 | IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, |
| 1953 | ni, "%s", | |
| 1954 | "probe station due to inactivity"); | |
| 1955 | /* | |
| 1956 | * Grab a reference before unlocking the table | |
| 1957 | * so the node cannot be reclaimed before we | |
| 1958 | * send the frame. ieee80211_send_nulldata | |
| 1959 | * understands we've done this and reclaims the | |
| 1960 | * ref for us as needed. | |
| 1961 | */ | |
| 1962 | ieee80211_ref_node(ni); | |
| 1963 | ieee80211_send_nulldata(ni); | |
| 1964 | /* XXX stat? */ | |
| 32176cfd | 1965 | goto restart; |
| 841ab66c SZ |
1966 | } |
| 1967 | } | |
| 32176cfd RP |
1968 | if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) && |
| 1969 | ni->ni_inact <= 0) { | |
| 1970 | IEEE80211_NOTE(vap, | |
| 841ab66c SZ |
1971 | IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, |
| 1972 | "station timed out due to inactivity " | |
| 1973 | "(refcnt %u)", ieee80211_node_refcnt(ni)); | |
| 1974 | /* | |
| 1975 | * Send a deauthenticate frame and drop the station. | |
| 1976 | * This is somewhat complicated due to reference counts | |
| 1977 | * and locking. At this point a station will typically | |
| 1978 | * have a reference count of 1. ieee80211_node_leave | |
| 1979 | * will do a "free" of the node which will drop the | |
| 1980 | * reference count. But in the meantime a reference | |
| 1981 | * wil be held by the deauth frame. The actual reclaim | |
| 1982 | * of the node will happen either after the tx is | |
| 1983 | * completed or by ieee80211_node_leave. | |
| 32176cfd RP |
1984 | * |
| 1985 | * Separately we must drop the node lock before sending | |
| 1986 | * in case the driver takes a lock, as this can result | |
| 1987 | * in a LOR between the node lock and the driver lock. | |
| 841ab66c | 1988 | */ |
| 32176cfd | 1989 | ieee80211_ref_node(ni); |
| 841ab66c | 1990 | if (ni->ni_associd != 0) { |
| 32176cfd | 1991 | IEEE80211_SEND_MGMT(ni, |
| 841ab66c SZ |
1992 | IEEE80211_FC0_SUBTYPE_DEAUTH, |
| 1993 | IEEE80211_REASON_AUTH_EXPIRE); | |
| 1994 | } | |
| 32176cfd RP |
1995 | ieee80211_node_leave(ni); |
| 1996 | ieee80211_free_node(ni); | |
| 1997 | vap->iv_stats.is_node_timeout++; | |
| 1998 | goto restart; | |
| 1999 | } | |
| 2000 | } | |
| 32176cfd RP |
2001 | } |
| 2002 | ||
| 2003 | /* | |
| 2004 | * Aggressively reclaim resources. This should be used | |
| 2005 | * only in a critical situation to reclaim mbuf resources. | |
| 2006 | */ | |
| 2007 | void | |
| 2008 | ieee80211_drain(struct ieee80211com *ic) | |
| 2009 | { | |
| 2010 | struct ieee80211_node_table *nt = &ic->ic_sta; | |
| 2011 | struct ieee80211vap *vap; | |
| 2012 | struct ieee80211_node *ni; | |
| 2013 | ||
| 32176cfd RP |
2014 | TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| 2015 | /* | |
| 2016 | * Ignore entries for which have yet to receive an | |
| 2017 | * authentication frame. These are transient and | |
| 2018 | * will be reclaimed when the last reference to them | |
| 2019 | * goes away (when frame xmits complete). | |
| 2020 | */ | |
| 2021 | vap = ni->ni_vap; | |
| 2022 | /* | |
| 2023 | * Only process stations when in RUN state. This | |
| 2024 | * insures, for example, that we don't timeout an | |
| 2025 | * inactive station during CAC. Note that CSA state | |
| 2026 | * is actually handled in ieee80211_node_timeout as | |
| 2027 | * it applies to more than timeout processing. | |
| 2028 | */ | |
| 2029 | if (vap->iv_state != IEEE80211_S_RUN) | |
| 841ab66c | 2030 | continue; |
| 32176cfd RP |
2031 | /* XXX can vap be NULL? */ |
| 2032 | if ((vap->iv_opmode == IEEE80211_M_HOSTAP || | |
| 2033 | vap->iv_opmode == IEEE80211_M_STA) && | |
| 2034 | (ni->ni_flags & IEEE80211_NODE_AREF) == 0) | |
| 2035 | continue; | |
| 2036 | /* | |
| 2037 | * Free fragments. | |
| 2038 | * XXX doesn't belong here, move to node_drain | |
| 2039 | */ | |
| 2040 | if (ni->ni_rxfrag[0] != NULL) { | |
| 2041 | m_freem(ni->ni_rxfrag[0]); | |
| 2042 | ni->ni_rxfrag[0] = NULL; | |
| 841ab66c | 2043 | } |
| 32176cfd RP |
2044 | /* |
| 2045 | * Drain resources held by the station. | |
| 2046 | */ | |
| 2047 | ic->ic_node_drain(ni); | |
| 841ab66c | 2048 | } |
| 32176cfd RP |
2049 | } |
| 2050 | ||
| 2051 | /* | |
| 2052 | * Per-ieee80211com inactivity timer callback. | |
| 2053 | */ | |
| 2054 | void | |
| 2055 | ieee80211_node_timeout(void *arg) | |
| 2056 | { | |
| 2057 | struct ieee80211com *ic = arg; | |
| 841ab66c | 2058 | |
| 32176cfd RP |
2059 | /* |
| 2060 | * Defer timeout processing if a channel switch is pending. | |
| 2061 | * We typically need to be mute so not doing things that | |
| 2062 | * might generate frames is good to handle in one place. | |
| 2063 | * Supressing the station timeout processing may extend the | |
| 2064 | * lifetime of inactive stations (by not decrementing their | |
| 2065 | * idle counters) but this should be ok unless the CSA is | |
| 2066 | * active for an unusually long time. | |
| 2067 | */ | |
| 2068 | if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) { | |
| 2069 | ieee80211_scan_timeout(ic); | |
| 2070 | ieee80211_timeout_stations(ic); | |
| 2071 | ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT); | |
| 2072 | ||
| 32176cfd RP |
2073 | ieee80211_erp_timeout(ic); |
| 2074 | ieee80211_ht_timeout(ic); | |
| 32176cfd RP |
2075 | } |
| 2076 | callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, | |
| 2077 | ieee80211_node_timeout, ic); | |
| 841ab66c SZ |
2078 | } |
| 2079 | ||
| 2080 | void | |
| 32176cfd RP |
2081 | ieee80211_iterate_nodes(struct ieee80211_node_table *nt, |
| 2082 | ieee80211_iter_func *f, void *arg) | |
| 841ab66c | 2083 | { |
| 32176cfd RP |
2084 | struct ieee80211_node *ni; |
| 2085 | u_int gen; | |
| 841ab66c | 2086 | |
| 32176cfd RP |
2087 | gen = ++nt->nt_scangen; |
| 2088 | restart: | |
| 32176cfd RP |
2089 | TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| 2090 | if (ni->ni_scangen != gen) { | |
| 2091 | ni->ni_scangen = gen; | |
| 2092 | (void) ieee80211_ref_node(ni); | |
| 32176cfd RP |
2093 | (*f)(arg, ni); |
| 2094 | ieee80211_free_node(ni); | |
| 2095 | goto restart; | |
| 2096 | } | |
| 2097 | } | |
| 841ab66c SZ |
2098 | } |
| 2099 | ||
| 2100 | void | |
| 2101 | ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) | |
| 2102 | { | |
| 32176cfd RP |
2103 | kprintf("0x%p: mac %6D refcnt %d\n", ni, ni->ni_macaddr, ":", |
| 2104 | ieee80211_node_refcnt(ni)); | |
| 2105 | kprintf("\tscangen %u authmode %u flags 0x%x\n", | |
| 2106 | ni->ni_scangen, ni->ni_authmode, ni->ni_flags); | |
| a6ec04bc | 2107 | kprintf("\tassocid 0x%x txpower %u vlan %u\n", |
| 841ab66c | 2108 | ni->ni_associd, ni->ni_txpower, ni->ni_vlan); |
| a6ec04bc | 2109 | kprintf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", |
| 32176cfd RP |
2110 | ni->ni_txseqs[IEEE80211_NONQOS_TID], |
| 2111 | ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT, | |
| 2112 | ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK, | |
| 841ab66c | 2113 | ni->ni_rxfragstamp); |
| 32176cfd RP |
2114 | kprintf("\trssi %d noise %d intval %u capinfo 0x%x\n", |
| 2115 | node_getrssi(ni), ni->ni_noise, | |
| 2116 | ni->ni_intval, ni->ni_capinfo); | |
| a6ec04bc | 2117 | kprintf("\tbssid %6D essid \"%.*s\" channel %u:0x%x\n", |
| 841ab66c SZ |
2118 | ni->ni_bssid, ":", |
| 2119 | ni->ni_esslen, ni->ni_essid, | |
| 2120 | ni->ni_chan->ic_freq, ni->ni_chan->ic_flags); | |
| 32176cfd RP |
2121 | kprintf("\tinact %u inact_reload %u txrate %u\n", |
| 2122 | ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate); | |
| 2123 | kprintf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n", | |
| 2124 | ni->ni_htcap, ni->ni_htparam, | |
| 2125 | ni->ni_htctlchan, ni->ni_ht2ndchan); | |
| 2126 | kprintf("\thtopmode %x htstbc %x chw %u\n", | |
| 2127 | ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw); | |
| 841ab66c SZ |
2128 | } |
| 2129 | ||
| 2130 | void | |
| 2131 | ieee80211_dump_nodes(struct ieee80211_node_table *nt) | |
| 2132 | { | |
| 2133 | ieee80211_iterate_nodes(nt, | |
| 2134 | (ieee80211_iter_func *) ieee80211_dump_node, nt); | |
| 2135 | } | |
| 2136 | ||
| 32176cfd RP |
2137 | static void |
| 2138 | ieee80211_notify_erp_locked(struct ieee80211com *ic) | |
| 2139 | { | |
| 2140 | struct ieee80211vap *vap; | |
| 2141 | ||
| 32176cfd RP |
2142 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) |
| 2143 | if (vap->iv_opmode == IEEE80211_M_HOSTAP) | |
| 2144 | ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP); | |
| 2145 | } | |
| 2146 | ||
| 2147 | void | |
| 2148 | ieee80211_notify_erp(struct ieee80211com *ic) | |
| 2149 | { | |
| 32176cfd | 2150 | ieee80211_notify_erp_locked(ic); |
| 32176cfd RP |
2151 | } |
| 2152 | ||
| 841ab66c SZ |
2153 | /* |
| 2154 | * Handle a station joining an 11g network. | |
| 2155 | */ | |
| 2156 | static void | |
| 32176cfd | 2157 | ieee80211_node_join_11g(struct ieee80211_node *ni) |
| 841ab66c | 2158 | { |
| 32176cfd RP |
2159 | struct ieee80211com *ic = ni->ni_ic; |
| 2160 | ||
| 841ab66c SZ |
2161 | /* |
| 2162 | * Station isn't capable of short slot time. Bump | |
| 2163 | * the count of long slot time stations and disable | |
| 2164 | * use of short slot time. Note that the actual switch | |
| 2165 | * over to long slot time use may not occur until the | |
| 2166 | * next beacon transmission (per sec. 7.3.1.4 of 11g). | |
| 2167 | */ | |
| 2168 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { | |
| 2169 | ic->ic_longslotsta++; | |
| 32176cfd RP |
2170 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, |
| 2171 | "station needs long slot time, count %d", | |
| 2172 | ic->ic_longslotsta); | |
| 841ab66c | 2173 | /* XXX vap's w/ conflicting needs won't work */ |
| 32176cfd RP |
2174 | if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) { |
| 2175 | /* | |
| 2176 | * Don't force slot time when switched to turbo | |
| 2177 | * mode as non-ERP stations won't be present; this | |
| 2178 | * need only be done when on the normal G channel. | |
| 2179 | */ | |
| 2180 | ieee80211_set_shortslottime(ic, 0); | |
| 2181 | } | |
| 841ab66c SZ |
2182 | } |
| 2183 | /* | |
| 2184 | * If the new station is not an ERP station | |
| 2185 | * then bump the counter and enable protection | |
| 2186 | * if configured. | |
| 2187 | */ | |
| 32176cfd | 2188 | if (!ieee80211_iserp_rateset(&ni->ni_rates)) { |
| 841ab66c | 2189 | ic->ic_nonerpsta++; |
| 32176cfd RP |
2190 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, |
| 2191 | "station is !ERP, %d non-ERP stations associated", | |
| 2192 | ic->ic_nonerpsta); | |
| 841ab66c SZ |
2193 | /* |
| 2194 | * If station does not support short preamble | |
| 2195 | * then we must enable use of Barker preamble. | |
| 2196 | */ | |
| 2197 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { | |
| 32176cfd RP |
2198 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, |
| 2199 | "%s", "station needs long preamble"); | |
| 2200 | ic->ic_flags |= IEEE80211_F_USEBARKER; | |
| 2201 | ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; | |
| 2202 | } | |
| 2203 | /* | |
| 2204 | * If protection is configured and this is the first | |
| 2205 | * indication we should use protection, enable it. | |
| 2206 | */ | |
| 2207 | if (ic->ic_protmode != IEEE80211_PROT_NONE && | |
| 2208 | ic->ic_nonerpsta == 1 && | |
| 2209 | (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { | |
| 2210 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, | |
| 2211 | "%s: enable use of protection\n", __func__); | |
| 2212 | ic->ic_flags |= IEEE80211_F_USEPROT; | |
| 2213 | ieee80211_notify_erp_locked(ic); | |
| 841ab66c | 2214 | } |
| 841ab66c SZ |
2215 | } else |
| 2216 | ni->ni_flags |= IEEE80211_NODE_ERP; | |
| 2217 | } | |
| 2218 | ||
| 2219 | void | |
| 32176cfd | 2220 | ieee80211_node_join(struct ieee80211_node *ni, int resp) |
| 841ab66c | 2221 | { |
| 32176cfd RP |
2222 | struct ieee80211com *ic = ni->ni_ic; |
| 2223 | struct ieee80211vap *vap = ni->ni_vap; | |
| 841ab66c SZ |
2224 | int newassoc; |
| 2225 | ||
| 2226 | if (ni->ni_associd == 0) { | |
| 2227 | uint16_t aid; | |
| 2228 | ||
| 32176cfd | 2229 | KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap")); |
| 841ab66c SZ |
2230 | /* |
| 2231 | * It would be good to search the bitmap | |
| 2232 | * more efficiently, but this will do for now. | |
| 2233 | */ | |
| 32176cfd RP |
2234 | for (aid = 1; aid < vap->iv_max_aid; aid++) { |
| 2235 | if (!IEEE80211_AID_ISSET(vap, aid)) | |
| 841ab66c SZ |
2236 | break; |
| 2237 | } | |
| 32176cfd RP |
2238 | if (aid >= vap->iv_max_aid) { |
| 2239 | IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_TOOMANY); | |
| 2240 | ieee80211_node_leave(ni); | |
| 841ab66c SZ |
2241 | return; |
| 2242 | } | |
| 2243 | ni->ni_associd = aid | 0xc000; | |
| 32176cfd | 2244 | ni->ni_jointime = time_second; |
| 32176cfd RP |
2245 | IEEE80211_AID_SET(vap, ni->ni_associd); |
| 2246 | vap->iv_sta_assoc++; | |
| 841ab66c | 2247 | ic->ic_sta_assoc++; |
| 32176cfd RP |
2248 | |
| 2249 | if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) | |
| 2250 | ieee80211_ht_node_join(ni); | |
| 2251 | if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && | |
| 2252 | IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) | |
| 2253 | ieee80211_node_join_11g(ni); | |
| 32176cfd | 2254 | |
| 841ab66c | 2255 | newassoc = 1; |
| 841ab66c SZ |
2256 | } else |
| 2257 | newassoc = 0; | |
| 2258 | ||
| 32176cfd RP |
2259 | IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, |
| 2260 | "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", | |
| 841ab66c SZ |
2261 | IEEE80211_NODE_AID(ni), |
| 2262 | ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", | |
| 2263 | ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", | |
| 2264 | ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", | |
| 32176cfd RP |
2265 | ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", |
| 2266 | ni->ni_flags & IEEE80211_NODE_HT ? | |
| 2267 | (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", | |
| 2268 | ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", | |
| 2269 | ni->ni_flags & IEEE80211_NODE_MIMO_RTS ? " (+SMPS-DYN)" : | |
| 2270 | ni->ni_flags & IEEE80211_NODE_MIMO_PS ? " (+SMPS)" : "", | |
| 2271 | ni->ni_flags & IEEE80211_NODE_RIFS ? " (+RIFS)" : "", | |
| 2272 | IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ? | |
| 2273 | ", fast-frames" : "", | |
| 2274 | IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ? | |
| 2275 | ", turbo" : "" | |
| 841ab66c SZ |
2276 | ); |
| 2277 | ||
| 32176cfd | 2278 | ieee80211_node_setuptxparms(ni); |
| 841ab66c SZ |
2279 | /* give driver a chance to setup state like ni_txrate */ |
| 2280 | if (ic->ic_newassoc != NULL) | |
| 2281 | ic->ic_newassoc(ni, newassoc); | |
| 32176cfd | 2282 | IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_SUCCESS); |
| 841ab66c | 2283 | /* tell the authenticator about new station */ |
| 32176cfd RP |
2284 | if (vap->iv_auth->ia_node_join != NULL) |
| 2285 | vap->iv_auth->ia_node_join(ni); | |
| 2286 | ieee80211_notify_node_join(ni, | |
| 2287 | resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); | |
| 2288 | } | |
| 2289 | ||
| 2290 | static void | |
| 2291 | disable_protection(struct ieee80211com *ic) | |
| 2292 | { | |
| 2293 | KASSERT(ic->ic_nonerpsta == 0 && | |
| 2294 | (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0, | |
| 2295 | ("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta, | |
| 2296 | ic->ic_flags_ext)); | |
| 2297 | ||
| 2298 | ic->ic_flags &= ~IEEE80211_F_USEPROT; | |
| 2299 | /* XXX verify mode? */ | |
| 2300 | if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { | |
| 2301 | ic->ic_flags |= IEEE80211_F_SHPREAMBLE; | |
| 2302 | ic->ic_flags &= ~IEEE80211_F_USEBARKER; | |
| 2303 | } | |
| 2304 | ieee80211_notify_erp_locked(ic); | |
| 841ab66c SZ |
2305 | } |
| 2306 | ||
| 2307 | /* | |
| 2308 | * Handle a station leaving an 11g network. | |
| 2309 | */ | |
| 2310 | static void | |
| 32176cfd | 2311 | ieee80211_node_leave_11g(struct ieee80211_node *ni) |
| 841ab66c | 2312 | { |
| 32176cfd RP |
2313 | struct ieee80211com *ic = ni->ni_ic; |
| 2314 | ||
| 32176cfd RP |
2315 | KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan), |
| 2316 | ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq, | |
| 2317 | ic->ic_bsschan->ic_flags)); | |
| 841ab66c SZ |
2318 | |
| 2319 | /* | |
| 2320 | * If a long slot station do the slot time bookkeeping. | |
| 2321 | */ | |
| 2322 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { | |
| 2323 | KASSERT(ic->ic_longslotsta > 0, | |
| 2324 | ("bogus long slot station count %d", ic->ic_longslotsta)); | |
| 2325 | ic->ic_longslotsta--; | |
| 32176cfd RP |
2326 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, |
| 2327 | "long slot time station leaves, count now %d", | |
| 2328 | ic->ic_longslotsta); | |
| 841ab66c SZ |
2329 | if (ic->ic_longslotsta == 0) { |
| 2330 | /* | |
| 2331 | * Re-enable use of short slot time if supported | |
| 2332 | * and not operating in IBSS mode (per spec). | |
| 2333 | */ | |
| 2334 | if ((ic->ic_caps & IEEE80211_C_SHSLOT) && | |
| 2335 | ic->ic_opmode != IEEE80211_M_IBSS) { | |
| 32176cfd RP |
2336 | IEEE80211_DPRINTF(ni->ni_vap, |
| 2337 | IEEE80211_MSG_ASSOC, | |
| 841ab66c SZ |
2338 | "%s: re-enable use of short slot time\n", |
| 2339 | __func__); | |
| 2340 | ieee80211_set_shortslottime(ic, 1); | |
| 2341 | } | |
| 2342 | } | |
| 2343 | } | |
| 2344 | /* | |
| 2345 | * If a non-ERP station do the protection-related bookkeeping. | |
| 2346 | */ | |
| 2347 | if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { | |
| 2348 | KASSERT(ic->ic_nonerpsta > 0, | |
| 2349 | ("bogus non-ERP station count %d", ic->ic_nonerpsta)); | |
| 2350 | ic->ic_nonerpsta--; | |
| 32176cfd RP |
2351 | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, |
| 2352 | "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta, | |
| 2353 | (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ? | |
| 2354 | " (non-ERP sta present)" : ""); | |
| 2355 | if (ic->ic_nonerpsta == 0 && | |
| 2356 | (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) { | |
| 2357 | IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, | |
| 841ab66c | 2358 | "%s: disable use of protection\n", __func__); |
| 32176cfd | 2359 | disable_protection(ic); |
| f186073c JS |
2360 | } |
| 2361 | } | |
| f186073c JS |
2362 | } |
| 2363 | ||
| 841ab66c | 2364 | /* |
| 32176cfd RP |
2365 | * Time out presence of an overlapping bss with non-ERP |
| 2366 | * stations. When operating in hostap mode we listen for | |
| 2367 | * beacons from other stations and if we identify a non-ERP | |
| 2368 | * station is present we enable protection. To identify | |
| 2369 | * when all non-ERP stations are gone we time out this | |
| 2370 | * condition. | |
| 2371 | */ | |
| 2372 | static void | |
| 2373 | ieee80211_erp_timeout(struct ieee80211com *ic) | |
| 2374 | { | |
| 32176cfd RP |
2375 | if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) && |
| 2376 | time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) { | |
| 2377 | #if 0 | |
| 2378 | IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, | |
| 2379 | "%s", "age out non-ERP sta present on channel"); | |
| 2380 | #endif | |
| 2381 | ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR; | |
| 2382 | if (ic->ic_nonerpsta == 0) | |
| 2383 | disable_protection(ic); | |
| 2384 | } | |
| 2385 | } | |
| 2386 | ||
| 2387 | /* | |
| 841ab66c SZ |
2388 | * Handle bookkeeping for station deauthentication/disassociation |
| 2389 | * when operating as an ap. | |
| 2390 | */ | |
| f186073c | 2391 | void |
| 32176cfd | 2392 | ieee80211_node_leave(struct ieee80211_node *ni) |
| f186073c | 2393 | { |
| 32176cfd RP |
2394 | struct ieee80211com *ic = ni->ni_ic; |
| 2395 | struct ieee80211vap *vap = ni->ni_vap; | |
| 841ab66c SZ |
2396 | struct ieee80211_node_table *nt = ni->ni_table; |
| 2397 | ||
| 32176cfd RP |
2398 | IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, |
| 2399 | "station with aid %d leaves", IEEE80211_NODE_AID(ni)); | |
| 841ab66c | 2400 | |
| 32176cfd RP |
2401 | KASSERT(vap->iv_opmode != IEEE80211_M_STA, |
| 2402 | ("unexpected operating mode %u", vap->iv_opmode)); | |
| 841ab66c SZ |
2403 | /* |
| 2404 | * If node wasn't previously associated all | |
| 2405 | * we need to do is reclaim the reference. | |
| 2406 | */ | |
| 2407 | /* XXX ibss mode bypasses 11g and notification */ | |
| 2408 | if (ni->ni_associd == 0) | |
| 2409 | goto done; | |
| 2410 | /* | |
| 2411 | * Tell the authenticator the station is leaving. | |
| 2412 | * Note that we must do this before yanking the | |
| 2413 | * association id as the authenticator uses the | |
| 2414 | * associd to locate it's state block. | |
| 2415 | */ | |
| 32176cfd RP |
2416 | if (vap->iv_auth->ia_node_leave != NULL) |
| 2417 | vap->iv_auth->ia_node_leave(ni); | |
| 2418 | ||
| 32176cfd | 2419 | IEEE80211_AID_CLR(vap, ni->ni_associd); |
| 841ab66c | 2420 | ni->ni_associd = 0; |
| 32176cfd | 2421 | vap->iv_sta_assoc--; |
| 841ab66c SZ |
2422 | ic->ic_sta_assoc--; |
| 2423 | ||
| 32176cfd RP |
2424 | if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) |
| 2425 | ieee80211_ht_node_leave(ni); | |
| 2426 | if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && | |
| 2427 | IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) | |
| 2428 | ieee80211_node_leave_11g(ni); | |
| 841ab66c SZ |
2429 | /* |
| 2430 | * Cleanup station state. In particular clear various | |
| 2431 | * state that might otherwise be reused if the node | |
| 2432 | * is reused before the reference count goes to zero | |
| 2433 | * (and memory is reclaimed). | |
| 2434 | */ | |
| 32176cfd | 2435 | ieee80211_sta_leave(ni); |
| 841ab66c SZ |
2436 | done: |
| 2437 | /* | |
| 2438 | * Remove the node from any table it's recorded in and | |
| 2439 | * drop the caller's reference. Removal from the table | |
| 2440 | * is important to insure the node is not reprocessed | |
| 2441 | * for inactivity. | |
| 2442 | */ | |
| 32176cfd | 2443 | if (nt != NULL) { |
| 841ab66c | 2444 | node_reclaim(nt, ni); |
| 32176cfd | 2445 | } else |
| 841ab66c SZ |
2446 | ieee80211_free_node(ni); |
| 2447 | } | |
| 2448 | ||
| 32176cfd RP |
2449 | struct rssiinfo { |
| 2450 | struct ieee80211vap *vap; | |
| 2451 | int rssi_samples; | |
| 2452 | uint32_t rssi_total; | |
| 2453 | }; | |
| 841ab66c | 2454 | |
| 841ab66c | 2455 | static void |
| 32176cfd | 2456 | get_hostap_rssi(void *arg, struct ieee80211_node *ni) |
| 841ab66c | 2457 | { |
| 32176cfd RP |
2458 | struct rssiinfo *info = arg; |
| 2459 | struct ieee80211vap *vap = ni->ni_vap; | |
| 2460 | int8_t rssi; | |
| 841ab66c | 2461 | |
| 32176cfd RP |
2462 | if (info->vap != vap) |
| 2463 | return; | |
| 2464 | /* only associated stations */ | |
| 2465 | if (ni->ni_associd == 0) | |
| 2466 | return; | |
| 2467 | rssi = vap->iv_ic->ic_node_getrssi(ni); | |
| 2468 | if (rssi != 0) { | |
| 2469 | info->rssi_samples++; | |
| 2470 | info->rssi_total += rssi; | |
| 841ab66c SZ |
2471 | } |
| 2472 | } | |
| 2473 | ||
| 841ab66c | 2474 | static void |
| 32176cfd | 2475 | get_adhoc_rssi(void *arg, struct ieee80211_node *ni) |
| 841ab66c | 2476 | { |
| 32176cfd RP |
2477 | struct rssiinfo *info = arg; |
| 2478 | struct ieee80211vap *vap = ni->ni_vap; | |
| 2479 | int8_t rssi; | |
| 841ab66c | 2480 | |
| 32176cfd RP |
2481 | if (info->vap != vap) |
| 2482 | return; | |
| 2483 | /* only neighbors */ | |
| 2484 | /* XXX check bssid */ | |
| 2485 | if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) | |
| 2486 | return; | |
| 2487 | rssi = vap->iv_ic->ic_node_getrssi(ni); | |
| 2488 | if (rssi != 0) { | |
| 2489 | info->rssi_samples++; | |
| 2490 | info->rssi_total += rssi; | |
| 841ab66c SZ |
2491 | } |
| 2492 | } | |
| 2493 | ||
| 32176cfd | 2494 | #ifdef IEEE80211_SUPPORT_MESH |
| 841ab66c | 2495 | static void |
| 32176cfd | 2496 | get_mesh_rssi(void *arg, struct ieee80211_node *ni) |
| 841ab66c | 2497 | { |
| 32176cfd RP |
2498 | struct rssiinfo *info = arg; |
| 2499 | struct ieee80211vap *vap = ni->ni_vap; | |
| 2500 | int8_t rssi; | |
| 841ab66c | 2501 | |
| 32176cfd RP |
2502 | if (info->vap != vap) |
| 2503 | return; | |
| 2504 | /* only neighbors that peered successfully */ | |
| 2505 | if (ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED) | |
| 2506 | return; | |
| 2507 | rssi = vap->iv_ic->ic_node_getrssi(ni); | |
| 2508 | if (rssi != 0) { | |
| 2509 | info->rssi_samples++; | |
| 2510 | info->rssi_total += rssi; | |
| 841ab66c | 2511 | } |
| f186073c | 2512 | } |
| 32176cfd | 2513 | #endif /* IEEE80211_SUPPORT_MESH */ |
| ab0665aa | 2514 | |
| 32176cfd RP |
2515 | int8_t |
| 2516 | ieee80211_getrssi(struct ieee80211vap *vap) | |
| ab0665aa | 2517 | { |
| 32176cfd RP |
2518 | #define NZ(x) ((x) == 0 ? 1 : (x)) |
| 2519 | struct ieee80211com *ic = vap->iv_ic; | |
| 2520 | struct rssiinfo info; | |
| ab0665aa | 2521 | |
| 32176cfd RP |
2522 | info.rssi_total = 0; |
| 2523 | info.rssi_samples = 0; | |
| 2524 | info.vap = vap; | |
| 2525 | switch (vap->iv_opmode) { | |
| 2526 | case IEEE80211_M_IBSS: /* average of all ibss neighbors */ | |
| 2527 | case IEEE80211_M_AHDEMO: /* average of all neighbors */ | |
| 2528 | ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info); | |
| ab0665aa | 2529 | break; |
| 32176cfd RP |
2530 | case IEEE80211_M_HOSTAP: /* average of all associated stations */ |
| 2531 | ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info); | |
| 2532 | break; | |
| 2533 | #ifdef IEEE80211_SUPPORT_MESH | |
| 2534 | case IEEE80211_M_MBSS: /* average of all mesh neighbors */ | |
| 2535 | ieee80211_iterate_nodes(&ic->ic_sta, get_mesh_rssi, &info); | |
| 2536 | break; | |
| 2537 | #endif | |
| 2538 | case IEEE80211_M_MONITOR: /* XXX */ | |
| 2539 | case IEEE80211_M_STA: /* use stats from associated ap */ | |
| ab0665aa | 2540 | default: |
| 32176cfd RP |
2541 | if (vap->iv_bss != NULL) |
| 2542 | info.rssi_total = ic->ic_node_getrssi(vap->iv_bss); | |
| 2543 | info.rssi_samples = 1; | |
| ab0665aa SZ |
2544 | break; |
| 2545 | } | |
| 32176cfd RP |
2546 | return info.rssi_total / NZ(info.rssi_samples); |
| 2547 | #undef NZ | |
| ab0665aa | 2548 | } |
| f39a365a SZ |
2549 | |
| 2550 | void | |
| 32176cfd | 2551 | ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise) |
| f39a365a | 2552 | { |
| 32176cfd RP |
2553 | |
| 2554 | if (vap->iv_bss == NULL) /* NB: shouldn't happen */ | |
| 2555 | return; | |
| 2556 | vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise); | |
| 2557 | /* for non-station mode return avg'd rssi accounting */ | |
| 2558 | if (vap->iv_opmode != IEEE80211_M_STA) | |
| 2559 | *rssi = ieee80211_getrssi(vap); | |
| f39a365a | 2560 | } |