Parcourir la source

nl80211: Move event handling into a separate file

Signed-off-by: Jouni Malinen <j@w1.fi>
Jouni Malinen il y a 10 ans
Parent
commit
8a906d1251

+ 1 - 635
src/drivers/driver_nl80211.c

@@ -186,7 +186,7 @@ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int i802_set_iface_flags(struct i802_bss *bss, int up);
 
 
-static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+const char * nl80211_command_to_string(enum nl80211_commands cmd)
 {
 #define C2S(x) case x: return #x;
 	switch (cmd) {
@@ -1220,48 +1220,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
 }
 
 
-static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
-			    const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-
-	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-	    drv->force_connect_cmd) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignore auth event when using driver SME");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24 + sizeof(mgmt->u.auth)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-			   "frame");
-		return;
-	}
-
-	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
-	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
-	os_memset(&event, 0, sizeof(event));
-	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
-	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
-	event.auth.auth_transaction =
-		le_to_host16(mgmt->u.auth.auth_transaction);
-	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
-	if (len > 24 + sizeof(mgmt->u.auth)) {
-		event.auth.ies = mgmt->u.auth.variable;
-		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
-}
-
-
 unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
 {
 	struct nl_msg *msg;
@@ -1296,598 +1254,6 @@ nla_put_failure:
 }
 
 
