/* * RadioTap utility routines for WL * This file housing the functions use by * wl driver. * * Copyright (C) 2022, Broadcom. * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that * you also meet, for each linked independent module, the terms and conditions of * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. * * * <> */ #include #include #include #include #include const struct rtap_field rtap_parse_info[] = { {8, 8}, /* 0: IEEE80211_RADIOTAP_TSFT */ {1, 1}, /* 1: IEEE80211_RADIOTAP_FLAGS */ {1, 1}, /* 2: IEEE80211_RADIOTAP_RATE */ {4, 2}, /* 3: IEEE80211_RADIOTAP_CHANNEL */ {2, 2}, /* 4: IEEE80211_RADIOTAP_FHSS */ {1, 1}, /* 5: IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ {1, 1}, /* 6: IEEE80211_RADIOTAP_DBM_ANTNOISE */ {2, 2}, /* 7: IEEE80211_RADIOTAP_LOCK_QUALITY */ {2, 2}, /* 8: IEEE80211_RADIOTAP_TX_ATTENUATION */ {2, 2}, /* 9: IEEE80211_RADIOTAP_DB_TX_ATTENUATION */ {1, 1}, /* 10: IEEE80211_RADIOTAP_DBM_TX_POWER */ {1, 1}, /* 11: IEEE80211_RADIOTAP_ANTENNA */ {1, 1}, /* 12: IEEE80211_RADIOTAP_DB_ANTSIGNAL */ {1, 1}, /* 13: IEEE80211_RADIOTAP_DB_ANTNOISE */ {0, 0}, /* 14: netbsd */ {2, 2}, /* 15: IEEE80211_RADIOTAP_TXFLAGS */ {0, 0}, /* 16: missing */ {1, 1}, /* 17: IEEE80211_RADIOTAP_RETRIES */ {8, 4}, /* 18: IEEE80211_RADIOTAP_XCHANNEL */ {3, 1}, /* 19: IEEE80211_RADIOTAP_MCS */ {8, 4}, /* 20: IEEE80211_RADIOTAP_AMPDU_STATUS */ {12, 2}, /* 21: IEEE80211_RADIOTAP_VHT */ {0, 0}, /* 22: */ {0, 0}, /* 23: */ {0, 0}, /* 24: */ {0, 0}, /* 25: */ {0, 0}, /* 26: */ {0, 0}, /* 27: */ {0, 0}, /* 28: */ {0, 0}, /* 29: IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE */ {6, 2}, /* 30: IEEE80211_RADIOTAP_VENDOR_NAMESPACE */ {0, 0} /* 31: IEEE80211_RADIOTAP_EXT */ }; static int bitmap = 0; void radiotap_add_vendor_ns(ieee80211_radiotap_header_t *hdr); void radiotap_encode_multi_rssi(monitor_pkt_rxsts_t* rxsts, ieee80211_radiotap_header_t *hdr); void radiotap_encode_bw_signaling(uint16 mask, struct wl_rxsts* rxsts, ieee80211_radiotap_header_t *hdr); #ifdef MONITOR_DNGL_CONV void radiotap_encode_alignpad(ieee80211_radiotap_header_t *hdr, uint16 pad_req); #endif static const uint8 brcm_oui[] = {0x00, 0x10, 0x18}; static void wl_rtapParseReset(radiotap_parse_t *rtap) { rtap->idx = 0; /* reset parse index */ rtap->offset = 0; /* reset current field pointer */ } static void* wl_rtapParseFindField(radiotap_parse_t *rtap, uint search_idx) { uint idx; /* first bit index to parse */ uint32 btmap; /* presence bitmap */ uint offset, field_offset; uint align, len; void *ptr = NULL; if (search_idx > IEEE80211_RADIOTAP_EXT) return ptr; if (search_idx < rtap->idx) wl_rtapParseReset(rtap); btmap = rtap->hdr->it_present; idx = rtap->idx; offset = rtap->offset; /* loop through each field index until we get to the target idx */ while (idx <= search_idx) { /* if field 'idx' is present, update the offset and check for a match */ if ((1 << idx) & btmap) { /* if we hit a field for which we have no parse info * we need to just bail out */ if (rtap_parse_info[idx].align == 0) break; /* step past any alignment padding */ align = rtap_parse_info[idx].align; len = rtap_parse_info[idx].len; /* ROUNDUP */ field_offset = ((offset + (align - 1)) / align) * align; /* if this field is not in the boulds of the header * just bail out */ if (field_offset + len > rtap->fields_len) break; /* did we find the field? */ if (idx == search_idx) ptr = (uint8*)rtap->fields + field_offset; /* step past this field */ offset = field_offset + len; } idx++; } rtap->idx = idx; rtap->offset = offset; return ptr; } ratespec_t wl_calcRspecFromRTap(uint8 *rtap_header) { ratespec_t rspec = 0; radiotap_parse_t rtap; uint8 rate = 0; uint8 flags = 0; int flags_present = FALSE; uint8 mcs = 0; uint8 mcs_flags = 0; uint8 mcs_known = 0; int mcs_present = FALSE; void *p; wl_rtapParseInit(&rtap, rtap_header); p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_FLAGS); if (p != NULL) { flags_present = TRUE; flags = ((uint8*)p)[0]; } p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_RATE); if (p != NULL) rate = ((uint8*)p)[0]; p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_MCS); if (p != NULL) { mcs_present = TRUE; mcs_known = ((uint8*)p)[0]; mcs_flags = ((uint8*)p)[1]; mcs = ((uint8*)p)[2]; } if (rate != 0) { /* validate the DSSS rates 1,2,5.5,11 */ if (rate == 2 || rate == 4 || rate == 11 || rate == 22) { rspec = LEGACY_RSPEC(rate) | WL_RSPEC_OVERRIDE_RATE; if (flags_present && (flags & IEEE80211_RADIOTAP_F_SHORTPRE)) { rspec |= WL_RSPEC_OVERRIDE_MODE | WL_RSPEC_SHORT_PREAMBLE; } } } else if (mcs_present) { /* validate the MCS value */ if (mcs <= 23 || mcs == 32) { uint32 override = 0; if (mcs_known & (IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_FMT | IEEE80211_RADIOTAP_MCS_HAVE_FEC)) { override = WL_RSPEC_OVERRIDE_MODE; } rspec = HT_RSPEC(mcs) | WL_RSPEC_OVERRIDE_RATE; if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI) && (mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)) rspec |= WL_RSPEC_SGI; if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_FMT) && (mcs_flags & IEEE80211_RADIOTAP_MCS_FMT_GF)) rspec |= WL_RSPEC_SHORT_PREAMBLE; if ((mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_FEC) && (mcs_flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC)) rspec |= WL_RSPEC_LDPC; rspec |= override; } } return rspec; } bool wl_rtapFlags(uint8 *rtap_header, uint8* flags) { radiotap_parse_t rtap; void *p; wl_rtapParseInit(&rtap, rtap_header); p = wl_rtapParseFindField(&rtap, IEEE80211_RADIOTAP_FLAGS); if (p != NULL) { *flags = ((uint8*)p)[0]; } return (p != NULL); } void wl_rtapParseInit(radiotap_parse_t *rtap, uint8 *rtap_header) { uint rlen; uint32 *present_word; struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header*)rtap_header; bzero(rtap, sizeof(radiotap_parse_t)); rlen = hdr->it_len; /* total space in rtap_header */ /* If a precence word has the IEEE80211_RADIOTAP_EXT bit set it indicates * that there is another precence word. * Step over the presence words until we find the end of the list */ present_word = &hdr->it_present; /* remaining length in header past it_present */ rlen -= sizeof(struct ieee80211_radiotap_header); while ((*present_word & (1<= 4) { present_word++; rlen -= 4; /* account for 4 bytes of present_word */ } rtap->hdr = hdr; rtap->fields = (uint8*)(present_word + 1); rtap->fields_len = rlen; wl_rtapParseReset(rtap); } uint wl_radiotap_rx(struct dot11_header *mac_header, wl_rxsts_t *rxsts, bsd_header_rx_t *bsd_header) { int channel_frequency; uint32 channel_flags; uint8 flags; uint8 *cp; uint pad_len; uint32 field_map; uint16 fc; uint bsd_header_len; uint16 ampdu_flags = 0; fc = LTOH16(mac_header->fc); pad_len = 3; field_map = WL_RADIOTAP_PRESENT_BASIC; if (CHSPEC_IS2G(rxsts->chanspec)) { channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_2_4_G); } else if (CHSPEC_IS5G(rxsts->chanspec)) { channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_5_G); } else { channel_flags = IEEE80211_CHAN_OFDM; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_6_G); } if ((rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) || (rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) { ampdu_flags = IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; } flags = IEEE80211_RADIOTAP_F_FCS; if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT) flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if ((fc & FC_WEP) == FC_WEP) flags |= IEEE80211_RADIOTAP_F_WEP; if ((fc & FC_MOREFRAG) == FC_MOREFRAG) flags |= IEEE80211_RADIOTAP_F_FRAG; if (rxsts->pkterror & WL_RXS_CRC_ERROR) flags |= IEEE80211_RADIOTAP_F_BADFCS; if (rxsts->encoding == WL_RXS_ENCODING_HT) field_map = WL_RADIOTAP_PRESENT_HT; else if (rxsts->encoding == WL_RXS_ENCODING_VHT) field_map = WL_RADIOTAP_PRESENT_VHT; bsd_header_len = sizeof(struct wl_radiotap_sna); /* start with sna size */ /* Test for signal/noise values and update length and field bitmap */ if (rxsts->signal == 0) { field_map &= ~(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); pad_len = (pad_len - 1); bsd_header_len--; } if (rxsts->noise == 0) { field_map &= ~(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE); pad_len = (pad_len - 1); bsd_header_len--; } if (rxsts->encoding == WL_RXS_ENCODING_HT || rxsts->encoding == WL_RXS_ENCODING_VHT) { struct wl_radiotap_hdr *rtht = &bsd_header->hdr; struct wl_radiotap_ht_tail *tail; /* * Header length is complicated due to dynamic * presence of signal and noise fields * and padding for xchannel following * signal/noise/ant. * Start with length of wl_radiotap_ht plus * signal/noise/ant */ bsd_header_len += sizeof(struct wl_radiotap_hdr) + pad_len; bsd_header_len += sizeof(struct wl_radiotap_xchan); if (rxsts->nfrmtype == WL_RXS_NFRM_AMPDU_FIRST || rxsts->nfrmtype == WL_RXS_NFRM_AMPDU_SUB) { bsd_header_len += sizeof(struct wl_radiotap_ampdu); } /* add the length of the tail end of the structure */ if (rxsts->encoding == WL_RXS_ENCODING_HT) bsd_header_len += sizeof(struct wl_htmcs); else if (rxsts->encoding == WL_RXS_ENCODING_VHT) bsd_header_len += sizeof(struct wl_vhtmcs); bzero((uint8 *)rtht, sizeof(*rtht)); rtht->ieee_radiotap.it_version = 0; rtht->ieee_radiotap.it_pad = 0; rtht->ieee_radiotap.it_len = (uint16)HTOL16(bsd_header_len); rtht->ieee_radiotap.it_present = HTOL32(field_map); rtht->tsft = HTOL64((uint64)rxsts->mactime); rtht->flags = flags; rtht->channel_freq = (uint16)HTOL16(channel_frequency); rtht->channel_flags = (uint16)HTOL16(channel_flags); cp = bsd_header->pad; /* add in signal/noise/ant */ if (rxsts->signal != 0) { *cp++ = (int8)rxsts->signal; pad_len--; } if (rxsts->noise != 0) { *cp++ = (int8)rxsts->noise; pad_len--; } *cp++ = (int8)rxsts->antenna; pad_len--; tail = (struct wl_radiotap_ht_tail *)(bsd_header->ht); /* Fill in XCHANNEL */ if (CHSPEC_IS40(rxsts->chanspec)) { if (CHSPEC_SB_UPPER(rxsts->chanspec)) channel_flags |= IEEE80211_CHAN_HT40D; else channel_flags |= IEEE80211_CHAN_HT40U; } else channel_flags |= IEEE80211_CHAN_HT20; tail->xc.xchannel_flags = HTOL32(channel_flags); tail->xc.xchannel_freq = (uint16)HTOL16(channel_frequency); tail->xc.xchannel_channel = wf_chspec_ctlchan(rxsts->chanspec); tail->xc.xchannel_maxpower = (17*2); /* fill in A-mpdu Status */ tail->ampdu.ref_num = mac_header->seq; tail->ampdu.flags = ampdu_flags; tail->ampdu.delimiter_crc = 0; tail->ampdu.reserved = 0; if (rxsts->encoding == WL_RXS_ENCODING_HT) { tail->u.ht.mcs_index = rxsts->mcs; tail->u.ht.mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW | IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_FMT); tail->u.ht.mcs_flags = 0; switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) { case WL_RXS_HTF_20L: tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L; break; case WL_RXS_HTF_20U: tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U; break; case WL_RXS_HTF_40: tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40; break; default: tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20; break; } if (rxsts->htflags & WL_RXS_HTF_SGI) tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI; if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF; if (rxsts->htflags & WL_RXS_HTF_LDPC) tail->u.ht.mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; } else if (rxsts->encoding == WL_RXS_ENCODING_VHT) { tail->u.vht.vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC | IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS | IEEE80211_RADIOTAP_VHT_HAVE_GI | IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA | IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA | IEEE80211_RADIOTAP_VHT_HAVE_BF | IEEE80211_RADIOTAP_VHT_HAVE_BW | IEEE80211_RADIOTAP_VHT_HAVE_GID | IEEE80211_RADIOTAP_VHT_HAVE_PAID); tail->u.vht.vht_flags = (uint8)HTOL16(rxsts->vhtflags); switch (rxsts->bw) { case WL_RXS_VHT_BW_20: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20; break; case WL_RXS_VHT_BW_40: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40; break; case WL_RXS_VHT_BW_20L: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20L; break; case WL_RXS_VHT_BW_20U: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20U; break; case WL_RXS_VHT_BW_80: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_80; break; case WL_RXS_VHT_BW_40L: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40L; break; case WL_RXS_VHT_BW_40U: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_40U; break; case WL_RXS_VHT_BW_20LL: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20LL; break; case WL_RXS_VHT_BW_20LU: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20LU; break; case WL_RXS_VHT_BW_20UL: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20UL; break; case WL_RXS_VHT_BW_20UU: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20UU; break; default: tail->u.vht.vht_bw = IEEE80211_RADIOTAP_VHT_BW_20; break; } tail->u.vht.vht_mcs_nss[0] = (rxsts->mcs << 4) | (rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS); tail->u.vht.vht_mcs_nss[1] = 0; tail->u.vht.vht_mcs_nss[2] = 0; tail->u.vht.vht_mcs_nss[3] = 0; tail->u.vht.vht_coding = rxsts->coding; tail->u.vht.vht_group_id = rxsts->gid; tail->u.vht.vht_partial_aid = HTOL16(rxsts->aid); } } else { struct wl_radiotap_hdr *rtl = &bsd_header->hdr; /* * Header length is complicated due to dynamic presence of signal and noise fields * Start with length of wl_radiotap_legacy plus signal/noise/ant */ bsd_header_len = sizeof(struct wl_radiotap_hdr) + pad_len; bzero((uint8 *)rtl, sizeof(*rtl)); rtl->ieee_radiotap.it_version = 0; rtl->ieee_radiotap.it_pad = 0; rtl->ieee_radiotap.it_len = (uint16)HTOL16(bsd_header_len); rtl->ieee_radiotap.it_present = HTOL32(field_map); rtl->tsft = HTOL64((uint64)rxsts->mactime); rtl->flags = flags; rtl->u.rate = (uint8)rxsts->datarate; rtl->channel_freq = (uint16)HTOL16(channel_frequency); rtl->channel_flags = (uint16)HTOL16(channel_flags); /* add in signal/noise/ant */ cp = bsd_header->pad; if (rxsts->signal != 0) *cp++ = (int8)rxsts->signal; if (rxsts->noise != 0) *cp++ = (int8)rxsts->noise; *cp++ = (int8)rxsts->antenna; } return bsd_header_len; } static int wl_radiotap_rx_channel_frequency(wl_rxsts_t *rxsts) { if (CHSPEC_IS2G(rxsts->chanspec)) { return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_2_4_G); } else if (CHSPEC_IS5G(rxsts->chanspec)) { return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_5_G); } else { return wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_6_G); } } static uint16 wl_radiotap_rx_channel_flags(wl_rxsts_t *rxsts) { if (CHSPEC_IS2G(rxsts->chanspec)) { return (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN); } else if (CHSPEC_IS5G(rxsts->chanspec)) { return (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM); } else { return (IEEE80211_CHAN_OFDM); } } static uint8 wl_radiotap_rx_flags(struct dot11_header *mac_header, wl_rxsts_t *rxsts) { uint8 flags; uint16 fc; fc = ltoh16(mac_header->fc); flags = IEEE80211_RADIOTAP_F_FCS; if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT) flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if (fc & FC_WEP) flags |= IEEE80211_RADIOTAP_F_WEP; if (fc & FC_MOREFRAG) flags |= IEEE80211_RADIOTAP_F_FRAG; return flags; } uint wl_radiotap_rx_legacy(struct dot11_header *mac_header, wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr) { int channel_frequency; uint16 channel_flags; uint8 flags; uint16 rtap_len = LTOH16(rtap_hdr->it_len); wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)((uint8*)rtap_hdr + rtap_len); rtap_len += sizeof(wl_radiotap_legacy_t); rtap_hdr->it_len = HTOL16(rtap_len); rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_LEGACY); channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts); channel_flags = wl_radiotap_rx_channel_flags(rxsts); flags = wl_radiotap_rx_flags(mac_header, rxsts); rtl->basic.tsft_l = HTOL32(rxsts->mactime); rtl->basic.tsft_h = 0; rtl->basic.flags = flags; rtl->basic.rate = (uint8)rxsts->datarate; rtl->basic.channel_freq = (uint16)HTOL16(channel_frequency); rtl->basic.channel_flags = HTOL16(channel_flags); rtl->basic.signal = (int8)rxsts->signal; rtl->basic.noise = (int8)rxsts->noise; rtl->basic.antenna = (int8)rxsts->antenna; return 0; } uint wl_radiotap_rx_ht(struct dot11_header *mac_header, wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr) { int channel_frequency; uint16 channel_flags; uint32 xchannel_flags; uint8 flags; uint16 rtap_len = LTOH16(rtap_hdr->it_len); wl_radiotap_ht_t *rtht = (wl_radiotap_ht_t *)((uint8*)rtap_hdr + rtap_len); rtap_len += sizeof(wl_radiotap_ht_t); rtap_hdr->it_len = HTOL16(rtap_len); rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_HT); channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts); channel_flags = wl_radiotap_rx_channel_flags(rxsts); flags = wl_radiotap_rx_flags(mac_header, rxsts); rtht->basic.tsft_l = HTOL32(rxsts->mactime); rtht->basic.tsft_h = 0; rtht->basic.flags = flags; rtht->basic.channel_freq = (uint16)HTOL16(channel_frequency); rtht->basic.channel_flags = HTOL16(channel_flags); rtht->basic.signal = (int8)rxsts->signal; rtht->basic.noise = (int8)rxsts->noise; rtht->basic.antenna = (uint8)rxsts->antenna; /* xchannel */ xchannel_flags = (uint32)channel_flags; if (CHSPEC_IS40(rxsts->chanspec)) { if (CHSPEC_SB_UPPER(rxsts->chanspec)) xchannel_flags |= IEEE80211_CHAN_HT40D; else { xchannel_flags |= IEEE80211_CHAN_HT40U; } } else { xchannel_flags |= IEEE80211_CHAN_HT20; } rtht->xchannel_flags = HTOL32(xchannel_flags); rtht->xchannel_freq = (uint16)HTOL16(channel_frequency); rtht->xchannel_channel = wf_chspec_ctlchan(rxsts->chanspec); rtht->xchannel_maxpower = (17*2); /* add standard MCS */ rtht->mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW | IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_FMT); rtht->mcs_flags = 0; switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) { case WL_RXS_HTF_20L: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L; break; case WL_RXS_HTF_20U: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U; break; case WL_RXS_HTF_40: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40; break; default: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20; } if (rxsts->htflags & WL_RXS_HTF_SGI) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI; } if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF; } if (rxsts->htflags & WL_RXS_HTF_LDPC) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; } rtht->mcs_index = rxsts->mcs; rtht->ampdu_flags = 0; rtht->ampdu_delim_crc = 0; rtht->ampdu_ref_num = rxsts->ampdu_counter; if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) && !(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) { rtht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; } else { rtht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; } return 0; } uint wl_radiotap_rx_vht(struct dot11_header *mac_header, wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr) { int channel_frequency; uint16 channel_flags; uint8 flags; uint16 rtap_len = LTOH16(rtap_hdr->it_len); wl_radiotap_vht_t *rtvht = (wl_radiotap_vht_t *)((uint8*)rtap_hdr + rtap_len); rtap_len += sizeof(wl_radiotap_vht_t); rtap_hdr->it_len = HTOL16(rtap_len); rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_VHT); channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts); channel_flags = wl_radiotap_rx_channel_flags(rxsts); flags = wl_radiotap_rx_flags(mac_header, rxsts); rtvht->basic.tsft_l = HTOL32(rxsts->mactime); rtvht->basic.tsft_h = 0; rtvht->basic.flags = flags; rtvht->basic.channel_freq = (uint16)HTOL16(channel_frequency); rtvht->basic.channel_flags = HTOL16(channel_flags); rtvht->basic.signal = (int8)rxsts->signal; rtvht->basic.noise = (int8)rxsts->noise; rtvht->basic.antenna = (uint8)rxsts->antenna; rtvht->vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC | IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS | IEEE80211_RADIOTAP_VHT_HAVE_GI | IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA | IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA | IEEE80211_RADIOTAP_VHT_HAVE_BF | IEEE80211_RADIOTAP_VHT_HAVE_BW | IEEE80211_RADIOTAP_VHT_HAVE_GID | IEEE80211_RADIOTAP_VHT_HAVE_PAID); STATIC_ASSERT(WL_RXS_VHTF_STBC == IEEE80211_RADIOTAP_VHT_STBC); STATIC_ASSERT(WL_RXS_VHTF_TXOP_PS == IEEE80211_RADIOTAP_VHT_TXOP_PS); STATIC_ASSERT(WL_RXS_VHTF_SGI == IEEE80211_RADIOTAP_VHT_SGI); STATIC_ASSERT(WL_RXS_VHTF_SGI_NSYM_DA == IEEE80211_RADIOTAP_VHT_SGI_NSYM_DA); STATIC_ASSERT(WL_RXS_VHTF_LDPC_EXTRA == IEEE80211_RADIOTAP_VHT_LDPC_EXTRA); STATIC_ASSERT(WL_RXS_VHTF_BF == IEEE80211_RADIOTAP_VHT_BF); rtvht->vht_flags = (uint8)HTOL16(rxsts->vhtflags); STATIC_ASSERT(WL_RXS_VHT_BW_20 == IEEE80211_RADIOTAP_VHT_BW_20); STATIC_ASSERT(WL_RXS_VHT_BW_40 == IEEE80211_RADIOTAP_VHT_BW_40); STATIC_ASSERT(WL_RXS_VHT_BW_20L == IEEE80211_RADIOTAP_VHT_BW_20L); STATIC_ASSERT(WL_RXS_VHT_BW_20U == IEEE80211_RADIOTAP_VHT_BW_20U); STATIC_ASSERT(WL_RXS_VHT_BW_80 == IEEE80211_RADIOTAP_VHT_BW_80); STATIC_ASSERT(WL_RXS_VHT_BW_40L == IEEE80211_RADIOTAP_VHT_BW_40L); STATIC_ASSERT(WL_RXS_VHT_BW_40U == IEEE80211_RADIOTAP_VHT_BW_40U); STATIC_ASSERT(WL_RXS_VHT_BW_20LL == IEEE80211_RADIOTAP_VHT_BW_20LL); STATIC_ASSERT(WL_RXS_VHT_BW_20LU == IEEE80211_RADIOTAP_VHT_BW_20LU); STATIC_ASSERT(WL_RXS_VHT_BW_20UL == IEEE80211_RADIOTAP_VHT_BW_20UL); STATIC_ASSERT(WL_RXS_VHT_BW_20UU == IEEE80211_RADIOTAP_VHT_BW_20UU); rtvht->vht_bw = rxsts->bw; rtvht->vht_mcs_nss[0] = (rxsts->mcs << 4) | (rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS); rtvht->vht_mcs_nss[1] = 0; rtvht->vht_mcs_nss[2] = 0; rtvht->vht_mcs_nss[3] = 0; STATIC_ASSERT(WL_RXS_VHTF_CODING_LDCP == IEEE80211_RADIOTAP_VHT_CODING_LDPC); rtvht->vht_coding = rxsts->coding; rtvht->vht_group_id = rxsts->gid; rtvht->vht_partial_aid = HTOL16(rxsts->aid); rtvht->ampdu_flags = 0; rtvht->ampdu_delim_crc = 0; rtvht->ampdu_ref_num = HTOL32(rxsts->ampdu_counter); if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) && !(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) { rtvht->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_IS_LAST); } else { rtvht->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN); } return 0; } /* Rx status to radiotap conversion of HE type */ uint wl_radiotap_rx_he(struct dot11_header *mac_header, wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr) { int channel_frequency; uint16 channel_flags; uint8 flags; uint16 rtap_len = LTOH16(rtap_hdr->it_len); wl_radiotap_he_t *rthe = (wl_radiotap_he_t *)((uint8*)rtap_hdr + rtap_len); rtap_len += sizeof(wl_radiotap_he_t); rtap_hdr->it_len = HTOL16(rtap_len); rtap_hdr->it_present |= HTOL32(WL_RADIOTAP_PRESENT_HE); channel_frequency = (uint16)wl_radiotap_rx_channel_frequency(rxsts); channel_flags = wl_radiotap_rx_channel_flags(rxsts); flags = wl_radiotap_rx_flags(mac_header, rxsts); rthe->basic.tsft_l = HTOL32(rxsts->mactime); rthe->basic.tsft_h = 0; rthe->basic.flags = flags; rthe->basic.channel_freq = (uint16)HTOL16(channel_frequency); rthe->basic.channel_flags = HTOL16(channel_flags); rthe->basic.signal = (int8)rxsts->signal; rthe->basic.noise = (int8)rxsts->noise; rthe->basic.antenna = (uint8)rxsts->antenna; rthe->ampdu_flags = 0; rthe->ampdu_delim_crc = 0; rthe->ampdu_ref_num = HTOL32(rxsts->ampdu_counter); if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) && !(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) { rthe->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_IS_LAST); } else { rthe->ampdu_flags |= HTOL16(IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN); } rthe->data1 = HTOL16(rxsts->data1); rthe->data2 = HTOL16(rxsts->data2); rthe->data3 = HTOL16(rxsts->data3); rthe->data4 = HTOL16(rxsts->data4); rthe->data5 = HTOL16(rxsts->data5); rthe->data6 = HTOL16(rxsts->data6); return 0; } /* Rx status to radiotap conversion of EHT type */ uint wl_radiotap_rx_eht(struct dot11_header *mac_header, wl_rxsts_t *rxsts, ieee80211_radiotap_header_t *rtap_hdr) { ASSERT(!"wl_radiotap_rx_eht: not implemented!"); return 0; } uint16 wl_rxsts_to_rtap(monitor_pkt_rxsts_t *pkt_rxsts, void *payload, uint16 len, void* pout, uint16 pad_req) { uint16 rtap_len = 0; struct dot11_header* mac_header; uint8* p = payload; ieee80211_radiotap_header_t* rtap_hdr = (ieee80211_radiotap_header_t*)pout; wl_rxsts_t* rxsts; ASSERT(p && pkt_rxsts); rxsts = pkt_rxsts->rxsts; rtap_hdr->it_version = 0; rtap_hdr->it_pad = 0; rtap_hdr->it_len = HTOL16(sizeof(*rtap_hdr)); rtap_hdr->it_present = 0; bitmap = 0; #ifdef MONITOR_DNGL_CONV if (pad_req) { radiotap_add_vendor_ns(rtap_hdr); } #endif #ifdef BCM_MON_QDBM_RSSI /* if per-core RSSI is present, add vendor element */ if (pkt_rxsts->corenum != 0) { radiotap_add_vendor_ns(rtap_hdr); } #endif mac_header = (struct dot11_header *)(p); if (rxsts->encoding == WL_RXS_ENCODING_EHT) { wl_radiotap_rx_eht(mac_header, rxsts, rtap_hdr); } else if (rxsts->encoding == WL_RXS_ENCODING_HE) { wl_radiotap_rx_he(mac_header, rxsts, rtap_hdr); } else if (rxsts->encoding == WL_RXS_ENCODING_VHT) { wl_radiotap_rx_vht(mac_header, rxsts, rtap_hdr); } else if (rxsts->encoding == WL_RXS_ENCODING_HT) { wl_radiotap_rx_ht(mac_header, rxsts, rtap_hdr); } else { uint16 mask = ltoh16(mac_header->fc) & FC_KIND_MASK; if (mask == FC_RTS || mask == FC_CTS) { radiotap_add_vendor_ns(rtap_hdr); } wl_radiotap_rx_legacy(mac_header, rxsts, rtap_hdr); if (mask == FC_RTS || mask == FC_CTS) { radiotap_encode_bw_signaling(mask, rxsts, rtap_hdr); } } #ifdef BCM_MON_QDBM_RSSI /* if per-core RSSI is present, add vendor element */ if (pkt_rxsts->corenum != 0) { radiotap_encode_multi_rssi(pkt_rxsts, rtap_hdr); } #endif #ifdef MONITOR_DNGL_CONV if (pad_req) { radiotap_encode_alignpad(rtap_hdr, pad_req); } #endif rtap_len = LTOH16(rtap_hdr->it_len); len += rtap_len; #ifndef MONITOR_DNGL_CONV if (len > MAX_MON_PKT_SIZE) { return 0; } /* copy payload */ if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMSDU_FIRST) && !(rxsts->nfrmtype & WL_RXS_NFRM_AMSDU_SUB)) { memcpy((uint8*)pout + rtap_len, (uint8*)p, len - rtap_len); } #endif #ifdef MONITOR_DNGL_CONV return rtap_len; #else return len; #endif } #ifdef BCM_MON_QDBM_RSSI void radiotap_encode_multi_rssi(monitor_pkt_rxsts_t* rxsts, ieee80211_radiotap_header_t *hdr) { uint16 cur_len = LTOH16(hdr->it_len); uint16 len = ROUNDUP(1 + rxsts->corenum * sizeof(monitor_pkt_rssi_t), 4); int i = 0; uint8 *vend_p = (uint8 *)hdr + cur_len; radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t*)vend_p; memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui)); vendor_ns->sns = 1; vendor_ns->skip_len = HTOL16(len); vend_p += sizeof(*vendor_ns); vend_p[0] = rxsts->corenum; for (i = 0; i < rxsts->corenum; i++) { vend_p[2*i + 1] = rxsts->rxpwr[i].dBm; vend_p[2*i + 2] = rxsts->rxpwr[i].decidBm; } hdr->it_len = HTOL16(cur_len + sizeof(radiotap_vendor_ns_t) + len); } #endif /* BCM_CORE_RSSI */ #ifdef MONITOR_DNGL_CONV #define AILIGN_4BYTES (4u) void radiotap_encode_alignpad(ieee80211_radiotap_header_t *hdr, uint16 pad_req) { uint16 cur_len = LTOH16(hdr->it_len); uint8 *vend_p = (uint8 *)hdr + cur_len; radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t*)vend_p; uint16 len; uint16 align_pad = 0; memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui)); vendor_ns->sns = WL_RADIOTAP_BRCM_PAD_SNS; len = cur_len + sizeof(radiotap_vendor_ns_t); if (len % AILIGN_4BYTES != 0) { align_pad = (AILIGN_4BYTES - (len % AILIGN_4BYTES)); } hdr->it_len = HTOL16(len + pad_req + align_pad); vendor_ns->skip_len = HTOL16(pad_req + align_pad); } #endif /* MONITOR_DNGL_CONV */ void radiotap_encode_bw_signaling(uint16 mask, struct wl_rxsts* rxsts, ieee80211_radiotap_header_t *hdr) { uint16 cur_len = LTOH16(hdr->it_len); uint8 *vend_p = (uint8 *)hdr + cur_len; radiotap_vendor_ns_t *vendor_ns = (radiotap_vendor_ns_t *)vend_p; wl_radiotap_nonht_vht_t* nonht_vht; memcpy(vendor_ns->vend_oui, brcm_oui, sizeof(vendor_ns->vend_oui)); vendor_ns->sns = 0; vendor_ns->skip_len = sizeof(wl_radiotap_nonht_vht_t); nonht_vht = (wl_radiotap_nonht_vht_t *)(vend_p + sizeof(*vendor_ns)); /* VHT b/w signalling */ bzero((uint8 *)nonht_vht, sizeof(wl_radiotap_nonht_vht_t)); nonht_vht->len = WL_RADIOTAP_NONHT_VHT_LEN; nonht_vht->flags |= WL_RADIOTAP_F_NONHT_VHT_BW; nonht_vht->bw = (uint8)rxsts->bw_nonht; if (mask == FC_RTS) { if (rxsts->vhtflags & WL_RXS_VHTF_DYN_BW_NONHT) { nonht_vht->flags |= WL_RADIOTAP_F_NONHT_VHT_DYN_BW; } } hdr->it_len = HTOL16(cur_len + sizeof(radiotap_vendor_ns_t) + sizeof(wl_radiotap_nonht_vht_t)); } void radiotap_add_vendor_ns(ieee80211_radiotap_header_t *hdr) { uint32 * it_present = &hdr->it_present; uint16 len = LTOH16(hdr->it_len); /* if the last bitmap has a vendor ns, add a new one */ if (it_present[bitmap] & (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) { it_present[bitmap] |= 1 << IEEE80211_RADIOTAP_EXT; bitmap++; /* align to 8 bytes */ if (bitmap%2) { hdr->it_len = HTOL16(len + 8); } it_present[bitmap] = 1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE; } else { it_present[bitmap] |= 1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE; } }