From f2c566027e7e97203c11b5cacc527aecb017394f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 1 Sep 2013 17:37:22 +0300 Subject: [PATCH] P2P: Add a command for removing a client from all groups The new control interface command P2P_REMOVE_CLIENT can now be used to remove the specified client from all groups (ongoing and persistent) in which the local device is a GO. This will remove any per-client PSK entries and deauthenticate the device. Signed-hostap: Jouni Malinen --- src/ap/sta_info.c | 26 ++++++- src/ap/sta_info.h | 1 + wpa_supplicant/ctrl_iface.c | 23 ++++++ wpa_supplicant/p2p_supplicant.c | 120 ++++++++++++++++++++++++++++++-- wpa_supplicant/p2p_supplicant.h | 2 + wpa_supplicant/wpa_cli.c | 10 +++ 6 files changed, 176 insertions(+), 6 deletions(-) diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index d38330f14..6704c09c0 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -70,6 +70,30 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) } +#ifdef CONFIG_P2P +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + const u8 *p2p_dev_addr; + + if (sta->p2p_ie == NULL) + continue; + + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + if (p2p_dev_addr == NULL) + continue; + + if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0) + return sta; + } + + return NULL; +} +#endif /* CONFIG_P2P */ + + static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index f8f5a8376..e5b506938 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -156,6 +156,7 @@ int ap_for_each_sta(struct hostapd_data *hapd, void *ctx), void *ctx); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 2abcf74c1..be24356d7 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -4629,6 +4629,25 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) return wpas_p2p_ext_listen(wpa_s, period, interval); } + +static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos; + u8 peer[ETH_ALEN]; + int iface_addr = 0; + + pos = cmd; + if (os_strncmp(pos, "iface=", 6) == 0) { + iface_addr = 1; + pos += 6; + } + if (hwaddr_aton(pos, peer)) + return -1; + + wpas_p2p_remove_client(wpa_s, peer, iface_addr); + return 0; +} + #endif /* CONFIG_P2P */ @@ -5450,6 +5469,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { + if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) + reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { @@ -5936,6 +5958,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "P2P_UNAUTHORIZE ", "P2P_PRESENCE_REQ ", "P2P_EXT_LISTEN ", + "P2P_REMOVE_CLIENT ", NULL }; int found = 0; diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 962236b17..f87cc7fc9 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -17,6 +17,8 @@ #include "p2p/p2p.h" #include "ap/hostapd.h" #include "ap/ap_config.h" +#include "ap/sta_info.h" +#include "ap/ap_drv_ops.h" #include "ap/p2p_hostapd.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" @@ -2700,7 +2702,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - const u8 *peer) + const u8 *peer, int inv) { size_t i; @@ -2725,8 +2727,9 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, } wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent " - "group %d client list due to invitation result", - MAC2STR(peer), ssid->id); + "group %d client list%s", + MAC2STR(peer), ssid->id, + inv ? " due to invitation result" : ""); os_memmove(ssid->p2p_client_list + i * ETH_ALEN, ssid->p2p_client_list + (i + 1) * ETH_ALEN, (ssid->num_p2p_clients - i - 1) * ETH_ALEN); @@ -2753,7 +2756,7 @@ static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, return; /* Not operating as a GO in persistent group */ ssid = wpas_p2p_get_persistent(wpa_s->parent, peer, ssid->ssid, ssid->ssid_len); - wpas_remove_persistent_peer(wpa_s, ssid, peer); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } @@ -2792,7 +2795,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, if (status == P2P_SC_FAIL_UNKNOWN_GROUP) { ssid = wpa_config_get_network( wpa_s->conf, wpa_s->pending_invite_ssid_id); - wpas_remove_persistent_peer(wpa_s, ssid, peer); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } wpas_p2p_remove_pending_group_interface(wpa_s); return; @@ -6116,3 +6119,110 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); #endif /* CONFIG_NO_CONFIG_WRITE */ } + + +static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s, + struct wpa_ssid *s, const u8 *addr, + int iface_addr) +{ + struct psk_list_entry *psk, *tmp; + int changed = 0; + + dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry, + list) { + if ((iface_addr && !psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove persistent group PSK list entry for " + MACSTR " p2p=%u", + MAC2STR(psk->addr), psk->p2p); + dl_list_del(&psk->list); + os_free(psk); + changed++; + } + } + + +#ifndef CONFIG_NO_CONFIG_WRITE + if (changed && wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + +static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s, + const u8 *peer, int iface_addr) +{ + struct hostapd_data *hapd; + struct hostapd_wpa_psk *psk, *prev, *rem; + struct sta_info *sta; + + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO) + return; + + /* Remove per-station PSK entry */ + hapd = wpa_s->ap_iface->bss[0]; + prev = NULL; + psk = hapd->conf->ssid.wpa_psk; + while (psk) { + if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && + os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for " + MACSTR " iface_addr=%d", + MAC2STR(peer), iface_addr); + if (prev) + prev->next = psk->next; + else + hapd->conf->ssid.wpa_psk = psk->next; + rem = psk; + psk = psk->next; + os_free(rem); + } else { + prev = psk; + psk = psk->next; + } + } + + /* Disconnect from group */ + if (iface_addr) + sta = ap_get_sta(hapd, peer); + else + sta = ap_get_sta_p2p(hapd, peer); + if (sta) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR + " (iface_addr=%d) from group", + MAC2STR(peer), iface_addr); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_DEAUTH_LEAVING); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING); + } +} + + +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr) +{ + struct wpa_ssid *s; + struct wpa_supplicant *w; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); + + /* Remove from any persistent group */ + for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) + continue; + if (!iface_addr) + wpas_remove_persistent_peer(wpa_s, s, peer, 0); + wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr); + } + + /* Remove from any operating group */ + for (w = wpa_s->global->ifaces; w; w = w->next) + wpas_p2p_remove_client_go(w, peer, iface_addr); +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index f72d56395..5c39a17b6 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -155,6 +155,8 @@ unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr); #ifdef CONFIG_P2P void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 9817eda68..65ca9a34b 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2149,6 +2149,13 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv); } + +static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); +} + #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY @@ -2745,6 +2752,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL, cli_cmd_flag_none, "[ ] = set extended listen timing" }, + { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + " = remove a peer from all groups" }, #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,