コード例 #1
1
ファイル: ieee802_11_auth.c プロジェクト: KHATEEBNSIT/AP
static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
				       struct os_reltime *now)
{
	struct hostapd_acl_query_data *prev, *entry, *tmp;

	prev = NULL;
	entry = hapd->acl_queries;

	while (entry) {
		if (os_reltime_expired(now, &entry->timestamp,
				       RADIUS_ACL_TIMEOUT)) {
			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
				   " has expired.", MAC2STR(entry->addr));
			if (prev)
				prev->next = entry->next;
			else
				hapd->acl_queries = entry->next;

			tmp = entry;
			entry = entry->next;
			hostapd_acl_query_free(tmp);
			continue;
		}

		prev = entry;
		entry = entry->next;
	}
}
void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
				 const u8 *da, u16 reason_code)
{
	struct wpa_ssid *ssid;
	struct os_reltime now;

	if (wpa_s->wpa_state != WPA_COMPLETED)
		return;
	ssid = wpa_s->current_ssid;
	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
		return;
	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
		return;
	if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
	    reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
		return;
	if (wpa_s->sme.sa_query_count > 0)
		return;

	os_get_reltime(&now);
	if (wpa_s->sme.last_unprot_disconnect.sec &&
	    !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
		return; /* limit SA Query procedure frequency */
	wpa_s->sme.last_unprot_disconnect = now;

	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
		"possible AP/STA state mismatch - trigger SA Query");
	sme_start_sa_query(wpa_s);
}
コード例 #3
0
void sta_track_expire(struct hostapd_iface *iface, int force)
{
	struct os_reltime now;
	struct hostapd_sta_info *info;

	if (!iface->num_sta_seen)
		return;

	os_get_reltime(&now);
	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
				     list))) {
		if (!force &&
		    !os_reltime_expired(&now, &info->last_seen,
					iface->conf->track_sta_max_age))
			break;
		force = 0;

		wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
			   MACSTR, iface->bss[0]->conf->iface,
			   MAC2STR(info->addr));
		dl_list_del(&info->list);
		iface->num_sta_seen--;
		os_free(info);
	}
}
コード例 #4
0
ファイル: ieee802_11_auth.c プロジェクト: KHATEEBNSIT/AP
static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
				     struct os_reltime *now)
{
	struct hostapd_cached_radius_acl *prev, *entry, *tmp;

	prev = NULL;
	entry = hapd->acl_cache;

	while (entry) {
		if (os_reltime_expired(now, &entry->timestamp,
				       RADIUS_ACL_TIMEOUT)) {
			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
				   " has expired.", MAC2STR(entry->addr));
			if (prev)
				prev->next = entry->next;
			else
				hapd->acl_cache = entry->next;
			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
			tmp = entry;
			entry = entry->next;
			hostapd_acl_cache_free_entry(tmp);
			continue;
		}