-static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
-			    const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 status;
-
-	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-	    drv->force_connect_cmd) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignore assoc event when using driver SME");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-			   "frame");
-		return;
-	}
-
-	status = le_to_host16(mgmt->u.assoc_resp.status_code);
-	if (status != WLAN_STATUS_SUCCESS) {
-		os_memset(&event, 0, sizeof(event));
-		event.assoc_reject.bssid = mgmt->bssid;
-		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-			event.assoc_reject.resp_ies =
-				(u8 *) mgmt->u.assoc_resp.variable;
-			event.assoc_reject.resp_ies_len =
-				len - 24 - sizeof(mgmt->u.assoc_resp);
-		}
-		event.assoc_reject.status_code = status;
-
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-		return;
-	}
-
-	drv->associated = 1;
-	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
-	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
-
-	os_memset(&event, 0, sizeof(event));
-	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
-		event.assoc_info.resp_ies_len =
-			len - 24 - sizeof(mgmt->u.assoc_resp);
-	}
-
-	event.assoc_info.freq = drv->assoc_freq;
-
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
-}
-
-
-static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
-			       enum nl80211_commands cmd, struct nlattr *status,
-			       struct nlattr *addr, struct nlattr *req_ie,
-			       struct nlattr *resp_ie,
-			       struct nlattr *authorized,
-			       struct nlattr *key_replay_ctr,
-			       struct nlattr *ptk_kck,
-			       struct nlattr *ptk_kek)
-{
-	union wpa_event_data event;
-
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
-			   "when using userspace SME", cmd);
-		return;
-	}
-
-	if (cmd == NL80211_CMD_CONNECT)
-		wpa_printf(MSG_DEBUG, "nl80211: Connect event");
-	else if (cmd == NL80211_CMD_ROAM)
-		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
-
-	os_memset(&event, 0, sizeof(event));
-	if (cmd == NL80211_CMD_CONNECT &&
-	    nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
-		if (addr)
-			event.assoc_reject.bssid = nla_data(addr);
-		if (resp_ie) {
-			event.assoc_reject.resp_ies = nla_data(resp_ie);
-			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
-		}
-		event.assoc_reject.status_code = nla_get_u16(status);
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-		return;
-	}
-
-	drv->associated = 1;
-	if (addr) {
-		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
-		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
-	}
-
-	if (req_ie) {
-		event.assoc_info.req_ies = nla_data(req_ie);
-		event.assoc_info.req_ies_len = nla_len(req_ie);
-	}
-	if (resp_ie) {
-		event.assoc_info.resp_ies = nla_data(resp_ie);
-		event.assoc_info.resp_ies_len = nla_len(resp_ie);
-	}
-
-	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
-
-	if (authorized && nla_get_u8(authorized)) {
-		event.assoc_info.authorized = 1;
-		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
-	}
-	if (key_replay_ctr) {
-		event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
-		event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
-	}
-	if (ptk_kck) {
-		event.assoc_info.ptk_kck = nla_data(ptk_kck);
-		event.assoc_info.ptk_kck_len = nla_len(ptk_kek);
-	}
-	if (ptk_kek) {
-		event.assoc_info.ptk_kek = nla_data(ptk_kek);
-		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
-}
-
-
-static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-				  struct nlattr *reason, struct nlattr *addr,
-				  struct nlattr *by_ap)
-{
-	union wpa_event_data data;
-	unsigned int locally_generated = by_ap == NULL;
-
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-		/*
-		 * Avoid reporting two disassociation events that could
-		 * confuse the core code.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-			   "event when using userspace SME");
-		return;
-	}
-
-	if (drv->ignore_next_local_disconnect) {
-		drv->ignore_next_local_disconnect = 0;
-		if (locally_generated) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-				   "event triggered during reassociation");
-			return;
-		}
-		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
-			   "disconnect but got another disconnect "
-			   "event first");
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
-	nl80211_mark_disconnected(drv);
-	os_memset(&data, 0, sizeof(data));
-	if (reason)
-		data.deauth_info.reason_code = nla_get_u16(reason);
-	data.deauth_info.locally_generated = by_ap == NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
-}
-
-
-static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
-{
-	int freq1 = 0;
-
-	switch (convert2width(width)) {
-	case CHAN_WIDTH_20_NOHT:
-	case CHAN_WIDTH_20:
-		return 0;
-	case CHAN_WIDTH_40:
-		freq1 = cf1 - 10;
-		break;
-	case CHAN_WIDTH_80:
-		freq1 = cf1 - 30;
-		break;
-	case CHAN_WIDTH_160:
-		freq1 = cf1 - 70;
-		break;
-	case CHAN_WIDTH_UNKNOWN:
-	case CHAN_WIDTH_80P80:
-		/* FIXME: implement this */
-		return 0;
-	}
-
-	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
-}
-
-
-static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr *ifindex, struct nlattr *freq,
-				 struct nlattr *type, struct nlattr *bw,
-				 struct nlattr *cf1, struct nlattr *cf2)
-{
-	struct i802_bss *bss;
-	union wpa_event_data data;
-	int ht_enabled = 1;
-	int chan_offset = 0;
-	int ifidx;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
-
-	if (!freq)
-		return;
-
-	ifidx = nla_get_u32(ifindex);
-	bss = get_bss_ifindex(drv, ifidx);
-	if (bss == NULL) {
-		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
-			   ifidx);
-		return;
-	}
-
-	if (type) {
-		enum nl80211_channel_type ch_type = nla_get_u32(type);
-
-		wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
-		switch (ch_type) {
-		case NL80211_CHAN_NO_HT:
-			ht_enabled = 0;
-			break;
-		case NL80211_CHAN_HT20:
-			break;
-		case NL80211_CHAN_HT40PLUS:
-			chan_offset = 1;
-			break;
-		case NL80211_CHAN_HT40MINUS:
-			chan_offset = -1;
-			break;
-		}
-	} else if (bw && cf1) {
-		/* This can happen for example with VHT80 ch switch */
-		chan_offset = calculate_chan_offset(nla_get_u32(bw),
-						    nla_get_u32(freq),
-						    nla_get_u32(cf1),
-						    cf2 ? nla_get_u32(cf2) : 0);
-	} else {
-		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
-	}
-
-	os_memset(&data, 0, sizeof(data));
-	data.ch_switch.freq = nla_get_u32(freq);
-	data.ch_switch.ht_enabled = ht_enabled;
-	data.ch_switch.ch_offset = chan_offset;
-	if (bw)
-		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
-	if (cf1)
-		data.ch_switch.cf1 = nla_get_u32(cf1);
-	if (cf2)
-		data.ch_switch.cf2 = nla_get_u32(cf2);
-
-	bss->freq = data.ch_switch.freq;
-
-	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
-}
-
-
-static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
-			       enum nl80211_commands cmd, struct nlattr *addr)
-{
-	union wpa_event_data event;
-	enum wpa_event_type ev;
-
-	if (nla_len(addr) != ETH_ALEN)
-		return;
-
-	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
-		   cmd, MAC2STR((u8 *) nla_data(addr)));
-
-	if (cmd == NL80211_CMD_AUTHENTICATE)
-		ev = EVENT_AUTH_TIMED_OUT;
-	else if (cmd == NL80211_CMD_ASSOCIATE)
-		ev = EVENT_ASSOC_TIMED_OUT;
-	else
-		return;
-
-	os_memset(&event, 0, sizeof(event));
-	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
-	wpa_supplicant_event(drv->ctx, ev, &event);
-}
-
-
-static void mlme_event_mgmt(struct i802_bss *bss,
-			    struct nlattr *freq, struct nlattr *sig,
-			    const u8 *frame, size_t len)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 fc, stype;
-	int ssi_signal = 0;
-	int rx_freq = 0;
-
-	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
-		return;
-	}
-
-	fc = le_to_host16(mgmt->frame_control);
-	stype = WLAN_FC_GET_STYPE(fc);
-
-	if (sig)
-		ssi_signal = (s32) nla_get_u32(sig);
-
-	os_memset(&event, 0, sizeof(event));
-	if (freq) {
-		event.rx_mgmt.freq = nla_get_u32(freq);
-		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
-	}
-	wpa_printf(MSG_DEBUG,
-		   "nl80211: RX frame sa=" MACSTR
-		   " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
-		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
-		   (unsigned int) len);
-	event.rx_mgmt.frame = frame;
-	event.rx_mgmt.frame_len = len;
-	event.rx_mgmt.ssi_signal = ssi_signal;
-	event.rx_mgmt.drv_priv = bss;
-	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-}
-
-
-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
-				      struct nlattr *cookie, const u8 *frame,
-				      size_t len, struct nlattr *ack)
-{
-	union wpa_event_data event;
-	const struct ieee80211_hdr *hdr;
-	u16 fc;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
-	if (!is_ap_interface(drv->nlmode)) {
-		u64 cookie_val;
-
-		if (!cookie)
-			return;
-
-		cookie_val = nla_get_u64(cookie);
-		wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
-			   " cookie=0%llx%s (ack=%d)",
-			   (long long unsigned int) cookie_val,
-			   cookie_val == drv->send_action_cookie ?
-			   " (match)" : " (unknown)", ack != NULL);
-		if (cookie_val != drv->send_action_cookie)
-			return;
-	}
-
-	hdr = (const struct ieee80211_hdr *) frame;
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-	event.tx_status.dst = hdr->addr1;
-	event.tx_status.data = frame;
-	event.tx_status.data_len = len;
-	event.tx_status.ack = ack != NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-}
-
-
-static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
-				       enum wpa_event_type type,
-				       const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	const u8 *bssid = NULL;
-	u16 reason_code = 0;
-
-	if (type == EVENT_DEAUTH)
-		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
-	else
-		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
-
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len >= 24) {
-		bssid = mgmt->bssid;
-
-		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-		    !drv->associated &&
-		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
-			/*
-			 * Avoid issues with some roaming cases where
-			 * disconnection event for the old AP may show up after
-			 * we have started connection with the new AP.
-			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
-				   MAC2STR(bssid),
-				   MAC2STR(drv->auth_attempt_bssid));
-			return;
-		}
-
-		if (drv->associated != 0 &&
-		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
-			/*
-			 * We have presumably received this deauth as a
-			 * response to a clear_state_mismatch() outgoing
-			 * deauth.  Don't let it take us offline!
-			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
-				   "from Unknown BSSID " MACSTR " -- ignoring",
-				   MAC2STR(bssid));
-			return;
-		}
-	}
-
-	nl80211_mark_disconnected(drv);
-	os_memset(&event, 0, sizeof(event));
-
-	/* Note: Same offset for Reason Code in both frame subtypes */
-	if (len >= 24 + sizeof(mgmt->u.deauth))
-		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-	if (type == EVENT_DISASSOC) {
-		event.disassoc_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
-		event.disassoc_info.addr = bssid;
-		event.disassoc_info.reason_code = reason_code;
-		if (frame + len > mgmt->u.disassoc.variable) {
-			event.disassoc_info.ie = mgmt->u.disassoc.variable;
-			event.disassoc_info.ie_len = frame + len -
-				mgmt->u.disassoc.variable;
-		}
-	} else {
-		if (drv->ignore_deauth_event) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
-			drv->ignore_deauth_event = 0;
-			return;
-		}
-		event.deauth_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
-		if (drv->ignore_next_local_deauth) {
-			drv->ignore_next_local_deauth = 0;
-			if (event.deauth_info.locally_generated) {
-				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
-				return;
-			}
-			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
-		}
-		event.deauth_info.addr = bssid;
-		event.deauth_info.reason_code = reason_code;
-		if (frame + len > mgmt->u.deauth.variable) {
-			event.deauth_info.ie = mgmt->u.deauth.variable;
-			event.deauth_info.ie_len = frame + len -
-				mgmt->u.deauth.variable;
-		}
-	}
-
-	wpa_supplicant_event(drv->ctx, type, &event);
-}
-
-
-static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
-					 enum wpa_event_type type,
-					 const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 reason_code = 0;
-
-	if (type == EVENT_UNPROT_DEAUTH)
-		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
-	else
-		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
-
-	if (len < 24)
-		return;
-
-	mgmt = (const struct ieee80211_mgmt *) frame;
-
-	os_memset(&event, 0, sizeof(event));
-	/* Note: Same offset for Reason Code in both frame subtypes */
-	if (len >= 24 + sizeof(mgmt->u.deauth))
-		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-	if (type == EVENT_UNPROT_DISASSOC) {
-		event.unprot_disassoc.sa = mgmt->sa;
-		event.unprot_disassoc.da = mgmt->da;
-		event.unprot_disassoc.reason_code = reason_code;
-	} else {
-		event.unprot_deauth.sa = mgmt->sa;
-		event.unprot_deauth.da = mgmt->da;
-		event.unprot_deauth.reason_code = reason_code;
-	}
-
-	wpa_supplicant_event(drv->ctx, type, &event);
-}
-
-
-static void mlme_event(struct i802_bss *bss,
-		       enum nl80211_commands cmd, struct nlattr *frame,
-		       struct nlattr *addr, struct nlattr *timed_out,
-		       struct nlattr *freq, struct nlattr *ack,
-		       struct nlattr *cookie, struct nlattr *sig)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	const u8 *data;
-	size_t len;
-
-	if (timed_out && addr) {
-		mlme_timeout_event(drv, cmd, addr);
-		return;
-	}
-
-	if (frame == NULL) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: MLME event %d (%s) without frame data",
-			   cmd, nl80211_command_to_string(cmd));
-		return;
-	}
-
-	data = nla_data(frame);
-	len = nla_len(frame);
-	if (len < 4 + 2 * ETH_ALEN) {
-		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
-			   MACSTR ") - too short",
-			   cmd, nl80211_command_to_string(cmd), bss->ifname,
-			   MAC2STR(bss->addr));
-		return;
-	}
-	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
-		   ") A1=" MACSTR " A2=" MACSTR, cmd,
-		   nl80211_command_to_string(cmd), bss->ifname,
-		   MAC2STR(bss->addr), MAC2STR(data + 4),
-		   MAC2STR(data + 4 + ETH_ALEN));
-	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
-	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
-		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
-			   "for foreign address", bss->ifname);
-		return;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
-		    nla_data(frame), nla_len(frame));
-
-	switch (cmd) {
-	case NL80211_CMD_AUTHENTICATE:
-		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_ASSOCIATE:
-		mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_DEAUTHENTICATE:
-		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
-					   nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_DISASSOCIATE:
-		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
-					   nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
-				nla_len(frame));
-		break;
-	case NL80211_CMD_FRAME_TX_STATUS:
-		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
-					  nla_len(frame), ack);
-		break;
-	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
-					     nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_UNPROT_DISASSOCIATE:
-		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
-					     nla_data(frame), nla_len(frame));
-		break;
-	default:
-		break;
-	}
-}
-
-
 static void mlme_event_michael_mic_failure(struct i802_bss *bss,
 					   struct nlattr *tb[])
 {

+ 23 - 0
src/drivers/driver_nl80211.h

@@ -181,6 +181,7 @@ struct wpa_driver_nl80211_data {
 };
 
 struct nl_msg;
+struct nlattr;
 
 int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss);
 void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
