static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; #ifdef CONFIG_MBO const u8 *vendor; #endif /* CONFIG_MBO */ if (end - pos < 5) return; #ifdef CONFIG_MBO wpa_s->wnm_mbo_trans_reason_present = 0; wpa_s->wnm_mbo_transition_reason = 0; #endif /* CONFIG_MBO */ if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else beacon_int = 100; /* best guess */ wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); valid_int = pos[4]; wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) if (wpa_s->reject_btm_req_reason) { wpa_printf(MSG_INFO, "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", wpa_s->reject_btm_req_reason); wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, wpa_s->reject_btm_req_reason, MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL); return; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; if (end - pos < 1 || 1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; } os_memcpy(url, pos + 1, pos[0]); url[pos[0]] = '\0'; pos += 1 + pos[0]; wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); wpa_supplicant_req_scan(wpa_s, 0, 0); } } #ifdef CONFIG_MBO vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); if (vendor) wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); #endif /* CONFIG_MBO */ if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); wnm_deallocate_memory(wpa_s); wpa_s->wnm_neighbor_report_elements = os_calloc( WNM_MAX_NEIGHBOR_REPORT, sizeof(struct neighbor_report)); if (wpa_s->wnm_neighbor_report_elements == NULL) return; while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; u8 len = *pos++; wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); if (len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } if (tag == WLAN_EID_NEIGHBOR_REPORT) { struct neighbor_report *rep; rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); wpa_s->wnm_num_neighbor_report++; #ifdef CONFIG_MBO if (wpa_s->wnm_mbo_trans_reason_present && wpa_s->wnm_num_neighbor_report == 1) { rep->is_first = 1; wpa_printf(MSG_DEBUG, "WNM: First transition candidate is " MACSTR, MAC2STR(rep->bssid)); } #endif /* CONFIG_MBO */ } pos += len; } if (!wpa_s->wnm_num_neighbor_report) { wpa_printf(MSG_DEBUG, "WNM: Candidate list included bit is set, but no candidates found"); wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL); return; } wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; wpa_s->wnm_cand_valid_until.sec += wpa_s->wnm_cand_valid_until.usec / 1000000; wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); /* * Fetch the latest scan results from the kernel and check for * candidates based on those results first. This can help in * finding more up-to-date information should the driver has * done some internal scanning operations after the last scan * result update in wpa_supplicant. */ if (wnm_fetch_scan_results(wpa_s) > 0) return; /* * Try to use previously received scan results, if they are * recent enough to use for a connection. */ if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; os_get_reltime(&now); if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { wpa_printf(MSG_DEBUG, "WNM: Try to use recent scan results"); if (wnm_scan_process(wpa_s, 0) > 0) return; wpa_printf(MSG_DEBUG, "WNM: No match in previous scan results - try a new scan"); } } wnm_set_scan_freqs(wpa_s); if (wpa_s->wnm_num_neighbor_report == 1) { os_memcpy(wpa_s->next_scan_bssid, wpa_s->wnm_neighbor_report_elements[0].bssid, ETH_ALEN); wpa_printf(MSG_DEBUG, "WNM: Scan only for a specific BSSID since there is only a single candidate " MACSTR, MAC2STR(wpa_s->next_scan_bssid)); } wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) status = WNM_BSS_TM_ACCEPT; else { wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, status, MBO_TRANSITION_REASON_UNSPECIFIED, 0, NULL); } }
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; if (pos + 5 > end) return; if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else beacon_int = 100; /* best guess */ wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); valid_int = pos[4]; wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (pos + 12 > end) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; if (pos + 1 > end || pos + 1 + pos[0] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; } os_memcpy(url, pos + 1, pos[0]); url[pos[0]] = '\0'; pos += 1 + pos[0]; wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); wpa_supplicant_req_scan(wpa_s, 0, 0); } } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); wnm_deallocate_memory(wpa_s); wpa_s->wnm_neighbor_report_elements = os_calloc( WNM_MAX_NEIGHBOR_REPORT, sizeof(struct neighbor_report)); if (wpa_s->wnm_neighbor_report_elements == NULL) return; while (pos + 2 <= end && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; u8 len = *pos++; wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); if (pos + len > end) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } if (tag == WLAN_EID_NEIGHBOR_REPORT) { struct neighbor_report *rep; rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); } pos += len; wpa_s->wnm_num_neighbor_report++; } wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; wpa_s->wnm_cand_valid_until.sec += wpa_s->wnm_cand_valid_until.usec / 1000000; wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; os_get_reltime(&now); if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { wpa_printf(MSG_DEBUG, "WNM: Try to use recent scan results"); if (wnm_scan_process(wpa_s, 0) > 0) return; wpa_printf(MSG_DEBUG, "WNM: No match in previous scan results - try a new scan"); } } wnm_set_scan_freqs(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) status = WNM_BSS_TM_ACCEPT; else { wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, status, 0, NULL); } }