void ieee80211_recv_beacon_ibss(struct ieee80211_node *ni, wbuf_t wbuf, int subtype, struct ieee80211_rx_status *rs, ieee80211_scan_entry_t scan_entry) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; u_int16_t capinfo; int ibssmerge = 0; struct ieee80211_channelswitch_ie *chanie = NULL; struct ieee80211_extendedchannelswitch_ie *echanie = NULL; struct ieee80211_frame *wh; u_int64_t tsf; u_int8_t *quiet_elm = NULL; bool free_node = FALSE; #if ATH_SUPPORT_IBSS_DFS struct ieee80211_ibssdfs_ie *ibssdfsie = NULL; #endif /* ATH_SUPPORT_IBSS_DFS */ wh = (struct ieee80211_frame *)wbuf_header(wbuf); capinfo = ieee80211_scan_entry_capinfo(scan_entry); if (!(capinfo & IEEE80211_CAPINFO_IBSS)) return; OS_MEMCPY((u_int8_t *)&tsf, ieee80211_scan_entry_tsf(scan_entry), sizeof(tsf)); if (ni != ni->ni_bss_node) { OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); /* update activity indication */ ni->ni_beacon_rstamp = OS_GET_TIMESTAMP(); ni->ni_probe_ticks = 0; } /* * Check BBSID before updating our beacon configuration to make * sure the received beacon really belongs to our Adhoc network. */ if ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) && ieee80211_vap_ready_is_set(vap) && (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(vap->iv_bss)))) { /* * if the neighbor is not in the list, add it to the list. */ if (ni == ni->ni_bss_node) { ni = ieee80211_add_neighbor(ni, scan_entry); if (ni == NULL) { return; } OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); free_node = TRUE; } ni->ni_rssi = rs->rs_rssi; /* * record tsf of last beacon into bss node */ OS_MEMCPY(ni->ni_bss_node->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); /* * record absolute time of last beacon */ vap->iv_last_beacon_time = OS_GET_TIMESTAMP(); ic->ic_beacon_update(ni, rs->rs_rssi); #if ATH_SUPPORT_IBSS_WMM /* * check for WMM parameters */ if ((ieee80211_scan_entry_wmeparam_ie(scan_entry) != NULL) || (ieee80211_scan_entry_wmeinfo_ie(scan_entry) != NULL)) { /* Node is WMM-capable if WME IE (either subtype) is present */ ni->ni_ext_caps |= IEEE80211_NODE_C_QOS; /* QOS-enable */ ieee80211node_set_flag(ni, IEEE80211_NODE_QOS); } else { /* If WME IE not present node is not WMM capable */ ni->ni_ext_caps &= ~IEEE80211_NODE_C_QOS; ieee80211node_clear_flag(ni, IEEE80211_NODE_QOS); } #endif #if ATH_SUPPORT_IBSS_DFS if (ic->ic_curchan->ic_flagext & IEEE80211_CHAN_DFS) { //check and see if we need to update other info for ibss_dfsie ibssdfsie = (struct ieee80211_ibssdfs_ie *)ieee80211_scan_entry_ibssdfs_ie(scan_entry); if(ibssdfsie) { if (ieee80211_check_and_update_ibss_dfs(vap, ibssdfsie)) { ieee80211_ibss_beacon_update_start(ic); } } /* update info from DFS owner */ if(ibssdfsie){ if(IEEE80211_ADDR_EQ(wh->i_addr2, ibssdfsie->owner)) { if(OS_MEMCMP(ibssdfsie, &vap->iv_ibssdfs_ie_data, MIN_IBSS_DFS_IE_CONTENT_SIZE)) { if(le64_to_cpu(tsf) >= rs->rs_tstamp.tsf){ IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, ibssdfsie->owner); vap->iv_ibssdfs_ie_data.rec_interval = ibssdfsie->rec_interval; ieee80211_ibss_beacon_update_start(ic); } } } } /* check if owner and it is not transmitting ibssIE */ else if(IEEE80211_ADDR_EQ(wh->i_addr2, vap->iv_ibssdfs_ie_data.owner)){ IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, vap->iv_myaddr); vap->iv_ibssdfs_state = IEEE80211_IBSSDFS_OWNER; ieee80211_ibss_beacon_update_start(ic); } } #endif /* ATH_SUPPORT_IBSS_DFS */ /* * check for spectrum management */ if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { chanie = (struct ieee80211_channelswitch_ie *) ieee80211_scan_entry_csa(scan_entry); #if ATH_SUPPORT_IBSS_DFS if(chanie) { OS_MEMCPY(&vap->iv_channelswitch_ie_data, chanie, sizeof(struct ieee80211_channelswitch_ie)); ieee80211_ibss_beacon_update_start(ic); } #endif /* ATH_SUPPORT_IBSS_DFS */ echanie = (struct ieee80211_extendedchannelswitch_ie *) ieee80211_scan_entry_xcsa(scan_entry); if ((!chanie) && (!echanie)) { quiet_elm = ieee80211_scan_entry_quiet(scan_entry); } } } /* * Handle ibss merge as needed; check the tsf on the * frame before attempting the merge. The 802.11 spec * says the station should change its bssid to match * the oldest station with the same ssid, where oldest * is determined by the tsf. Note that hardware * reconfiguration happens through callback to * ath as the state machine will go from * RUN -> RUN when this happens. */ if (ieee80211_vap_ready_is_set(vap) && (le64_to_cpu(tsf) >= rs->rs_tstamp.tsf)) { ibssmerge = ieee80211_ibss_merge(ni,scan_entry); } if (ibssmerge) { ieee80211_mlme_adhoc_merge_start(ni); } /* * RNWF-specific: indicate to NDIS about the potential association. * It should be done after IBSS merge, which is called from ath_beacon_update(). */ if (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(ni))) { /* Indicate node joined IBSS */ if ((capinfo & IEEE80211_CAPINFO_RADIOMEAS) && ieee80211_vap_rrm_is_set(vap)) { if(ni->ni_assoc_state != IEEE80211_NODE_ADHOC_STATE_AUTH_ASSOC) ieee80211_set_node_rrm(ni,TRUE); } else { ieee80211_set_node_rrm(ni,FALSE); } ieee80211_mlme_adhoc_join_indication(ni, wbuf); /* notify mlme of beacon reception */ ieee80211_mlme_join_complete_adhoc(ni); } if (ibssmerge) { ieee80211_mlme_adhoc_merge_completion(ni); } if (quiet_elm) { ic->ic_set_quiet(ni, quiet_elm); } if (free_node == TRUE) { ieee80211_free_node(ni); } }
/* * function to handle station joining infrastructure network. * used for AP mode vap only. */ int ieee80211_node_join(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; IEEE80211_NODE_STATE_LOCK(ni); if (ni->ni_associd == 0) { u_int16_t aid; if (vap->iv_aid_bitmap == NULL || IEEE80211_IS_TDLS_NODE(ni)) { IEEE80211_NODE_STATE_UNLOCK(ni); return -1; /* vap is being deleted */ } /* * It would be good to search the bitmap * more efficiently, but this will do for now. */ if (!IEEE80211_IS_TDLS_NODE(ni)) { for (aid = 1; aid < vap->iv_max_aid; aid++) { if (!IEEE80211_AID_ISSET(vap, aid)) break; } if (aid >= vap->iv_max_aid) { /* * Keep stats on this situation. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "aid (%d)" " greater than max aid (%d)\n", aid, vap->iv_max_aid); IEEE80211_NODE_STATE_UNLOCK(ni); IEEE80211_NODE_LEAVE(ni); return -1; } ni->ni_associd = aid | 0xc000; IEEE80211_AID_SET(vap, ni->ni_associd); } else { /* To keep the other parts of code happy. Otherwise the code assumes * node disconnected and tries to delete it.There are no associd for * TDLS nodes. */ ni->ni_associd = vap->iv_bss->ni_associd; } vap->iv_sta_assoc++; ic->ic_sta_assoc++; /* Update bss load element in beacon */ ieee80211_vap_bssload_update_set(vap); if ((ni->ni_flags & IEEE80211_NODE_HT) && (ni->ni_flags & IEEE80211_NODE_40_INTOLERANT)) { ieee80211_change_cw(ic); } if (IEEE80211_IS_AMPDU_ENABLED(ic)) { ieee80211node_clear_flag(ni, IEEE80211_NODE_NOAMPDU); } else { ieee80211node_set_flag(ni, IEEE80211_NODE_NOAMPDU); } if (IEEE80211_NODE_USEAMPDU(ni)) { ic->ic_ht_sta_assoc++; if (ni->ni_htcap & IEEE80211_HTCAP_C_GREENFIELD) ic->ic_ht_gf_sta_assoc++; } if (ni->ni_chwidth == IEEE80211_CWM_WIDTH40) ic->ic_ht40_sta_assoc++; if ((IEEE80211_IS_CHAN_ANYG(vap->iv_bsschan) || IEEE80211_IS_CHAN_11NG(vap->iv_bsschan)) && !IEEE80211_IS_TDLS_NODE(ni)) ieee80211_node_join_11g(ni); } #if ATH_SUPPORT_WIFIPOS // Time stamp to send in status response ni->last_rx_desc_tsf = ic->ic_get_TSF32(ic); #endif ni->ni_inact_reload = ni->ni_vap->iv_inact_auth; ni->ni_inact = ni->ni_inact_reload; /* Multicast enhancement: If the entry with the node's address exists in * the snoop table, it should be removed. */ if (vap->iv_ique_ops.me_clean) { vap->iv_ique_ops.me_clean(ni); } /* * HBR / headline block removal: add the node entity to the table * for HBR purpose */ if (vap->iv_ique_ops.hbr_nodejoin) { vap->iv_ique_ops.hbr_nodejoin(vap, ni); } IEEE80211_NODE_STATE_UNLOCK(ni); IEEE80211_ADD_NODE_TARGET(ni, ni->ni_vap, 0); return 0; }
/* * Process information elements from association response frame. * This includes rate negociation, wmm parameter updating and etc. */ static int mlme_process_asresp_elements(struct ieee80211_node *ni, u_int8_t *frm, u_int32_t ie_len) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_rsnparms *rsn = &vap->iv_rsn; struct ieee80211com *ic = ni->ni_ic; u_int8_t *efrm = frm + ie_len; u_int8_t *rates, *xrates, *wme, *htcap, *tspecie, *athextcap; u_int8_t qosinfo; int ht_rates_allowed; ASSERT((vap->iv_opmode != IEEE80211_M_STA) || (ni == vap->iv_bss)); rates = xrates = wme = htcap = tspecie = athextcap = NULL; while (frm < efrm) { switch (*frm) { case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_XRATES: xrates = frm; break; case IEEE80211_ELEMID_HTCAP_ANA: htcap = (u_int8_t *)&((struct ieee80211_ie_htcap *)frm)->hc_ie; break; case IEEE80211_ELEMID_HTCAP: if (htcap == NULL) { htcap = (u_int8_t *)&((struct ieee80211_ie_htcap *)frm)->hc_ie; } break; case IEEE80211_ELEMID_VENDOR: if (iswmeoui(frm, WME_PARAM_OUI_SUBTYPE)) wme = frm; else if (iswmeoui(frm, WME_INFO_OUI_SUBTYPE)) wme = frm; else if (iswmeoui(frm, WME_TSPEC_OUI_SUBTYPE)) tspecie = frm; else if(ishtcap(frm)) { if (htcap == NULL) { htcap = (u_int8_t *)&((struct vendor_ie_htcap *)frm)->hc_ie; } } else if (isatheros_extcap_oui(frm)) athextcap = frm; break; } frm += frm[1] + 2; } if (!rates || (rates[1] > IEEE80211_RATE_MAXSIZE)) { /* XXX: msg + stats */ return -EINVAL; } if (!ieee80211_setup_rates(ni, rates, xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DOXSECT)) { printk("%s: association failed (rate set mismatch)\n", __func__); return -EINVAL; } /* * U-APSD is enabled/disabled per-association. */ if (wme != NULL) { /* Parse IE according to subtype */ if (iswmeparam(wme)) { /* Association is QOS-enabled */ ieee80211node_set_flag(ni, IEEE80211_NODE_QOS); if (vap->iv_opmode != IEEE80211_M_BTAMP && ieee80211_parse_wmeparams(vap, wme, &qosinfo, 1) >= 0) { /* Check if association is UAPSD-enabled */ if (qosinfo & WME_CAPINFO_UAPSD_EN) { ieee80211node_set_flag(ni, IEEE80211_NODE_UAPSD); } else { ieee80211node_clear_flag(ni, IEEE80211_NODE_UAPSD); } ieee80211_wme_updateparams(vap); } } else { /*** QOS requires WME Param */ ieee80211node_clear_flag(ni, IEEE80211_NODE_QOS); if (ieee80211_parse_wmeinfo(vap, wme, &qosinfo) >= 0) { /* Check if association is UAPSD-enabled */ if (qosinfo & WME_CAPINFO_UAPSD_EN) { ieee80211node_set_flag(ni, IEEE80211_NODE_UAPSD); } else { ieee80211node_clear_flag(ni, IEEE80211_NODE_UAPSD); } ieee80211_wme_updateinfo(vap); } } } else { ieee80211node_clear_flag(ni, IEEE80211_NODE_QOS); ieee80211node_clear_flag(ni, IEEE80211_NODE_UAPSD); } if ((tspecie != NULL) && (ieee80211_parse_tspecparams(vap, tspecie) >= 0)) { /* store the tspec */ } if (athextcap != NULL) ieee80211_process_athextcap_ie(ni, athextcap); /* * With WEP and TKIP encryption algorithms: * Disable aggregation if IEEE80211_NODE_WEPTKIPAGGR is not set. * Disable 11n if IEEE80211_FEXT_WEP_TKIP_HTRATE is not set. */ ht_rates_allowed = IEEE80211_IS_CHAN_11N(ic->ic_curchan); if (IEEE80211_VAP_IS_PRIVACY_ENABLED(vap) && (RSN_CIPHER_IS_WEP(rsn) || (RSN_CIPHER_IS_TKIP(rsn) && !RSN_CIPHER_IS_CCMP(rsn)))) { ieee80211node_set_flag(ni, IEEE80211_NODE_WEPTKIP); if (ieee80211_ic_wep_tkip_htrate_is_set(ic)) { if (!ieee80211_has_weptkipaggr(ni)) ieee80211node_set_flag(ni, IEEE80211_NODE_NOAMPDU); } else { ht_rates_allowed = 0; } } /* 11n - HT rates not allowed using WEP and TKIP */ if ((htcap != NULL) && (ht_rates_allowed)) { /* record capabilities, mark node as capable of HT */ ieee80211_parse_htcap(ni, htcap); if (!ieee80211_setup_ht_rates(ni, htcap, IEEE80211_F_DOFRATE | IEEE80211_F_DOXSECT | IEEE80211_F_DOBRS)) { printk("%s: association failed (rate set mismatch)\n", __func__); return -EINVAL; } #ifdef ATH_SUPPORT_TxBF // set keycache for txbf after sta associated successfully. if ( ni->ni_explicit_compbf || ni->ni_explicit_noncompbf || ni->ni_implicit_bf){ struct ieee80211com *ic = vap->iv_ic; ieee80211_set_TxBF_keycache(ic,ni); ni->ni_bf_update_cv = 1; ni->ni_allow_cv_update = 1; } #endif } else { /* * Flush any state from a previous association. */ ieee80211node_clear_flag(ni, IEEE80211_NODE_HT); IEEE80211_NODE_CLEAR_HTCAP(ni); } return EOK; }