diff mbox

Force disconnect a P2P Client/STA from GO/AP side

Message ID 6C370B347C3FE8438C9692873287D2E1195AE35D65@SJEXCHCCR01.corp.ad.broadcom.com
State Superseded, archived
Headers show

Commit Message

Jithu Jance Dec. 14, 2011, 2:12 p.m. UTC
This patch adds a new command "disconnect_sta <mac addr>" to force disconnect
a STA/P2P Client from AP/P2P GO side. Note that P2P Client/STA may connect back 
immediately since AP profile may not be disabled at the client/STA side. Currently, this
is primarily useful for testing purposes.

This will be much more useful in scenarios where client side doesn't reconnect back (after disconnection). 
This will allow the applications to tear-down connections from the P2P-GO side as well.
For e.g, if mobile acts a GO and it has around 2-3 connections, this cmd will allow to 
explicitly tear down a particular client from GO side. But again, this depends on the client side
application framework. For temporary connections such as WiFi-Direct where roaming
cases are extremely rare, Client side application framework may choose to disable P2P
Client interface on getting the CTRL-EVENT-DISCONNECTED event rather than retrying 
to connect.

Kindly see whether this command is okay to be included.

Signed-hostap: Jithu Jance <jithu@broadcom.com>

---
 hostapd/hostapd_cli.c       |   18 ++++++++++++++++++
 src/ap/ctrl_iface_ap.c      |   22 ++++++++++++++++++++++
 src/ap/ctrl_iface_ap.h      |    2 ++
 src/ap/sta_info.c           |   21 +++++++++++++++++++++
 src/ap/sta_info.h           |    2 ++
 wpa_supplicant/README-P2P   |    7 ++++++-
 wpa_supplicant/ap.c         |    8 ++++++++
 wpa_supplicant/ap.h         |    2 ++
 wpa_supplicant/ctrl_iface.c |    3 +++
 wpa_supplicant/wpa_cli.c    |   21 +++++++++++++++++++++
 10 files changed, 105 insertions(+), 1 deletions(-)

Comments

Jouni Malinen Dec. 17, 2011, 4:17 p.m. UTC | #1
On Wed, Dec 14, 2011 at 06:12:49AM -0800, Jithu Jance wrote:
> This patch adds a new command "disconnect_sta <mac addr>" to force disconnect
> a STA/P2P Client from AP/P2P GO side. Note that P2P Client/STA may connect back 
> immediately since AP profile may not be disabled at the client/STA side. Currently, this
> is primarily useful for testing purposes.

Why would a new command be needed for this? hostapd already has
deauthenticate and disassociate ctrl_iface commands. Wouldn't one of
those be suitable for this need, too?

> This will be much more useful in scenarios where client side doesn't reconnect back (after disconnection). 
> This will allow the applications to tear-down connections from the P2P-GO side as well.
> For e.g, if mobile acts a GO and it has around 2-3 connections, this cmd will allow to 
> explicitly tear down a particular client from GO side. But again, this depends on the client side
> application framework. For temporary connections such as WiFi-Direct where roaming
> cases are extremely rare, Client side application framework may choose to disable P2P
> Client interface on getting the CTRL-EVENT-DISCONNECTED event rather than retrying 
> to connect.

This does not sound like something that I would trust on. If the needed
functionality is to kick a P2P client away from the group, that client
would need to be prevented from reconnecting. Just sending a
Deauthentication frame to it does not guarantee anything. And well, even
this is not really suitable from the security view point if the stations
in the group share the same PSK. Unfortunately, P2P specification does
not provide a very good mechanism for handling this type of operations
reliably.

> diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
> index 527860c..5eeefea 100644
> --- a/hostapd/hostapd_cli.c
> +++ b/hostapd/hostapd_cli.c
> @@ -732,6 +749,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
> +	{ "disconnect_sta", hostapd_cli_cmd_disconnect_sta },
>  	{ "new_sta", hostapd_cli_cmd_new_sta },
>  	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
>  	{ "disassociate", hostapd_cli_cmd_disassociate },

Those deauth/disassoc commands should be used rather than adding a new
command. Sure, they would need to be moved from hostapd/ctrl_iface.c to
src/ap/ctrl_iface_ap.c, but that is better than introducing duplicated
functionality.
diff mbox

Patch

diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 527860c..5eeefea 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -592,6 +592,23 @@  static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
 	return -1;
 }
 
+static int hostapd_cli_cmd_disconnect_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[128];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid command: needs one argument, sta mac address \n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "DISCONNECT_STA %s ", argv[0]);
+	if (res < 0 || (size_t) res >= sizeof(cmd))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
 
 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -732,6 +749,7 @@  static struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "relog", hostapd_cli_cmd_relog },
 	{ "sta", hostapd_cli_cmd_sta },
 	{ "all_sta", hostapd_cli_cmd_all_sta },
+	{ "disconnect_sta", hostapd_cli_cmd_disconnect_sta },
 	{ "new_sta", hostapd_cli_cmd_new_sta },
 	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
 	{ "disassociate", hostapd_cli_cmd_disassociate },
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index d348dc1..5bc7793 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -19,6 +19,7 @@ 
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
 #include "ieee802_11.h"