		prev = entry;
		entry = entry->next;
	}
}
コード例 #5
0
ファイル: ap_list.c プロジェクト: drashti304/TizenRT
static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
{
	struct hostapd_iface *iface = eloop_ctx;
	struct os_reltime now;
	struct ap_info *ap;
	int set_beacon = 0;

	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);

	if (!iface->ap_list) {
		return;
	}

	os_get_reltime(&now);

	while (iface->ap_list) {
		ap = iface->ap_list->prev;
		if (!os_reltime_expired(&now, &ap->last_beacon, iface->conf->ap_table_expiration_time)) {
			break;
		}

		ap_free_ap(iface, ap);
	}

	if (iface->olbc || iface->olbc_ht) {
		int olbc = 0;
		int olbc_ht = 0;

		ap = iface->ap_list;
		while (ap && (olbc == 0 || olbc_ht == 0)) {
			if (ap_list_beacon_olbc(iface, ap)) {
				olbc = 1;
			}
			if (!ap->ht_support) {
				olbc_ht = 1;
			}
			ap = ap->next;
		}
		if (!olbc && iface->olbc) {
			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
			iface->olbc = 0;
			set_beacon++;
		}
#ifdef CONFIG_IEEE80211N
		if (!olbc_ht && iface->olbc_ht) {
			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
			iface->olbc_ht = 0;
			hostapd_ht_operation_update(iface);
			set_beacon++;
		}
#endif							/* CONFIG_IEEE80211N */
	}

	if (set_beacon) {
		ieee802_11_update_beacons(iface);
	}
}
コード例 #6
0
static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
				 u32 *session_timeout,
				 u32 *acct_interim_interval, int *vlan_id,
				 struct hostapd_sta_wpa_psk_short **psk,
				 char **identity, char **radius_cui)
{
	struct hostapd_cached_radius_acl *entry;
	struct os_reltime now;

	wpa_printf(MSG_INFO, "WGTT: %s %s():%d\n", __FILE__, __FUNCTION__, __LINE__);

	os_get_reltime(&now);

	for (entry = hapd->acl_cache; entry; entry = entry->next) {
		if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
			continue;

		if (os_reltime_expired(&now, &entry->timestamp,
				       RADIUS_ACL_TIMEOUT))
			return -1; /* entry has expired */
		if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
			if (session_timeout)
				*session_timeout = entry->session_timeout;
		if (acct_interim_interval)
			*acct_interim_interval =
				entry->acct_interim_interval;
		if (vlan_id)
			*vlan_id = entry->vlan_id;
		copy_psk_list(psk, entry->psk);
		if (identity) {
			if (entry->identity)
				*identity = os_strdup(entry->identity);
			else
				*identity = NULL;
		}
		if (radius_cui) {
			if (entry->radius_cui)
				*radius_cui = os_strdup(entry->radius_cui);
			else
				*radius_cui = NULL;
		}
		return entry->accepted;
	}

	return -1;
}
コード例 #7
0
ファイル: wnm_sta.c プロジェクト: gxk/hostap
static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
{
	u8 *pos = buf;
	unsigned int i, pref = 255;
	struct os_reltime now;
	struct wpa_ssid *ssid = wpa_s->current_ssid;

	if (!ssid)
		return 0;

	/*
	 * TODO: Define when scan results are no longer valid for the candidate
	 * list.
	 */
	os_get_reltime(&now);
	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
		return 0;

	wpa_printf(MSG_DEBUG,
		   "WNM: Add candidate list to BSS Transition Management Response frame");
	for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
		struct wpa_bss *bss = wpa_s->last_scan_res[i];
		int res;

		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
			if (res == -2)
				continue; /* could not build entry for BSS */
			if (res < 0)
				break; /* no more room for candidates */
			if (pref == 1)
				break;

			pos += res;
			len -= res;
		}
	}

	wpa_hexdump(MSG_DEBUG,
		    "WNM: BSS Transition Management Response candidate list",
		    buf, pos - buf);

	return pos - buf;
}
コード例 #8
0
ファイル: wpa_ctrl.c プロジェクト: schapa/OrangeWiFi
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
		     char *reply, size_t *reply_len,
		     void (*msg_cb)(char *msg, size_t len))
{
	struct timeval tv;
	struct os_reltime started_at;
	int res;
	fd_set rfds;
	const char *_cmd;
	char *cmd_buf = NULL;
	size_t _cmd_len;

#ifdef CONFIG_CTRL_IFACE_UDP
	if (ctrl->cookie) {
		char *pos;
		_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
		cmd_buf = os_malloc(_cmd_len);
		if (cmd_buf == NULL)
			return -1;
		_cmd = cmd_buf;
		pos = cmd_buf;
		os_strlcpy(pos, ctrl->cookie, _cmd_len);
		pos += os_strlen(ctrl->cookie);
		*pos++ = ' ';
		os_memcpy(pos, cmd, cmd_len);
	} else
#endif /* CONFIG_CTRL_IFACE_UDP */
	{
		_cmd = cmd;
		_cmd_len = cmd_len;
	}

	errno = 0;
	started_at.sec = 0;
	started_at.usec = 0;
retry_send:
	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
		if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
		{
			/*
			 * Must be a non-blocking socket... Try for a bit
			 * longer before giving up.
			 */
			if (started_at.sec == 0)
				os_get_reltime(&started_at);
			else {
				struct os_reltime n;
				os_get_reltime(&n);
				/* Try for a few seconds. */
				if (os_reltime_expired(&n, &started_at, 5))
					goto send_err;
			}
			os_sleep(1, 0);
			goto retry_send;
		}
	send_err:
		os_free(cmd_buf);
		return -1;
	}
	os_free(cmd_buf);

	for (;;) {
		tv.tv_sec = 10;
		tv.tv_usec = 0;
		FD_ZERO(&rfds);
		FD_SET(ctrl->s, &rfds);
		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
		if (res < 0 && errno == EINTR)
			continue;
		if (res < 0)
			return res;
		if (FD_ISSET(ctrl->s, &rfds)) {
			res = recv(ctrl->s, reply, *reply_len, 0);
			if (res < 0)
				return res;
			if (res > 0 && reply[0] == '<') {
				/* This is an unsolicited message from
				 * wpa_supplicant, not the reply to the
				 * request. Use msg_cb to report this to the
				 * caller. */
				if (msg_cb) {
					/* Make sure the message is nul
					 * terminated. */
					if ((size_t) res == *reply_len)
						res = (*reply_len) - 1;
					reply[res] = '\0';
					msg_cb(reply, res);
				}
				continue;
			}
			*reply_len = res;
			break;
		} else {
			return -2;
		}
	}
	return 0;
}
コード例 #9
0
ファイル: wnm_sta.c プロジェクト: gxk/hostap
static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
			      enum mbo_transition_reject_reason *reason)
{
	u8 i;
	struct wpa_bss *bss = wpa_s->current_bss;
	struct wpa_bss *target;

