static int hwmp_send_action(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], const uint8_t da[IEEE80211_ADDR_LEN], uint8_t *ie, size_t len) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; #ifdef IEEE80211_DEBUG_REFCNT char ethstr[ETHER_ADDRSTRLEN + 1]; #endif if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "HWMP action"); vap->iv_stats.is_tx_badstate++; return EIO; /* XXX */ } KASSERT(ni != NULL, ("null node")); /* * Hold a reference on the node so it doesn't go away until after * the xmit is complete all the way in the driver. On error we * will remove our reference. */ #ifdef IEEE80211_DEBUG_REFCNT IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, kether_ntoa(ni->ni_macaddr, ethstr), ieee80211_node_refcnt(ni)+1); #endif ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(struct ieee80211_action) + len ); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } *frm++ = IEEE80211_ACTION_CAT_MESHPATH; *frm++ = IEEE80211_ACTION_MESHPATH_SEL; switch (*ie) { case IEEE80211_ELEMID_MESHPREQ: frm = hwmp_add_meshpreq(frm, (struct ieee80211_meshpreq_ie *)ie); break; case IEEE80211_ELEMID_MESHPREP: frm = hwmp_add_meshprep(frm, (struct ieee80211_meshprep_ie *)ie); break; case IEEE80211_ELEMID_MESHPERR: frm = hwmp_add_meshperr(frm, (struct ieee80211_meshperr_ie *)ie); break; case IEEE80211_ELEMID_MESHRANN: frm = hwmp_add_meshrann(frm, (struct ieee80211_meshrann_ie *)ie); break; } m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT); if (m == NULL) { ieee80211_free_node(ni); vap->iv_stats.is_tx_nobuf++; return ENOMEM; } ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, IEEE80211_NONQOS_TID, sa, da, sa); m->m_flags |= M_ENCAP; /* mark encapsulated */ IEEE80211_NODE_STAT(ni, tx_mgmt); memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; params.ibp_rate0 = ni->ni_txparms->mgmtrate; if (IEEE80211_IS_MULTICAST(da)) params.ibp_try0 = 1; else params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; return ic->ic_raw_xmit(ni, m, ¶ms); }
static void tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, int rssi, int nf) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tdma_state *ts = vap->iv_tdma; if (subtype == IEEE80211_FC0_SUBTYPE_BEACON && (ic->ic_flags & IEEE80211_F_SCAN) == 0) { struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); struct ieee80211_scanparams scan; if (ieee80211_parse_beacon(ni, m0, &scan) != 0) return; if (scan.tdma == NULL) { /* * TDMA stations must beacon a TDMA ie; ignore * any other station. * XXX detect overlapping bss and change channel */ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT, wh, ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], "%s", "no TDMA ie"); vap->iv_stats.is_rx_mgtdiscard++; return; } if (ni == vap->iv_bss && !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { /* * Fake up a node for this newly * discovered member of the IBSS. */ ni = ieee80211_add_neighbor(vap, wh, &scan); if (ni == NULL) { /* NB: stat kept for alloc failure */ return; } } /* * Check for state updates. */ if (IEEE80211_ADDR_EQ(wh->i_addr3, ni->ni_bssid)) { /* * Count frame now that we know it's to be processed. */ vap->iv_stats.is_rx_beacon++; IEEE80211_NODE_STAT(ni, rx_beacons); /* * Record tsf of last beacon. NB: this must be * done before calling tdma_process_params * as deeper routines reference it. */ memcpy(&ni->ni_tstamp.data, scan.tstamp, sizeof(ni->ni_tstamp.data)); /* * Count beacon frame for s/w bmiss handling. */ vap->iv_swbmiss_count++; /* * Process tdma ie. The contents are used to sync * the slot timing, reconfigure the bss, etc. */ (void) tdma_process_params(ni, scan.tdma, rssi, nf, wh); return; } /* * NB: defer remaining work to the adhoc code; this causes * 2x parsing of the frame but should happen infrequently */ }
/* * Save an outbound packet for a node in power-save sleep state. * The new packet is placed on the node's saved queue, and the TIM * is changed, if necessary. */ void ieee80211_node_saveq_queue(struct ieee80211_node *ni, wbuf_t wbuf, u_int8_t frame_type) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; int qlen, age; struct node_powersave_queue *dataq,*mgtq,*psq; struct ieee80211_tx_status ts; #if LMAC_SUPPORT_POWERSAVE_QUEUE /* mark the frame and will give to ath layer */ wbuf_set_legacy_ps(wbuf); if (ic->ic_get_lmac_pwrsaveq_len(ic, ni, 0) == 0 && vap->iv_set_tim != NULL) { vap->iv_set_tim(ni, 1, false); } return; #endif dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); IEEE80211_NODE_SAVEQ_LOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); if (frame_type == IEEE80211_FC0_TYPE_MGT) { psq = mgtq; } else { psq = dataq; } if (IEEE80211_NODE_SAVEQ_FULL(psq)) { IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "%s pwr save q: overflow, drops (size %d) \n", (psq == dataq) ? "data" : "mgt", IEEE80211_PS_MAX_QUEUE); IEEE80211_NODE_STAT(ni,psq_drops); #ifdef IEEE80211_DEBUG if (ieee80211_msg_dumppkts(vap)) ieee80211_dump_pkt(ni->ni_ic, wtod(wbuf, caddr_t), wbuf_get_len(wbuf), -1, -1); #endif ts.ts_flags = IEEE80211_TX_ERROR; ieee80211_release_wbuf(ni,wbuf, &ts); return; } /* * special handling of frames with PS = 1. */ ieee80211_node_saveq_handle_ps_frames(ni,wbuf,frame_type); /* * Tag the frame with it's expiry time and insert * it in the queue. The aging interval is 4 times * the listen interval specified by the station. * Frames that sit around too long are reclaimed * using this information. */ age = ((ni->ni_intval * ic->ic_lintval) << 2) >> 10; /* TU -> secs */ /* * Note: our aging algorithm is not working well. In fact, due to the long interval * when the aging algorithm is called (IEEE80211_INACT_WAIT is 15 secs), we depend on * the associated station node to be disassociated to clear its stale frames. However, * as a temporary fix, I will make sure that the age is at least greater than * IEEE80211_INACT_WAIT. Otherwise, we will discard all frames in ps queue even though * it is just queued. */ if (age < IEEE80211_INACT_WAIT) { IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "%s Note: increased age from %d to %d secs.\n", __func__, age, IEEE80211_INACT_WAIT); age = IEEE80211_INACT_WAIT; } IEEE80211_NODE_SAVEQ_ENQUEUE(psq, wbuf, qlen, age); /* * calculate the combined queue length of management and data queues. */ qlen = IEEE80211_NODE_SAVEQ_QLEN(dataq); qlen += IEEE80211_NODE_SAVEQ_QLEN(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "%s pwr queue:save frame, %u now queued \n", (psq == dataq) ? "data" : "mgt" ,qlen); if (qlen == 1 && vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 1, false); }