diff options
Diffstat (limited to 'freebsd/contrib/wpa/wpa_supplicant/rrm.c')
-rw-r--r-- | freebsd/contrib/wpa/wpa_supplicant/rrm.c | 239 |
1 files changed, 177 insertions, 62 deletions
diff --git a/freebsd/contrib/wpa/wpa_supplicant/rrm.c b/freebsd/contrib/wpa/wpa_supplicant/rrm.c index f6d9cd34..4dcd8b92 100644 --- a/freebsd/contrib/wpa/wpa_supplicant/rrm.c +++ b/freebsd/contrib/wpa/wpa_supplicant/rrm.c @@ -394,17 +394,66 @@ static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s, } +static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) +{ + struct rrm_measurement_report_element *msr_rep; + u8 *end = pos + len; + u8 *msr_rep_end; + struct rrm_measurement_beacon_report *rep = NULL; + u8 *subelem; + + /* Find the last beacon report element */ + while (end - pos >= (int) sizeof(*msr_rep)) { + msr_rep = (struct rrm_measurement_report_element *) pos; + msr_rep_end = pos + msr_rep->len + 2; + + if (msr_rep->eid != WLAN_EID_MEASURE_REPORT || + msr_rep_end > end) { + /* Should not happen. This indicates a bug. */ + wpa_printf(MSG_ERROR, + "RRM: non-measurement report element in measurement report frame"); + return -1; + } + + if (msr_rep->type == MEASURE_TYPE_BEACON) + rep = (struct rrm_measurement_beacon_report *) + msr_rep->variable; + + pos += pos[1] + 2; + } + + if (!rep) + return 0; + + subelem = rep->variable; + while (subelem + 2 < msr_rep_end && + subelem[0] != WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) + subelem += 2 + subelem[1]; + + if (subelem + 2 < msr_rep_end && + subelem[0] == WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && + subelem[1] == 1 && + subelem + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) + subelem[2] = 1; + + return 0; +} + + static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { int len = wpabuf_len(buf); - const u8 *pos = wpabuf_head_u8(buf), *next = pos; + u8 *pos = wpabuf_mhead_u8(buf), *next = pos; #define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3) while (len) { int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len; + if (send_len == len) + wpas_rrm_beacon_rep_update_last_frame(pos, len); + if (send_len == len || (send_len + next[1] + 2) > MPDU_REPORT_LEN) { wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len); @@ -670,20 +719,20 @@ static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; if (seg1 && abs(seg1 - seg0) == 8) - vht = VHT_CHANWIDTH_160MHZ; + vht = CHANWIDTH_160MHZ; else if (seg1) - vht = VHT_CHANWIDTH_80P80MHZ; + vht = CHANWIDTH_80P80MHZ; else - vht = VHT_CHANWIDTH_80MHZ; + vht = CHANWIDTH_80MHZ; break; case 2: - vht = VHT_CHANWIDTH_160MHZ; + vht = CHANWIDTH_160MHZ; break; case 3: - vht = VHT_CHANWIDTH_80P80MHZ; + vht = CHANWIDTH_80P80MHZ; break; default: - vht = VHT_CHANWIDTH_USE_HT; + vht = CHANWIDTH_USE_HT; break; } } @@ -709,15 +758,17 @@ static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, enum beacon_report_detail detail, struct wpa_bss *bss, u8 *buf, - size_t buf_len) + size_t buf_len, u8 **ies_buf, + size_t *ie_len, int add_fixed) { - u8 *ies = (u8 *) (bss + 1); - size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + u8 *ies = *ies_buf; + size_t ies_len = *ie_len; u8 *pos = buf; int rem_len; rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) - - sizeof(struct rrm_measurement_report_element) - 2; + sizeof(struct rrm_measurement_report_element) - 2 - + REPORTED_FRAME_BODY_SUBELEM_LEN; if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { wpa_printf(MSG_DEBUG, @@ -733,18 +784,21 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) + * beacon interval(2) + capabilities(2) = 14 bytes */ - if (buf_len < 14) - return 0; + if (add_fixed && buf_len < 14) + return -1; *pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY; /* The length will be filled later */ pos++; - WPA_PUT_LE64(pos, bss->tsf); - pos += sizeof(bss->tsf); - WPA_PUT_LE16(pos, bss->beacon_int); - pos += 2; - WPA_PUT_LE16(pos, bss->caps); - pos += 2; + + if (add_fixed) { + WPA_PUT_LE64(pos, bss->tsf); + pos += sizeof(bss->tsf); + WPA_PUT_LE16(pos, bss->beacon_int); + pos += 2; + WPA_PUT_LE16(pos, bss->caps); + pos += 2; + } rem_len -= pos - buf; @@ -759,15 +813,7 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) { if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS || (eids && bitfield_is_set(eids, ies[0]))) { - u8 eid = ies[0], elen = ies[1]; - - if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) && - elen > 4) - elen = 4; - /* - * TODO: Truncate IBSS DFS element as described in - * IEEE Std 802.11-2016, 9.4.2.22.7. - */ + u8 elen = ies[1]; if (2 + elen > buf + buf_len - pos || 2 + elen > rem_len) @@ -784,22 +830,91 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, ies += 2 + ies[1]; } + *ie_len = ies_len; + *ies_buf = ies; + /* Now the length is known */ buf[1] = pos - buf - 2; return pos - buf; } +static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data, + struct wpa_bss *bss, + struct wpabuf **wpa_buf, + struct rrm_measurement_beacon_report *rep, + u8 **ie, size_t *ie_len, u8 idx) +{ + int ret; + u8 *buf, *pos; + u32 subelems_len = REPORTED_FRAME_BODY_SUBELEM_LEN + + (data->last_indication ? + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN : 0); + + /* Maximum element length: Beacon Report element + Reported Frame Body + * subelement + all IEs of the reported Beacon frame + Reported Frame + * Body Fragment ID subelement */ + buf = os_malloc(sizeof(*rep) + 14 + *ie_len + subelems_len); + if (!buf) + return -1; + + os_memcpy(buf, rep, sizeof(*rep)); + + ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, + bss, buf + sizeof(*rep), + 14 + *ie_len, ie, ie_len, + idx == 0); + if (ret < 0) + goto out; + + pos = buf + ret + sizeof(*rep); + pos[0] = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID; + pos[1] = 2; + + /* + * Only one Beacon Report Measurement is supported at a time, so + * the Beacon Report ID can always be set to 1. + */ + pos[2] = 1; + + /* Fragment ID Number (bits 0..6) and More Frame Body Fragments (bit 7) + */ + pos[3] = idx; + if (data->report_detail != BEACON_REPORT_DETAIL_NONE && *ie_len) + pos[3] |= REPORTED_FRAME_BODY_MORE_FRAGMENTS; + else + pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS; + + pos += REPORTED_FRAME_BODY_SUBELEM_LEN; + + if (data->last_indication) { + pos[0] = WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION; + pos[1] = 1; + + /* This field will be updated later if this is the last frame */ + pos[2] = 0; + } + + ret = wpas_rrm_report_elem(wpa_buf, data->token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, buf, + ret + sizeof(*rep) + subelems_len); +out: + os_free(buf); + return ret; +} + + static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, struct wpabuf **wpa_buf, struct wpa_bss *bss, u64 start, u64 parent_tsf) { struct beacon_rep_data *data = &wpa_s->beacon_rep_data; - u8 *ie = (u8 *) (bss + 1); - size_t ie_len = bss->ie_len + bss->beacon_ie_len; - int ret; - u8 *buf; - struct rrm_measurement_beacon_report *rep; + u8 *ies = (u8 *) (bss + 1); + u8 *pos = ies; + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + struct rrm_measurement_beacon_report rep; + u8 idx = 0; if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 && os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0) @@ -810,39 +925,29 @@ static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0)) return 0; - /* Maximum element length: beacon report element + reported frame body - * subelement + all IEs of the reported beacon */ - buf = os_malloc(sizeof(*rep) + 14 + ie_len); - if (!buf) - return -1; + if (wpas_get_op_chan_phy(bss->freq, ies, ies_len, &rep.op_class, + &rep.channel, &rep.report_info) < 0) + return 0; - rep = (struct rrm_measurement_beacon_report *) buf; - if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class, - &rep->channel, &rep->report_info) < 0) { - ret = 0; - goto out; - } + rep.start_time = host_to_le64(start); + rep.duration = host_to_le16(data->scan_params.duration); + rep.rcpi = rssi_to_rcpi(bss->level); + rep.rsni = 255; /* 255 indicates that RSNI is not available */ + os_memcpy(rep.bssid, bss->bssid, ETH_ALEN); + rep.antenna_id = 0; /* unknown */ + rep.parent_tsf = host_to_le32(parent_tsf); - rep->start_time = host_to_le64(start); - rep->duration = host_to_le16(data->scan_params.duration); - rep->rcpi = rssi_to_rcpi(bss->level); - rep->rsni = 255; /* 255 indicates that RSNI is not available */ - os_memcpy(rep->bssid, bss->bssid, ETH_ALEN); - rep->antenna_id = 0; /* unknown */ - rep->parent_tsf = host_to_le32(parent_tsf); + do { + int ret; - ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, - bss, rep->variable, 14 + ie_len); - if (ret < 0) - goto out; + ret = wpas_add_beacon_rep_elem(data, bss, wpa_buf, &rep, + &pos, &ies_len, idx++); + if (ret) + return ret; + } while (data->report_detail != BEACON_REPORT_DETAIL_NONE && + ies_len >= 2); - ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token, - MEASUREMENT_REPORT_MODE_ACCEPT, - MEASURE_TYPE_BEACON, buf, - ret + sizeof(*rep)); -out: - os_free(buf); - return ret; + return 0; } @@ -1008,6 +1113,16 @@ static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: /* Skip - it will be processed when freqs are added */ break; + case WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Beacon request: Invalid last indication request subelement length: %u", + slen); + return -1; + } + + data->last_indication = subelem[0]; + break; default: wpa_printf(MSG_DEBUG, "Beacon request: Unknown subelement id %u", sid); |