	if (!bss)
		return NULL;

	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
		   MAC2STR(wpa_s->bssid), bss->level);

	wnm_clear_acceptable(wpa_s);

	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
		struct neighbor_report *nei;

		nei = &wpa_s->wnm_neighbor_report_elements[i];
		if (nei->preference_present && nei->preference == 0) {
			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
				   MAC2STR(nei->bssid));
			continue;
		}

		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
		if (!target) {
			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
				   " (pref %d) not found in scan results",
				   MAC2STR(nei->bssid),
				   nei->preference_present ? nei->preference :
				   -1);
			continue;
		}

		if (age_secs) {
			struct os_reltime now;

			if (os_get_reltime(&now) == 0 &&
			    os_reltime_expired(&now, &target->last_update,
					       age_secs)) {
				wpa_printf(MSG_DEBUG,
					   "Candidate BSS is more than %ld seconds old",
					   age_secs);
				continue;
			}
		}

		if (bss->ssid_len != target->ssid_len ||
		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
			/*
			 * TODO: Could consider allowing transition to another
			 * ESS if PMF was enabled for the association.
			 */
			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
				   " (pref %d) in different ESS",
				   MAC2STR(nei->bssid),
				   nei->preference_present ? nei->preference :
				   -1);
			continue;
		}

		if (wpa_s->current_ssid &&
		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
					1, 0)) {
			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
				   " (pref %d) does not match the current network profile",
				   MAC2STR(nei->bssid),
				   nei->preference_present ? nei->preference :
				   -1);
			continue;
		}

		if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
			wpa_printf(MSG_DEBUG,
				   "MBO: Candidate BSS " MACSTR
				   " retry delay is not over yet",
				   MAC2STR(nei->bssid));
			continue;
		}

		if (target->level < bss->level && target->level < -80) {
			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
				   " (pref %d) does not have sufficient signal level (%d)",
				   MAC2STR(nei->bssid),
				   nei->preference_present ? nei->preference :
				   -1,
				   target->level);
			continue;
		}

		nei->acceptable = 1;
	}

#ifdef CONFIG_MBO
	if (wpa_s->wnm_mbo_trans_reason_present)
		target = get_mbo_transition_candidate(wpa_s, reason);
	else
		target = get_first_acceptable(wpa_s);
#else /* CONFIG_MBO */
	target = get_first_acceptable(wpa_s);
#endif /* CONFIG_MBO */

	if (target) {
		wpa_printf(MSG_DEBUG,
			   "WNM: Found an acceptable preferred transition candidate BSS "
			   MACSTR " (RSSI %d)",
			   MAC2STR(target->bssid), target->level);
	}

	return target;
}
コード例 #10
0
ファイル: wnm_sta.c プロジェクト: gxk/hostap
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);
	}
}
コード例 #11
0
ファイル: wnm_sta.c プロジェクト: ajinkya93/netbsd-src
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);
	}
}