@@ -209,6 +210,7 @@ int nl80211_get_wiphy_index(struct i802_bss *bss);
 int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
 				enum nl80211_iftype nlmode);
 void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+const char * nl80211_command_to_string(enum nl80211_commands cmd);
 
 int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
 void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
@@ -220,6 +222,27 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
 struct hostapd_hw_modes *
 nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
 
+void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			enum nl80211_commands cmd, struct nlattr *status,
+			struct nlattr *addr, struct nlattr *req_ie,
+			struct nlattr *resp_ie,
+			struct nlattr *authorized,
+			struct nlattr *key_replay_ctr,
+			struct nlattr *ptk_kck,
+			struct nlattr *ptk_kek);
+void mlme_event(struct i802_bss *bss,
+		enum nl80211_commands cmd, struct nlattr *frame,
+		struct nlattr *addr, struct nlattr *timed_out,
+		struct nlattr *freq, struct nlattr *ack,
+		struct nlattr *cookie, struct nlattr *sig);
+void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+			  struct nlattr *ifindex, struct nlattr *freq,
+			  struct nlattr *type, struct nlattr *bw,
+			  struct nlattr *cf1, struct nlattr *cf2);
+void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+			   struct nlattr *reason, struct nlattr *addr,
+			   struct nlattr *by_ap);
+
 #ifdef ANDROID
 int android_nl_socket_set_nonblocking(struct nl_handle *handle);
 int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name);