+#include "common/ieee802_11_defs.h"
 #include "sta_info.h"
 #include "wps_hostapd.h"
 #include "p2p_hostapd.h"
@@ -106,3 +107,24 @@  int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
 	}		
 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
 }
+
+int hostapd_ctrl_iface_disconnect_sta(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	int ret;
+
+	if (hwaddr_aton(txtaddr, addr) ||
+	    (sta = ap_get_sta(hapd, addr)) == NULL) {
+		wpa_printf(MSG_ERROR, "AP: Couldn't find sta_info for the "
+					"given mac_addr-"MACSTR, MAC2STR(addr));
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (ret < 0 || (size_t) ret >= buflen)
+			return 0;
+		return ret;
+	}
+	ap_sta_abrupt_disconnect(hapd->iface->bss[0], sta, addr,
+                                                WLAN_REASON_UNSPECIFIED);
+	return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 8690bea..1054fd5 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -21,5 +21,7 @@  int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
 			   char *buf, size_t buflen);
 int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
 				char *buf, size_t buflen);
+int hostapd_ctrl_iface_disconnect_sta(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index d9c348e..de29efa 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -827,6 +827,27 @@  void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 					sta->addr, authorized);
 }
 
+/* This call won't wait for the deauth callback. The sta_info for the sta
+ * is immediately removed after sending the deauth frame. This is required
+ * when sta is explicitly disconnected from AP/P2P Go side. We can't wait
+ * till AP_MAX_INACTIVITY_AFTER_DEAUTH to clear sta_info, because client
+ * may connect back before timer expiry. This is primarily intended for
+ * testing purpose to force disconnect a STA/P2P Client from AP/P2P GO side.
+*/
+void ap_sta_abrupt_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+                       const u8 *addr, u16 reason) {
+
+	if (sta == NULL)
+		return;
+
+	if (addr)
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+	ap_sta_set_authorized(hapd, sta, 0);
+	mlme_deauthenticate_indication(
+			hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	ap_free_sta(hapd, sta);
+
+}
 
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *addr, u16 reason)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 4d3b5e9..cc1e78d 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -159,6 +159,8 @@  int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_abrupt_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason);
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *addr, u16 reason);
 
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index db6e4ae..aa24561 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -485,6 +485,12 @@  p2p_peer <P2P Device Address>
 
 Fetch information about a known P2P peer.
 
+disconnect_sta <mac addr>
+
+Force disconnect a P2P Client from P2P GO. Note that the P2P Client may or
+may not connect back depending on how the CTRL-EVENT-DISCONNECTED is handled
+by the client side application. This is primarly intended for testing purpose.
+
 Group Status
 
 (These are used on the group interface.)
@@ -515,7 +521,6 @@  remove_network <network id>
 
 Remove a network entry from configuration. 
 
-
 wpa_cli action script
 ---------------------
 
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index a3b460e..0aaeb3d 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -875,6 +875,14 @@  int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 					   buf, buflen);
 }
 
+int ap_ctrl_iface_disconnect_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+			   char *buf, size_t buflen)
+{
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	return hostapd_ctrl_iface_disconnect_sta(wpa_s->ap_iface->bss[0], txtaddr,
+					   buf, buflen);
+}
 
 int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 				 size_t buflen, int verbose)
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index aa4c362..edd3d60 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -37,6 +37,8 @@  int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
 		      char *buf, size_t buflen);
 int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 			   char *buf, size_t buflen);
+int ap_ctrl_iface_disconnect_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+			   char *buf, size_t buflen);
 int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 				 size_t buflen, int verbose);
 void ap_tx_status(void *ctx, const u8 *addr,
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b9ec347..ff4496d 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -3673,6 +3673,9 @@  char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
 		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
 						   reply_size);
+	} else if (os_strncmp(buf, "DISCONNECT_STA ", 15) == 0) {
+		reply_len = ap_ctrl_iface_disconnect_sta(wpa_s, buf + 15, reply,
+					      reply_size);
 #endif /* CONFIG_AP */
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
 		wpas_notify_suspend(wpa_s->global);
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 42c81cd..bcadac9 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1879,6 +1879,24 @@  static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 
 	return -1;
 }
+
+static int wpa_cli_cmd_disconnect_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[128];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid command: needs one argument, sta mac address \n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "DISCONNECT_STA %s ", argv[0]);
+	if (res < 0 || (size_t) res >= sizeof(cmd))
+		return -1;
+	cmd[sizeof(cmd) - 1] = '\0';
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
 #endif /* CONFIG_AP */
 
 
@@ -2911,6 +2929,9 @@  static struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "all_sta", wpa_cli_cmd_all_sta,
 	  cli_cmd_flag_none,
 	  "= get information about all associated stations (AP)" },
+	{ "disconnect_sta", wpa_cli_cmd_disconnect_sta,
+	  cli_cmd_flag_none,
+	  "= disconnect a particluar STA" },
 #endif /* CONFIG_AP */
 	{ "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none,
 	  "= notification of suspend/hibernate" },