+ 651 - 0
src/drivers/driver_nl80211_event.c

@@ -0,0 +1,651 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_nl80211.h"
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore auth event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	event.auth.auth_transaction =
+		le_to_host16(mgmt->u.auth.auth_transaction);
+	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+	if (len > 24 + sizeof(mgmt->u.auth)) {
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 status;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore assoc event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	if (status != WLAN_STATUS_SUCCESS) {
+		os_memset(&event, 0, sizeof(event));
+		event.assoc_reject.bssid = mgmt->bssid;
+		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+			event.assoc_reject.resp_ies =
+				(u8 *) mgmt->u.assoc_resp.variable;
+			event.assoc_reject.resp_ies_len =
+				len - 24 - sizeof(mgmt->u.assoc_resp);
+		}
+		event.assoc_reject.status_code = status;
+
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+	os_memset(&event, 0, sizeof(event));
+	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+		event.assoc_info.resp_ies_len =
+			len - 24 - sizeof(mgmt->u.assoc_resp);
+	}
+
+	event.assoc_info.freq = drv->assoc_freq;
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			enum nl80211_commands cmd, struct nlattr *status,
+			struct nlattr *addr, struct nlattr *req_ie,
+			struct nlattr *resp_ie,
+			struct nlattr *authorized,
+			struct nlattr *key_replay_ctr,
+			struct nlattr *ptk_kck,
+			struct nlattr *ptk_kek)
+{
+	union wpa_event_data event;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+			   "when using userspace SME", cmd);
+		return;
+	}
+
+	if (cmd == NL80211_CMD_CONNECT)
+		wpa_printf(MSG_DEBUG, "nl80211: Connect event");
+	else if (cmd == NL80211_CMD_ROAM)
+		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+
+	os_memset(&event, 0, sizeof(event));
+	if (cmd == NL80211_CMD_CONNECT &&
+	    nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+		if (addr)
+			event.assoc_reject.bssid = nla_data(addr);
+		if (resp_ie) {
+			event.assoc_reject.resp_ies = nla_data(resp_ie);
+			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+		}
+		event.assoc_reject.status_code = nla_get_u16(status);
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	if (addr) {
+		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	}
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+	}
+	if (resp_ie) {
+		event.assoc_info.resp_ies = nla_data(resp_ie);
+		event.assoc_info.resp_ies_len = nla_len(resp_ie);
+	}
+
+	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
+	if (authorized && nla_get_u8(authorized)) {
+		event.assoc_info.authorized = 1;
+		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+	}
+	if (key_replay_ctr) {
+		event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+		event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+	}
+	if (ptk_kck) {
+		event.assoc_info.ptk_kck = nla_data(ptk_kck);
+		event.assoc_info.ptk_kck_len = nla_len(ptk_kek);
+	}
+	if (ptk_kek) {
+		event.assoc_info.ptk_kek = nla_data(ptk_kek);
+		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+			   struct nlattr *reason, struct nlattr *addr,
+			   struct nlattr *by_ap)
+{
+	union wpa_event_data data;
+	unsigned int locally_generated = by_ap == NULL;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two disassociation events that could
+		 * confuse the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+			   "event when using userspace SME");
+		return;
+	}
+
+	if (drv->ignore_next_local_disconnect) {
+		drv->ignore_next_local_disconnect = 0;
+		if (locally_generated) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+				   "event triggered during reassociation");
+			return;
+		}
+		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+			   "disconnect but got another disconnect "
+			   "event first");
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+	nl80211_mark_disconnected(drv);
+	os_memset(&data, 0, sizeof(data));
+	if (reason)
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
+{
+	int freq1 = 0;
+
+	switch (convert2width(width)) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		return 0;
+	case CHAN_WIDTH_40:
+		freq1 = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		freq1 = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		freq1 = cf1 - 70;
+		break;
+	case CHAN_WIDTH_UNKNOWN:
+	case CHAN_WIDTH_80P80:
+		/* FIXME: implement this */
+		return 0;
+	}
+
+	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+			  struct nlattr *ifindex, struct nlattr *freq,
+			  struct nlattr *type, struct nlattr *bw,
+			  struct nlattr *cf1, struct nlattr *cf2)
+{
+	struct i802_bss *bss;
+	union wpa_event_data data;
+	int ht_enabled = 1;
+	int chan_offset = 0;
+	int ifidx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+
+	if (!freq)
+		return;
+
+	ifidx = nla_get_u32(ifindex);
+	bss = get_bss_ifindex(drv, ifidx);
+	if (bss == NULL) {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+			   ifidx);
+		return;
+	}
+
+	if (type) {
+		enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+		wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+		switch (ch_type) {
+		case NL80211_CHAN_NO_HT:
+			ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chan_offset = -1;
+			break;
+		}
+	} else if (bw && cf1) {
+		/* This can happen for example with VHT80 ch switch */
+		chan_offset = calculate_chan_offset(nla_get_u32(bw),
+						    nla_get_u32(freq),
+						    nla_get_u32(cf1),
+						    cf2 ? nla_get_u32(cf2) : 0);
+	} else {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.ch_switch.freq = nla_get_u32(freq);
+	data.ch_switch.ht_enabled = ht_enabled;
+	data.ch_switch.ch_offset = chan_offset;
+	if (bw)
+		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+	if (cf1)
+		data.ch_switch.cf1 = nla_get_u32(cf1);
+	if (cf2)
+		data.ch_switch.cf2 = nla_get_u32(cf2);
+
+	bss->freq = data.ch_switch.freq;
+
+	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *addr)
+{
+	union wpa_event_data event;
+	enum wpa_event_type ev;
+
+	if (nla_len(addr) != ETH_ALEN)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+		   cmd, MAC2STR((u8 *) nla_data(addr)));
+
+	if (cmd == NL80211_CMD_AUTHENTICATE)
+		ev = EVENT_AUTH_TIMED_OUT;
+	else if (cmd == NL80211_CMD_ASSOCIATE)
+		ev = EVENT_ASSOC_TIMED_OUT;
+	else
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+static void mlme_event_mgmt(struct i802_bss *bss,
+			    struct nlattr *freq, struct nlattr *sig,
+			    const u8 *frame, size_t len)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ssi_signal = 0;
+	int rx_freq = 0;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+		return;
+	}
+
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
+	os_memset(&event, 0, sizeof(event));
+	if (freq) {
+		event.rx_mgmt.freq = nla_get_u32(freq);
+		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: RX frame sa=" MACSTR
+		   " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
+		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
+		   (unsigned int) len);
+	event.rx_mgmt.frame = frame;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.drv_priv = bss;
+	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *cookie, const u8 *frame,
+				      size_t len, struct nlattr *ack)
+{
+	union wpa_event_data event;
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
+	if (!is_ap_interface(drv->nlmode)) {
+		u64 cookie_val;
+
+		if (!cookie)
+			return;
+
+		cookie_val = nla_get_u64(cookie);
+		wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
+			   " cookie=0%llx%s (ack=%d)",
+			   (long long unsigned int) cookie_val,
+			   cookie_val == drv->send_action_cookie ?
+			   " (match)" : " (unknown)", ack != NULL);
+		if (cookie_val != drv->send_action_cookie)
+			return;
+	}
+
+	hdr = (const struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = frame;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ack != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+				       enum wpa_event_type type,
+				       const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	const u8 *bssid = NULL;
+	u16 reason_code = 0;
+
+	if (type == EVENT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len >= 24) {
+		bssid = mgmt->bssid;
+
+		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    !drv->associated &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+				   MAC2STR(bssid),
+				   MAC2STR(drv->auth_attempt_bssid));
+			return;
+		}
+
+		if (drv->associated != 0 &&
+		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+			/*
+			 * We have presumably received this deauth as a
+			 * response to a clear_state_mismatch() outgoing
+			 * deauth.  Don't let it take us offline!
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+				   "from Unknown BSSID " MACSTR " -- ignoring",
+				   MAC2STR(bssid));
+			return;
+		}
+	}
+
+	nl80211_mark_disconnected(drv);
+	os_memset(&event, 0, sizeof(event));
+
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_DISASSOC) {
+		event.disassoc_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		event.disassoc_info.addr = bssid;
+		event.disassoc_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.disassoc.variable) {
+			event.disassoc_info.ie = mgmt->u.disassoc.variable;
+			event.disassoc_info.ie_len = frame + len -
+				mgmt->u.disassoc.variable;
+		}
+	} else {
+		if (drv->ignore_deauth_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+			drv->ignore_deauth_event = 0;
+			return;
+		}
+		event.deauth_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		if (drv->ignore_next_local_deauth) {
+			drv->ignore_next_local_deauth = 0;
+			if (event.deauth_info.locally_generated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+				return;
+			}
+			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+		}
+		event.deauth_info.addr = bssid;
+		event.deauth_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.deauth.variable) {
+			event.deauth_info.ie = mgmt->u.deauth.variable;
+			event.deauth_info.ie_len = frame + len -
+				mgmt->u.deauth.variable;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+					 enum wpa_event_type type,
+					 const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 reason_code = 0;
+
+	if (type == EVENT_UNPROT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+	if (len < 24)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+
+	os_memset(&event, 0, sizeof(event));
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_UNPROT_DISASSOC) {
+		event.unprot_disassoc.sa = mgmt->sa;
+		event.unprot_disassoc.da = mgmt->da;
+		event.unprot_disassoc.reason_code = reason_code;
+	} else {
+		event.unprot_deauth.sa = mgmt->sa;
+		event.unprot_deauth.da = mgmt->da;
+		event.unprot_deauth.reason_code = reason_code;
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+void mlme_event(struct i802_bss *bss,
+		enum nl80211_commands cmd, struct nlattr *frame,
+		struct nlattr *addr, struct nlattr *timed_out,
+		struct nlattr *freq, struct nlattr *ack,
+		struct nlattr *cookie, struct nlattr *sig)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const u8 *data;
+	size_t len;
+
+	if (timed_out && addr) {
+		mlme_timeout_event(drv, cmd, addr);
+		return;
+	}
+
+	if (frame == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLME event %d (%s) without frame data",
+			   cmd, nl80211_command_to_string(cmd));
+		return;
+	}
+
+	data = nla_data(frame);
+	len = nla_len(frame);
+	if (len < 4 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+			   MACSTR ") - too short",
+			   cmd, nl80211_command_to_string(cmd), bss->ifname,
+			   MAC2STR(bss->addr));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+		   ") A1=" MACSTR " A2=" MACSTR, cmd,
+		   nl80211_command_to_string(cmd), bss->ifname,
+		   MAC2STR(bss->addr), MAC2STR(data + 4),
+		   MAC2STR(data + 4 + ETH_ALEN));
+	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+			   "for foreign address", bss->ifname);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+		    nla_data(frame), nla_len(frame));
+
+	switch (cmd) {
+	case NL80211_CMD_AUTHENTICATE:
+		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_ASSOCIATE:
+		mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_DEAUTHENTICATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_DISASSOCIATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME:
+		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+				nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+					  nla_len(frame), ack);
+		break;
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+					     nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+					     nla_data(frame), nla_len(frame));
+		break;
+	default:
+		break;
+	}
+}

+ 1 - 0
src/drivers/drivers.mak

@@ -26,6 +26,7 @@ ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += ../src/drivers/driver_nl80211.o
 DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
 DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
 DRV_OBJS += ../src/utils/radiotap.o
 NEED_SME=y

+ 1 - 0
src/drivers/drivers.mk

@@ -22,6 +22,7 @@ DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += src/drivers/driver_nl80211.c
 DRV_OBJS += src/drivers/driver_nl80211_android.c
 DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
 DRV_OBJS += src/drivers/driver_nl80211_monitor.c
 DRV_OBJS += src/utils/radiotap.c
 NEED_SME=y