xref: /freebsd/contrib/wpa/hostapd/ctrl_iface.c (revision 32a95656)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * hostapd / UNIX domain socket -based control interface
385732ac8SCy Schubert  * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
9e28a4053SRui Paulo #include "utils/includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
1239beb93cSSam Leffler 
135b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
14c1d255d3SCy Schubert #ifdef __NetBSD__
15c1d255d3SCy Schubert #include <net/if_ether.h>
16c1d255d3SCy Schubert #else
175b9c547cSRui Paulo #include <net/ethernet.h>
18c1d255d3SCy Schubert #endif
195b9c547cSRui Paulo #include <netinet/ip.h>
205b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
215b9c547cSRui Paulo 
2239beb93cSSam Leffler #include <sys/un.h>
2339beb93cSSam Leffler #include <sys/stat.h>
243157ba21SRui Paulo #include <stddef.h>
2539beb93cSSam Leffler 
26780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
27780fb4a2SCy Schubert #include <netdb.h>
28780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
29780fb4a2SCy Schubert 
30e28a4053SRui Paulo #include "utils/common.h"
31e28a4053SRui Paulo #include "utils/eloop.h"
32780fb4a2SCy Schubert #include "utils/module_tests.h"
33f05cddf9SRui Paulo #include "common/version.h"
34e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
35780fb4a2SCy Schubert #include "common/ctrl_iface_common.h"
3685732ac8SCy Schubert #ifdef CONFIG_DPP
3785732ac8SCy Schubert #include "common/dpp.h"
3885732ac8SCy Schubert #endif /* CONFIG_DPP */
3985732ac8SCy Schubert #include "common/wpa_ctrl.h"
40c1d255d3SCy Schubert #include "common/ptksa_cache.h"
415b9c547cSRui Paulo #include "crypto/tls.h"
42e28a4053SRui Paulo #include "drivers/driver.h"
43325151a3SRui Paulo #include "eapol_auth/eapol_auth_sm.h"
4439beb93cSSam Leffler #include "radius/radius_client.h"
455b9c547cSRui Paulo #include "radius/radius_server.h"
465b9c547cSRui Paulo #include "l2_packet/l2_packet.h"
47e28a4053SRui Paulo #include "ap/hostapd.h"
48e28a4053SRui Paulo #include "ap/ap_config.h"
49e28a4053SRui Paulo #include "ap/ieee802_1x.h"
50e28a4053SRui Paulo #include "ap/wpa_auth.h"
51c1d255d3SCy Schubert #include "ap/pmksa_cache_auth.h"
52e28a4053SRui Paulo #include "ap/ieee802_11.h"
53e28a4053SRui Paulo #include "ap/sta_info.h"
54e28a4053SRui Paulo #include "ap/wps_hostapd.h"
55e28a4053SRui Paulo #include "ap/ctrl_iface_ap.h"
56f05cddf9SRui Paulo #include "ap/ap_drv_ops.h"
575b9c547cSRui Paulo #include "ap/hs20.h"
585b9c547cSRui Paulo #include "ap/wnm_ap.h"
595b9c547cSRui Paulo #include "ap/wpa_auth.h"
605b9c547cSRui Paulo #include "ap/beacon.h"
61780fb4a2SCy Schubert #include "ap/neighbor_db.h"
62780fb4a2SCy Schubert #include "ap/rrm.h"
6385732ac8SCy Schubert #include "ap/dpp_hostapd.h"
64c1d255d3SCy Schubert #include "ap/dfs.h"
65f05cddf9SRui Paulo #include "wps/wps_defs.h"
66f05cddf9SRui Paulo #include "wps/wps.h"
67325151a3SRui Paulo #include "fst/fst_ctrl_iface.h"
68f05cddf9SRui Paulo #include "config_file.h"
6939beb93cSSam Leffler #include "ctrl_iface.h"
7039beb93cSSam Leffler 
7139beb93cSSam Leffler 
72325151a3SRui Paulo #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
73325151a3SRui Paulo 
74780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
75780fb4a2SCy Schubert #define HOSTAPD_CTRL_IFACE_PORT		8877
76780fb4a2SCy Schubert #define HOSTAPD_CTRL_IFACE_PORT_LIMIT	50
77780fb4a2SCy Schubert #define HOSTAPD_GLOBAL_CTRL_IFACE_PORT		8878
78780fb4a2SCy Schubert #define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT	50
79780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
8039beb93cSSam Leffler 
8139beb93cSSam Leffler static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
82325151a3SRui Paulo 				    enum wpa_msg_type type,
8339beb93cSSam Leffler 				    const char *buf, size_t len);
8439beb93cSSam Leffler 
8539beb93cSSam Leffler 
hostapd_ctrl_iface_attach(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen,const char * input)8639beb93cSSam Leffler static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
87780fb4a2SCy Schubert 				     struct sockaddr_storage *from,
8885732ac8SCy Schubert 				     socklen_t fromlen, const char *input)
8939beb93cSSam Leffler {
9085732ac8SCy Schubert 	return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input);
9139beb93cSSam Leffler }
9239beb93cSSam Leffler 
9339beb93cSSam Leffler 
hostapd_ctrl_iface_detach(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen)9439beb93cSSam Leffler static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
95780fb4a2SCy Schubert 				     struct sockaddr_storage *from,
9639beb93cSSam Leffler 				     socklen_t fromlen)
9739beb93cSSam Leffler {
98780fb4a2SCy Schubert 	return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen);
9939beb93cSSam Leffler }
10039beb93cSSam Leffler 
10139beb93cSSam Leffler 
hostapd_ctrl_iface_level(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen,char * level)10239beb93cSSam Leffler static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
103780fb4a2SCy Schubert 				    struct sockaddr_storage *from,
10439beb93cSSam Leffler 				    socklen_t fromlen,
10539beb93cSSam Leffler 				    char *level)
10639beb93cSSam Leffler {
107780fb4a2SCy Schubert 	return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level);
10839beb93cSSam Leffler }
10939beb93cSSam Leffler 
11039beb93cSSam Leffler 
hostapd_ctrl_iface_new_sta(struct hostapd_data * hapd,const char * txtaddr)11139beb93cSSam Leffler static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
11239beb93cSSam Leffler 				      const char *txtaddr)
11339beb93cSSam Leffler {
11439beb93cSSam Leffler 	u8 addr[ETH_ALEN];
11539beb93cSSam Leffler 	struct sta_info *sta;
11639beb93cSSam Leffler 
11739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
11839beb93cSSam Leffler 
11939beb93cSSam Leffler 	if (hwaddr_aton(txtaddr, addr))
12039beb93cSSam Leffler 		return -1;
12139beb93cSSam Leffler 
12239beb93cSSam Leffler 	sta = ap_get_sta(hapd, addr);
12339beb93cSSam Leffler 	if (sta)
12439beb93cSSam Leffler 		return 0;
12539beb93cSSam Leffler 
12639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
12739beb93cSSam Leffler 		   "notification", MAC2STR(addr));
12839beb93cSSam Leffler 	sta = ap_sta_add(hapd, addr);
12939beb93cSSam Leffler 	if (sta == NULL)
13039beb93cSSam Leffler 		return -1;
13139beb93cSSam Leffler 
13239beb93cSSam Leffler 	hostapd_new_assoc_sta(hapd, sta, 0);
13339beb93cSSam Leffler 	return 0;
13439beb93cSSam Leffler }
13539beb93cSSam Leffler 
13639beb93cSSam Leffler 
137e28a4053SRui Paulo #ifdef NEED_AP_MLME
hostapd_ctrl_iface_sa_query(struct hostapd_data * hapd,const char * txtaddr)13839beb93cSSam Leffler static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
13939beb93cSSam Leffler 				       const char *txtaddr)
14039beb93cSSam Leffler {
14139beb93cSSam Leffler 	u8 addr[ETH_ALEN];
14239beb93cSSam Leffler 	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
14339beb93cSSam Leffler 
14439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
14539beb93cSSam Leffler 
146e28a4053SRui Paulo 	if (hwaddr_aton(txtaddr, addr) ||
147e28a4053SRui Paulo 	    os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
14839beb93cSSam Leffler 		return -1;
14939beb93cSSam Leffler 
15039beb93cSSam Leffler 	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
15139beb93cSSam Leffler 
15239beb93cSSam Leffler 	return 0;
15339beb93cSSam Leffler }
154e28a4053SRui Paulo #endif /* NEED_AP_MLME */
15539beb93cSSam Leffler 
15639beb93cSSam Leffler 
15739beb93cSSam Leffler #ifdef CONFIG_WPS
hostapd_ctrl_iface_wps_pin(struct hostapd_data * hapd,char * txt)15839beb93cSSam Leffler static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
15939beb93cSSam Leffler {
16039beb93cSSam Leffler 	char *pin = os_strchr(txt, ' ');
1613157ba21SRui Paulo 	char *timeout_txt;
1623157ba21SRui Paulo 	int timeout;
163f05cddf9SRui Paulo 	u8 addr_buf[ETH_ALEN], *addr = NULL;
164f05cddf9SRui Paulo 	char *pos;
1653157ba21SRui Paulo 
16639beb93cSSam Leffler 	if (pin == NULL)
16739beb93cSSam Leffler 		return -1;
16839beb93cSSam Leffler 	*pin++ = '\0';
1693157ba21SRui Paulo 
1703157ba21SRui Paulo 	timeout_txt = os_strchr(pin, ' ');
1713157ba21SRui Paulo 	if (timeout_txt) {
1723157ba21SRui Paulo 		*timeout_txt++ = '\0';
1733157ba21SRui Paulo 		timeout = atoi(timeout_txt);
174f05cddf9SRui Paulo 		pos = os_strchr(timeout_txt, ' ');
175f05cddf9SRui Paulo 		if (pos) {
176f05cddf9SRui Paulo 			*pos++ = '\0';
177f05cddf9SRui Paulo 			if (hwaddr_aton(pos, addr_buf) == 0)
178f05cddf9SRui Paulo 				addr = addr_buf;
179f05cddf9SRui Paulo 		}
1803157ba21SRui Paulo 	} else
1813157ba21SRui Paulo 		timeout = 0;
1823157ba21SRui Paulo 
183f05cddf9SRui Paulo 	return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
18439beb93cSSam Leffler }
185e28a4053SRui Paulo 
186e28a4053SRui Paulo 
hostapd_ctrl_iface_wps_check_pin(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)187f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_check_pin(
188f05cddf9SRui Paulo 	struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
189e28a4053SRui Paulo {
190f05cddf9SRui Paulo 	char pin[9];
191f05cddf9SRui Paulo 	size_t len;
192f05cddf9SRui Paulo 	char *pos;
193f05cddf9SRui Paulo 	int ret;
194e28a4053SRui Paulo 
195f05cddf9SRui Paulo 	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
196f05cddf9SRui Paulo 			      (u8 *) cmd, os_strlen(cmd));
197f05cddf9SRui Paulo 	for (pos = cmd, len = 0; *pos != '\0'; pos++) {
198f05cddf9SRui Paulo 		if (*pos < '0' || *pos > '9')
199f05cddf9SRui Paulo 			continue;
200f05cddf9SRui Paulo 		pin[len++] = *pos;
201f05cddf9SRui Paulo 		if (len == 9) {
202f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
203e28a4053SRui Paulo 			return -1;
204e28a4053SRui Paulo 		}
205f05cddf9SRui Paulo 	}
206f05cddf9SRui Paulo 	if (len != 4 && len != 8) {
207f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
208f05cddf9SRui Paulo 		return -1;
209f05cddf9SRui Paulo 	}
210f05cddf9SRui Paulo 	pin[len] = '\0';
211f05cddf9SRui Paulo 
212f05cddf9SRui Paulo 	if (len == 8) {
213f05cddf9SRui Paulo 		unsigned int pin_val;
214f05cddf9SRui Paulo 		pin_val = atoi(pin);
215f05cddf9SRui Paulo 		if (!wps_pin_valid(pin_val)) {
216f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
217f05cddf9SRui Paulo 			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
2185b9c547cSRui Paulo 			if (os_snprintf_error(buflen, ret))
219f05cddf9SRui Paulo 				return -1;
220f05cddf9SRui Paulo 			return ret;
221f05cddf9SRui Paulo 		}
222f05cddf9SRui Paulo 	}
223f05cddf9SRui Paulo 
224f05cddf9SRui Paulo 	ret = os_snprintf(buf, buflen, "%s", pin);
2255b9c547cSRui Paulo 	if (os_snprintf_error(buflen, ret))
226f05cddf9SRui Paulo 		return -1;
227f05cddf9SRui Paulo 
228f05cddf9SRui Paulo 	return ret;
229f05cddf9SRui Paulo }
230f05cddf9SRui Paulo 
231f05cddf9SRui Paulo 
232f05cddf9SRui Paulo #ifdef CONFIG_WPS_NFC
hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data * hapd,char * pos)233f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
234f05cddf9SRui Paulo 					       char *pos)
235f05cddf9SRui Paulo {
236f05cddf9SRui Paulo 	size_t len;
237f05cddf9SRui Paulo 	struct wpabuf *buf;
238f05cddf9SRui Paulo 	int ret;
239f05cddf9SRui Paulo 
240f05cddf9SRui Paulo 	len = os_strlen(pos);
241f05cddf9SRui Paulo 	if (len & 0x01)
242f05cddf9SRui Paulo 		return -1;
243f05cddf9SRui Paulo 	len /= 2;
244f05cddf9SRui Paulo 
245f05cddf9SRui Paulo 	buf = wpabuf_alloc(len);
246f05cddf9SRui Paulo 	if (buf == NULL)
247f05cddf9SRui Paulo 		return -1;
248f05cddf9SRui Paulo 	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
249f05cddf9SRui Paulo 		wpabuf_free(buf);
250f05cddf9SRui Paulo 		return -1;
251f05cddf9SRui Paulo 	}
252f05cddf9SRui Paulo 
253f05cddf9SRui Paulo 	ret = hostapd_wps_nfc_tag_read(hapd, buf);
254f05cddf9SRui Paulo 	wpabuf_free(buf);
255f05cddf9SRui Paulo 
256f05cddf9SRui Paulo 	return ret;
257f05cddf9SRui Paulo }
258f05cddf9SRui Paulo 
259f05cddf9SRui Paulo 
hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)260f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
261f05cddf9SRui Paulo 						   char *cmd, char *reply,
262f05cddf9SRui Paulo 						   size_t max_len)
263f05cddf9SRui Paulo {
264f05cddf9SRui Paulo 	int ndef;
265f05cddf9SRui Paulo 	struct wpabuf *buf;
266f05cddf9SRui Paulo 	int res;
267f05cddf9SRui Paulo 
268f05cddf9SRui Paulo 	if (os_strcmp(cmd, "WPS") == 0)
269f05cddf9SRui Paulo 		ndef = 0;
270f05cddf9SRui Paulo 	else if (os_strcmp(cmd, "NDEF") == 0)
271f05cddf9SRui Paulo 		ndef = 1;
272f05cddf9SRui Paulo 	else
273f05cddf9SRui Paulo 		return -1;
274f05cddf9SRui Paulo 
275f05cddf9SRui Paulo 	buf = hostapd_wps_nfc_config_token(hapd, ndef);
276f05cddf9SRui Paulo 	if (buf == NULL)
277f05cddf9SRui Paulo 		return -1;
278f05cddf9SRui Paulo 
279f05cddf9SRui Paulo 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
280f05cddf9SRui Paulo 					 wpabuf_len(buf));
281f05cddf9SRui Paulo 	reply[res++] = '\n';
282f05cddf9SRui Paulo 	reply[res] = '\0';
283f05cddf9SRui Paulo 
284f05cddf9SRui Paulo 	wpabuf_free(buf);
285f05cddf9SRui Paulo 
286f05cddf9SRui Paulo 	return res;
287f05cddf9SRui Paulo }
288f05cddf9SRui Paulo 
289f05cddf9SRui Paulo 
hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data * hapd,char * reply,size_t max_len,int ndef)290f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
291f05cddf9SRui Paulo 						char *reply, size_t max_len,
292f05cddf9SRui Paulo 						int ndef)
293f05cddf9SRui Paulo {
294f05cddf9SRui Paulo 	struct wpabuf *buf;
295f05cddf9SRui Paulo 	int res;
296f05cddf9SRui Paulo 
297f05cddf9SRui Paulo 	buf = hostapd_wps_nfc_token_gen(hapd, ndef);
298f05cddf9SRui Paulo 	if (buf == NULL)
299f05cddf9SRui Paulo 		return -1;
300f05cddf9SRui Paulo 
301f05cddf9SRui Paulo 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
302f05cddf9SRui Paulo 					 wpabuf_len(buf));
303f05cddf9SRui Paulo 	reply[res++] = '\n';
304f05cddf9SRui Paulo 	reply[res] = '\0';
305f05cddf9SRui Paulo 
306f05cddf9SRui Paulo 	wpabuf_free(buf);
307f05cddf9SRui Paulo 
308f05cddf9SRui Paulo 	return res;
309f05cddf9SRui Paulo }
310f05cddf9SRui Paulo 
311f05cddf9SRui Paulo 
hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)312f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
313f05cddf9SRui Paulo 					    char *cmd, char *reply,
314f05cddf9SRui Paulo 					    size_t max_len)
315f05cddf9SRui Paulo {
316f05cddf9SRui Paulo 	if (os_strcmp(cmd, "WPS") == 0)
317f05cddf9SRui Paulo 		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
318f05cddf9SRui Paulo 							    max_len, 0);
319f05cddf9SRui Paulo 
320f05cddf9SRui Paulo 	if (os_strcmp(cmd, "NDEF") == 0)
321f05cddf9SRui Paulo 		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
322f05cddf9SRui Paulo 							    max_len, 1);
323f05cddf9SRui Paulo 
324f05cddf9SRui Paulo 	if (os_strcmp(cmd, "enable") == 0)
325f05cddf9SRui Paulo 		return hostapd_wps_nfc_token_enable(hapd);
326f05cddf9SRui Paulo 
327f05cddf9SRui Paulo 	if (os_strcmp(cmd, "disable") == 0) {
328f05cddf9SRui Paulo 		hostapd_wps_nfc_token_disable(hapd);
329f05cddf9SRui Paulo 		return 0;
330f05cddf9SRui Paulo 	}
331f05cddf9SRui Paulo 
332f05cddf9SRui Paulo 	return -1;
333f05cddf9SRui Paulo }
3345b9c547cSRui Paulo 
3355b9c547cSRui Paulo 
hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)3365b9c547cSRui Paulo static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
3375b9c547cSRui Paulo 						   char *cmd, char *reply,
3385b9c547cSRui Paulo 						   size_t max_len)
3395b9c547cSRui Paulo {
3405b9c547cSRui Paulo 	struct wpabuf *buf;
3415b9c547cSRui Paulo 	int res;
3425b9c547cSRui Paulo 	char *pos;
3435b9c547cSRui Paulo 	int ndef;
3445b9c547cSRui Paulo 
3455b9c547cSRui Paulo 	pos = os_strchr(cmd, ' ');
3465b9c547cSRui Paulo 	if (pos == NULL)
3475b9c547cSRui Paulo 		return -1;
3485b9c547cSRui Paulo 	*pos++ = '\0';
3495b9c547cSRui Paulo 
3505b9c547cSRui Paulo 	if (os_strcmp(cmd, "WPS") == 0)
3515b9c547cSRui Paulo 		ndef = 0;
3525b9c547cSRui Paulo 	else if (os_strcmp(cmd, "NDEF") == 0)
3535b9c547cSRui Paulo 		ndef = 1;
3545b9c547cSRui Paulo 	else
3555b9c547cSRui Paulo 		return -1;
3565b9c547cSRui Paulo 
3575b9c547cSRui Paulo 	if (os_strcmp(pos, "WPS-CR") == 0)
3585b9c547cSRui Paulo 		buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
3595b9c547cSRui Paulo 	else
3605b9c547cSRui Paulo 		buf = NULL;
3615b9c547cSRui Paulo 	if (buf == NULL)
3625b9c547cSRui Paulo 		return -1;
3635b9c547cSRui Paulo 
3645b9c547cSRui Paulo 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
3655b9c547cSRui Paulo 					 wpabuf_len(buf));
3665b9c547cSRui Paulo 	reply[res++] = '\n';
3675b9c547cSRui Paulo 	reply[res] = '\0';
3685b9c547cSRui Paulo 
3695b9c547cSRui Paulo 	wpabuf_free(buf);
3705b9c547cSRui Paulo 
3715b9c547cSRui Paulo 	return res;
3725b9c547cSRui Paulo }
3735b9c547cSRui Paulo 
3745b9c547cSRui Paulo 
hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data * hapd,char * cmd)3755b9c547cSRui Paulo static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
3765b9c547cSRui Paulo 						  char *cmd)
3775b9c547cSRui Paulo {
3785b9c547cSRui Paulo 	size_t len;
3795b9c547cSRui Paulo 	struct wpabuf *req, *sel;
3805b9c547cSRui Paulo 	int ret;
3815b9c547cSRui Paulo 	char *pos, *role, *type, *pos2;
3825b9c547cSRui Paulo 
3835b9c547cSRui Paulo 	role = cmd;
3845b9c547cSRui Paulo 	pos = os_strchr(role, ' ');
3855b9c547cSRui Paulo 	if (pos == NULL)
3865b9c547cSRui Paulo 		return -1;
3875b9c547cSRui Paulo 	*pos++ = '\0';
3885b9c547cSRui Paulo 
3895b9c547cSRui Paulo 	type = pos;
3905b9c547cSRui Paulo 	pos = os_strchr(type, ' ');
3915b9c547cSRui Paulo 	if (pos == NULL)
3925b9c547cSRui Paulo 		return -1;
3935b9c547cSRui Paulo 	*pos++ = '\0';
3945b9c547cSRui Paulo 
3955b9c547cSRui Paulo 	pos2 = os_strchr(pos, ' ');
3965b9c547cSRui Paulo 	if (pos2 == NULL)
3975b9c547cSRui Paulo 		return -1;
3985b9c547cSRui Paulo 	*pos2++ = '\0';
3995b9c547cSRui Paulo 
4005b9c547cSRui Paulo 	len = os_strlen(pos);
4015b9c547cSRui Paulo 	if (len & 0x01)
4025b9c547cSRui Paulo 		return -1;
4035b9c547cSRui Paulo 	len /= 2;
4045b9c547cSRui Paulo 
4055b9c547cSRui Paulo 	req = wpabuf_alloc(len);
4065b9c547cSRui Paulo 	if (req == NULL)
4075b9c547cSRui Paulo 		return -1;
4085b9c547cSRui Paulo 	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
4095b9c547cSRui Paulo 		wpabuf_free(req);
4105b9c547cSRui Paulo 		return -1;
4115b9c547cSRui Paulo 	}
4125b9c547cSRui Paulo 
4135b9c547cSRui Paulo 	len = os_strlen(pos2);
4145b9c547cSRui Paulo 	if (len & 0x01) {
4155b9c547cSRui Paulo 		wpabuf_free(req);
4165b9c547cSRui Paulo 		return -1;
4175b9c547cSRui Paulo 	}
4185b9c547cSRui Paulo 	len /= 2;
4195b9c547cSRui Paulo 
4205b9c547cSRui Paulo 	sel = wpabuf_alloc(len);
4215b9c547cSRui Paulo 	if (sel == NULL) {
4225b9c547cSRui Paulo 		wpabuf_free(req);
4235b9c547cSRui Paulo 		return -1;
4245b9c547cSRui Paulo 	}
4255b9c547cSRui Paulo 	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
4265b9c547cSRui Paulo 		wpabuf_free(req);
4275b9c547cSRui Paulo 		wpabuf_free(sel);
4285b9c547cSRui Paulo 		return -1;
4295b9c547cSRui Paulo 	}
4305b9c547cSRui Paulo 
4315b9c547cSRui Paulo 	if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
4325b9c547cSRui Paulo 		ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
4335b9c547cSRui Paulo 	} else {
4345b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
4355b9c547cSRui Paulo 			   "reported: role=%s type=%s", role, type);
4365b9c547cSRui Paulo 		ret = -1;
4375b9c547cSRui Paulo 	}
4385b9c547cSRui Paulo 	wpabuf_free(req);
4395b9c547cSRui Paulo 	wpabuf_free(sel);
4405b9c547cSRui Paulo 
4415b9c547cSRui Paulo 	return ret;
4425b9c547cSRui Paulo }
4435b9c547cSRui Paulo 
444f05cddf9SRui Paulo #endif /* CONFIG_WPS_NFC */
445e28a4053SRui Paulo 
446e28a4053SRui Paulo 
hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data * hapd,char * txt,char * buf,size_t buflen)447e28a4053SRui Paulo static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
448e28a4053SRui Paulo 					 char *buf, size_t buflen)
449e28a4053SRui Paulo {
450e28a4053SRui Paulo 	int timeout = 300;
451e28a4053SRui Paulo 	char *pos;
452e28a4053SRui Paulo 	const char *pin_txt;
453e28a4053SRui Paulo 
454e28a4053SRui Paulo 	pos = os_strchr(txt, ' ');
455e28a4053SRui Paulo 	if (pos)
456e28a4053SRui Paulo 		*pos++ = '\0';
457e28a4053SRui Paulo 
458e28a4053SRui Paulo 	if (os_strcmp(txt, "disable") == 0) {
459e28a4053SRui Paulo 		hostapd_wps_ap_pin_disable(hapd);
460e28a4053SRui Paulo 		return os_snprintf(buf, buflen, "OK\n");
461e28a4053SRui Paulo 	}
462e28a4053SRui Paulo 
463e28a4053SRui Paulo 	if (os_strcmp(txt, "random") == 0) {
464e28a4053SRui Paulo 		if (pos)
465e28a4053SRui Paulo 			timeout = atoi(pos);
466e28a4053SRui Paulo 		pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
467e28a4053SRui Paulo 		if (pin_txt == NULL)
468e28a4053SRui Paulo 			return -1;
469e28a4053SRui Paulo 		return os_snprintf(buf, buflen, "%s", pin_txt);
470e28a4053SRui Paulo 	}
471e28a4053SRui Paulo 
472e28a4053SRui Paulo 	if (os_strcmp(txt, "get") == 0) {
473e28a4053SRui Paulo 		pin_txt = hostapd_wps_ap_pin_get(hapd);
474e28a4053SRui Paulo 		if (pin_txt == NULL)
475e28a4053SRui Paulo 			return -1;
476e28a4053SRui Paulo 		return os_snprintf(buf, buflen, "%s", pin_txt);
477e28a4053SRui Paulo 	}
478e28a4053SRui Paulo 
479e28a4053SRui Paulo 	if (os_strcmp(txt, "set") == 0) {
480e28a4053SRui Paulo 		char *pin;
481e28a4053SRui Paulo 		if (pos == NULL)
482e28a4053SRui Paulo 			return -1;
483e28a4053SRui Paulo 		pin = pos;
484e28a4053SRui Paulo 		pos = os_strchr(pos, ' ');
485e28a4053SRui Paulo 		if (pos) {
486e28a4053SRui Paulo 			*pos++ = '\0';
487e28a4053SRui Paulo 			timeout = atoi(pos);
488e28a4053SRui Paulo 		}
489e28a4053SRui Paulo 		if (os_strlen(pin) > buflen)
490e28a4053SRui Paulo 			return -1;
491e28a4053SRui Paulo 		if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
492e28a4053SRui Paulo 			return -1;
493e28a4053SRui Paulo 		return os_snprintf(buf, buflen, "%s", pin);
494e28a4053SRui Paulo 	}
495e28a4053SRui Paulo 
496e28a4053SRui Paulo 	return -1;
497e28a4053SRui Paulo }
498f05cddf9SRui Paulo 
499f05cddf9SRui Paulo 
hostapd_ctrl_iface_wps_config(struct hostapd_data * hapd,char * txt)500f05cddf9SRui Paulo static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
501f05cddf9SRui Paulo {
502f05cddf9SRui Paulo 	char *pos;
503f05cddf9SRui Paulo 	char *ssid, *auth, *encr = NULL, *key = NULL;
504f05cddf9SRui Paulo 
505f05cddf9SRui Paulo 	ssid = txt;
506f05cddf9SRui Paulo 	pos = os_strchr(txt, ' ');
507f05cddf9SRui Paulo 	if (!pos)
508f05cddf9SRui Paulo 		return -1;
509f05cddf9SRui Paulo 	*pos++ = '\0';
510f05cddf9SRui Paulo 
511f05cddf9SRui Paulo 	auth = pos;
512f05cddf9SRui Paulo 	pos = os_strchr(pos, ' ');
513f05cddf9SRui Paulo 	if (pos) {
514f05cddf9SRui Paulo 		*pos++ = '\0';
515f05cddf9SRui Paulo 		encr = pos;
516f05cddf9SRui Paulo 		pos = os_strchr(pos, ' ');
517f05cddf9SRui Paulo 		if (pos) {
518f05cddf9SRui Paulo 			*pos++ = '\0';
519f05cddf9SRui Paulo 			key = pos;
520f05cddf9SRui Paulo 		}
521f05cddf9SRui Paulo 	}
522f05cddf9SRui Paulo 
523f05cddf9SRui Paulo 	return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
524f05cddf9SRui Paulo }
5255b9c547cSRui Paulo 
5265b9c547cSRui Paulo 
pbc_status_str(enum pbc_status status)5275b9c547cSRui Paulo static const char * pbc_status_str(enum pbc_status status)
5285b9c547cSRui Paulo {
5295b9c547cSRui Paulo 	switch (status) {
5305b9c547cSRui Paulo 	case WPS_PBC_STATUS_DISABLE:
5315b9c547cSRui Paulo 		return "Disabled";
5325b9c547cSRui Paulo 	case WPS_PBC_STATUS_ACTIVE:
5335b9c547cSRui Paulo 		return "Active";
5345b9c547cSRui Paulo 	case WPS_PBC_STATUS_TIMEOUT:
5355b9c547cSRui Paulo 		return "Timed-out";
5365b9c547cSRui Paulo 	case WPS_PBC_STATUS_OVERLAP:
5375b9c547cSRui Paulo 		return "Overlap";
5385b9c547cSRui Paulo 	default:
5395b9c547cSRui Paulo 		return "Unknown";
5405b9c547cSRui Paulo 	}
5415b9c547cSRui Paulo }
5425b9c547cSRui Paulo 
5435b9c547cSRui Paulo 
hostapd_ctrl_iface_wps_get_status(struct hostapd_data * hapd,char * buf,size_t buflen)5445b9c547cSRui Paulo static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
5455b9c547cSRui Paulo 					     char *buf, size_t buflen)
5465b9c547cSRui Paulo {
5475b9c547cSRui Paulo 	int ret;
5485b9c547cSRui Paulo 	char *pos, *end;
5495b9c547cSRui Paulo 
5505b9c547cSRui Paulo 	pos = buf;
5515b9c547cSRui Paulo 	end = buf + buflen;
5525b9c547cSRui Paulo 
5535b9c547cSRui Paulo 	ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
5545b9c547cSRui Paulo 			  pbc_status_str(hapd->wps_stats.pbc_status));
5555b9c547cSRui Paulo 
5565b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret))
5575b9c547cSRui Paulo 		return pos - buf;
5585b9c547cSRui Paulo 	pos += ret;
5595b9c547cSRui Paulo 
5605b9c547cSRui Paulo 	ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
5615b9c547cSRui Paulo 			  (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
5625b9c547cSRui Paulo 			   "Success":
5635b9c547cSRui Paulo 			   (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
5645b9c547cSRui Paulo 			    "Failed" : "None")));
5655b9c547cSRui Paulo 
5665b9c547cSRui Paulo 	if (os_snprintf_error(end - pos, ret))
5675b9c547cSRui Paulo 		return pos - buf;
5685b9c547cSRui Paulo 	pos += ret;
5695b9c547cSRui Paulo 
5705b9c547cSRui Paulo 	/* If status == Failure - Add possible Reasons */
5715b9c547cSRui Paulo 	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
5725b9c547cSRui Paulo 	   hapd->wps_stats.failure_reason > 0) {
5735b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos,
5745b9c547cSRui Paulo 				  "Failure Reason: %s\n",
5755b9c547cSRui Paulo 				  wps_ei_str(hapd->wps_stats.failure_reason));
5765b9c547cSRui Paulo 
5775b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
5785b9c547cSRui Paulo 			return pos - buf;
5795b9c547cSRui Paulo 		pos += ret;
5805b9c547cSRui Paulo 	}
5815b9c547cSRui Paulo 
5825b9c547cSRui Paulo 	if (hapd->wps_stats.status) {
5835b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
5845b9c547cSRui Paulo 				  MAC2STR(hapd->wps_stats.peer_addr));
5855b9c547cSRui Paulo 
5865b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
5875b9c547cSRui Paulo 			return pos - buf;
5885b9c547cSRui Paulo 		pos += ret;
5895b9c547cSRui Paulo 	}
5905b9c547cSRui Paulo 
5915b9c547cSRui Paulo 	return pos - buf;
5925b9c547cSRui Paulo }
5935b9c547cSRui Paulo 
59439beb93cSSam Leffler #endif /* CONFIG_WPS */
59539beb93cSSam Leffler 
5965b9c547cSRui Paulo #ifdef CONFIG_HS20
5975b9c547cSRui Paulo 
hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data * hapd,const char * cmd)5985b9c547cSRui Paulo static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
5995b9c547cSRui Paulo 					     const char *cmd)
6005b9c547cSRui Paulo {
6015b9c547cSRui Paulo 	u8 addr[ETH_ALEN];
6025b9c547cSRui Paulo 	const char *url;
6035b9c547cSRui Paulo 
6045b9c547cSRui Paulo 	if (hwaddr_aton(cmd, addr))
6055b9c547cSRui Paulo 		return -1;
6065b9c547cSRui Paulo 	url = cmd + 17;
6075b9c547cSRui Paulo 	if (*url == '\0') {
6085b9c547cSRui Paulo 		url = NULL;
6095b9c547cSRui Paulo 	} else {
6105b9c547cSRui Paulo 		if (*url != ' ')
6115b9c547cSRui Paulo 			return -1;
6125b9c547cSRui Paulo 		url++;
6135b9c547cSRui Paulo 		if (*url == '\0')
6145b9c547cSRui Paulo 			url = NULL;
6155b9c547cSRui Paulo 	}
6165b9c547cSRui Paulo 
6175b9c547cSRui Paulo 	return hs20_send_wnm_notification(hapd, addr, 1, url);
6185b9c547cSRui Paulo }
6195b9c547cSRui Paulo 
6205b9c547cSRui Paulo 
hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data * hapd,const char * cmd)6215b9c547cSRui Paulo static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
6225b9c547cSRui Paulo 					      const char *cmd)
6235b9c547cSRui Paulo {
6245b9c547cSRui Paulo 	u8 addr[ETH_ALEN];
6255b9c547cSRui Paulo 	int code, reauth_delay, ret;
6265b9c547cSRui Paulo 	const char *pos;
6275b9c547cSRui Paulo 	size_t url_len;
6285b9c547cSRui Paulo 	struct wpabuf *req;
6295b9c547cSRui Paulo 
6305b9c547cSRui Paulo 	/* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
6315b9c547cSRui Paulo 	if (hwaddr_aton(cmd, addr))
6325b9c547cSRui Paulo 		return -1;
6335b9c547cSRui Paulo 
6345b9c547cSRui Paulo 	pos = os_strchr(cmd, ' ');
6355b9c547cSRui Paulo 	if (pos == NULL)
6365b9c547cSRui Paulo 		return -1;
6375b9c547cSRui Paulo 	pos++;
6385b9c547cSRui Paulo 	code = atoi(pos);
6395b9c547cSRui Paulo 
6405b9c547cSRui Paulo 	pos = os_strchr(pos, ' ');
6415b9c547cSRui Paulo 	if (pos == NULL)
6425b9c547cSRui Paulo 		return -1;
6435b9c547cSRui Paulo 	pos++;
6445b9c547cSRui Paulo 	reauth_delay = atoi(pos);
6455b9c547cSRui Paulo 
6465b9c547cSRui Paulo 	url_len = 0;
6475b9c547cSRui Paulo 	pos = os_strchr(pos, ' ');
6485b9c547cSRui Paulo 	if (pos) {
6495b9c547cSRui Paulo 		pos++;
6505b9c547cSRui Paulo 		url_len = os_strlen(pos);
6515b9c547cSRui Paulo 	}
6525b9c547cSRui Paulo 
6535b9c547cSRui Paulo 	req = wpabuf_alloc(4 + url_len);
6545b9c547cSRui Paulo 	if (req == NULL)
6555b9c547cSRui Paulo 		return -1;
6565b9c547cSRui Paulo 	wpabuf_put_u8(req, code);
6575b9c547cSRui Paulo 	wpabuf_put_le16(req, reauth_delay);
6585b9c547cSRui Paulo 	wpabuf_put_u8(req, url_len);
6595b9c547cSRui Paulo 	if (pos)
6605b9c547cSRui Paulo 		wpabuf_put_data(req, pos, url_len);
6615b9c547cSRui Paulo 
6625b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
6635b9c547cSRui Paulo 		   " to indicate imminent deauthentication (code=%d "
6645b9c547cSRui Paulo 		   "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
6655b9c547cSRui Paulo 	ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
6665b9c547cSRui Paulo 	wpabuf_free(req);
6675b9c547cSRui Paulo 	return ret;
6685b9c547cSRui Paulo }
6695b9c547cSRui Paulo 
6705b9c547cSRui Paulo #endif /* CONFIG_HS20 */
6715b9c547cSRui Paulo 
6725b9c547cSRui Paulo 
6735b9c547cSRui Paulo #ifdef CONFIG_INTERWORKING
6745b9c547cSRui Paulo 
hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data * hapd,const char * cmd)6755b9c547cSRui Paulo static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
6765b9c547cSRui Paulo 					      const char *cmd)
6775b9c547cSRui Paulo {
6785b9c547cSRui Paulo 	u8 qos_map_set[16 + 2 * 21], count = 0;
6795b9c547cSRui Paulo 	const char *pos = cmd;
6805b9c547cSRui Paulo 	int val, ret;
6815b9c547cSRui Paulo 
6825b9c547cSRui Paulo 	for (;;) {
6835b9c547cSRui Paulo 		if (count == sizeof(qos_map_set)) {
6845b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
6855b9c547cSRui Paulo 			return -1;
6865b9c547cSRui Paulo 		}
6875b9c547cSRui Paulo 
6885b9c547cSRui Paulo 		val = atoi(pos);
6895b9c547cSRui Paulo 		if (val < 0 || val > 255) {
6905b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "Invalid QoS Map Set");
6915b9c547cSRui Paulo 			return -1;
6925b9c547cSRui Paulo 		}
6935b9c547cSRui Paulo 
6945b9c547cSRui Paulo 		qos_map_set[count++] = val;
6955b9c547cSRui Paulo 		pos = os_strchr(pos, ',');
6965b9c547cSRui Paulo 		if (!pos)
6975b9c547cSRui Paulo 			break;
6985b9c547cSRui Paulo 		pos++;
6995b9c547cSRui Paulo 	}
7005b9c547cSRui Paulo 
7015b9c547cSRui Paulo 	if (count < 16 || count & 1) {
7025b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Invalid QoS Map Set");
7035b9c547cSRui Paulo 		return -1;
7045b9c547cSRui Paulo 	}
7055b9c547cSRui Paulo 
7065b9c547cSRui Paulo 	ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
7075b9c547cSRui Paulo 	if (ret) {
7085b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
7095b9c547cSRui Paulo 		return -1;
7105b9c547cSRui Paulo 	}
7115b9c547cSRui Paulo 
7125b9c547cSRui Paulo 	os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
7135b9c547cSRui Paulo 	hapd->conf->qos_map_set_len = count;
7145b9c547cSRui Paulo 
7155b9c547cSRui Paulo 	return 0;
7165b9c547cSRui Paulo }
7175b9c547cSRui Paulo 
7185b9c547cSRui Paulo 
hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data * hapd,const char * cmd)7195b9c547cSRui Paulo static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
7205b9c547cSRui Paulo 						const char *cmd)
7215b9c547cSRui Paulo {
7225b9c547cSRui Paulo 	u8 addr[ETH_ALEN];
7235b9c547cSRui Paulo 	struct sta_info *sta;
7245b9c547cSRui Paulo 	struct wpabuf *buf;
7255b9c547cSRui Paulo 	u8 *qos_map_set = hapd->conf->qos_map_set;
7265b9c547cSRui Paulo 	u8 qos_map_set_len = hapd->conf->qos_map_set_len;
7275b9c547cSRui Paulo 	int ret;
7285b9c547cSRui Paulo 
7295b9c547cSRui Paulo 	if (!qos_map_set_len) {
7305b9c547cSRui Paulo 		wpa_printf(MSG_INFO, "QoS Map Set is not set");
7315b9c547cSRui Paulo 		return -1;
7325b9c547cSRui Paulo 	}
7335b9c547cSRui Paulo 
7345b9c547cSRui Paulo 	if (hwaddr_aton(cmd, addr))
7355b9c547cSRui Paulo 		return -1;
7365b9c547cSRui Paulo 
7375b9c547cSRui Paulo 	sta = ap_get_sta(hapd, addr);
7385b9c547cSRui Paulo 	if (sta == NULL) {
7395b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
7405b9c547cSRui Paulo 			   "for QoS Map Configuration message",
7415b9c547cSRui Paulo 			   MAC2STR(addr));
7425b9c547cSRui Paulo 		return -1;
7435b9c547cSRui Paulo 	}
7445b9c547cSRui Paulo 
7455b9c547cSRui Paulo 	if (!sta->qos_map_enabled) {
7465b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
7475b9c547cSRui Paulo 			   "support for QoS Map", MAC2STR(addr));
7485b9c547cSRui Paulo 		return -1;
7495b9c547cSRui Paulo 	}
7505b9c547cSRui Paulo 
7515b9c547cSRui Paulo 	buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
7525b9c547cSRui Paulo 	if (buf == NULL)
7535b9c547cSRui Paulo 		return -1;
7545b9c547cSRui Paulo 
7555b9c547cSRui Paulo 	wpabuf_put_u8(buf, WLAN_ACTION_QOS);
7565b9c547cSRui Paulo 	wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
7575b9c547cSRui Paulo 
7585b9c547cSRui Paulo 	/* QoS Map Set Element */
7595b9c547cSRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
7605b9c547cSRui Paulo 	wpabuf_put_u8(buf, qos_map_set_len);
7615b9c547cSRui Paulo 	wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
7625b9c547cSRui Paulo 
7635b9c547cSRui Paulo 	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
7645b9c547cSRui Paulo 				      wpabuf_head(buf), wpabuf_len(buf));
7655b9c547cSRui Paulo 	wpabuf_free(buf);
7665b9c547cSRui Paulo 
7675b9c547cSRui Paulo 	return ret;
7685b9c547cSRui Paulo }
7695b9c547cSRui Paulo 
7705b9c547cSRui Paulo #endif /* CONFIG_INTERWORKING */
7715b9c547cSRui Paulo 
77239beb93cSSam Leffler 
77385732ac8SCy Schubert #ifdef CONFIG_WNM_AP
774f05cddf9SRui Paulo 
hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data * hapd,const char * cmd)775f05cddf9SRui Paulo static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
776f05cddf9SRui Paulo 						const char *cmd)
777f05cddf9SRui Paulo {
778f05cddf9SRui Paulo 	u8 addr[ETH_ALEN];
779f05cddf9SRui Paulo 	int disassoc_timer;
7805b9c547cSRui Paulo 	struct sta_info *sta;
781f05cddf9SRui Paulo 
782f05cddf9SRui Paulo 	if (hwaddr_aton(cmd, addr))
783f05cddf9SRui Paulo 		return -1;
784f05cddf9SRui Paulo 	if (cmd[17] != ' ')
785f05cddf9SRui Paulo 		return -1;
786f05cddf9SRui Paulo 	disassoc_timer = atoi(cmd + 17);
787f05cddf9SRui Paulo 
7885b9c547cSRui Paulo 	sta = ap_get_sta(hapd, addr);
7895b9c547cSRui Paulo 	if (sta == NULL) {
7905b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Station " MACSTR
7915b9c547cSRui Paulo 			   " not found for disassociation imminent message",
7925b9c547cSRui Paulo 			   MAC2STR(addr));
793f05cddf9SRui Paulo 		return -1;
794f05cddf9SRui Paulo 	}
795f05cddf9SRui Paulo 
7965b9c547cSRui Paulo 	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
797f05cddf9SRui Paulo }
798f05cddf9SRui Paulo 
799f05cddf9SRui Paulo 
hostapd_ctrl_iface_ess_disassoc(struct hostapd_data * hapd,const char * cmd)800f05cddf9SRui Paulo static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
801f05cddf9SRui Paulo 					   const char *cmd)
802f05cddf9SRui Paulo {
803f05cddf9SRui Paulo 	u8 addr[ETH_ALEN];
8045b9c547cSRui Paulo 	const char *url, *timerstr;
8055b9c547cSRui Paulo 	int disassoc_timer;
8065b9c547cSRui Paulo 	struct sta_info *sta;
807f05cddf9SRui Paulo 
808f05cddf9SRui Paulo 	if (hwaddr_aton(cmd, addr))
809f05cddf9SRui Paulo 		return -1;
810f05cddf9SRui Paulo 
8115b9c547cSRui Paulo 	sta = ap_get_sta(hapd, addr);
8125b9c547cSRui Paulo 	if (sta == NULL) {
8135b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Station " MACSTR
8145b9c547cSRui Paulo 			   " not found for ESS disassociation imminent message",
8155b9c547cSRui Paulo 			   MAC2STR(addr));
816f05cddf9SRui Paulo 		return -1;
817f05cddf9SRui Paulo 	}
818f05cddf9SRui Paulo 
8195b9c547cSRui Paulo 	timerstr = cmd + 17;
8205b9c547cSRui Paulo 	if (*timerstr != ' ')
8215b9c547cSRui Paulo 		return -1;
8225b9c547cSRui Paulo 	timerstr++;
8235b9c547cSRui Paulo 	disassoc_timer = atoi(timerstr);
8245b9c547cSRui Paulo 	if (disassoc_timer < 0 || disassoc_timer > 65535)
8255b9c547cSRui Paulo 		return -1;
8265b9c547cSRui Paulo 
8275b9c547cSRui Paulo 	url = os_strchr(timerstr, ' ');
8285b9c547cSRui Paulo 	if (url == NULL)
8295b9c547cSRui Paulo 		return -1;
8305b9c547cSRui Paulo 	url++;
8315b9c547cSRui Paulo 
8325b9c547cSRui Paulo 	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
8335b9c547cSRui Paulo }
8345b9c547cSRui Paulo 
8355b9c547cSRui Paulo 
hostapd_ctrl_iface_bss_tm_req(struct hostapd_data * hapd,const char * cmd)8365b9c547cSRui Paulo static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
8375b9c547cSRui Paulo 					 const char *cmd)
8385b9c547cSRui Paulo {
8395b9c547cSRui Paulo 	u8 addr[ETH_ALEN];
8405b9c547cSRui Paulo 	const char *pos, *end;
8415b9c547cSRui Paulo 	int disassoc_timer = 0;
8425b9c547cSRui Paulo 	struct sta_info *sta;
8434b72b91aSCy Schubert 	u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
8445b9c547cSRui Paulo 	u8 bss_term_dur[12];
8455b9c547cSRui Paulo 	char *url = NULL;
8465b9c547cSRui Paulo 	int ret;
8475b9c547cSRui Paulo 	u8 nei_rep[1000];
84885732ac8SCy Schubert 	int nei_len;
849780fb4a2SCy Schubert 	u8 mbo[10];
850780fb4a2SCy Schubert 	size_t mbo_len = 0;
8515b9c547cSRui Paulo 
8525b9c547cSRui Paulo 	if (hwaddr_aton(cmd, addr)) {
8535b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
8545b9c547cSRui Paulo 		return -1;
8555b9c547cSRui Paulo 	}
8565b9c547cSRui Paulo 
8575b9c547cSRui Paulo 	sta = ap_get_sta(hapd, addr);
8585b9c547cSRui Paulo 	if (sta == NULL) {
8595b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Station " MACSTR
8605b9c547cSRui Paulo 			   " not found for BSS TM Request message",
8615b9c547cSRui Paulo 			   MAC2STR(addr));
8625b9c547cSRui Paulo 		return -1;
8635b9c547cSRui Paulo 	}
8645b9c547cSRui Paulo 
8655b9c547cSRui Paulo 	pos = os_strstr(cmd, " disassoc_timer=");
8665b9c547cSRui Paulo 	if (pos) {
8675b9c547cSRui Paulo 		pos += 16;
8685b9c547cSRui Paulo 		disassoc_timer = atoi(pos);
8695b9c547cSRui Paulo 		if (disassoc_timer < 0 || disassoc_timer > 65535) {
8705b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
8715b9c547cSRui Paulo 			return -1;
8725b9c547cSRui Paulo 		}
8735b9c547cSRui Paulo 	}
8745b9c547cSRui Paulo 
8755b9c547cSRui Paulo 	pos = os_strstr(cmd, " valid_int=");
8765b9c547cSRui Paulo 	if (pos) {
8775b9c547cSRui Paulo 		pos += 11;
8785b9c547cSRui Paulo 		valid_int = atoi(pos);
8795b9c547cSRui Paulo 	}
8805b9c547cSRui Paulo 
8814b72b91aSCy Schubert 	pos = os_strstr(cmd, " dialog_token=");
8824b72b91aSCy Schubert 	if (pos) {
8834b72b91aSCy Schubert 		pos += 14;
8844b72b91aSCy Schubert 		dialog_token = atoi(pos);
8854b72b91aSCy Schubert 	}
8864b72b91aSCy Schubert 
8875b9c547cSRui Paulo 	pos = os_strstr(cmd, " bss_term=");
8885b9c547cSRui Paulo 	if (pos) {
8895b9c547cSRui Paulo 		pos += 10;
8905b9c547cSRui Paulo 		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
8915b9c547cSRui Paulo 		/* TODO: TSF configurable/learnable */
8925b9c547cSRui Paulo 		bss_term_dur[0] = 4; /* Subelement ID */
8935b9c547cSRui Paulo 		bss_term_dur[1] = 10; /* Length */
8944bc52338SCy Schubert 		os_memset(&bss_term_dur[2], 0, 8);
8955b9c547cSRui Paulo 		end = os_strchr(pos, ',');
8965b9c547cSRui Paulo 		if (end == NULL) {
8975b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
8985b9c547cSRui Paulo 			return -1;
8995b9c547cSRui Paulo 		}
9005b9c547cSRui Paulo 		end++;
9015b9c547cSRui Paulo 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
9025b9c547cSRui Paulo 	}
9035b9c547cSRui Paulo 
90485732ac8SCy Schubert 	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
90585732ac8SCy Schubert 						  sizeof(nei_rep));
90685732ac8SCy Schubert 	if (nei_len < 0)
9075b9c547cSRui Paulo 		return -1;
9085b9c547cSRui Paulo 
9095b9c547cSRui Paulo 	pos = os_strstr(cmd, " url=");
9105b9c547cSRui Paulo 	if (pos) {
9115b9c547cSRui Paulo 		size_t len;
9125b9c547cSRui Paulo 		pos += 5;
9135b9c547cSRui Paulo 		end = os_strchr(pos, ' ');
9145b9c547cSRui Paulo 		if (end)
9155b9c547cSRui Paulo 			len = end - pos;
9165b9c547cSRui Paulo 		else
9175b9c547cSRui Paulo 			len = os_strlen(pos);
9185b9c547cSRui Paulo 		url = os_malloc(len + 1);
9195b9c547cSRui Paulo 		if (url == NULL)
9205b9c547cSRui Paulo 			return -1;
9215b9c547cSRui Paulo 		os_memcpy(url, pos, len);
9225b9c547cSRui Paulo 		url[len] = '\0';
9235b9c547cSRui Paulo 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
9245b9c547cSRui Paulo 	}
9255b9c547cSRui Paulo 
9265b9c547cSRui Paulo 	if (os_strstr(cmd, " pref=1"))
9275b9c547cSRui Paulo 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
9285b9c547cSRui Paulo 	if (os_strstr(cmd, " abridged=1"))
9295b9c547cSRui Paulo 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
9305b9c547cSRui Paulo 	if (os_strstr(cmd, " disassoc_imminent=1"))
9315b9c547cSRui Paulo 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
9325b9c547cSRui Paulo 
933780fb4a2SCy Schubert #ifdef CONFIG_MBO
934780fb4a2SCy Schubert 	pos = os_strstr(cmd, "mbo=");
935780fb4a2SCy Schubert 	if (pos) {
936780fb4a2SCy Schubert 		unsigned int mbo_reason, cell_pref, reassoc_delay;
937780fb4a2SCy Schubert 		u8 *mbo_pos = mbo;
938780fb4a2SCy Schubert 
939780fb4a2SCy Schubert 		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
940780fb4a2SCy Schubert 			     &reassoc_delay, &cell_pref);
941780fb4a2SCy Schubert 		if (ret != 3) {
942780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
943780fb4a2SCy Schubert 				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
94485732ac8SCy Schubert 			ret = -1;
94585732ac8SCy Schubert 			goto fail;
946780fb4a2SCy Schubert 		}
947780fb4a2SCy Schubert 
948780fb4a2SCy Schubert 		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
949780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
950780fb4a2SCy Schubert 				   "Invalid MBO transition reason code %u",
951780fb4a2SCy Schubert 				   mbo_reason);
95285732ac8SCy Schubert 			ret = -1;
95385732ac8SCy Schubert 			goto fail;
954780fb4a2SCy Schubert 		}
955780fb4a2SCy Schubert 
956780fb4a2SCy Schubert 		/* Valid values for Cellular preference are: 0, 1, 255 */
957780fb4a2SCy Schubert 		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
958780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
959780fb4a2SCy Schubert 				   "Invalid MBO cellular capability %u",
960780fb4a2SCy Schubert 				   cell_pref);
96185732ac8SCy Schubert 			ret = -1;
96285732ac8SCy Schubert 			goto fail;
963780fb4a2SCy Schubert 		}
964780fb4a2SCy Schubert 
965780fb4a2SCy Schubert 		if (reassoc_delay > 65535 ||
966780fb4a2SCy Schubert 		    (reassoc_delay &&
967780fb4a2SCy Schubert 		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
968780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
969780fb4a2SCy Schubert 				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
97085732ac8SCy Schubert 			ret = -1;
97185732ac8SCy Schubert 			goto fail;
972780fb4a2SCy Schubert 		}
973780fb4a2SCy Schubert 
974780fb4a2SCy Schubert 		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
975780fb4a2SCy Schubert 		*mbo_pos++ = 1;
976780fb4a2SCy Schubert 		*mbo_pos++ = mbo_reason;
977780fb4a2SCy Schubert 		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
978780fb4a2SCy Schubert 		*mbo_pos++ = 1;
979780fb4a2SCy Schubert 		*mbo_pos++ = cell_pref;
980780fb4a2SCy Schubert 
981780fb4a2SCy Schubert 		if (reassoc_delay) {
982780fb4a2SCy Schubert 			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
983780fb4a2SCy Schubert 			*mbo_pos++ = 2;
984780fb4a2SCy Schubert 			WPA_PUT_LE16(mbo_pos, reassoc_delay);
985780fb4a2SCy Schubert 			mbo_pos += 2;
986780fb4a2SCy Schubert 		}
987780fb4a2SCy Schubert 
988780fb4a2SCy Schubert 		mbo_len = mbo_pos - mbo;
989780fb4a2SCy Schubert 	}
990780fb4a2SCy Schubert #endif /* CONFIG_MBO */
991780fb4a2SCy Schubert 
9925b9c547cSRui Paulo 	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
9934b72b91aSCy Schubert 				  valid_int, bss_term_dur, dialog_token, url,
99485732ac8SCy Schubert 				  nei_len ? nei_rep : NULL, nei_len,
99585732ac8SCy Schubert 				  mbo_len ? mbo : NULL, mbo_len);
99685732ac8SCy Schubert #ifdef CONFIG_MBO
99785732ac8SCy Schubert fail:
99885732ac8SCy Schubert #endif /* CONFIG_MBO */
9995b9c547cSRui Paulo 	os_free(url);
10005b9c547cSRui Paulo 	return ret;
1001f05cddf9SRui Paulo }
1002f05cddf9SRui Paulo 
100385732ac8SCy Schubert 
hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data * hapd,const char * cmd)100485732ac8SCy Schubert static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
100585732ac8SCy Schubert 					     const char *cmd)
100685732ac8SCy Schubert {
100785732ac8SCy Schubert 	u8 addr[ETH_ALEN];
100885732ac8SCy Schubert 	struct sta_info *sta;
100985732ac8SCy Schubert 	const char *pos;
101085732ac8SCy Schubert 	unsigned int auto_report, timeout;
101185732ac8SCy Schubert 
101285732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr)) {
101385732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
101485732ac8SCy Schubert 		return -1;
101585732ac8SCy Schubert 	}
101685732ac8SCy Schubert 
101785732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
101885732ac8SCy Schubert 	if (!sta) {
101985732ac8SCy Schubert 		wpa_printf(MSG_DEBUG, "Station " MACSTR
102085732ac8SCy Schubert 			   " not found for Collocated Interference Request",
102185732ac8SCy Schubert 			   MAC2STR(addr));
102285732ac8SCy Schubert 		return -1;
102385732ac8SCy Schubert 	}
102485732ac8SCy Schubert 
102585732ac8SCy Schubert 	pos = cmd + 17;
102685732ac8SCy Schubert 	if (*pos != ' ')
102785732ac8SCy Schubert 		return -1;
102885732ac8SCy Schubert 	pos++;
102985732ac8SCy Schubert 	auto_report = atoi(pos);
103085732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
103185732ac8SCy Schubert 	if (!pos)
103285732ac8SCy Schubert 		return -1;
103385732ac8SCy Schubert 	pos++;
103485732ac8SCy Schubert 	timeout = atoi(pos);
103585732ac8SCy Schubert 
103685732ac8SCy Schubert 	return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
103785732ac8SCy Schubert }
103885732ac8SCy Schubert 
103985732ac8SCy Schubert #endif /* CONFIG_WNM_AP */
1040f05cddf9SRui Paulo 
1041f05cddf9SRui Paulo 
hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data * hapd,char * buf,size_t buflen)1042325151a3SRui Paulo static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
1043f05cddf9SRui Paulo 					   char *buf, size_t buflen)
1044f05cddf9SRui Paulo {
1045325151a3SRui Paulo 	int ret = 0;
1046f05cddf9SRui Paulo 	char *pos, *end;
1047f05cddf9SRui Paulo 
1048f05cddf9SRui Paulo 	pos = buf;
1049f05cddf9SRui Paulo 	end = buf + buflen;
1050f05cddf9SRui Paulo 
1051325151a3SRui Paulo 	WPA_ASSERT(hapd->conf->wpa_key_mgmt);
1052f05cddf9SRui Paulo 
1053f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
1054f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "WPA-PSK ");
10555b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1056f05cddf9SRui Paulo 			return pos - buf;
1057f05cddf9SRui Paulo 		pos += ret;
1058f05cddf9SRui Paulo 	}
1059f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
1060f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "WPA-EAP ");
10615b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1062f05cddf9SRui Paulo 			return pos - buf;
1063f05cddf9SRui Paulo 		pos += ret;
1064f05cddf9SRui Paulo 	}
106585732ac8SCy Schubert #ifdef CONFIG_IEEE80211R_AP
1066f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
1067f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "FT-PSK ");
10685b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1069f05cddf9SRui Paulo 			return pos - buf;
1070f05cddf9SRui Paulo 		pos += ret;
1071f05cddf9SRui Paulo 	}
1072f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
1073f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "FT-EAP ");
10745b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1075f05cddf9SRui Paulo 			return pos - buf;
1076f05cddf9SRui Paulo 		pos += ret;
1077f05cddf9SRui Paulo 	}
107885732ac8SCy Schubert #ifdef CONFIG_SHA384
107985732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
108085732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 ");
108185732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
108285732ac8SCy Schubert 			return pos - buf;
108385732ac8SCy Schubert 		pos += ret;
108485732ac8SCy Schubert 	}
108585732ac8SCy Schubert #endif /* CONFIG_SHA384 */
10865b9c547cSRui Paulo #ifdef CONFIG_SAE
10875b9c547cSRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
10885b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "FT-SAE ");
10895b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
10905b9c547cSRui Paulo 			return pos - buf;
10915b9c547cSRui Paulo 		pos += ret;
10925b9c547cSRui Paulo 	}
10935b9c547cSRui Paulo #endif /* CONFIG_SAE */
109485732ac8SCy Schubert #ifdef CONFIG_FILS
109585732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
109685732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
109785732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
109885732ac8SCy Schubert 			return pos - buf;
109985732ac8SCy Schubert 		pos += ret;
110085732ac8SCy Schubert 	}
110185732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
110285732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
110385732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
110485732ac8SCy Schubert 			return pos - buf;
110585732ac8SCy Schubert 		pos += ret;
110685732ac8SCy Schubert 	}
110785732ac8SCy Schubert #endif /* CONFIG_FILS */
110885732ac8SCy Schubert #endif /* CONFIG_IEEE80211R_AP */
1109f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
1110f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
11115b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1112f05cddf9SRui Paulo 			return pos - buf;
1113f05cddf9SRui Paulo 		pos += ret;
1114f05cddf9SRui Paulo 	}
1115f05cddf9SRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
1116f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
11175b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1118f05cddf9SRui Paulo 			return pos - buf;
1119f05cddf9SRui Paulo 		pos += ret;
1120f05cddf9SRui Paulo 	}
11215b9c547cSRui Paulo #ifdef CONFIG_SAE
11225b9c547cSRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
11235b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "SAE ");
11245b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
11255b9c547cSRui Paulo 			return pos - buf;
11265b9c547cSRui Paulo 		pos += ret;
11275b9c547cSRui Paulo 	}
11285b9c547cSRui Paulo #endif /* CONFIG_SAE */
11295b9c547cSRui Paulo 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
11305b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
11315b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
11325b9c547cSRui Paulo 			return pos - buf;
11335b9c547cSRui Paulo 		pos += ret;
11345b9c547cSRui Paulo 	}
11355b9c547cSRui Paulo 	if (hapd->conf->wpa_key_mgmt &
11365b9c547cSRui Paulo 	    WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
11375b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos,
11385b9c547cSRui Paulo 				  "WPA-EAP-SUITE-B-192 ");
11395b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1140f05cddf9SRui Paulo 			return pos - buf;
1141f05cddf9SRui Paulo 		pos += ret;
1142f05cddf9SRui Paulo 	}
114385732ac8SCy Schubert #ifdef CONFIG_FILS
114485732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
114585732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
114685732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
114785732ac8SCy Schubert 			return pos - buf;
114885732ac8SCy Schubert 		pos += ret;
114985732ac8SCy Schubert 	}
115085732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
115185732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
115285732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
115385732ac8SCy Schubert 			return pos - buf;
115485732ac8SCy Schubert 		pos += ret;
115585732ac8SCy Schubert 	}
115685732ac8SCy Schubert #endif /* CONFIG_FILS */
115785732ac8SCy Schubert 
115885732ac8SCy Schubert #ifdef CONFIG_OWE
115985732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
116085732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "OWE ");
116185732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
116285732ac8SCy Schubert 			return pos - buf;
116385732ac8SCy Schubert 		pos += ret;
116485732ac8SCy Schubert 	}
116585732ac8SCy Schubert #endif /* CONFIG_OWE */
116685732ac8SCy Schubert 
116785732ac8SCy Schubert #ifdef CONFIG_DPP
116885732ac8SCy Schubert 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
116985732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, "DPP ");
117085732ac8SCy Schubert 		if (os_snprintf_error(end - pos, ret))
117185732ac8SCy Schubert 			return pos - buf;
117285732ac8SCy Schubert 		pos += ret;
117385732ac8SCy Schubert 	}
117485732ac8SCy Schubert #endif /* CONFIG_DPP */
1175f05cddf9SRui Paulo 
1176325151a3SRui Paulo 	if (pos > buf && *(pos - 1) == ' ') {
1177325151a3SRui Paulo 		*(pos - 1) = '\0';
1178325151a3SRui Paulo 		pos--;
1179325151a3SRui Paulo 	}
1180325151a3SRui Paulo 
1181325151a3SRui Paulo 	return pos - buf;
1182325151a3SRui Paulo }
1183325151a3SRui Paulo 
1184325151a3SRui Paulo 
hostapd_ctrl_iface_get_config(struct hostapd_data * hapd,char * buf,size_t buflen)1185325151a3SRui Paulo static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
1186325151a3SRui Paulo 					 char *buf, size_t buflen)
1187325151a3SRui Paulo {
1188325151a3SRui Paulo 	int ret;
1189325151a3SRui Paulo 	char *pos, *end;
1190325151a3SRui Paulo 
1191325151a3SRui Paulo 	pos = buf;
1192325151a3SRui Paulo 	end = buf + buflen;
1193325151a3SRui Paulo 
1194325151a3SRui Paulo 	ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
1195325151a3SRui Paulo 			  "ssid=%s\n",
1196325151a3SRui Paulo 			  MAC2STR(hapd->own_addr),
1197325151a3SRui Paulo 			  wpa_ssid_txt(hapd->conf->ssid.ssid,
1198325151a3SRui Paulo 				       hapd->conf->ssid.ssid_len));
1199325151a3SRui Paulo 	if (os_snprintf_error(end - pos, ret))
1200325151a3SRui Paulo 		return pos - buf;
1201325151a3SRui Paulo 	pos += ret;
1202325151a3SRui Paulo 
1203325151a3SRui Paulo #ifdef CONFIG_WPS
1204325151a3SRui Paulo 	ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
1205325151a3SRui Paulo 			  hapd->conf->wps_state == 0 ? "disabled" :
1206325151a3SRui Paulo 			  (hapd->conf->wps_state == 1 ? "not configured" :
1207325151a3SRui Paulo 			   "configured"));
1208325151a3SRui Paulo 	if (os_snprintf_error(end - pos, ret))
1209325151a3SRui Paulo 		return pos - buf;
1210325151a3SRui Paulo 	pos += ret;
1211325151a3SRui Paulo 
1212325151a3SRui Paulo 	if (hapd->conf->wps_state && hapd->conf->wpa &&
1213325151a3SRui Paulo 	    hapd->conf->ssid.wpa_passphrase) {
1214325151a3SRui Paulo 		ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
1215325151a3SRui Paulo 				  hapd->conf->ssid.wpa_passphrase);
1216325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
1217325151a3SRui Paulo 			return pos - buf;
1218325151a3SRui Paulo 		pos += ret;
1219325151a3SRui Paulo 	}
1220325151a3SRui Paulo 
1221325151a3SRui Paulo 	if (hapd->conf->wps_state && hapd->conf->wpa &&
1222325151a3SRui Paulo 	    hapd->conf->ssid.wpa_psk &&
1223325151a3SRui Paulo 	    hapd->conf->ssid.wpa_psk->group) {
1224325151a3SRui Paulo 		char hex[PMK_LEN * 2 + 1];
1225325151a3SRui Paulo 		wpa_snprintf_hex(hex, sizeof(hex),
1226325151a3SRui Paulo 				 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
1227325151a3SRui Paulo 		ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
1228325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
1229325151a3SRui Paulo 			return pos - buf;
1230325151a3SRui Paulo 		pos += ret;
1231325151a3SRui Paulo 	}
1232c1d255d3SCy Schubert 
1233c1d255d3SCy Schubert 	if (hapd->conf->multi_ap) {
1234c1d255d3SCy Schubert 		struct hostapd_ssid *ssid = &hapd->conf->multi_ap_backhaul_ssid;
1235c1d255d3SCy Schubert 
1236c1d255d3SCy Schubert 		ret = os_snprintf(pos, end - pos, "multi_ap=%d\n",
1237c1d255d3SCy Schubert 				  hapd->conf->multi_ap);
1238c1d255d3SCy Schubert 		if (os_snprintf_error(end - pos, ret))
1239c1d255d3SCy Schubert 			return pos - buf;
1240c1d255d3SCy Schubert 		pos += ret;
1241c1d255d3SCy Schubert 
1242c1d255d3SCy Schubert 		if (ssid->ssid_len) {
1243c1d255d3SCy Schubert 			ret = os_snprintf(pos, end - pos,
1244c1d255d3SCy Schubert 					  "multi_ap_backhaul_ssid=%s\n",
1245c1d255d3SCy Schubert 					  wpa_ssid_txt(ssid->ssid,
1246c1d255d3SCy Schubert 						       ssid->ssid_len));
1247c1d255d3SCy Schubert 			if (os_snprintf_error(end - pos, ret))
1248c1d255d3SCy Schubert 				return pos - buf;
1249c1d255d3SCy Schubert 			pos += ret;
1250c1d255d3SCy Schubert 		}
1251c1d255d3SCy Schubert 
1252c1d255d3SCy Schubert 		if (hapd->conf->wps_state && hapd->conf->wpa &&
1253c1d255d3SCy Schubert 			ssid->wpa_passphrase) {
1254c1d255d3SCy Schubert 			ret = os_snprintf(pos, end - pos,
1255c1d255d3SCy Schubert 					  "multi_ap_backhaul_wpa_passphrase=%s\n",
1256c1d255d3SCy Schubert 					  ssid->wpa_passphrase);
1257c1d255d3SCy Schubert 			if (os_snprintf_error(end - pos, ret))
1258c1d255d3SCy Schubert 				return pos - buf;
1259c1d255d3SCy Schubert 			pos += ret;
1260c1d255d3SCy Schubert 		}
1261c1d255d3SCy Schubert 
1262c1d255d3SCy Schubert 		if (hapd->conf->wps_state && hapd->conf->wpa &&
1263c1d255d3SCy Schubert 		    ssid->wpa_psk &&
1264c1d255d3SCy Schubert 		    ssid->wpa_psk->group) {
1265c1d255d3SCy Schubert 			char hex[PMK_LEN * 2 + 1];
1266c1d255d3SCy Schubert 
1267c1d255d3SCy Schubert 			wpa_snprintf_hex(hex, sizeof(hex), ssid->wpa_psk->psk,
1268c1d255d3SCy Schubert 					 PMK_LEN);
1269c1d255d3SCy Schubert 			ret = os_snprintf(pos, end - pos,
1270c1d255d3SCy Schubert 					  "multi_ap_backhaul_wpa_psk=%s\n",
1271c1d255d3SCy Schubert 					  hex);
1272c1d255d3SCy Schubert 			forced_memzero(hex, sizeof(hex));
1273c1d255d3SCy Schubert 			if (os_snprintf_error(end - pos, ret))
1274c1d255d3SCy Schubert 				return pos - buf;
1275c1d255d3SCy Schubert 			pos += ret;
1276c1d255d3SCy Schubert 		}
1277c1d255d3SCy Schubert 	}
1278325151a3SRui Paulo #endif /* CONFIG_WPS */
1279325151a3SRui Paulo 
1280325151a3SRui Paulo 	if (hapd->conf->wpa) {
1281325151a3SRui Paulo 		ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
1282325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
1283325151a3SRui Paulo 			return pos - buf;
1284325151a3SRui Paulo 		pos += ret;
1285325151a3SRui Paulo 	}
1286325151a3SRui Paulo 
1287325151a3SRui Paulo 	if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
1288325151a3SRui Paulo 		ret = os_snprintf(pos, end - pos, "key_mgmt=");
1289325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
1290325151a3SRui Paulo 			return pos - buf;
1291325151a3SRui Paulo 		pos += ret;
1292325151a3SRui Paulo 
1293325151a3SRui Paulo 		pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
1294325151a3SRui Paulo 
12955b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "\n");
12965b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1297f05cddf9SRui Paulo 			return pos - buf;
1298f05cddf9SRui Paulo 		pos += ret;
12995b9c547cSRui Paulo 	}
13005b9c547cSRui Paulo 
13015b9c547cSRui Paulo 	if (hapd->conf->wpa) {
13025b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
13035b9c547cSRui Paulo 				  wpa_cipher_txt(hapd->conf->wpa_group));
13045b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1305f05cddf9SRui Paulo 			return pos - buf;
1306f05cddf9SRui Paulo 		pos += ret;
1307f05cddf9SRui Paulo 	}
1308f05cddf9SRui Paulo 
1309f05cddf9SRui Paulo 	if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
1310f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
13115b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1312f05cddf9SRui Paulo 			return pos - buf;
1313f05cddf9SRui Paulo 		pos += ret;
1314f05cddf9SRui Paulo 
13155b9c547cSRui Paulo 		ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
13165b9c547cSRui Paulo 					" ");
13175b9c547cSRui Paulo 		if (ret < 0)
1318f05cddf9SRui Paulo 			return pos - buf;
1319f05cddf9SRui Paulo 		pos += ret;
1320f05cddf9SRui Paulo 
1321f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "\n");
13225b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1323f05cddf9SRui Paulo 			return pos - buf;
1324f05cddf9SRui Paulo 		pos += ret;
1325f05cddf9SRui Paulo 	}
1326f05cddf9SRui Paulo 
1327f05cddf9SRui Paulo 	if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
1328f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
13295b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1330f05cddf9SRui Paulo 			return pos - buf;
1331f05cddf9SRui Paulo 		pos += ret;
1332f05cddf9SRui Paulo 
13335b9c547cSRui Paulo 		ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
13345b9c547cSRui Paulo 					" ");
13355b9c547cSRui Paulo 		if (ret < 0)
1336f05cddf9SRui Paulo 			return pos - buf;
1337f05cddf9SRui Paulo 		pos += ret;
1338f05cddf9SRui Paulo 
1339f05cddf9SRui Paulo 		ret = os_snprintf(pos, end - pos, "\n");
13405b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1341f05cddf9SRui Paulo 			return pos - buf;
1342f05cddf9SRui Paulo 		pos += ret;
1343f05cddf9SRui Paulo 	}
1344f05cddf9SRui Paulo 
1345c1d255d3SCy Schubert 	if (hapd->conf->wpa && hapd->conf->wpa_deny_ptk0_rekey) {
1346c1d255d3SCy Schubert 		ret = os_snprintf(pos, end - pos, "wpa_deny_ptk0_rekey=%d\n",
1347c1d255d3SCy Schubert 				  hapd->conf->wpa_deny_ptk0_rekey);
1348c1d255d3SCy Schubert 		if (os_snprintf_error(end - pos, ret))
1349c1d255d3SCy Schubert 			return pos - buf;
1350c1d255d3SCy Schubert 		pos += ret;
1351c1d255d3SCy Schubert 	}
1352c1d255d3SCy Schubert 
1353c1d255d3SCy Schubert 	if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
1354c1d255d3SCy Schubert 		ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
1355c1d255d3SCy Schubert 				  hapd->conf->extended_key_id);
1356c1d255d3SCy Schubert 		if (os_snprintf_error(end - pos, ret))
1357c1d255d3SCy Schubert 			return pos - buf;
1358c1d255d3SCy Schubert 		pos += ret;
1359c1d255d3SCy Schubert 	}
1360c1d255d3SCy Schubert 
1361f05cddf9SRui Paulo 	return pos - buf;
1362f05cddf9SRui Paulo }
1363f05cddf9SRui Paulo 
1364f05cddf9SRui Paulo 
hostapd_disassoc_accept_mac(struct hostapd_data * hapd)136585732ac8SCy Schubert static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
136685732ac8SCy Schubert {
136785732ac8SCy Schubert 	struct sta_info *sta;
136885732ac8SCy Schubert 	struct vlan_description vlan_id;
136985732ac8SCy Schubert 
137085732ac8SCy Schubert 	if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
137185732ac8SCy Schubert 		return;
137285732ac8SCy Schubert 
137385732ac8SCy Schubert 	for (sta = hapd->sta_list; sta; sta = sta->next) {
137485732ac8SCy Schubert 		if (!hostapd_maclist_found(hapd->conf->accept_mac,
137585732ac8SCy Schubert 					   hapd->conf->num_accept_mac,
137685732ac8SCy Schubert 					   sta->addr, &vlan_id) ||
137785732ac8SCy Schubert 		    (vlan_id.notempty &&
137885732ac8SCy Schubert 		     vlan_compare(&vlan_id, sta->vlan_desc)))
137985732ac8SCy Schubert 			ap_sta_disconnect(hapd, sta, sta->addr,
138085732ac8SCy Schubert 					  WLAN_REASON_UNSPECIFIED);
138185732ac8SCy Schubert 	}
138285732ac8SCy Schubert }
138385732ac8SCy Schubert 
138485732ac8SCy Schubert 
hostapd_disassoc_deny_mac(struct hostapd_data * hapd)138585732ac8SCy Schubert static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
138685732ac8SCy Schubert {
138785732ac8SCy Schubert 	struct sta_info *sta;
138885732ac8SCy Schubert 	struct vlan_description vlan_id;
138985732ac8SCy Schubert 
139085732ac8SCy Schubert 	for (sta = hapd->sta_list; sta; sta = sta->next) {
139185732ac8SCy Schubert 		if (hostapd_maclist_found(hapd->conf->deny_mac,
139285732ac8SCy Schubert 					  hapd->conf->num_deny_mac, sta->addr,
139385732ac8SCy Schubert 					  &vlan_id) &&
139485732ac8SCy Schubert 		    (!vlan_id.notempty ||
139585732ac8SCy Schubert 		     !vlan_compare(&vlan_id, sta->vlan_desc)))
139685732ac8SCy Schubert 			ap_sta_disconnect(hapd, sta, sta->addr,
139785732ac8SCy Schubert 					  WLAN_REASON_UNSPECIFIED);
139885732ac8SCy Schubert 	}
139985732ac8SCy Schubert }
140085732ac8SCy Schubert 
1401c1d255d3SCy Schubert 
hostapd_ctrl_iface_set_band(struct hostapd_data * hapd,const char * bands)1402c1d255d3SCy Schubert static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd,
1403c1d255d3SCy Schubert 				       const char *bands)
1404c1d255d3SCy Schubert {
1405c1d255d3SCy Schubert 	union wpa_event_data event;
1406c1d255d3SCy Schubert 	u32 setband_mask = WPA_SETBAND_AUTO;
1407c1d255d3SCy Schubert 
1408c1d255d3SCy Schubert 	/*
1409c1d255d3SCy Schubert 	 * For example:
1410c1d255d3SCy Schubert 	 *  SET setband 2G,6G
1411c1d255d3SCy Schubert 	 *  SET setband 5G
1412c1d255d3SCy Schubert 	 *  SET setband AUTO
1413c1d255d3SCy Schubert 	 */
1414c1d255d3SCy Schubert 	if (!os_strstr(bands, "AUTO")) {
1415c1d255d3SCy Schubert 		if (os_strstr(bands, "5G"))
1416c1d255d3SCy Schubert 			setband_mask |= WPA_SETBAND_5G;
1417c1d255d3SCy Schubert 		if (os_strstr(bands, "6G"))
1418c1d255d3SCy Schubert 			setband_mask |= WPA_SETBAND_6G;
1419c1d255d3SCy Schubert 		if (os_strstr(bands, "2G"))
1420c1d255d3SCy Schubert 			setband_mask |= WPA_SETBAND_2G;
1421c1d255d3SCy Schubert 		if (setband_mask == WPA_SETBAND_AUTO)
1422c1d255d3SCy Schubert 			return -1;
1423c1d255d3SCy Schubert 	}
1424c1d255d3SCy Schubert 
1425c1d255d3SCy Schubert 	if (hostapd_drv_set_band(hapd, setband_mask) == 0) {
1426c1d255d3SCy Schubert 		os_memset(&event, 0, sizeof(event));
1427c1d255d3SCy Schubert 		event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
1428c1d255d3SCy Schubert 		event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
1429c1d255d3SCy Schubert 		wpa_supplicant_event(hapd, EVENT_CHANNEL_LIST_CHANGED, &event);
1430c1d255d3SCy Schubert 	}
1431c1d255d3SCy Schubert 
1432c1d255d3SCy Schubert 	return 0;
1433c1d255d3SCy Schubert }
1434c1d255d3SCy Schubert 
1435c1d255d3SCy Schubert 
hostapd_ctrl_iface_set(struct hostapd_data * hapd,char * cmd)1436f05cddf9SRui Paulo static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
1437f05cddf9SRui Paulo {
1438f05cddf9SRui Paulo 	char *value;
1439f05cddf9SRui Paulo 	int ret = 0;
1440f05cddf9SRui Paulo 
1441f05cddf9SRui Paulo 	value = os_strchr(cmd, ' ');
1442f05cddf9SRui Paulo 	if (value == NULL)
1443f05cddf9SRui Paulo 		return -1;
1444f05cddf9SRui Paulo 	*value++ = '\0';
1445f05cddf9SRui Paulo 
1446f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
1447f05cddf9SRui Paulo 	if (0) {
1448f05cddf9SRui Paulo #ifdef CONFIG_WPS_TESTING
1449f05cddf9SRui Paulo 	} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
1450f05cddf9SRui Paulo 		long int val;
1451f05cddf9SRui Paulo 		val = strtol(value, NULL, 0);
1452f05cddf9SRui Paulo 		if (val < 0 || val > 0xff) {
1453f05cddf9SRui Paulo 			ret = -1;
1454f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid "
1455f05cddf9SRui Paulo 				   "wps_version_number %ld", val);
1456f05cddf9SRui Paulo 		} else {
1457f05cddf9SRui Paulo 			wps_version_number = val;
1458f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
1459f05cddf9SRui Paulo 				   "version %u.%u",
1460f05cddf9SRui Paulo 				   (wps_version_number & 0xf0) >> 4,
1461f05cddf9SRui Paulo 				   wps_version_number & 0x0f);
1462f05cddf9SRui Paulo 			hostapd_wps_update_ie(hapd);
1463f05cddf9SRui Paulo 		}
14644b72b91aSCy Schubert 	} else if (os_strcasecmp(cmd, "wps_testing_stub_cred") == 0) {
14654b72b91aSCy Schubert 		wps_testing_stub_cred = atoi(value);
14664b72b91aSCy Schubert 		wpa_printf(MSG_DEBUG, "WPS: Testing - stub_cred=%d",
14674b72b91aSCy Schubert 			   wps_testing_stub_cred);
14685b9c547cSRui Paulo 	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
14695b9c547cSRui Paulo 		wps_corrupt_pkhash = atoi(value);
14705b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
14715b9c547cSRui Paulo 			   wps_corrupt_pkhash);
1472f05cddf9SRui Paulo #endif /* CONFIG_WPS_TESTING */
14735b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
14745b9c547cSRui Paulo 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
14755b9c547cSRui Paulo 		hapd->ext_mgmt_frame_handling = atoi(value);
14765b9c547cSRui Paulo 	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
14775b9c547cSRui Paulo 		hapd->ext_eapol_frame_io = atoi(value);
1478c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "force_backlog_bytes") == 0) {
1479c1d255d3SCy Schubert 		hapd->force_backlog_bytes = atoi(value);
148085732ac8SCy Schubert #ifdef CONFIG_DPP
148185732ac8SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
148285732ac8SCy Schubert 		os_free(hapd->dpp_config_obj_override);
148385732ac8SCy Schubert 		hapd->dpp_config_obj_override = os_strdup(value);
148485732ac8SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
148585732ac8SCy Schubert 		os_free(hapd->dpp_discovery_override);
148685732ac8SCy Schubert 		hapd->dpp_discovery_override = os_strdup(value);
148785732ac8SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
148885732ac8SCy Schubert 		os_free(hapd->dpp_groups_override);
148985732ac8SCy Schubert 		hapd->dpp_groups_override = os_strdup(value);
149085732ac8SCy Schubert 	} else if (os_strcasecmp(cmd,
149185732ac8SCy Schubert 				 "dpp_ignore_netaccesskey_mismatch") == 0) {
149285732ac8SCy Schubert 		hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
149385732ac8SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
149485732ac8SCy Schubert 		dpp_test = atoi(value);
1495c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
1496c1d255d3SCy Schubert 		dpp_version_override = atoi(value);
149785732ac8SCy Schubert #endif /* CONFIG_DPP */
14985b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
1499780fb4a2SCy Schubert #ifdef CONFIG_MBO
1500780fb4a2SCy Schubert 	} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
1501780fb4a2SCy Schubert 		int val;
1502780fb4a2SCy Schubert 
1503780fb4a2SCy Schubert 		if (!hapd->conf->mbo_enabled)
1504780fb4a2SCy Schubert 			return -1;
1505780fb4a2SCy Schubert 
1506780fb4a2SCy Schubert 		val = atoi(value);
150732a95656SCy Schubert 		if (val < 0 || val > MBO_ASSOC_DISALLOW_REASON_LOW_RSSI)
1508780fb4a2SCy Schubert 			return -1;
1509780fb4a2SCy Schubert 
1510780fb4a2SCy Schubert 		hapd->mbo_assoc_disallow = val;
1511780fb4a2SCy Schubert 		ieee802_11_update_beacons(hapd->iface);
1512780fb4a2SCy Schubert 
1513780fb4a2SCy Schubert 		/*
1514780fb4a2SCy Schubert 		 * TODO: Need to configure drivers that do AP MLME offload with
1515780fb4a2SCy Schubert 		 * disallowing station logic.
1516780fb4a2SCy Schubert 		 */
1517780fb4a2SCy Schubert #endif /* CONFIG_MBO */
151885732ac8SCy Schubert #ifdef CONFIG_DPP
151985732ac8SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
152085732ac8SCy Schubert 		os_free(hapd->dpp_configurator_params);
152185732ac8SCy Schubert 		hapd->dpp_configurator_params = os_strdup(value);
1522c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
1523c1d255d3SCy Schubert 		hapd->dpp_init_max_tries = atoi(value);
1524c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
1525c1d255d3SCy Schubert 		hapd->dpp_init_retry_time = atoi(value);
1526c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
1527c1d255d3SCy Schubert 		hapd->dpp_resp_wait_time = atoi(value);
1528c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
1529c1d255d3SCy Schubert 		hapd->dpp_resp_max_tries = atoi(value);
1530c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
1531c1d255d3SCy Schubert 		hapd->dpp_resp_retry_time = atoi(value);
153285732ac8SCy Schubert #endif /* CONFIG_DPP */
1533c1d255d3SCy Schubert 	} else if (os_strcasecmp(cmd, "setband") == 0) {
1534c1d255d3SCy Schubert 		ret = hostapd_ctrl_iface_set_band(hapd, value);
1535f05cddf9SRui Paulo 	} else {
1536f05cddf9SRui Paulo 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
15375b9c547cSRui Paulo 		if (ret)
15385b9c547cSRui Paulo 			return ret;
15395b9c547cSRui Paulo 
15405b9c547cSRui Paulo 		if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
154185732ac8SCy Schubert 			hostapd_disassoc_deny_mac(hapd);
154285732ac8SCy Schubert 		} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
154385732ac8SCy Schubert 			hostapd_disassoc_accept_mac(hapd);
154485732ac8SCy Schubert 		} else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
154585732ac8SCy Schubert 			   os_strncmp(cmd, "wmm_ac_", 7) == 0) {
154685732ac8SCy Schubert 			hapd->parameter_set_count++;
154785732ac8SCy Schubert 			if (ieee802_11_update_beacons(hapd->iface))
154885732ac8SCy Schubert 				wpa_printf(MSG_DEBUG,
154985732ac8SCy Schubert 					   "Failed to update beacons with WMM parameters");
1550c1d255d3SCy Schubert 		} else if (os_strcmp(cmd, "wpa_passphrase") == 0 ||
1551c1d255d3SCy Schubert 			   os_strcmp(cmd, "sae_password") == 0 ||
1552c1d255d3SCy Schubert 			   os_strcmp(cmd, "sae_pwe") == 0) {
1553c1d255d3SCy Schubert 			if (hapd->started)
1554c1d255d3SCy Schubert 				hostapd_setup_sae_pt(hapd->conf);
1555c1d255d3SCy Schubert 		} else if (os_strcasecmp(cmd, "transition_disable") == 0) {
1556c1d255d3SCy Schubert 			wpa_auth_set_transition_disable(hapd->wpa_auth,
1557c1d255d3SCy Schubert 							hapd->conf->transition_disable);
15585b9c547cSRui Paulo 		}
1559c1d255d3SCy Schubert 
1560c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
1561c1d255d3SCy Schubert 		if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
1562c1d255d3SCy Schubert 			wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
1563c1d255d3SCy Schubert 						   hapd->conf->ft_rsnxe_used);
1564c1d255d3SCy Schubert 		else if (os_strcmp(cmd, "oci_freq_override_eapol_m3") == 0)
1565c1d255d3SCy Schubert 			wpa_auth_set_ocv_override_freq(
1566c1d255d3SCy Schubert 				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
1567c1d255d3SCy Schubert 				atoi(value));
1568c1d255d3SCy Schubert 		else if (os_strcmp(cmd, "oci_freq_override_eapol_g1") == 0)
1569c1d255d3SCy Schubert 			wpa_auth_set_ocv_override_freq(
1570c1d255d3SCy Schubert 				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
1571c1d255d3SCy Schubert 				atoi(value));
1572c1d255d3SCy Schubert 		else if (os_strcmp(cmd, "oci_freq_override_ft_assoc") == 0)
1573c1d255d3SCy Schubert 			wpa_auth_set_ocv_override_freq(
1574c1d255d3SCy Schubert 				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
1575c1d255d3SCy Schubert 				atoi(value));
1576c1d255d3SCy Schubert 		else if (os_strcmp(cmd, "oci_freq_override_fils_assoc") == 0)
1577c1d255d3SCy Schubert 			wpa_auth_set_ocv_override_freq(
1578c1d255d3SCy Schubert 				hapd->wpa_auth,
1579c1d255d3SCy Schubert 				WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC, atoi(value));
1580c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
1581f05cddf9SRui Paulo 	}
1582f05cddf9SRui Paulo 
1583f05cddf9SRui Paulo 	return ret;
1584f05cddf9SRui Paulo }
1585f05cddf9SRui Paulo 
1586f05cddf9SRui Paulo 
hostapd_ctrl_iface_get(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)1587f05cddf9SRui Paulo static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
1588f05cddf9SRui Paulo 				  char *buf, size_t buflen)
1589f05cddf9SRui Paulo {
1590f05cddf9SRui Paulo 	int res;
1591f05cddf9SRui Paulo 
1592f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
1593f05cddf9SRui Paulo 
1594f05cddf9SRui Paulo 	if (os_strcmp(cmd, "version") == 0) {
1595f05cddf9SRui Paulo 		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
15965b9c547cSRui Paulo 		if (os_snprintf_error(buflen, res))
15975b9c547cSRui Paulo 			return -1;
15985b9c547cSRui Paulo 		return res;
15995b9c547cSRui Paulo 	} else if (os_strcmp(cmd, "tls_library") == 0) {
16005b9c547cSRui Paulo 		res = tls_get_library_version(buf, buflen);
16015b9c547cSRui Paulo 		if (os_snprintf_error(buflen, res))
1602f05cddf9SRui Paulo 			return -1;
1603f05cddf9SRui Paulo 		return res;
1604f05cddf9SRui Paulo 	}
1605f05cddf9SRui Paulo 
1606f05cddf9SRui Paulo 	return -1;
1607f05cddf9SRui Paulo }
1608f05cddf9SRui Paulo 
1609f05cddf9SRui Paulo 
hostapd_ctrl_iface_enable(struct hostapd_iface * iface)1610f05cddf9SRui Paulo static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
1611f05cddf9SRui Paulo {
1612f05cddf9SRui Paulo 	if (hostapd_enable_iface(iface) < 0) {
1613f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Enabling of interface failed");
1614f05cddf9SRui Paulo 		return -1;
1615f05cddf9SRui Paulo 	}
1616f05cddf9SRui Paulo 	return 0;
1617f05cddf9SRui Paulo }
1618f05cddf9SRui Paulo 
1619f05cddf9SRui Paulo 
hostapd_ctrl_iface_reload(struct hostapd_iface * iface)1620f05cddf9SRui Paulo static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
1621f05cddf9SRui Paulo {
1622f05cddf9SRui Paulo 	if (hostapd_reload_iface(iface) < 0) {
1623f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Reloading of interface failed");
1624f05cddf9SRui Paulo 		return -1;
1625f05cddf9SRui Paulo 	}
1626f05cddf9SRui Paulo 	return 0;
1627f05cddf9SRui Paulo }
1628f05cddf9SRui Paulo 
1629f05cddf9SRui Paulo 
hostapd_ctrl_iface_disable(struct hostapd_iface * iface)1630f05cddf9SRui Paulo static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
1631f05cddf9SRui Paulo {
1632f05cddf9SRui Paulo 	if (hostapd_disable_iface(iface) < 0) {
1633f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Disabling of interface failed");
1634f05cddf9SRui Paulo 		return -1;
1635f05cddf9SRui Paulo 	}
1636f05cddf9SRui Paulo 	return 0;
1637f05cddf9SRui Paulo }
1638f05cddf9SRui Paulo 
1639f05cddf9SRui Paulo 
16404bc52338SCy Schubert static int
hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data * hapd,struct sta_info * sta,void * ctx)16414bc52338SCy Schubert hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd,
16424bc52338SCy Schubert 					      struct sta_info *sta, void *ctx)
16434bc52338SCy Schubert {
16444bc52338SCy Schubert 	struct hostapd_wpa_psk *psk;
16454bc52338SCy Schubert 	const u8 *pmk;
16464bc52338SCy Schubert 	int pmk_len;
16474bc52338SCy Schubert 	int pmk_match;
16484bc52338SCy Schubert 	int sta_match;
16494bc52338SCy Schubert 	int bss_match;
16504bc52338SCy Schubert 	int reason;
16514bc52338SCy Schubert 
16524bc52338SCy Schubert 	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
16534bc52338SCy Schubert 
16544bc52338SCy Schubert 	for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) {
16554bc52338SCy Schubert 		pmk_match = PMK_LEN == pmk_len &&
16564bc52338SCy Schubert 			os_memcmp(psk->psk, pmk, pmk_len) == 0;
16574bc52338SCy Schubert 		sta_match = psk->group == 0 &&
16584bc52338SCy Schubert 			os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0;
16594bc52338SCy Schubert 		bss_match = psk->group == 1;
16604bc52338SCy Schubert 
16614bc52338SCy Schubert 		if (pmk_match && (sta_match || bss_match))
16624bc52338SCy Schubert 			return 0;
16634bc52338SCy Schubert 	}
16644bc52338SCy Schubert 
16654bc52338SCy Schubert 	wpa_printf(MSG_INFO, "STA " MACSTR
16664bc52338SCy Schubert 		   " PSK/passphrase no longer valid - disconnect",
16674bc52338SCy Schubert 		   MAC2STR(sta->addr));
16684bc52338SCy Schubert 	reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
16694bc52338SCy Schubert 	hostapd_drv_sta_deauth(hapd, sta->addr, reason);
16704bc52338SCy Schubert 	ap_sta_deauthenticate(hapd, sta, reason);
16714bc52338SCy Schubert 
16724bc52338SCy Schubert 	return 0;
16734bc52338SCy Schubert }
16744bc52338SCy Schubert 
16754bc52338SCy Schubert 
hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data * hapd)16764bc52338SCy Schubert static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd)
16774bc52338SCy Schubert {
16784bc52338SCy Schubert 	struct hostapd_bss_config *conf = hapd->conf;
16794bc52338SCy Schubert 	int err;
16804bc52338SCy Schubert 
16814bc52338SCy Schubert 	hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
16824bc52338SCy Schubert 
16834bc52338SCy Schubert 	err = hostapd_setup_wpa_psk(conf);
16844bc52338SCy Schubert 	if (err < 0) {
16854bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d",
16864bc52338SCy Schubert 			   err);
16874bc52338SCy Schubert 		return -1;
16884bc52338SCy Schubert 	}
16894bc52338SCy Schubert 
16904bc52338SCy Schubert 	ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter,
16914bc52338SCy Schubert 			NULL);
16924bc52338SCy Schubert 
16934bc52338SCy Schubert 	return 0;
16944bc52338SCy Schubert }
16954bc52338SCy Schubert 
16964bc52338SCy Schubert 
16975b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
16985b9c547cSRui Paulo 
hostapd_ctrl_iface_radar(struct hostapd_data * hapd,char * cmd)16995b9c547cSRui Paulo static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
17005b9c547cSRui Paulo {
17015b9c547cSRui Paulo 	union wpa_event_data data;
17025b9c547cSRui Paulo 	char *pos, *param;
17035b9c547cSRui Paulo 	enum wpa_event_type event;
17045b9c547cSRui Paulo 
17055b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
17065b9c547cSRui Paulo 
17075b9c547cSRui Paulo 	os_memset(&data, 0, sizeof(data));
17085b9c547cSRui Paulo 
17095b9c547cSRui Paulo 	param = os_strchr(cmd, ' ');
17105b9c547cSRui Paulo 	if (param == NULL)
17115b9c547cSRui Paulo 		return -1;
17125b9c547cSRui Paulo 	*param++ = '\0';
17135b9c547cSRui Paulo 
17145b9c547cSRui Paulo 	if (os_strcmp(cmd, "DETECTED") == 0)
17155b9c547cSRui Paulo 		event = EVENT_DFS_RADAR_DETECTED;
17165b9c547cSRui Paulo 	else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
17175b9c547cSRui Paulo 		event = EVENT_DFS_CAC_FINISHED;
17185b9c547cSRui Paulo 	else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
17195b9c547cSRui Paulo 		event = EVENT_DFS_CAC_ABORTED;
17205b9c547cSRui Paulo 	else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
17215b9c547cSRui Paulo 		event = EVENT_DFS_NOP_FINISHED;
17225b9c547cSRui Paulo 	else {
17235b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
17245b9c547cSRui Paulo 			   cmd);
17255b9c547cSRui Paulo 		return -1;
17265b9c547cSRui Paulo 	}
17275b9c547cSRui Paulo 
17285b9c547cSRui Paulo 	pos = os_strstr(param, "freq=");
17295b9c547cSRui Paulo 	if (pos)
17305b9c547cSRui Paulo 		data.dfs_event.freq = atoi(pos + 5);
17315b9c547cSRui Paulo 
17325b9c547cSRui Paulo 	pos = os_strstr(param, "ht_enabled=1");
17335b9c547cSRui Paulo 	if (pos)
17345b9c547cSRui Paulo 		data.dfs_event.ht_enabled = 1;
17355b9c547cSRui Paulo 
17365b9c547cSRui Paulo 	pos = os_strstr(param, "chan_offset=");
17375b9c547cSRui Paulo 	if (pos)
17385b9c547cSRui Paulo 		data.dfs_event.chan_offset = atoi(pos + 12);
17395b9c547cSRui Paulo 
17405b9c547cSRui Paulo 	pos = os_strstr(param, "chan_width=");
17415b9c547cSRui Paulo 	if (pos)
17425b9c547cSRui Paulo 		data.dfs_event.chan_width = atoi(pos + 11);
17435b9c547cSRui Paulo 
17445b9c547cSRui Paulo 	pos = os_strstr(param, "cf1=");
17455b9c547cSRui Paulo 	if (pos)
17465b9c547cSRui Paulo 		data.dfs_event.cf1 = atoi(pos + 4);
17475b9c547cSRui Paulo 
17485b9c547cSRui Paulo 	pos = os_strstr(param, "cf2=");
17495b9c547cSRui Paulo 	if (pos)
17505b9c547cSRui Paulo 		data.dfs_event.cf2 = atoi(pos + 4);
17515b9c547cSRui Paulo 
17525b9c547cSRui Paulo 	wpa_supplicant_event(hapd, event, &data);
17535b9c547cSRui Paulo 
17545b9c547cSRui Paulo 	return 0;
17555b9c547cSRui Paulo }
17565b9c547cSRui Paulo 
17575b9c547cSRui Paulo 
hostapd_ctrl_iface_mgmt_tx(struct hostapd_data * hapd,char * cmd)17585b9c547cSRui Paulo static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
17595b9c547cSRui Paulo {
17605b9c547cSRui Paulo 	size_t len;
17615b9c547cSRui Paulo 	u8 *buf;
17625b9c547cSRui Paulo 	int res;
17635b9c547cSRui Paulo 
17645b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
17655b9c547cSRui Paulo 
17665b9c547cSRui Paulo 	len = os_strlen(cmd);
17675b9c547cSRui Paulo 	if (len & 1)
17685b9c547cSRui Paulo 		return -1;
17695b9c547cSRui Paulo 	len /= 2;
17705b9c547cSRui Paulo 
17715b9c547cSRui Paulo 	buf = os_malloc(len);
17725b9c547cSRui Paulo 	if (buf == NULL)
17735b9c547cSRui Paulo 		return -1;
17745b9c547cSRui Paulo 
17755b9c547cSRui Paulo 	if (hexstr2bin(cmd, buf, len) < 0) {
17765b9c547cSRui Paulo 		os_free(buf);
17775b9c547cSRui Paulo 		return -1;
17785b9c547cSRui Paulo 	}
17795b9c547cSRui Paulo 
1780c1d255d3SCy Schubert 	res = hostapd_drv_send_mlme(hapd, buf, len, 0, NULL, 0, 0);
17815b9c547cSRui Paulo 	os_free(buf);
17825b9c547cSRui Paulo 	return res;
17835b9c547cSRui Paulo }
17845b9c547cSRui Paulo 
17855b9c547cSRui Paulo 
hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data * hapd,char * cmd)178685732ac8SCy Schubert static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
178785732ac8SCy Schubert 						     char *cmd)
178885732ac8SCy Schubert {
178985732ac8SCy Schubert 	char *pos, *param;
179085732ac8SCy Schubert 	size_t len;
179185732ac8SCy Schubert 	u8 *buf;
179285732ac8SCy Schubert 	int stype = 0, ok = 0;
179385732ac8SCy Schubert 	union wpa_event_data event;
179485732ac8SCy Schubert 
179585732ac8SCy Schubert 	if (!hapd->ext_mgmt_frame_handling)
179685732ac8SCy Schubert 		return -1;
179785732ac8SCy Schubert 
179885732ac8SCy Schubert 	/* stype=<val> ok=<0/1> buf=<frame hexdump> */
179985732ac8SCy Schubert 
180085732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
180185732ac8SCy Schubert 
180285732ac8SCy Schubert 	pos = cmd;
180385732ac8SCy Schubert 	param = os_strstr(pos, "stype=");
180485732ac8SCy Schubert 	if (param) {
180585732ac8SCy Schubert 		param += 6;
180685732ac8SCy Schubert 		stype = atoi(param);
180785732ac8SCy Schubert 	}
180885732ac8SCy Schubert 
180985732ac8SCy Schubert 	param = os_strstr(pos, " ok=");
181085732ac8SCy Schubert 	if (param) {
181185732ac8SCy Schubert 		param += 4;
181285732ac8SCy Schubert 		ok = atoi(param);
181385732ac8SCy Schubert 	}
181485732ac8SCy Schubert 
181585732ac8SCy Schubert 	param = os_strstr(pos, " buf=");
181685732ac8SCy Schubert 	if (!param)
181785732ac8SCy Schubert 		return -1;
181885732ac8SCy Schubert 	param += 5;
181985732ac8SCy Schubert 
182085732ac8SCy Schubert 	len = os_strlen(param);
182185732ac8SCy Schubert 	if (len & 1)
182285732ac8SCy Schubert 		return -1;
182385732ac8SCy Schubert 	len /= 2;
182485732ac8SCy Schubert 
182585732ac8SCy Schubert 	buf = os_malloc(len);
182685732ac8SCy Schubert 	if (!buf || hexstr2bin(param, buf, len) < 0) {
182785732ac8SCy Schubert 		os_free(buf);
182885732ac8SCy Schubert 		return -1;
182985732ac8SCy Schubert 	}
183085732ac8SCy Schubert 
183185732ac8SCy Schubert 	os_memset(&event, 0, sizeof(event));
183285732ac8SCy Schubert 	event.tx_status.type = WLAN_FC_TYPE_MGMT;
183385732ac8SCy Schubert 	event.tx_status.data = buf;
183485732ac8SCy Schubert 	event.tx_status.data_len = len;
183585732ac8SCy Schubert 	event.tx_status.stype = stype;
183685732ac8SCy Schubert 	event.tx_status.ack = ok;
183785732ac8SCy Schubert 	hapd->ext_mgmt_frame_handling = 0;
183885732ac8SCy Schubert 	wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
183985732ac8SCy Schubert 	hapd->ext_mgmt_frame_handling = 1;
184085732ac8SCy Schubert 
184185732ac8SCy Schubert 	os_free(buf);
184285732ac8SCy Schubert 
184385732ac8SCy Schubert 	return 0;
184485732ac8SCy Schubert }
184585732ac8SCy Schubert 
184685732ac8SCy Schubert 
hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data * hapd,char * cmd)184785732ac8SCy Schubert static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
184885732ac8SCy Schubert 					      char *cmd)
184985732ac8SCy Schubert {
185085732ac8SCy Schubert 	char *pos, *param;
185185732ac8SCy Schubert 	size_t len;
185285732ac8SCy Schubert 	u8 *buf;
185385732ac8SCy Schubert 	int freq = 0, datarate = 0, ssi_signal = 0;
185485732ac8SCy Schubert 	union wpa_event_data event;
185585732ac8SCy Schubert 
185685732ac8SCy Schubert 	if (!hapd->ext_mgmt_frame_handling)
185785732ac8SCy Schubert 		return -1;
185885732ac8SCy Schubert 
185985732ac8SCy Schubert 	/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
186085732ac8SCy Schubert 
186185732ac8SCy Schubert 	wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
186285732ac8SCy Schubert 
186385732ac8SCy Schubert 	pos = cmd;
186485732ac8SCy Schubert 	param = os_strstr(pos, "freq=");
186585732ac8SCy Schubert 	if (param) {
186685732ac8SCy Schubert 		param += 5;
186785732ac8SCy Schubert 		freq = atoi(param);
186885732ac8SCy Schubert 	}
186985732ac8SCy Schubert 
187085732ac8SCy Schubert 	param = os_strstr(pos, " datarate=");
187185732ac8SCy Schubert 	if (param) {
187285732ac8SCy Schubert 		param += 10;
187385732ac8SCy Schubert 		datarate = atoi(param);
187485732ac8SCy Schubert 	}
187585732ac8SCy Schubert 
187685732ac8SCy Schubert 	param = os_strstr(pos, " ssi_signal=");
187785732ac8SCy Schubert 	if (param) {
187885732ac8SCy Schubert 		param += 12;
187985732ac8SCy Schubert 		ssi_signal = atoi(param);
188085732ac8SCy Schubert 	}
188185732ac8SCy Schubert 
188285732ac8SCy Schubert 	param = os_strstr(pos, " frame=");
188385732ac8SCy Schubert 	if (param == NULL)
188485732ac8SCy Schubert 		return -1;
188585732ac8SCy Schubert 	param += 7;
188685732ac8SCy Schubert 
188785732ac8SCy Schubert 	len = os_strlen(param);
188885732ac8SCy Schubert 	if (len & 1)
188985732ac8SCy Schubert 		return -1;
189085732ac8SCy Schubert 	len /= 2;
189185732ac8SCy Schubert 
189285732ac8SCy Schubert 	buf = os_malloc(len);
189385732ac8SCy Schubert 	if (buf == NULL)
189485732ac8SCy Schubert 		return -1;
189585732ac8SCy Schubert 
189685732ac8SCy Schubert 	if (hexstr2bin(param, buf, len) < 0) {
189785732ac8SCy Schubert 		os_free(buf);
189885732ac8SCy Schubert 		return -1;
189985732ac8SCy Schubert 	}
190085732ac8SCy Schubert 
190185732ac8SCy Schubert 	os_memset(&event, 0, sizeof(event));
190285732ac8SCy Schubert 	event.rx_mgmt.freq = freq;
190385732ac8SCy Schubert 	event.rx_mgmt.frame = buf;
190485732ac8SCy Schubert 	event.rx_mgmt.frame_len = len;
190585732ac8SCy Schubert 	event.rx_mgmt.ssi_signal = ssi_signal;
190685732ac8SCy Schubert 	event.rx_mgmt.datarate = datarate;
190785732ac8SCy Schubert 	hapd->ext_mgmt_frame_handling = 0;
190885732ac8SCy Schubert 	wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
190985732ac8SCy Schubert 	hapd->ext_mgmt_frame_handling = 1;
191085732ac8SCy Schubert 
191185732ac8SCy Schubert 	os_free(buf);
191285732ac8SCy Schubert 
191385732ac8SCy Schubert 	return 0;
191485732ac8SCy Schubert }
191585732ac8SCy Schubert 
191685732ac8SCy Schubert 
hostapd_ctrl_iface_eapol_rx(struct hostapd_data * hapd,char * cmd)19175b9c547cSRui Paulo static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
19185b9c547cSRui Paulo {
19195b9c547cSRui Paulo 	char *pos;
19205b9c547cSRui Paulo 	u8 src[ETH_ALEN], *buf;
19215b9c547cSRui Paulo 	int used;
19225b9c547cSRui Paulo 	size_t len;
19235b9c547cSRui Paulo 
19245b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
19255b9c547cSRui Paulo 
19265b9c547cSRui Paulo 	pos = cmd;
19275b9c547cSRui Paulo 	used = hwaddr_aton2(pos, src);
19285b9c547cSRui Paulo 	if (used < 0)
19295b9c547cSRui Paulo 		return -1;
19305b9c547cSRui Paulo 	pos += used;
19315b9c547cSRui Paulo 	while (*pos == ' ')
19325b9c547cSRui Paulo 		pos++;
19335b9c547cSRui Paulo 
19345b9c547cSRui Paulo 	len = os_strlen(pos);
19355b9c547cSRui Paulo 	if (len & 1)
19365b9c547cSRui Paulo 		return -1;
19375b9c547cSRui Paulo 	len /= 2;
19385b9c547cSRui Paulo 
19395b9c547cSRui Paulo 	buf = os_malloc(len);
19405b9c547cSRui Paulo 	if (buf == NULL)
19415b9c547cSRui Paulo 		return -1;
19425b9c547cSRui Paulo 
19435b9c547cSRui Paulo 	if (hexstr2bin(pos, buf, len) < 0) {
19445b9c547cSRui Paulo 		os_free(buf);
19455b9c547cSRui Paulo 		return -1;
19465b9c547cSRui Paulo 	}
19475b9c547cSRui Paulo 
19485b9c547cSRui Paulo 	ieee802_1x_receive(hapd, src, buf, len);
19495b9c547cSRui Paulo 	os_free(buf);
19505b9c547cSRui Paulo 
19515b9c547cSRui Paulo 	return 0;
19525b9c547cSRui Paulo }
19535b9c547cSRui Paulo 
19545b9c547cSRui Paulo 
hostapd_ctrl_iface_eapol_tx(struct hostapd_data * hapd,char * cmd)1955c1d255d3SCy Schubert static int hostapd_ctrl_iface_eapol_tx(struct hostapd_data *hapd, char *cmd)
1956c1d255d3SCy Schubert {
1957c1d255d3SCy Schubert 	char *pos, *pos2;
1958c1d255d3SCy Schubert 	u8 dst[ETH_ALEN], *buf;
1959c1d255d3SCy Schubert 	int used, ret;
1960c1d255d3SCy Schubert 	size_t len;
1961c1d255d3SCy Schubert 	unsigned int prev;
1962c1d255d3SCy Schubert 	int encrypt = 0;
1963c1d255d3SCy Schubert 
1964c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "External EAPOL TX: %s", cmd);
1965c1d255d3SCy Schubert 
1966c1d255d3SCy Schubert 	pos = cmd;
1967c1d255d3SCy Schubert 	used = hwaddr_aton2(pos, dst);
1968c1d255d3SCy Schubert 	if (used < 0)
1969c1d255d3SCy Schubert 		return -1;
1970c1d255d3SCy Schubert 	pos += used;
1971c1d255d3SCy Schubert 	while (*pos == ' ')
1972c1d255d3SCy Schubert 		pos++;
1973c1d255d3SCy Schubert 
1974c1d255d3SCy Schubert 	pos2 = os_strchr(pos, ' ');
1975c1d255d3SCy Schubert 	if (pos2) {
1976c1d255d3SCy Schubert 		len = pos2 - pos;
1977c1d255d3SCy Schubert 		encrypt = os_strstr(pos2, "encrypt=1") != NULL;
1978c1d255d3SCy Schubert 	} else {
1979c1d255d3SCy Schubert 		len = os_strlen(pos);
1980c1d255d3SCy Schubert 	}
1981c1d255d3SCy Schubert 	if (len & 1)
1982c1d255d3SCy Schubert 		return -1;
1983c1d255d3SCy Schubert 	len /= 2;
1984c1d255d3SCy Schubert 
1985c1d255d3SCy Schubert 	buf = os_malloc(len);
1986c1d255d3SCy Schubert 	if (!buf || hexstr2bin(pos, buf, len) < 0) {
1987c1d255d3SCy Schubert 		os_free(buf);
1988c1d255d3SCy Schubert 		return -1;
1989c1d255d3SCy Schubert 	}
1990c1d255d3SCy Schubert 
1991c1d255d3SCy Schubert 	prev = hapd->ext_eapol_frame_io;
1992c1d255d3SCy Schubert 	hapd->ext_eapol_frame_io = 0;
1993c1d255d3SCy Schubert 	ret = hostapd_wpa_auth_send_eapol(hapd, dst, buf, len, encrypt);
1994c1d255d3SCy Schubert 	hapd->ext_eapol_frame_io = prev;
1995c1d255d3SCy Schubert 	os_free(buf);
1996c1d255d3SCy Schubert 
1997c1d255d3SCy Schubert 	return ret;
1998c1d255d3SCy Schubert }
1999c1d255d3SCy Schubert 
2000c1d255d3SCy Schubert 
ipv4_hdr_checksum(const void * buf,size_t len)20015b9c547cSRui Paulo static u16 ipv4_hdr_checksum(const void *buf, size_t len)
20025b9c547cSRui Paulo {
20035b9c547cSRui Paulo 	size_t i;
20045b9c547cSRui Paulo 	u32 sum = 0;
20055b9c547cSRui Paulo 	const u16 *pos = buf;
20065b9c547cSRui Paulo 
20075b9c547cSRui Paulo 	for (i = 0; i < len / 2; i++)
20085b9c547cSRui Paulo 		sum += *pos++;
20095b9c547cSRui Paulo 
20105b9c547cSRui Paulo 	while (sum >> 16)
20115b9c547cSRui Paulo 		sum = (sum & 0xffff) + (sum >> 16);
20125b9c547cSRui Paulo 
20135b9c547cSRui Paulo 	return sum ^ 0xffff;
20145b9c547cSRui Paulo }
20155b9c547cSRui Paulo 
20165b9c547cSRui Paulo 
20175b9c547cSRui Paulo #define HWSIM_PACKETLEN 1500
20185b9c547cSRui Paulo #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
20195b9c547cSRui Paulo 
hostapd_data_test_rx(void * ctx,const u8 * src_addr,const u8 * buf,size_t len)2020780fb4a2SCy Schubert static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
20215b9c547cSRui Paulo 				 size_t len)
20225b9c547cSRui Paulo {
20235b9c547cSRui Paulo 	struct hostapd_data *hapd = ctx;
20245b9c547cSRui Paulo 	const struct ether_header *eth;
2025c1d255d3SCy Schubert 	struct ip ip;
20265b9c547cSRui Paulo 	const u8 *pos;
20275b9c547cSRui Paulo 	unsigned int i;
2028206b73d0SCy Schubert 	char extra[30];
20295b9c547cSRui Paulo 
2030206b73d0SCy Schubert 	if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) {
2031206b73d0SCy Schubert 		wpa_printf(MSG_DEBUG,
2032206b73d0SCy Schubert 			   "test data: RX - ignore unexpected length %d",
2033206b73d0SCy Schubert 			   (int) len);
20345b9c547cSRui Paulo 		return;
2035206b73d0SCy Schubert 	}
20365b9c547cSRui Paulo 
20375b9c547cSRui Paulo 	eth = (const struct ether_header *) buf;
2038325151a3SRui Paulo 	os_memcpy(&ip, eth + 1, sizeof(ip));
2039325151a3SRui Paulo 	pos = &buf[sizeof(*eth) + sizeof(ip)];
20405b9c547cSRui Paulo 
2041c1d255d3SCy Schubert 	if (ip.ip_hl != 5 || ip.ip_v != 4 ||
2042c1d255d3SCy Schubert 	    ntohs(ip.ip_len) > HWSIM_IP_LEN) {
2043206b73d0SCy Schubert 		wpa_printf(MSG_DEBUG,
2044c1d255d3SCy Schubert 			   "test data: RX - ignore unexpected IP header");
20455b9c547cSRui Paulo 		return;
2046206b73d0SCy Schubert 	}
20475b9c547cSRui Paulo 
2048c1d255d3SCy Schubert 	for (i = 0; i < ntohs(ip.ip_len) - sizeof(ip); i++) {
2049206b73d0SCy Schubert 		if (*pos != (u8) i) {
2050206b73d0SCy Schubert 			wpa_printf(MSG_DEBUG,
2051206b73d0SCy Schubert 				   "test data: RX - ignore mismatching payload");
20525b9c547cSRui Paulo 			return;
2053206b73d0SCy Schubert 		}
20545b9c547cSRui Paulo 		pos++;
20555b9c547cSRui Paulo 	}
20565b9c547cSRui Paulo 
2057206b73d0SCy Schubert 	extra[0] = '\0';
2058c1d255d3SCy Schubert 	if (ntohs(ip.ip_len) != HWSIM_IP_LEN)
2059c1d255d3SCy Schubert 		os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.ip_len));
2060206b73d0SCy Schubert 	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
2061206b73d0SCy Schubert 		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
20625b9c547cSRui Paulo }
20635b9c547cSRui Paulo 
20645b9c547cSRui Paulo 
hostapd_ctrl_iface_data_test_config(struct hostapd_data * hapd,char * cmd)20655b9c547cSRui Paulo static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
20665b9c547cSRui Paulo 					       char *cmd)
20675b9c547cSRui Paulo {
20685b9c547cSRui Paulo 	int enabled = atoi(cmd);
20695b9c547cSRui Paulo 	char *pos;
20705b9c547cSRui Paulo 	const char *ifname;
20715b9c547cSRui Paulo 
20725b9c547cSRui Paulo 	if (!enabled) {
20735b9c547cSRui Paulo 		if (hapd->l2_test) {
20745b9c547cSRui Paulo 			l2_packet_deinit(hapd->l2_test);
20755b9c547cSRui Paulo 			hapd->l2_test = NULL;
20765b9c547cSRui Paulo 			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
20775b9c547cSRui Paulo 				"test data: Disabled");
20785b9c547cSRui Paulo 		}
20795b9c547cSRui Paulo 		return 0;
20805b9c547cSRui Paulo 	}
20815b9c547cSRui Paulo 
20825b9c547cSRui Paulo 	if (hapd->l2_test)
20835b9c547cSRui Paulo 		return 0;
20845b9c547cSRui Paulo 
20855b9c547cSRui Paulo 	pos = os_strstr(cmd, " ifname=");
20865b9c547cSRui Paulo 	if (pos)
20875b9c547cSRui Paulo 		ifname = pos + 8;
20885b9c547cSRui Paulo 	else
20895b9c547cSRui Paulo 		ifname = hapd->conf->iface;
20905b9c547cSRui Paulo 
20915b9c547cSRui Paulo 	hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
20925b9c547cSRui Paulo 					ETHERTYPE_IP, hostapd_data_test_rx,
20935b9c547cSRui Paulo 					hapd, 1);
20945b9c547cSRui Paulo 	if (hapd->l2_test == NULL)
20955b9c547cSRui Paulo 		return -1;
20965b9c547cSRui Paulo 
20975b9c547cSRui Paulo 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
20985b9c547cSRui Paulo 
20995b9c547cSRui Paulo 	return 0;
21005b9c547cSRui Paulo }
21015b9c547cSRui Paulo 
21025b9c547cSRui Paulo 
hostapd_ctrl_iface_data_test_tx(struct hostapd_data * hapd,char * cmd)21035b9c547cSRui Paulo static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
21045b9c547cSRui Paulo {
21055b9c547cSRui Paulo 	u8 dst[ETH_ALEN], src[ETH_ALEN];
2106206b73d0SCy Schubert 	char *pos, *pos2;
21075b9c547cSRui Paulo 	int used;
21085b9c547cSRui Paulo 	long int val;
21095b9c547cSRui Paulo 	u8 tos;
2110325151a3SRui Paulo 	u8 buf[2 + HWSIM_PACKETLEN];
21115b9c547cSRui Paulo 	struct ether_header *eth;
2112c1d255d3SCy Schubert 	struct ip *ip;
21135b9c547cSRui Paulo 	u8 *dpos;
21145b9c547cSRui Paulo 	unsigned int i;
2115206b73d0SCy Schubert 	size_t send_len = HWSIM_IP_LEN;
21165b9c547cSRui Paulo 
21175b9c547cSRui Paulo 	if (hapd->l2_test == NULL)
21185b9c547cSRui Paulo 		return -1;
21195b9c547cSRui Paulo 
2120206b73d0SCy Schubert 	/* format: <dst> <src> <tos> [len=<length>] */
21215b9c547cSRui Paulo 
21225b9c547cSRui Paulo 	pos = cmd;
21235b9c547cSRui Paulo 	used = hwaddr_aton2(pos, dst);
21245b9c547cSRui Paulo 	if (used < 0)
21255b9c547cSRui Paulo 		return -1;
21265b9c547cSRui Paulo 	pos += used;
21275b9c547cSRui Paulo 	while (*pos == ' ')
21285b9c547cSRui Paulo 		pos++;
21295b9c547cSRui Paulo 	used = hwaddr_aton2(pos, src);
21305b9c547cSRui Paulo 	if (used < 0)
21315b9c547cSRui Paulo 		return -1;
21325b9c547cSRui Paulo 	pos += used;
21335b9c547cSRui Paulo 
2134206b73d0SCy Schubert 	val = strtol(pos, &pos2, 0);
21355b9c547cSRui Paulo 	if (val < 0 || val > 0xff)
21365b9c547cSRui Paulo 		return -1;
21375b9c547cSRui Paulo 	tos = val;
21385b9c547cSRui Paulo 
2139206b73d0SCy Schubert 	pos = os_strstr(pos2, " len=");
2140206b73d0SCy Schubert 	if (pos) {
2141206b73d0SCy Schubert 		i = atoi(pos + 5);
2142206b73d0SCy Schubert 		if (i < sizeof(*ip) || i > HWSIM_IP_LEN)
2143206b73d0SCy Schubert 			return -1;
2144206b73d0SCy Schubert 		send_len = i;
2145206b73d0SCy Schubert 	}
2146206b73d0SCy Schubert 
2147325151a3SRui Paulo 	eth = (struct ether_header *) &buf[2];
21485b9c547cSRui Paulo 	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
21495b9c547cSRui Paulo 	os_memcpy(eth->ether_shost, src, ETH_ALEN);
21505b9c547cSRui Paulo 	eth->ether_type = htons(ETHERTYPE_IP);
2151c1d255d3SCy Schubert 	ip = (struct ip *) (eth + 1);
21525b9c547cSRui Paulo 	os_memset(ip, 0, sizeof(*ip));
2153c1d255d3SCy Schubert 	ip->ip_hl = 5;
2154c1d255d3SCy Schubert 	ip->ip_v = 4;
2155c1d255d3SCy Schubert 	ip->ip_ttl = 64;
2156c1d255d3SCy Schubert 	ip->ip_tos = tos;
2157c1d255d3SCy Schubert 	ip->ip_len = htons(send_len);
2158c1d255d3SCy Schubert 	ip->ip_p = 1;
2159c1d255d3SCy Schubert 	ip->ip_src.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
2160c1d255d3SCy Schubert 	ip->ip_dst.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
2161c1d255d3SCy Schubert 	ip->ip_sum = ipv4_hdr_checksum(ip, sizeof(*ip));
21625b9c547cSRui Paulo 	dpos = (u8 *) (ip + 1);
2163206b73d0SCy Schubert 	for (i = 0; i < send_len - sizeof(*ip); i++)
21645b9c547cSRui Paulo 		*dpos++ = i;
21655b9c547cSRui Paulo 
2166325151a3SRui Paulo 	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
2167206b73d0SCy Schubert 			   sizeof(struct ether_header) + send_len) < 0)
21685b9c547cSRui Paulo 		return -1;
21695b9c547cSRui Paulo 
21705b9c547cSRui Paulo 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
21715b9c547cSRui Paulo 		" src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
21725b9c547cSRui Paulo 
21735b9c547cSRui Paulo 	return 0;
21745b9c547cSRui Paulo }
21755b9c547cSRui Paulo 
21765b9c547cSRui Paulo 
hostapd_ctrl_iface_data_test_frame(struct hostapd_data * hapd,char * cmd)21775b9c547cSRui Paulo static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
21785b9c547cSRui Paulo 					      char *cmd)
21795b9c547cSRui Paulo {
21805b9c547cSRui Paulo 	u8 *buf;
21815b9c547cSRui Paulo 	struct ether_header *eth;
21825b9c547cSRui Paulo 	struct l2_packet_data *l2 = NULL;
21835b9c547cSRui Paulo 	size_t len;
21845b9c547cSRui Paulo 	u16 ethertype;
21855b9c547cSRui Paulo 	int res = -1;
21865b9c547cSRui Paulo 	const char *ifname = hapd->conf->iface;
21875b9c547cSRui Paulo 
21885b9c547cSRui Paulo 	if (os_strncmp(cmd, "ifname=", 7) == 0) {
21895b9c547cSRui Paulo 		cmd += 7;
21905b9c547cSRui Paulo 		ifname = cmd;
21915b9c547cSRui Paulo 		cmd = os_strchr(cmd, ' ');
21925b9c547cSRui Paulo 		if (cmd == NULL)
21935b9c547cSRui Paulo 			return -1;
21945b9c547cSRui Paulo 		*cmd++ = '\0';
21955b9c547cSRui Paulo 	}
21965b9c547cSRui Paulo 
21975b9c547cSRui Paulo 	len = os_strlen(cmd);
21985b9c547cSRui Paulo 	if (len & 1 || len < ETH_HLEN * 2)
21995b9c547cSRui Paulo 		return -1;
22005b9c547cSRui Paulo 	len /= 2;
22015b9c547cSRui Paulo 
22025b9c547cSRui Paulo 	buf = os_malloc(len);
22035b9c547cSRui Paulo 	if (buf == NULL)
22045b9c547cSRui Paulo 		return -1;
22055b9c547cSRui Paulo 
22065b9c547cSRui Paulo 	if (hexstr2bin(cmd, buf, len) < 0)
22075b9c547cSRui Paulo 		goto done;
22085b9c547cSRui Paulo 
22095b9c547cSRui Paulo 	eth = (struct ether_header *) buf;
22105b9c547cSRui Paulo 	ethertype = ntohs(eth->ether_type);
22115b9c547cSRui Paulo 
22125b9c547cSRui Paulo 	l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
22135b9c547cSRui Paulo 			    hostapd_data_test_rx, hapd, 1);
22145b9c547cSRui Paulo 	if (l2 == NULL)
22155b9c547cSRui Paulo 		goto done;
22165b9c547cSRui Paulo 
22175b9c547cSRui Paulo 	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
22185b9c547cSRui Paulo 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
22195b9c547cSRui Paulo done:
22205b9c547cSRui Paulo 	if (l2)
22215b9c547cSRui Paulo 		l2_packet_deinit(l2);
22225b9c547cSRui Paulo 	os_free(buf);
22235b9c547cSRui Paulo 
22245b9c547cSRui Paulo 	return res < 0 ? -1 : 0;
22255b9c547cSRui Paulo }
22265b9c547cSRui Paulo 
22275b9c547cSRui Paulo 
hostapd_ctrl_test_alloc_fail(struct hostapd_data * hapd,char * cmd)22285b9c547cSRui Paulo static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
22295b9c547cSRui Paulo {
22305b9c547cSRui Paulo #ifdef WPA_TRACE_BFD
22315b9c547cSRui Paulo 	char *pos;
22325b9c547cSRui Paulo 
22335b9c547cSRui Paulo 	wpa_trace_fail_after = atoi(cmd);
22345b9c547cSRui Paulo 	pos = os_strchr(cmd, ':');
22355b9c547cSRui Paulo 	if (pos) {
22365b9c547cSRui Paulo 		pos++;
22375b9c547cSRui Paulo 		os_strlcpy(wpa_trace_fail_func, pos,
22385b9c547cSRui Paulo 			   sizeof(wpa_trace_fail_func));
22395b9c547cSRui Paulo 	} else {
22405b9c547cSRui Paulo 		wpa_trace_fail_after = 0;
22415b9c547cSRui Paulo 	}
22425b9c547cSRui Paulo 
22435b9c547cSRui Paulo 	return 0;
22445b9c547cSRui Paulo #else /* WPA_TRACE_BFD */
22455b9c547cSRui Paulo 	return -1;
22465b9c547cSRui Paulo #endif /* WPA_TRACE_BFD */
22475b9c547cSRui Paulo }
22485b9c547cSRui Paulo 
22495b9c547cSRui Paulo 
hostapd_ctrl_get_alloc_fail(struct hostapd_data * hapd,char * buf,size_t buflen)22505b9c547cSRui Paulo static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
22515b9c547cSRui Paulo 				       char *buf, size_t buflen)
22525b9c547cSRui Paulo {
22535b9c547cSRui Paulo #ifdef WPA_TRACE_BFD
22545b9c547cSRui Paulo 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
22555b9c547cSRui Paulo 			   wpa_trace_fail_func);
22565b9c547cSRui Paulo #else /* WPA_TRACE_BFD */
22575b9c547cSRui Paulo 	return -1;
22585b9c547cSRui Paulo #endif /* WPA_TRACE_BFD */
22595b9c547cSRui Paulo }
22605b9c547cSRui Paulo 
2261325151a3SRui Paulo 
hostapd_ctrl_test_fail(struct hostapd_data * hapd,char * cmd)2262325151a3SRui Paulo static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
2263325151a3SRui Paulo {
2264325151a3SRui Paulo #ifdef WPA_TRACE_BFD
2265325151a3SRui Paulo 	char *pos;
2266325151a3SRui Paulo 
2267325151a3SRui Paulo 	wpa_trace_test_fail_after = atoi(cmd);
2268325151a3SRui Paulo 	pos = os_strchr(cmd, ':');
2269325151a3SRui Paulo 	if (pos) {
2270325151a3SRui Paulo 		pos++;
2271325151a3SRui Paulo 		os_strlcpy(wpa_trace_test_fail_func, pos,
2272325151a3SRui Paulo 			   sizeof(wpa_trace_test_fail_func));
2273325151a3SRui Paulo 	} else {
2274325151a3SRui Paulo 		wpa_trace_test_fail_after = 0;
2275325151a3SRui Paulo 	}
2276325151a3SRui Paulo 
2277325151a3SRui Paulo 	return 0;
2278325151a3SRui Paulo #else /* WPA_TRACE_BFD */
2279325151a3SRui Paulo 	return -1;
2280325151a3SRui Paulo #endif /* WPA_TRACE_BFD */
2281325151a3SRui Paulo }
2282325151a3SRui Paulo 
2283325151a3SRui Paulo 
hostapd_ctrl_get_fail(struct hostapd_data * hapd,char * buf,size_t buflen)2284325151a3SRui Paulo static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
2285325151a3SRui Paulo 				 char *buf, size_t buflen)
2286325151a3SRui Paulo {
2287325151a3SRui Paulo #ifdef WPA_TRACE_BFD
2288325151a3SRui Paulo 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
2289325151a3SRui Paulo 			   wpa_trace_test_fail_func);
2290325151a3SRui Paulo #else /* WPA_TRACE_BFD */
2291325151a3SRui Paulo 	return -1;
2292325151a3SRui Paulo #endif /* WPA_TRACE_BFD */
2293325151a3SRui Paulo }
2294325151a3SRui Paulo 
229585732ac8SCy Schubert 
hostapd_ctrl_reset_pn(struct hostapd_data * hapd,const char * cmd)229685732ac8SCy Schubert static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
229785732ac8SCy Schubert {
229885732ac8SCy Schubert 	struct sta_info *sta;
229985732ac8SCy Schubert 	u8 addr[ETH_ALEN];
230085732ac8SCy Schubert 	u8 zero[WPA_TK_MAX_LEN];
230185732ac8SCy Schubert 
230285732ac8SCy Schubert 	os_memset(zero, 0, sizeof(zero));
230385732ac8SCy Schubert 
230485732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
230585732ac8SCy Schubert 		return -1;
230685732ac8SCy Schubert 
2307c1d255d3SCy Schubert 	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, " BIGTK")) {
2308c1d255d3SCy Schubert 		if (hapd->last_bigtk_alg == WPA_ALG_NONE)
2309c1d255d3SCy Schubert 			return -1;
2310c1d255d3SCy Schubert 
2311c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Reset BIPN for BIGTK");
2312c1d255d3SCy Schubert 
2313c1d255d3SCy Schubert 		/* First, use a zero key to avoid any possible duplicate key
2314c1d255d3SCy Schubert 		 * avoidance in the driver. */
2315c1d255d3SCy Schubert 		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
2316c1d255d3SCy Schubert 					hapd->last_bigtk_alg,
2317c1d255d3SCy Schubert 					broadcast_ether_addr,
2318c1d255d3SCy Schubert 					hapd->last_bigtk_key_idx, 0, 1, NULL, 0,
2319c1d255d3SCy Schubert 					zero, hapd->last_bigtk_len,
2320c1d255d3SCy Schubert 					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
2321c1d255d3SCy Schubert 			return -1;
2322c1d255d3SCy Schubert 
2323c1d255d3SCy Schubert 		/* Set the previously configured key to reset its TSC */
2324c1d255d3SCy Schubert 		return hostapd_drv_set_key(hapd->conf->iface, hapd,
2325c1d255d3SCy Schubert 					   hapd->last_bigtk_alg,
2326c1d255d3SCy Schubert 					   broadcast_ether_addr,
2327c1d255d3SCy Schubert 					   hapd->last_bigtk_key_idx, 0, 1, NULL,
2328c1d255d3SCy Schubert 					   0, hapd->last_bigtk,
2329c1d255d3SCy Schubert 					   hapd->last_bigtk_len,
2330c1d255d3SCy Schubert 					   KEY_FLAG_GROUP_TX_DEFAULT);
2331c1d255d3SCy Schubert 	}
2332c1d255d3SCy Schubert 
233385732ac8SCy Schubert 	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
233485732ac8SCy Schubert 		if (hapd->last_igtk_alg == WPA_ALG_NONE)
233585732ac8SCy Schubert 			return -1;
233685732ac8SCy Schubert 
233785732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
233885732ac8SCy Schubert 
233985732ac8SCy Schubert 		/* First, use a zero key to avoid any possible duplicate key
234085732ac8SCy Schubert 		 * avoidance in the driver. */
234185732ac8SCy Schubert 		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
234285732ac8SCy Schubert 					hapd->last_igtk_alg,
234385732ac8SCy Schubert 					broadcast_ether_addr,
2344c1d255d3SCy Schubert 					hapd->last_igtk_key_idx, 0, 1, NULL, 0,
2345c1d255d3SCy Schubert 					zero, hapd->last_igtk_len,
2346c1d255d3SCy Schubert 					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
234785732ac8SCy Schubert 			return -1;
234885732ac8SCy Schubert 
234985732ac8SCy Schubert 		/* Set the previously configured key to reset its TSC */
235085732ac8SCy Schubert 		return hostapd_drv_set_key(hapd->conf->iface, hapd,
235185732ac8SCy Schubert 					   hapd->last_igtk_alg,
235285732ac8SCy Schubert 					   broadcast_ether_addr,
2353c1d255d3SCy Schubert 					   hapd->last_igtk_key_idx, 0, 1, NULL,
2354c1d255d3SCy Schubert 					   0, hapd->last_igtk,
2355c1d255d3SCy Schubert 					   hapd->last_igtk_len,
2356c1d255d3SCy Schubert 					   KEY_FLAG_GROUP_TX_DEFAULT);
235785732ac8SCy Schubert 	}
235885732ac8SCy Schubert 
235985732ac8SCy Schubert 	if (is_broadcast_ether_addr(addr)) {
236085732ac8SCy Schubert 		if (hapd->last_gtk_alg == WPA_ALG_NONE)
236185732ac8SCy Schubert 			return -1;
236285732ac8SCy Schubert 
236385732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
236485732ac8SCy Schubert 
236585732ac8SCy Schubert 		/* First, use a zero key to avoid any possible duplicate key
236685732ac8SCy Schubert 		 * avoidance in the driver. */
236785732ac8SCy Schubert 		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
236885732ac8SCy Schubert 					hapd->last_gtk_alg,
236985732ac8SCy Schubert 					broadcast_ether_addr,
2370c1d255d3SCy Schubert 					hapd->last_gtk_key_idx, 0, 1, NULL, 0,
2371c1d255d3SCy Schubert 					zero, hapd->last_gtk_len,
2372c1d255d3SCy Schubert 					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
237385732ac8SCy Schubert 			return -1;
237485732ac8SCy Schubert 
237585732ac8SCy Schubert 		/* Set the previously configured key to reset its TSC */
237685732ac8SCy Schubert 		return hostapd_drv_set_key(hapd->conf->iface, hapd,
237785732ac8SCy Schubert 					   hapd->last_gtk_alg,
237885732ac8SCy Schubert 					   broadcast_ether_addr,
2379c1d255d3SCy Schubert 					   hapd->last_gtk_key_idx, 0, 1, NULL,
2380c1d255d3SCy Schubert 					   0, hapd->last_gtk,
2381c1d255d3SCy Schubert 					   hapd->last_gtk_len,
2382c1d255d3SCy Schubert 					   KEY_FLAG_GROUP_TX_DEFAULT);
238385732ac8SCy Schubert 	}
238485732ac8SCy Schubert 
238585732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
238685732ac8SCy Schubert 	if (!sta)
238785732ac8SCy Schubert 		return -1;
238885732ac8SCy Schubert 
238985732ac8SCy Schubert 	if (sta->last_tk_alg == WPA_ALG_NONE)
239085732ac8SCy Schubert 		return -1;
239185732ac8SCy Schubert 
239285732ac8SCy Schubert 	wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
239385732ac8SCy Schubert 		   MAC2STR(sta->addr));
239485732ac8SCy Schubert 
239585732ac8SCy Schubert 	/* First, use a zero key to avoid any possible duplicate key avoidance
239685732ac8SCy Schubert 	 * in the driver. */
239785732ac8SCy Schubert 	if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2398c1d255d3SCy Schubert 				sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
2399c1d255d3SCy Schubert 				zero, sta->last_tk_len,
2400c1d255d3SCy Schubert 				KEY_FLAG_PAIRWISE_RX_TX) < 0)
240185732ac8SCy Schubert 		return -1;
240285732ac8SCy Schubert 
240385732ac8SCy Schubert 	/* Set the previously configured key to reset its TSC/RSC */
240485732ac8SCy Schubert 	return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2405c1d255d3SCy Schubert 				   sta->addr, sta->last_tk_key_idx, 0, 1, NULL,
2406c1d255d3SCy Schubert 				   0, sta->last_tk, sta->last_tk_len,
2407c1d255d3SCy Schubert 				   KEY_FLAG_PAIRWISE_RX_TX);
240885732ac8SCy Schubert }
240985732ac8SCy Schubert 
241085732ac8SCy Schubert 
hostapd_ctrl_set_key(struct hostapd_data * hapd,const char * cmd)241185732ac8SCy Schubert static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
241285732ac8SCy Schubert {
241385732ac8SCy Schubert 	u8 addr[ETH_ALEN];
241485732ac8SCy Schubert 	const char *pos = cmd;
241585732ac8SCy Schubert 	enum wpa_alg alg;
2416c1d255d3SCy Schubert 	enum key_flag key_flag;
241785732ac8SCy Schubert 	int idx, set_tx;
241885732ac8SCy Schubert 	u8 seq[6], key[WPA_TK_MAX_LEN];
241985732ac8SCy Schubert 	size_t key_len;
242085732ac8SCy Schubert 
2421c1d255d3SCy Schubert 	/* parameters: alg addr idx set_tx seq key key_flag */
242285732ac8SCy Schubert 
242385732ac8SCy Schubert 	alg = atoi(pos);
242485732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
242585732ac8SCy Schubert 	if (!pos)
242685732ac8SCy Schubert 		return -1;
242785732ac8SCy Schubert 	pos++;
242885732ac8SCy Schubert 	if (hwaddr_aton(pos, addr))
242985732ac8SCy Schubert 		return -1;
243085732ac8SCy Schubert 	pos += 17;
243185732ac8SCy Schubert 	if (*pos != ' ')
243285732ac8SCy Schubert 		return -1;
243385732ac8SCy Schubert 	pos++;
243485732ac8SCy Schubert 	idx = atoi(pos);
243585732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
243685732ac8SCy Schubert 	if (!pos)
243785732ac8SCy Schubert 		return -1;
243885732ac8SCy Schubert 	pos++;
243985732ac8SCy Schubert 	set_tx = atoi(pos);
244085732ac8SCy Schubert 	pos = os_strchr(pos, ' ');
244185732ac8SCy Schubert 	if (!pos)
244285732ac8SCy Schubert 		return -1;
244385732ac8SCy Schubert 	pos++;
244485732ac8SCy Schubert 	if (hexstr2bin(pos, seq, sizeof(seq)) < 0)
244585732ac8SCy Schubert 		return -1;
244685732ac8SCy Schubert 	pos += 2 * 6;
244785732ac8SCy Schubert 	if (*pos != ' ')
244885732ac8SCy Schubert 		return -1;
244985732ac8SCy Schubert 	pos++;
2450c1d255d3SCy Schubert 	if (!os_strchr(pos, ' '))
2451c1d255d3SCy Schubert 		return -1;
2452c1d255d3SCy Schubert 	key_len = (os_strchr(pos, ' ') - pos) / 2;
245385732ac8SCy Schubert 	if (hexstr2bin(pos, key, key_len) < 0)
245485732ac8SCy Schubert 		return -1;
2455c1d255d3SCy Schubert 	pos += 2 * key_len;
2456c1d255d3SCy Schubert 	if (*pos != ' ')
2457c1d255d3SCy Schubert 		return -1;
2458c1d255d3SCy Schubert 
2459c1d255d3SCy Schubert 	pos++;
2460c1d255d3SCy Schubert 	key_flag = atoi(pos);
2461c1d255d3SCy Schubert 	pos = os_strchr(pos, ' ');
2462c1d255d3SCy Schubert 	if (pos)
2463c1d255d3SCy Schubert 		return -1;
246485732ac8SCy Schubert 
246585732ac8SCy Schubert 	wpa_printf(MSG_INFO, "TESTING: Set key");
2466c1d255d3SCy Schubert 	return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, 0,
2467c1d255d3SCy Schubert 				   set_tx, seq, 6, key, key_len, key_flag);
246885732ac8SCy Schubert }
246985732ac8SCy Schubert 
247085732ac8SCy Schubert 
restore_tk(void * ctx1,void * ctx2)247185732ac8SCy Schubert static void restore_tk(void *ctx1, void *ctx2)
247285732ac8SCy Schubert {
247385732ac8SCy Schubert 	struct hostapd_data *hapd = ctx1;
247485732ac8SCy Schubert 	struct sta_info *sta = ctx2;
247585732ac8SCy Schubert 
247685732ac8SCy Schubert 	wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
247785732ac8SCy Schubert 		   MAC2STR(sta->addr));
247885732ac8SCy Schubert 	/* This does not really restore the TSC properly, so this will result
247985732ac8SCy Schubert 	 * in replay protection issues for now since there is no clean way of
248085732ac8SCy Schubert 	 * preventing encryption of a single EAPOL frame. */
248185732ac8SCy Schubert 	hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2482c1d255d3SCy Schubert 			    sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
2483c1d255d3SCy Schubert 			    sta->last_tk, sta->last_tk_len,
2484c1d255d3SCy Schubert 			    KEY_FLAG_PAIRWISE_RX_TX);
248585732ac8SCy Schubert }
248685732ac8SCy Schubert 
248785732ac8SCy Schubert 
hostapd_ctrl_resend_m1(struct hostapd_data * hapd,const char * cmd)248885732ac8SCy Schubert static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
248985732ac8SCy Schubert {
249085732ac8SCy Schubert 	struct sta_info *sta;
249185732ac8SCy Schubert 	u8 addr[ETH_ALEN];
249285732ac8SCy Schubert 	int plain = os_strstr(cmd, "plaintext") != NULL;
249385732ac8SCy Schubert 
249485732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
249585732ac8SCy Schubert 		return -1;
249685732ac8SCy Schubert 
249785732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
249885732ac8SCy Schubert 	if (!sta || !sta->wpa_sm)
249985732ac8SCy Schubert 		return -1;
250085732ac8SCy Schubert 
250185732ac8SCy Schubert 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
250285732ac8SCy Schubert 		plain = 0; /* no need for special processing */
250385732ac8SCy Schubert 	if (plain) {
250485732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
250585732ac8SCy Schubert 			   MAC2STR(sta->addr));
250685732ac8SCy Schubert 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2507c1d255d3SCy Schubert 				    sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
2508c1d255d3SCy Schubert 				    0, NULL, 0, KEY_FLAG_PAIRWISE);
250985732ac8SCy Schubert 	}
251085732ac8SCy Schubert 
251185732ac8SCy Schubert 	wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
251285732ac8SCy Schubert 	return wpa_auth_resend_m1(sta->wpa_sm,
251385732ac8SCy Schubert 				  os_strstr(cmd, "change-anonce") != NULL,
251485732ac8SCy Schubert 				  plain ? restore_tk : NULL, hapd, sta);
251585732ac8SCy Schubert }
251685732ac8SCy Schubert 
251785732ac8SCy Schubert 
hostapd_ctrl_resend_m3(struct hostapd_data * hapd,const char * cmd)251885732ac8SCy Schubert static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
251985732ac8SCy Schubert {
252085732ac8SCy Schubert 	struct sta_info *sta;
252185732ac8SCy Schubert 	u8 addr[ETH_ALEN];
252285732ac8SCy Schubert 	int plain = os_strstr(cmd, "plaintext") != NULL;
252385732ac8SCy Schubert 
252485732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
252585732ac8SCy Schubert 		return -1;
252685732ac8SCy Schubert 
252785732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
252885732ac8SCy Schubert 	if (!sta || !sta->wpa_sm)
252985732ac8SCy Schubert 		return -1;
253085732ac8SCy Schubert 
253185732ac8SCy Schubert 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
253285732ac8SCy Schubert 		plain = 0; /* no need for special processing */
253385732ac8SCy Schubert 	if (plain) {
253485732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
253585732ac8SCy Schubert 			   MAC2STR(sta->addr));
253685732ac8SCy Schubert 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2537c1d255d3SCy Schubert 				    sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
2538c1d255d3SCy Schubert 				    0, NULL, 0, KEY_FLAG_PAIRWISE);
253985732ac8SCy Schubert 	}
254085732ac8SCy Schubert 
254185732ac8SCy Schubert 	wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
254285732ac8SCy Schubert 	return wpa_auth_resend_m3(sta->wpa_sm,
254385732ac8SCy Schubert 				  plain ? restore_tk : NULL, hapd, sta);
254485732ac8SCy Schubert }
254585732ac8SCy Schubert 
254685732ac8SCy Schubert 
hostapd_ctrl_resend_group_m1(struct hostapd_data * hapd,const char * cmd)254785732ac8SCy Schubert static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
254885732ac8SCy Schubert 					const char *cmd)
254985732ac8SCy Schubert {
255085732ac8SCy Schubert 	struct sta_info *sta;
255185732ac8SCy Schubert 	u8 addr[ETH_ALEN];
255285732ac8SCy Schubert 	int plain = os_strstr(cmd, "plaintext") != NULL;
255385732ac8SCy Schubert 
255485732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
255585732ac8SCy Schubert 		return -1;
255685732ac8SCy Schubert 
255785732ac8SCy Schubert 	sta = ap_get_sta(hapd, addr);
255885732ac8SCy Schubert 	if (!sta || !sta->wpa_sm)
255985732ac8SCy Schubert 		return -1;
256085732ac8SCy Schubert 
256185732ac8SCy Schubert 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
256285732ac8SCy Schubert 		plain = 0; /* no need for special processing */
256385732ac8SCy Schubert 	if (plain) {
256485732ac8SCy Schubert 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
256585732ac8SCy Schubert 			   MAC2STR(sta->addr));
256685732ac8SCy Schubert 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2567c1d255d3SCy Schubert 				    sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
2568c1d255d3SCy Schubert 				    0, NULL, 0, KEY_FLAG_PAIRWISE);
256985732ac8SCy Schubert 	}
257085732ac8SCy Schubert 
257185732ac8SCy Schubert 	wpa_printf(MSG_INFO,
257285732ac8SCy Schubert 		   "TESTING: Send group M1 for the same GTK and zero RSC to "
257385732ac8SCy Schubert 		   MACSTR, MAC2STR(sta->addr));
257485732ac8SCy Schubert 	return wpa_auth_resend_group_m1(sta->wpa_sm,
257585732ac8SCy Schubert 					plain ? restore_tk : NULL, hapd, sta);
257685732ac8SCy Schubert }
257785732ac8SCy Schubert 
2578c1d255d3SCy Schubert 
hostapd_ctrl_rekey_ptk(struct hostapd_data * hapd,const char * cmd)2579c1d255d3SCy Schubert static int hostapd_ctrl_rekey_ptk(struct hostapd_data *hapd, const char *cmd)
2580c1d255d3SCy Schubert {
2581c1d255d3SCy Schubert 	struct sta_info *sta;
2582c1d255d3SCy Schubert 	u8 addr[ETH_ALEN];
2583c1d255d3SCy Schubert 
2584c1d255d3SCy Schubert 	if (hwaddr_aton(cmd, addr))
2585c1d255d3SCy Schubert 		return -1;
2586c1d255d3SCy Schubert 
2587c1d255d3SCy Schubert 	sta = ap_get_sta(hapd, addr);
2588c1d255d3SCy Schubert 	if (!sta || !sta->wpa_sm)
2589c1d255d3SCy Schubert 		return -1;
2590c1d255d3SCy Schubert 
2591c1d255d3SCy Schubert 	return wpa_auth_rekey_ptk(hapd->wpa_auth, sta->wpa_sm);
2592c1d255d3SCy Schubert }
2593c1d255d3SCy Schubert 
2594c1d255d3SCy Schubert 
hostapd_ctrl_get_pmksa_pmk(struct hostapd_data * hapd,const u8 * addr,char * buf,size_t buflen)2595c1d255d3SCy Schubert static int hostapd_ctrl_get_pmksa_pmk(struct hostapd_data *hapd, const u8 *addr,
2596c1d255d3SCy Schubert 				      char *buf, size_t buflen)
2597c1d255d3SCy Schubert {
2598c1d255d3SCy Schubert 	struct rsn_pmksa_cache_entry *pmksa;
2599c1d255d3SCy Schubert 
2600c1d255d3SCy Schubert 	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, addr, NULL);
2601c1d255d3SCy Schubert 	if (!pmksa)
2602c1d255d3SCy Schubert 		return -1;
2603c1d255d3SCy Schubert 
2604c1d255d3SCy Schubert 	return wpa_snprintf_hex(buf, buflen, pmksa->pmk, pmksa->pmk_len);
2605c1d255d3SCy Schubert }
2606c1d255d3SCy Schubert 
2607c1d255d3SCy Schubert 
hostapd_ctrl_get_pmk(struct hostapd_data * hapd,const char * cmd,char * buf,size_t buflen)2608c1d255d3SCy Schubert static int hostapd_ctrl_get_pmk(struct hostapd_data *hapd, const char *cmd,
2609c1d255d3SCy Schubert 				char *buf, size_t buflen)
2610c1d255d3SCy Schubert {
2611c1d255d3SCy Schubert 	struct sta_info *sta;
2612c1d255d3SCy Schubert 	u8 addr[ETH_ALEN];
2613c1d255d3SCy Schubert 	const u8 *pmk;
2614c1d255d3SCy Schubert 	int pmk_len;
2615c1d255d3SCy Schubert 
2616c1d255d3SCy Schubert 	if (hwaddr_aton(cmd, addr))
2617c1d255d3SCy Schubert 		return -1;
2618c1d255d3SCy Schubert 
2619c1d255d3SCy Schubert 	sta = ap_get_sta(hapd, addr);
2620c1d255d3SCy Schubert 	if (!sta || !sta->wpa_sm) {
2621c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "No STA WPA state machine for " MACSTR,
2622c1d255d3SCy Schubert 			   MAC2STR(addr));
2623c1d255d3SCy Schubert 		return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
2624c1d255d3SCy Schubert 	}
2625c1d255d3SCy Schubert 	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
2626c1d255d3SCy Schubert 	if (!pmk || !pmk_len) {
2627c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "No PMK stored for " MACSTR,
2628c1d255d3SCy Schubert 			   MAC2STR(addr));
2629c1d255d3SCy Schubert 		return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
2630c1d255d3SCy Schubert 	}
2631c1d255d3SCy Schubert 
2632c1d255d3SCy Schubert 	return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
2633c1d255d3SCy Schubert }
2634c1d255d3SCy Schubert 
2635c1d255d3SCy Schubert 
hostapd_ctrl_register_frame(struct hostapd_data * hapd,const char * cmd)2636c1d255d3SCy Schubert static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
2637c1d255d3SCy Schubert 				       const char *cmd)
2638c1d255d3SCy Schubert {
2639c1d255d3SCy Schubert 	u16 type;
2640c1d255d3SCy Schubert 	char *pos, *end;
2641c1d255d3SCy Schubert 	u8 match[10];
2642c1d255d3SCy Schubert 	size_t match_len;
2643c1d255d3SCy Schubert 	bool multicast = false;
2644c1d255d3SCy Schubert 
2645c1d255d3SCy Schubert 	type = strtol(cmd, &pos, 16);
2646c1d255d3SCy Schubert 	if (*pos != ' ')
2647c1d255d3SCy Schubert 		return -1;
2648c1d255d3SCy Schubert 	pos++;
2649c1d255d3SCy Schubert 	end = os_strchr(pos, ' ');
2650c1d255d3SCy Schubert 	if (end) {
2651c1d255d3SCy Schubert 		match_len = end - pos;
2652c1d255d3SCy Schubert 		multicast = os_strstr(end, "multicast") != NULL;
2653c1d255d3SCy Schubert 	} else {
2654c1d255d3SCy Schubert 		match_len = os_strlen(pos) / 2;
2655c1d255d3SCy Schubert 	}
2656c1d255d3SCy Schubert 	if (hexstr2bin(pos, match, match_len))
2657c1d255d3SCy Schubert 		return -1;
2658c1d255d3SCy Schubert 
2659c1d255d3SCy Schubert 	return hostapd_drv_register_frame(hapd, type, match, match_len,
2660c1d255d3SCy Schubert 					  multicast);
2661c1d255d3SCy Schubert }
2662c1d255d3SCy Schubert 
26635b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
26645b9c547cSRui Paulo 
26655b9c547cSRui Paulo 
2666c1d255d3SCy Schubert #ifdef NEED_AP_MLME
hostapd_ctrl_check_freq_params(struct hostapd_freq_params * params)2667c1d255d3SCy Schubert static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
2668c1d255d3SCy Schubert {
2669c1d255d3SCy Schubert 	switch (params->bandwidth) {
2670c1d255d3SCy Schubert 	case 0:
2671c1d255d3SCy Schubert 		/* bandwidth not specified: use 20 MHz by default */
2672c1d255d3SCy Schubert 		/* fall-through */
2673c1d255d3SCy Schubert 	case 20:
2674c1d255d3SCy Schubert 		if (params->center_freq1 &&
2675c1d255d3SCy Schubert 		    params->center_freq1 != params->freq)
2676c1d255d3SCy Schubert 			return -1;
2677c1d255d3SCy Schubert 
2678c1d255d3SCy Schubert 		if (params->center_freq2 || params->sec_channel_offset)
2679c1d255d3SCy Schubert 			return -1;
2680c1d255d3SCy Schubert 		break;
2681c1d255d3SCy Schubert 	case 40:
2682c1d255d3SCy Schubert 		if (params->center_freq2 || !params->sec_channel_offset)
2683c1d255d3SCy Schubert 			return -1;
2684c1d255d3SCy Schubert 
2685c1d255d3SCy Schubert 		if (!params->center_freq1)
2686c1d255d3SCy Schubert 			break;
2687c1d255d3SCy Schubert 		switch (params->sec_channel_offset) {
2688c1d255d3SCy Schubert 		case 1:
2689c1d255d3SCy Schubert 			if (params->freq + 10 != params->center_freq1)
2690c1d255d3SCy Schubert 				return -1;
2691c1d255d3SCy Schubert 			break;
2692c1d255d3SCy Schubert 		case -1:
2693c1d255d3SCy Schubert 			if (params->freq - 10 != params->center_freq1)
2694c1d255d3SCy Schubert 				return -1;
2695c1d255d3SCy Schubert 			break;
2696c1d255d3SCy Schubert 		default:
2697c1d255d3SCy Schubert 			return -1;
2698c1d255d3SCy Schubert 		}
2699c1d255d3SCy Schubert 		break;
2700c1d255d3SCy Schubert 	case 80:
2701c1d255d3SCy Schubert 		if (!params->center_freq1 || !params->sec_channel_offset)
2702c1d255d3SCy Schubert 			return 1;
2703c1d255d3SCy Schubert 
2704c1d255d3SCy Schubert 		switch (params->sec_channel_offset) {
2705c1d255d3SCy Schubert 		case 1:
2706c1d255d3SCy Schubert 			if (params->freq - 10 != params->center_freq1 &&
2707c1d255d3SCy Schubert 			    params->freq + 30 != params->center_freq1)
2708c1d255d3SCy Schubert 				return 1;
2709c1d255d3SCy Schubert 			break;
2710c1d255d3SCy Schubert 		case -1:
2711c1d255d3SCy Schubert 			if (params->freq + 10 != params->center_freq1 &&
2712c1d255d3SCy Schubert 			    params->freq - 30 != params->center_freq1)
2713c1d255d3SCy Schubert 				return -1;
2714c1d255d3SCy Schubert 			break;
2715c1d255d3SCy Schubert 		default:
2716c1d255d3SCy Schubert 			return -1;
2717c1d255d3SCy Schubert 		}
2718c1d255d3SCy Schubert 
2719c1d255d3SCy Schubert 		/* Adjacent and overlapped are not allowed for 80+80 */
2720c1d255d3SCy Schubert 		if (params->center_freq2 &&
2721c1d255d3SCy Schubert 		    params->center_freq1 - params->center_freq2 <= 80 &&
2722c1d255d3SCy Schubert 		    params->center_freq2 - params->center_freq1 <= 80)
2723c1d255d3SCy Schubert 			return 1;
2724c1d255d3SCy Schubert 		break;
2725c1d255d3SCy Schubert 	case 160:
2726c1d255d3SCy Schubert 		if (!params->center_freq1 || params->center_freq2 ||
2727c1d255d3SCy Schubert 		    !params->sec_channel_offset)
2728c1d255d3SCy Schubert 			return -1;
2729c1d255d3SCy Schubert 
2730c1d255d3SCy Schubert 		switch (params->sec_channel_offset) {
2731c1d255d3SCy Schubert 		case 1:
2732c1d255d3SCy Schubert 			if (params->freq + 70 != params->center_freq1 &&
2733c1d255d3SCy Schubert 			    params->freq + 30 != params->center_freq1 &&
2734c1d255d3SCy Schubert 			    params->freq - 10 != params->center_freq1 &&
2735c1d255d3SCy Schubert 			    params->freq - 50 != params->center_freq1)
2736c1d255d3SCy Schubert 				return -1;
2737c1d255d3SCy Schubert 			break;
2738c1d255d3SCy Schubert 		case -1:
2739c1d255d3SCy Schubert 			if (params->freq + 50 != params->center_freq1 &&
2740c1d255d3SCy Schubert 			    params->freq + 10 != params->center_freq1 &&
2741c1d255d3SCy Schubert 			    params->freq - 30 != params->center_freq1 &&
2742c1d255d3SCy Schubert 			    params->freq - 70 != params->center_freq1)
2743c1d255d3SCy Schubert 				return -1;
2744c1d255d3SCy Schubert 			break;
2745c1d255d3SCy Schubert 		default:
2746c1d255d3SCy Schubert 			return -1;
2747c1d255d3SCy Schubert 		}
2748c1d255d3SCy Schubert 		break;
2749c1d255d3SCy Schubert 	default:
2750c1d255d3SCy Schubert 		return -1;
2751c1d255d3SCy Schubert 	}
2752c1d255d3SCy Schubert 
2753c1d255d3SCy Schubert 	return 0;
2754c1d255d3SCy Schubert }
2755c1d255d3SCy Schubert #endif /* NEED_AP_MLME */
2756c1d255d3SCy Schubert 
2757c1d255d3SCy Schubert 
hostapd_ctrl_iface_chan_switch(struct hostapd_iface * iface,char * pos)27585b9c547cSRui Paulo static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
27595b9c547cSRui Paulo 					  char *pos)
27605b9c547cSRui Paulo {
27615b9c547cSRui Paulo #ifdef NEED_AP_MLME
27625b9c547cSRui Paulo 	struct csa_settings settings;
27635b9c547cSRui Paulo 	int ret;
2764c1d255d3SCy Schubert 	int dfs_range = 0;
27655b9c547cSRui Paulo 	unsigned int i;
2766c1d255d3SCy Schubert 	int bandwidth;
2767c1d255d3SCy Schubert 	u8 chan;
27685b9c547cSRui Paulo 
27695b9c547cSRui Paulo 	ret = hostapd_parse_csa_settings(pos, &settings);
27705b9c547cSRui Paulo 	if (ret)
27715b9c547cSRui Paulo 		return ret;
27725b9c547cSRui Paulo 
2773c1d255d3SCy Schubert 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
2774c1d255d3SCy Schubert 	if (ret) {
2775c1d255d3SCy Schubert 		wpa_printf(MSG_INFO,
2776c1d255d3SCy Schubert 			   "chanswitch: invalid frequency settings provided");
2777c1d255d3SCy Schubert 		return ret;
2778c1d255d3SCy Schubert 	}
2779c1d255d3SCy Schubert 
2780c1d255d3SCy Schubert 	switch (settings.freq_params.bandwidth) {
2781c1d255d3SCy Schubert 	case 40:
2782c1d255d3SCy Schubert 		bandwidth = CHAN_WIDTH_40;
2783c1d255d3SCy Schubert 		break;
2784c1d255d3SCy Schubert 	case 80:
2785c1d255d3SCy Schubert 		if (settings.freq_params.center_freq2)
2786c1d255d3SCy Schubert 			bandwidth = CHAN_WIDTH_80P80;
2787c1d255d3SCy Schubert 		else
2788c1d255d3SCy Schubert 			bandwidth = CHAN_WIDTH_80;
2789c1d255d3SCy Schubert 		break;
2790c1d255d3SCy Schubert 	case 160:
2791c1d255d3SCy Schubert 		bandwidth = CHAN_WIDTH_160;
2792c1d255d3SCy Schubert 		break;
2793c1d255d3SCy Schubert 	default:
2794c1d255d3SCy Schubert 		bandwidth = CHAN_WIDTH_20;
2795c1d255d3SCy Schubert 		break;
2796c1d255d3SCy Schubert 	}
2797c1d255d3SCy Schubert 
2798c1d255d3SCy Schubert 	if (settings.freq_params.center_freq1)
2799c1d255d3SCy Schubert 		dfs_range += hostapd_is_dfs_overlap(
2800c1d255d3SCy Schubert 			iface, bandwidth, settings.freq_params.center_freq1);
2801c1d255d3SCy Schubert 	else
2802c1d255d3SCy Schubert 		dfs_range += hostapd_is_dfs_overlap(
2803c1d255d3SCy Schubert 			iface, bandwidth, settings.freq_params.freq);
2804c1d255d3SCy Schubert 
2805c1d255d3SCy Schubert 	if (settings.freq_params.center_freq2)
2806c1d255d3SCy Schubert 		dfs_range += hostapd_is_dfs_overlap(
2807c1d255d3SCy Schubert 			iface, bandwidth, settings.freq_params.center_freq2);
2808c1d255d3SCy Schubert 
2809c1d255d3SCy Schubert 	if (dfs_range) {
2810c1d255d3SCy Schubert 		ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
2811c1d255d3SCy Schubert 		if (ret == NUM_HOSTAPD_MODES) {
2812c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR,
2813c1d255d3SCy Schubert 				   "Failed to get channel for (freq=%d, sec_channel_offset=%d, bw=%d)",
2814c1d255d3SCy Schubert 				   settings.freq_params.freq,
2815c1d255d3SCy Schubert 				   settings.freq_params.sec_channel_offset,
2816c1d255d3SCy Schubert 				   settings.freq_params.bandwidth);
2817c1d255d3SCy Schubert 			return -1;
2818c1d255d3SCy Schubert 		}
2819c1d255d3SCy Schubert 
2820c1d255d3SCy Schubert 		settings.freq_params.channel = chan;
2821c1d255d3SCy Schubert 
2822c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG,
2823c1d255d3SCy Schubert 			   "DFS/CAC to (channel=%u, freq=%d, sec_channel_offset=%d, bw=%d, center_freq1=%d)",
2824c1d255d3SCy Schubert 			   settings.freq_params.channel,
2825c1d255d3SCy Schubert 			   settings.freq_params.freq,
2826c1d255d3SCy Schubert 			   settings.freq_params.sec_channel_offset,
2827c1d255d3SCy Schubert 			   settings.freq_params.bandwidth,
2828c1d255d3SCy Schubert 			   settings.freq_params.center_freq1);
2829c1d255d3SCy Schubert 
2830c1d255d3SCy Schubert 		/* Perform CAC and switch channel */
2831c1d255d3SCy Schubert 		hostapd_switch_channel_fallback(iface, &settings.freq_params);
2832c1d255d3SCy Schubert 		return 0;
2833c1d255d3SCy Schubert 	}
2834c1d255d3SCy Schubert 
28355b9c547cSRui Paulo 	for (i = 0; i < iface->num_bss; i++) {
283685732ac8SCy Schubert 
2837c1d255d3SCy Schubert 		/* Save CHAN_SWITCH VHT and HE config */
2838c1d255d3SCy Schubert 		hostapd_chan_switch_config(iface->bss[i],
2839c1d255d3SCy Schubert 					   &settings.freq_params);
284085732ac8SCy Schubert 
28415b9c547cSRui Paulo 		ret = hostapd_switch_channel(iface->bss[i], &settings);
28425b9c547cSRui Paulo 		if (ret) {
28435b9c547cSRui Paulo 			/* FIX: What do we do if CSA fails in the middle of
28445b9c547cSRui Paulo 			 * submitting multi-BSS CSA requests? */
28455b9c547cSRui Paulo 			return ret;
28465b9c547cSRui Paulo 		}
28475b9c547cSRui Paulo 	}
28485b9c547cSRui Paulo 
28495b9c547cSRui Paulo 	return 0;
28505b9c547cSRui Paulo #else /* NEED_AP_MLME */
28515b9c547cSRui Paulo 	return -1;
28525b9c547cSRui Paulo #endif /* NEED_AP_MLME */
28535b9c547cSRui Paulo }
28545b9c547cSRui Paulo 
28555b9c547cSRui Paulo 
hostapd_ctrl_iface_mib(struct hostapd_data * hapd,char * reply,int reply_size,const char * param)28565b9c547cSRui Paulo static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
28575b9c547cSRui Paulo 				  int reply_size, const char *param)
28585b9c547cSRui Paulo {
28595b9c547cSRui Paulo #ifdef RADIUS_SERVER
28605b9c547cSRui Paulo 	if (os_strcmp(param, "radius_server") == 0) {
28615b9c547cSRui Paulo 		return radius_server_get_mib(hapd->radius_srv, reply,
28625b9c547cSRui Paulo 					     reply_size);
28635b9c547cSRui Paulo 	}
28645b9c547cSRui Paulo #endif /* RADIUS_SERVER */
28655b9c547cSRui Paulo 	return -1;
28665b9c547cSRui Paulo }
28675b9c547cSRui Paulo 
28685b9c547cSRui Paulo 
hostapd_ctrl_iface_vendor(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)28695b9c547cSRui Paulo static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
28705b9c547cSRui Paulo 				     char *buf, size_t buflen)
28715b9c547cSRui Paulo {
28725b9c547cSRui Paulo 	int ret;
2873c1d255d3SCy Schubert 	char *pos, *temp = NULL;
28745b9c547cSRui Paulo 	u8 *data = NULL;
28755b9c547cSRui Paulo 	unsigned int vendor_id, subcmd;
2876c1d255d3SCy Schubert 	enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
28775b9c547cSRui Paulo 	struct wpabuf *reply;
28785b9c547cSRui Paulo 	size_t data_len = 0;
28795b9c547cSRui Paulo 
2880c1d255d3SCy Schubert 	/**
2881c1d255d3SCy Schubert 	 * cmd: <vendor id> <subcommand id> [<hex formatted data>]
2882c1d255d3SCy Schubert 	 * [nested=<0|1>]
2883c1d255d3SCy Schubert 	 */
28845b9c547cSRui Paulo 	vendor_id = strtoul(cmd, &pos, 16);
2885780fb4a2SCy Schubert 	if (!isblank((unsigned char) *pos))
28865b9c547cSRui Paulo 		return -EINVAL;
28875b9c547cSRui Paulo 
28885b9c547cSRui Paulo 	subcmd = strtoul(pos, &pos, 10);
28895b9c547cSRui Paulo 
28905b9c547cSRui Paulo 	if (*pos != '\0') {
2891780fb4a2SCy Schubert 		if (!isblank((unsigned char) *pos++))
28925b9c547cSRui Paulo 			return -EINVAL;
2893c1d255d3SCy Schubert 
2894c1d255d3SCy Schubert 		temp = os_strchr(pos, ' ');
2895c1d255d3SCy Schubert 		data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
28965b9c547cSRui Paulo 	}
28975b9c547cSRui Paulo 
28985b9c547cSRui Paulo 	if (data_len) {
28995b9c547cSRui Paulo 		data_len /= 2;
29005b9c547cSRui Paulo 		data = os_malloc(data_len);
29015b9c547cSRui Paulo 		if (!data)
29025b9c547cSRui Paulo 			return -ENOBUFS;
29035b9c547cSRui Paulo 
29045b9c547cSRui Paulo 		if (hexstr2bin(pos, data, data_len)) {
29055b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
29065b9c547cSRui Paulo 				   "Vendor command: wrong parameter format");
29075b9c547cSRui Paulo 			os_free(data);
29085b9c547cSRui Paulo 			return -EINVAL;
29095b9c547cSRui Paulo 		}
29105b9c547cSRui Paulo 	}
29115b9c547cSRui Paulo 
2912c1d255d3SCy Schubert 	pos = os_strstr(cmd, "nested=");
2913c1d255d3SCy Schubert 	if (pos)
2914c1d255d3SCy Schubert 		nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
2915c1d255d3SCy Schubert 			NESTED_ATTR_NOT_USED;
2916c1d255d3SCy Schubert 
29175b9c547cSRui Paulo 	reply = wpabuf_alloc((buflen - 1) / 2);
29185b9c547cSRui Paulo 	if (!reply) {
29195b9c547cSRui Paulo 		os_free(data);
29205b9c547cSRui Paulo 		return -ENOBUFS;
29215b9c547cSRui Paulo 	}
29225b9c547cSRui Paulo 
29235b9c547cSRui Paulo 	ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
2924c1d255d3SCy Schubert 				     nested_attr_flag, reply);
29255b9c547cSRui Paulo 
29265b9c547cSRui Paulo 	if (ret == 0)
29275b9c547cSRui Paulo 		ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
29285b9c547cSRui Paulo 				       wpabuf_len(reply));
29295b9c547cSRui Paulo 
29305b9c547cSRui Paulo 	wpabuf_free(reply);
29315b9c547cSRui Paulo 	os_free(data);
29325b9c547cSRui Paulo 
29335b9c547cSRui Paulo 	return ret;
29345b9c547cSRui Paulo }
29355b9c547cSRui Paulo 
29365b9c547cSRui Paulo 
hostapd_ctrl_iface_eapol_reauth(struct hostapd_data * hapd,const char * cmd)2937325151a3SRui Paulo static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
2938325151a3SRui Paulo 					   const char *cmd)
293939beb93cSSam Leffler {
2940325151a3SRui Paulo 	u8 addr[ETH_ALEN];
2941325151a3SRui Paulo 	struct sta_info *sta;
294239beb93cSSam Leffler 
2943325151a3SRui Paulo 	if (hwaddr_aton(cmd, addr))
2944325151a3SRui Paulo 		return -1;
294539beb93cSSam Leffler 
2946325151a3SRui Paulo 	sta = ap_get_sta(hapd, addr);
2947325151a3SRui Paulo 	if (!sta || !sta->eapol_sm)
2948325151a3SRui Paulo 		return -1;
2949325151a3SRui Paulo 
2950325151a3SRui Paulo 	eapol_auth_reauthenticate(sta->eapol_sm);
2951325151a3SRui Paulo 	return 0;
29525b9c547cSRui Paulo }
2953325151a3SRui Paulo 
2954325151a3SRui Paulo 
hostapd_ctrl_iface_eapol_set(struct hostapd_data * hapd,char * cmd)2955325151a3SRui Paulo static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
2956325151a3SRui Paulo {
2957325151a3SRui Paulo 	u8 addr[ETH_ALEN];
2958325151a3SRui Paulo 	struct sta_info *sta;
2959325151a3SRui Paulo 	char *pos = cmd, *param;
2960325151a3SRui Paulo 
2961325151a3SRui Paulo 	if (hwaddr_aton(pos, addr) || pos[17] != ' ')
2962325151a3SRui Paulo 		return -1;
2963325151a3SRui Paulo 	pos += 18;
2964325151a3SRui Paulo 	param = pos;
2965325151a3SRui Paulo 	pos = os_strchr(pos, ' ');
2966325151a3SRui Paulo 	if (!pos)
2967325151a3SRui Paulo 		return -1;
2968325151a3SRui Paulo 	*pos++ = '\0';
2969325151a3SRui Paulo 
2970325151a3SRui Paulo 	sta = ap_get_sta(hapd, addr);
2971325151a3SRui Paulo 	if (!sta || !sta->eapol_sm)
2972325151a3SRui Paulo 		return -1;
2973325151a3SRui Paulo 
2974325151a3SRui Paulo 	return eapol_auth_set_conf(sta->eapol_sm, param, pos);
297539beb93cSSam Leffler }
297639beb93cSSam Leffler 
2977325151a3SRui Paulo 
hostapd_ctrl_iface_log_level(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)2978325151a3SRui Paulo static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
2979325151a3SRui Paulo 					char *buf, size_t buflen)
2980325151a3SRui Paulo {
2981325151a3SRui Paulo 	char *pos, *end, *stamp;
2982325151a3SRui Paulo 	int ret;
2983325151a3SRui Paulo 
2984325151a3SRui Paulo 	/* cmd: "LOG_LEVEL [<level>]" */
2985325151a3SRui Paulo 	if (*cmd == '\0') {
2986325151a3SRui Paulo 		pos = buf;
2987325151a3SRui Paulo 		end = buf + buflen;
2988325151a3SRui Paulo 		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
2989325151a3SRui Paulo 				  "Timestamp: %d\n",
2990325151a3SRui Paulo 				  debug_level_str(wpa_debug_level),
2991325151a3SRui Paulo 				  wpa_debug_timestamp);
2992325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
2993325151a3SRui Paulo 			ret = 0;
2994325151a3SRui Paulo 
2995325151a3SRui Paulo 		return ret;
2996325151a3SRui Paulo 	}
2997325151a3SRui Paulo 
2998325151a3SRui Paulo 	while (*cmd == ' ')
2999325151a3SRui Paulo 		cmd++;
3000325151a3SRui Paulo 
3001325151a3SRui Paulo 	stamp = os_strchr(cmd, ' ');
3002325151a3SRui Paulo 	if (stamp) {
3003325151a3SRui Paulo 		*stamp++ = '\0';
3004325151a3SRui Paulo 		while (*stamp == ' ') {
3005325151a3SRui Paulo 			stamp++;
3006325151a3SRui Paulo 		}
3007325151a3SRui Paulo 	}
3008325151a3SRui Paulo 
3009325151a3SRui Paulo 	if (os_strlen(cmd)) {
3010325151a3SRui Paulo 		int level = str_to_debug_level(cmd);
3011325151a3SRui Paulo 		if (level < 0)
3012325151a3SRui Paulo 			return -1;
3013325151a3SRui Paulo 		wpa_debug_level = level;
3014325151a3SRui Paulo 	}
3015325151a3SRui Paulo 
3016325151a3SRui Paulo 	if (stamp && os_strlen(stamp))
3017325151a3SRui Paulo 		wpa_debug_timestamp = atoi(stamp);
3018325151a3SRui Paulo 
3019325151a3SRui Paulo 	os_memcpy(buf, "OK\n", 3);
3020325151a3SRui Paulo 	return 3;
3021325151a3SRui Paulo }
3022325151a3SRui Paulo 
3023325151a3SRui Paulo 
3024325151a3SRui Paulo #ifdef NEED_AP_MLME
hostapd_ctrl_iface_track_sta_list(struct hostapd_data * hapd,char * buf,size_t buflen)3025325151a3SRui Paulo static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
3026325151a3SRui Paulo 					     char *buf, size_t buflen)
3027325151a3SRui Paulo {
3028325151a3SRui Paulo 	struct hostapd_iface *iface = hapd->iface;
3029325151a3SRui Paulo 	char *pos, *end;
3030325151a3SRui Paulo 	struct hostapd_sta_info *info;
3031325151a3SRui Paulo 	struct os_reltime now;
3032325151a3SRui Paulo 
3033780fb4a2SCy Schubert 	if (!iface->num_sta_seen)
3034780fb4a2SCy Schubert 		return 0;
3035780fb4a2SCy Schubert 
3036325151a3SRui Paulo 	sta_track_expire(iface, 0);
3037325151a3SRui Paulo 
3038325151a3SRui Paulo 	pos = buf;
3039325151a3SRui Paulo 	end = buf + buflen;
3040325151a3SRui Paulo 
3041325151a3SRui Paulo 	os_get_reltime(&now);
3042325151a3SRui Paulo 	dl_list_for_each_reverse(info, &iface->sta_seen,
3043325151a3SRui Paulo 				 struct hostapd_sta_info, list) {
3044325151a3SRui Paulo 		struct os_reltime age;
3045325151a3SRui Paulo 		int ret;
3046325151a3SRui Paulo 
3047325151a3SRui Paulo 		os_reltime_sub(&now, &info->last_seen, &age);
304885732ac8SCy Schubert 		ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
304985732ac8SCy Schubert 				  MAC2STR(info->addr), (unsigned int) age.sec,
305085732ac8SCy Schubert 				  info->ssi_signal);
3051325151a3SRui Paulo 		if (os_snprintf_error(end - pos, ret))
3052325151a3SRui Paulo 			break;
3053325151a3SRui Paulo 		pos += ret;
3054325151a3SRui Paulo 	}
3055325151a3SRui Paulo 
3056325151a3SRui Paulo 	return pos - buf;
3057325151a3SRui Paulo }
3058325151a3SRui Paulo #endif /* NEED_AP_MLME */
3059325151a3SRui Paulo 
3060325151a3SRui Paulo 
hostapd_ctrl_iface_req_lci(struct hostapd_data * hapd,const char * cmd)3061780fb4a2SCy Schubert static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
3062780fb4a2SCy Schubert 				      const char *cmd)
3063780fb4a2SCy Schubert {
3064780fb4a2SCy Schubert 	u8 addr[ETH_ALEN];
3065780fb4a2SCy Schubert 
3066780fb4a2SCy Schubert 	if (hwaddr_aton(cmd, addr)) {
3067780fb4a2SCy Schubert 		wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
3068780fb4a2SCy Schubert 		return -1;
3069780fb4a2SCy Schubert 	}
3070780fb4a2SCy Schubert 
3071780fb4a2SCy Schubert 	return hostapd_send_lci_req(hapd, addr);
3072780fb4a2SCy Schubert }
3073780fb4a2SCy Schubert 
3074780fb4a2SCy Schubert 
hostapd_ctrl_iface_req_range(struct hostapd_data * hapd,char * cmd)3075780fb4a2SCy Schubert static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
3076780fb4a2SCy Schubert {
3077780fb4a2SCy Schubert 	u8 addr[ETH_ALEN];
3078780fb4a2SCy Schubert 	char *token, *context = NULL;
3079780fb4a2SCy Schubert 	int random_interval, min_ap;
3080780fb4a2SCy Schubert 	u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
3081780fb4a2SCy Schubert 	unsigned int n_responders;
3082780fb4a2SCy Schubert 
3083780fb4a2SCy Schubert 	token = str_token(cmd, " ", &context);
3084780fb4a2SCy Schubert 	if (!token || hwaddr_aton(token, addr)) {
3085780fb4a2SCy Schubert 		wpa_printf(MSG_INFO,
3086780fb4a2SCy Schubert 			   "CTRL: REQ_RANGE - Bad destination address");
3087780fb4a2SCy Schubert 		return -1;
3088780fb4a2SCy Schubert 	}
3089780fb4a2SCy Schubert 
3090780fb4a2SCy Schubert 	token = str_token(cmd, " ", &context);
3091780fb4a2SCy Schubert 	if (!token)
3092780fb4a2SCy Schubert 		return -1;
3093780fb4a2SCy Schubert 
3094780fb4a2SCy Schubert 	random_interval = atoi(token);
3095780fb4a2SCy Schubert 	if (random_interval < 0 || random_interval > 0xffff)
3096780fb4a2SCy Schubert 		return -1;
3097780fb4a2SCy Schubert 
3098780fb4a2SCy Schubert 	token = str_token(cmd, " ", &context);
3099780fb4a2SCy Schubert 	if (!token)
3100780fb4a2SCy Schubert 		return -1;
3101780fb4a2SCy Schubert 
3102780fb4a2SCy Schubert 	min_ap = atoi(token);
3103780fb4a2SCy Schubert 	if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
3104780fb4a2SCy Schubert 		return -1;
3105780fb4a2SCy Schubert 
3106780fb4a2SCy Schubert 	n_responders = 0;
3107780fb4a2SCy Schubert 	while ((token = str_token(cmd, " ", &context))) {
3108780fb4a2SCy Schubert 		if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
3109780fb4a2SCy Schubert 			wpa_printf(MSG_INFO,
3110780fb4a2SCy Schubert 				   "CTRL: REQ_RANGE: Too many responders");
3111780fb4a2SCy Schubert 			return -1;
3112780fb4a2SCy Schubert 		}
3113780fb4a2SCy Schubert 
3114780fb4a2SCy Schubert 		if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
3115780fb4a2SCy Schubert 			wpa_printf(MSG_INFO,
3116780fb4a2SCy Schubert 				   "CTRL: REQ_RANGE: Bad responder address");
3117780fb4a2SCy Schubert 			return -1;
3118780fb4a2SCy Schubert 		}
3119780fb4a2SCy Schubert 
3120780fb4a2SCy Schubert 		n_responders++;
3121780fb4a2SCy Schubert 	}
3122780fb4a2SCy Schubert 
3123780fb4a2SCy Schubert 	if (!n_responders) {
3124780fb4a2SCy Schubert 		wpa_printf(MSG_INFO,
3125780fb4a2SCy Schubert 			   "CTRL: REQ_RANGE - No FTM responder address");
3126780fb4a2SCy Schubert 		return -1;
3127780fb4a2SCy Schubert 	}
3128780fb4a2SCy Schubert 
3129780fb4a2SCy Schubert 	return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
3130780fb4a2SCy Schubert 				      responders, n_responders);
3131780fb4a2SCy Schubert }
3132780fb4a2SCy Schubert 
3133780fb4a2SCy Schubert 
hostapd_ctrl_iface_req_beacon(struct hostapd_data * hapd,const char * cmd,char * reply,size_t reply_size)313485732ac8SCy Schubert static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
313585732ac8SCy Schubert 					 const char *cmd, char *reply,
313685732ac8SCy Schubert 					 size_t reply_size)
313785732ac8SCy Schubert {
313885732ac8SCy Schubert 	u8 addr[ETH_ALEN];
313985732ac8SCy Schubert 	const char *pos;
314085732ac8SCy Schubert 	struct wpabuf *req;
314185732ac8SCy Schubert 	int ret;
314285732ac8SCy Schubert 	u8 req_mode = 0;
314385732ac8SCy Schubert 
314485732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
314585732ac8SCy Schubert 		return -1;
314685732ac8SCy Schubert 	pos = os_strchr(cmd, ' ');
314785732ac8SCy Schubert 	if (!pos)
314885732ac8SCy Schubert 		return -1;
314985732ac8SCy Schubert 	pos++;
315085732ac8SCy Schubert 	if (os_strncmp(pos, "req_mode=", 9) == 0) {
315185732ac8SCy Schubert 		int val = hex2byte(pos + 9);
315285732ac8SCy Schubert 
315385732ac8SCy Schubert 		if (val < 0)
315485732ac8SCy Schubert 			return -1;
315585732ac8SCy Schubert 		req_mode = val;
315685732ac8SCy Schubert 		pos += 11;
315785732ac8SCy Schubert 		pos = os_strchr(pos, ' ');
315885732ac8SCy Schubert 		if (!pos)
315985732ac8SCy Schubert 			return -1;
316085732ac8SCy Schubert 		pos++;
316185732ac8SCy Schubert 	}
316285732ac8SCy Schubert 	req = wpabuf_parse_bin(pos);
316385732ac8SCy Schubert 	if (!req)
316485732ac8SCy Schubert 		return -1;
316585732ac8SCy Schubert 
316685732ac8SCy Schubert 	ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
316785732ac8SCy Schubert 	wpabuf_free(req);
316885732ac8SCy Schubert 	if (ret >= 0)
316985732ac8SCy Schubert 		ret = os_snprintf(reply, reply_size, "%d", ret);
317085732ac8SCy Schubert 	return ret;
317185732ac8SCy Schubert }
317285732ac8SCy Schubert 
317385732ac8SCy Schubert 
hostapd_ctrl_iface_show_neighbor(struct hostapd_data * hapd,char * buf,size_t buflen)3174c1d255d3SCy Schubert static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
3175c1d255d3SCy Schubert 					    char *buf, size_t buflen)
3176c1d255d3SCy Schubert {
3177c1d255d3SCy Schubert 	if (!(hapd->conf->radio_measurements[0] &
3178c1d255d3SCy Schubert 	      WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
3179c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR,
3180c1d255d3SCy Schubert 			   "CTRL: SHOW_NEIGHBOR: Neighbor report is not enabled");
3181c1d255d3SCy Schubert 		return -1;
3182c1d255d3SCy Schubert 	}
3183c1d255d3SCy Schubert 
3184c1d255d3SCy Schubert 	return hostapd_neighbor_show(hapd, buf, buflen);
3185c1d255d3SCy Schubert }
3186c1d255d3SCy Schubert 
3187c1d255d3SCy Schubert 
hostapd_ctrl_iface_set_neighbor(struct hostapd_data * hapd,char * buf)3188780fb4a2SCy Schubert static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
3189780fb4a2SCy Schubert {
3190780fb4a2SCy Schubert 	struct wpa_ssid_value ssid;
3191780fb4a2SCy Schubert 	u8 bssid[ETH_ALEN];
3192780fb4a2SCy Schubert 	struct wpabuf *nr, *lci = NULL, *civic = NULL;
319385732ac8SCy Schubert 	int stationary = 0;
31944b72b91aSCy Schubert 	int bss_parameters = 0;
3195780fb4a2SCy Schubert 	char *tmp;
31964b72b91aSCy Schubert 	int ret = -1;
3197780fb4a2SCy Schubert 
3198780fb4a2SCy Schubert 	if (!(hapd->conf->radio_measurements[0] &
3199780fb4a2SCy Schubert 	      WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
3200780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
3201780fb4a2SCy Schubert 			   "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
3202780fb4a2SCy Schubert 		return -1;
3203780fb4a2SCy Schubert 	}
3204780fb4a2SCy Schubert 
3205780fb4a2SCy Schubert 	if (hwaddr_aton(buf, bssid)) {
3206780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
3207780fb4a2SCy Schubert 		return -1;
3208780fb4a2SCy Schubert 	}
3209780fb4a2SCy Schubert 
3210780fb4a2SCy Schubert 	tmp = os_strstr(buf, "ssid=");
3211780fb4a2SCy Schubert 	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
3212780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
3213780fb4a2SCy Schubert 			   "CTRL: SET_NEIGHBOR: Bad or missing SSID");
3214780fb4a2SCy Schubert 		return -1;
3215780fb4a2SCy Schubert 	}
3216780fb4a2SCy Schubert 	buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
3217780fb4a2SCy Schubert 	if (!buf)
3218780fb4a2SCy Schubert 		return -1;
3219780fb4a2SCy Schubert 
3220780fb4a2SCy Schubert 	tmp = os_strstr(buf, "nr=");
3221780fb4a2SCy Schubert 	if (!tmp) {
3222780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
3223780fb4a2SCy Schubert 			   "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
3224780fb4a2SCy Schubert 		return -1;
3225780fb4a2SCy Schubert 	}
3226780fb4a2SCy Schubert 
3227780fb4a2SCy Schubert 	buf = os_strchr(tmp, ' ');
3228780fb4a2SCy Schubert 	if (buf)
3229780fb4a2SCy Schubert 		*buf++ = '\0';
3230780fb4a2SCy Schubert 
3231780fb4a2SCy Schubert 	nr = wpabuf_parse_bin(tmp + 3);
3232780fb4a2SCy Schubert 	if (!nr) {
3233780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR,
3234780fb4a2SCy Schubert 			   "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
3235780fb4a2SCy Schubert 		return -1;
3236780fb4a2SCy Schubert 	}
3237780fb4a2SCy Schubert 
3238780fb4a2SCy Schubert 	if (!buf)
3239780fb4a2SCy Schubert 		goto set;
3240780fb4a2SCy Schubert 
3241780fb4a2SCy Schubert 	tmp = os_strstr(buf, "lci=");
3242780fb4a2SCy Schubert 	if (tmp) {
3243780fb4a2SCy Schubert 		buf = os_strchr(tmp, ' ');
3244780fb4a2SCy Schubert 		if (buf)
3245780fb4a2SCy Schubert 			*buf++ = '\0';
3246780fb4a2SCy Schubert 		lci = wpabuf_parse_bin(tmp + 4);
3247780fb4a2SCy Schubert 		if (!lci) {
3248780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
3249780fb4a2SCy Schubert 				   "CTRL: SET_NEIGHBOR: Bad LCI subelement");
32504b72b91aSCy Schubert 			goto fail;
3251780fb4a2SCy Schubert 		}
3252780fb4a2SCy Schubert 	}
3253780fb4a2SCy Schubert 
3254780fb4a2SCy Schubert 	if (!buf)
3255780fb4a2SCy Schubert 		goto set;
3256780fb4a2SCy Schubert 
3257780fb4a2SCy Schubert 	tmp = os_strstr(buf, "civic=");
3258780fb4a2SCy Schubert 	if (tmp) {
3259780fb4a2SCy Schubert 		buf = os_strchr(tmp, ' ');
3260780fb4a2SCy Schubert 		if (buf)
3261780fb4a2SCy Schubert 			*buf++ = '\0';
3262780fb4a2SCy Schubert 		civic = wpabuf_parse_bin(tmp + 6);
3263780fb4a2SCy Schubert 		if (!civic) {
3264780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
3265780fb4a2SCy Schubert 				   "CTRL: SET_NEIGHBOR: Bad civic subelement");
32664b72b91aSCy Schubert 			goto fail;
3267780fb4a2SCy Schubert 		}
3268780fb4a2SCy Schubert 	}
3269780fb4a2SCy Schubert 
327085732ac8SCy Schubert 	if (!buf)
327185732ac8SCy Schubert 		goto set;
327285732ac8SCy Schubert 
327385732ac8SCy Schubert 	if (os_strstr(buf, "stat"))
327485732ac8SCy Schubert 		stationary = 1;
327585732ac8SCy Schubert 
32764b72b91aSCy Schubert 	tmp = os_strstr(buf, "bss_parameter=");
32774b72b91aSCy Schubert 	if (tmp) {
32784b72b91aSCy Schubert 		bss_parameters = atoi(tmp + 14);
32794b72b91aSCy Schubert 		if (bss_parameters < 0 || bss_parameters > 0xff) {
32804b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
32814b72b91aSCy Schubert 				   "CTRL: SET_NEIGHBOR: Bad bss_parameters subelement");
32824b72b91aSCy Schubert 			goto fail;
32834b72b91aSCy Schubert 		}
32844b72b91aSCy Schubert 	}
32854b72b91aSCy Schubert 
3286780fb4a2SCy Schubert set:
328785732ac8SCy Schubert 	ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
32884b72b91aSCy Schubert 				   stationary, bss_parameters);
3289780fb4a2SCy Schubert 
32904b72b91aSCy Schubert fail:
3291780fb4a2SCy Schubert 	wpabuf_free(nr);
3292780fb4a2SCy Schubert 	wpabuf_free(lci);
3293780fb4a2SCy Schubert 	wpabuf_free(civic);
3294780fb4a2SCy Schubert 
3295780fb4a2SCy Schubert 	return ret;
3296780fb4a2SCy Schubert }
3297780fb4a2SCy Schubert 
3298780fb4a2SCy Schubert 
hostapd_ctrl_iface_remove_neighbor(struct hostapd_data * hapd,char * buf)3299780fb4a2SCy Schubert static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
3300780fb4a2SCy Schubert 					      char *buf)
3301780fb4a2SCy Schubert {
3302780fb4a2SCy Schubert 	struct wpa_ssid_value ssid;
3303c1d255d3SCy Schubert 	struct wpa_ssid_value *ssidp = NULL;
3304780fb4a2SCy Schubert 	u8 bssid[ETH_ALEN];
3305780fb4a2SCy Schubert 	char *tmp;
3306780fb4a2SCy Schubert 
3307780fb4a2SCy Schubert 	if (hwaddr_aton(buf, bssid)) {
3308780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
3309780fb4a2SCy Schubert 		return -1;
3310780fb4a2SCy Schubert 	}
3311780fb4a2SCy Schubert 
3312780fb4a2SCy Schubert 	tmp = os_strstr(buf, "ssid=");
3313c1d255d3SCy Schubert 	if (tmp) {
3314c1d255d3SCy Schubert 		ssidp = &ssid;
3315c1d255d3SCy Schubert 		if (ssid_parse(tmp + 5, &ssid)) {
3316780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
3317c1d255d3SCy Schubert 				   "CTRL: REMOVE_NEIGHBOR: Bad SSID");
3318780fb4a2SCy Schubert 			return -1;
3319780fb4a2SCy Schubert 		}
3320c1d255d3SCy Schubert 	}
3321780fb4a2SCy Schubert 
3322c1d255d3SCy Schubert 	return hostapd_neighbor_remove(hapd, bssid, ssidp);
3323780fb4a2SCy Schubert }
3324780fb4a2SCy Schubert 
3325780fb4a2SCy Schubert 
hostapd_ctrl_driver_flags(struct hostapd_iface * iface,char * buf,size_t buflen)3326780fb4a2SCy Schubert static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
3327780fb4a2SCy Schubert 				     size_t buflen)
3328780fb4a2SCy Schubert {
3329780fb4a2SCy Schubert 	int ret, i;
3330780fb4a2SCy Schubert 	char *pos, *end;
3331780fb4a2SCy Schubert 
3332780fb4a2SCy Schubert 	ret = os_snprintf(buf, buflen, "%016llX:\n",
3333780fb4a2SCy Schubert 			  (long long unsigned) iface->drv_flags);
3334780fb4a2SCy Schubert 	if (os_snprintf_error(buflen, ret))
3335780fb4a2SCy Schubert 		return -1;
3336780fb4a2SCy Schubert 
3337780fb4a2SCy Schubert 	pos = buf + ret;
3338780fb4a2SCy Schubert 	end = buf + buflen;
3339780fb4a2SCy Schubert 
3340780fb4a2SCy Schubert 	for (i = 0; i < 64; i++) {
3341780fb4a2SCy Schubert 		if (iface->drv_flags & (1LLU << i)) {
3342780fb4a2SCy Schubert 			ret = os_snprintf(pos, end - pos, "%s\n",
3343780fb4a2SCy Schubert 					  driver_flag_to_string(1LLU << i));
3344780fb4a2SCy Schubert 			if (os_snprintf_error(end - pos, ret))
3345780fb4a2SCy Schubert 				return -1;
3346780fb4a2SCy Schubert 			pos += ret;
3347780fb4a2SCy Schubert 		}
3348780fb4a2SCy Schubert 	}
3349780fb4a2SCy Schubert 
3350780fb4a2SCy Schubert 	return pos - buf;
3351780fb4a2SCy Schubert }
3352780fb4a2SCy Schubert 
3353780fb4a2SCy Schubert 
hostapd_ctrl_driver_flags2(struct hostapd_iface * iface,char * buf,size_t buflen)3354c1d255d3SCy Schubert static int hostapd_ctrl_driver_flags2(struct hostapd_iface *iface, char *buf,
3355c1d255d3SCy Schubert 				      size_t buflen)
3356c1d255d3SCy Schubert {
3357c1d255d3SCy Schubert 	int ret, i;
3358c1d255d3SCy Schubert 	char *pos, *end;
3359c1d255d3SCy Schubert 
3360c1d255d3SCy Schubert 	ret = os_snprintf(buf, buflen, "%016llX:\n",
3361c1d255d3SCy Schubert 			  (long long unsigned) iface->drv_flags2);
3362c1d255d3SCy Schubert 	if (os_snprintf_error(buflen, ret))
3363c1d255d3SCy Schubert 		return -1;
3364c1d255d3SCy Schubert 
3365c1d255d3SCy Schubert 	pos = buf + ret;
3366c1d255d3SCy Schubert 	end = buf + buflen;
3367c1d255d3SCy Schubert 
3368c1d255d3SCy Schubert 	for (i = 0; i < 64; i++) {
3369c1d255d3SCy Schubert 		if (iface->drv_flags2 & (1LLU << i)) {
3370c1d255d3SCy Schubert 			ret = os_snprintf(pos, end - pos, "%s\n",
3371c1d255d3SCy Schubert 					  driver_flag2_to_string(1LLU << i));
3372c1d255d3SCy Schubert 			if (os_snprintf_error(end - pos, ret))
3373c1d255d3SCy Schubert 				return -1;
3374c1d255d3SCy Schubert 			pos += ret;
3375c1d255d3SCy Schubert 		}
3376c1d255d3SCy Schubert 	}
3377c1d255d3SCy Schubert 
3378c1d255d3SCy Schubert 	return pos - buf;
3379c1d255d3SCy Schubert }
3380c1d255d3SCy Schubert 
3381c1d255d3SCy Schubert 
hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry ** acl,int * num,const char * txtaddr)338285732ac8SCy Schubert static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
338385732ac8SCy Schubert 					  const char *txtaddr)
338485732ac8SCy Schubert {
338585732ac8SCy Schubert 	u8 addr[ETH_ALEN];
338685732ac8SCy Schubert 	struct vlan_description vlan_id;
338785732ac8SCy Schubert 
338885732ac8SCy Schubert 	if (!(*num))
338985732ac8SCy Schubert 		return 0;
339085732ac8SCy Schubert 
339185732ac8SCy Schubert 	if (hwaddr_aton(txtaddr, addr))
339285732ac8SCy Schubert 		return -1;
339385732ac8SCy Schubert 
339485732ac8SCy Schubert 	if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
339585732ac8SCy Schubert 		hostapd_remove_acl_mac(acl, num, addr);
339685732ac8SCy Schubert 
339785732ac8SCy Schubert 	return 0;
339885732ac8SCy Schubert }
339985732ac8SCy Schubert 
340085732ac8SCy Schubert 
hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry ** acl,int * num)340185732ac8SCy Schubert static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
340285732ac8SCy Schubert 					      int *num)
340385732ac8SCy Schubert {
340485732ac8SCy Schubert 	while (*num)
340585732ac8SCy Schubert 		hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
340685732ac8SCy Schubert }
340785732ac8SCy Schubert 
340885732ac8SCy Schubert 
hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry * acl,int num,char * buf,size_t buflen)340985732ac8SCy Schubert static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
341085732ac8SCy Schubert 					   char *buf, size_t buflen)
341185732ac8SCy Schubert {
341285732ac8SCy Schubert 	int i = 0, len = 0, ret = 0;
341385732ac8SCy Schubert 
341485732ac8SCy Schubert 	if (!acl)
341585732ac8SCy Schubert 		return 0;
341685732ac8SCy Schubert 
341785732ac8SCy Schubert 	while (i < num) {
341885732ac8SCy Schubert 		ret = os_snprintf(buf + len, buflen - len,
341985732ac8SCy Schubert 				  MACSTR " VLAN_ID=%d\n",
342085732ac8SCy Schubert 				  MAC2STR(acl[i].addr),
342185732ac8SCy Schubert 				  acl[i].vlan_id.untagged);
342285732ac8SCy Schubert 		if (ret < 0 || (size_t) ret >= buflen - len)
342385732ac8SCy Schubert 			return len;
342485732ac8SCy Schubert 		i++;
342585732ac8SCy Schubert 		len += ret;
342685732ac8SCy Schubert 	}
342785732ac8SCy Schubert 	return len;
342885732ac8SCy Schubert }
342985732ac8SCy Schubert 
343085732ac8SCy Schubert 
hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry ** acl,int * num,const char * cmd)343185732ac8SCy Schubert static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
343285732ac8SCy Schubert 					  const char *cmd)
343385732ac8SCy Schubert {
343485732ac8SCy Schubert 	u8 addr[ETH_ALEN];
343585732ac8SCy Schubert 	struct vlan_description vlan_id;
343685732ac8SCy Schubert 	int ret = 0, vlanid = 0;
343785732ac8SCy Schubert 	const char *pos;
343885732ac8SCy Schubert 
343985732ac8SCy Schubert 	if (hwaddr_aton(cmd, addr))
344085732ac8SCy Schubert 		return -1;
344185732ac8SCy Schubert 
344285732ac8SCy Schubert 	pos = os_strstr(cmd, "VLAN_ID=");
344385732ac8SCy Schubert 	if (pos)
344485732ac8SCy Schubert 		vlanid = atoi(pos + 8);
344585732ac8SCy Schubert 
344685732ac8SCy Schubert 	if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
344785732ac8SCy Schubert 		ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
344885732ac8SCy Schubert 		if (ret != -1 && *acl)
344985732ac8SCy Schubert 			qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
345085732ac8SCy Schubert 	}
345185732ac8SCy Schubert 
345285732ac8SCy Schubert 	return ret < 0 ? -1 : 0;
345385732ac8SCy Schubert }
345485732ac8SCy Schubert 
345585732ac8SCy Schubert 
hostapd_ctrl_iface_get_capability(struct hostapd_data * hapd,const char * field,char * buf,size_t buflen)34564bc52338SCy Schubert static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
34574bc52338SCy Schubert 					     const char *field, char *buf,
34584bc52338SCy Schubert 					     size_t buflen)
34594bc52338SCy Schubert {
34604bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field);
34614bc52338SCy Schubert 
34624bc52338SCy Schubert #ifdef CONFIG_DPP
34634bc52338SCy Schubert 	if (os_strcmp(field, "dpp") == 0) {
34644bc52338SCy Schubert 		int res;
34654bc52338SCy Schubert 
346632a95656SCy Schubert #ifdef CONFIG_DPP3
346732a95656SCy Schubert 		res = os_snprintf(buf, buflen, "DPP=3");
346832a95656SCy Schubert #elif defined(CONFIG_DPP2)
34694bc52338SCy Schubert 		res = os_snprintf(buf, buflen, "DPP=2");
34704bc52338SCy Schubert #else /* CONFIG_DPP2 */
34714bc52338SCy Schubert 		res = os_snprintf(buf, buflen, "DPP=1");
34724bc52338SCy Schubert #endif /* CONFIG_DPP2 */
34734bc52338SCy Schubert 		if (os_snprintf_error(buflen, res))
34744bc52338SCy Schubert 			return -1;
34754bc52338SCy Schubert 		return res;
34764bc52338SCy Schubert 	}
34774bc52338SCy Schubert #endif /* CONFIG_DPP */
34784bc52338SCy Schubert 
34794bc52338SCy Schubert 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
34804bc52338SCy Schubert 		   field);
34814bc52338SCy Schubert 
34824bc52338SCy Schubert 	return -1;
34834bc52338SCy Schubert }
34844bc52338SCy Schubert 
34854bc52338SCy Schubert 
3486c1d255d3SCy Schubert #ifdef ANDROID
hostapd_ctrl_iface_driver_cmd(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)3487c1d255d3SCy Schubert static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
3488c1d255d3SCy Schubert 					 char *buf, size_t buflen)
3489c1d255d3SCy Schubert {
3490c1d255d3SCy Schubert 	int ret;
3491c1d255d3SCy Schubert 
3492c1d255d3SCy Schubert 	ret = hostapd_drv_driver_cmd(hapd, cmd, buf, buflen);
3493c1d255d3SCy Schubert 	if (ret == 0) {
3494c1d255d3SCy Schubert 		ret = os_snprintf(buf, buflen, "%s\n", "OK");
3495c1d255d3SCy Schubert 		if (os_snprintf_error(buflen, ret))
3496c1d255d3SCy Schubert 			ret = -1;
3497c1d255d3SCy Schubert 	}
3498c1d255d3SCy Schubert 	return ret;
3499c1d255d3SCy Schubert }
3500c1d255d3SCy Schubert #endif /* ANDROID */
3501c1d255d3SCy Schubert 
3502c1d255d3SCy Schubert 
hostapd_ctrl_iface_receive_process(struct hostapd_data * hapd,char * buf,char * reply,int reply_size,struct sockaddr_storage * from,socklen_t fromlen)3503325151a3SRui Paulo static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
3504325151a3SRui Paulo 					      char *buf, char *reply,
3505325151a3SRui Paulo 					      int reply_size,
3506780fb4a2SCy Schubert 					      struct sockaddr_storage *from,
3507325151a3SRui Paulo 					      socklen_t fromlen)
3508325151a3SRui Paulo {
3509325151a3SRui Paulo 	int reply_len, res;
3510325151a3SRui Paulo 
351139beb93cSSam Leffler 	os_memcpy(reply, "OK\n", 3);
351239beb93cSSam Leffler 	reply_len = 3;
351339beb93cSSam Leffler 
351439beb93cSSam Leffler 	if (os_strcmp(buf, "PING") == 0) {
351539beb93cSSam Leffler 		os_memcpy(reply, "PONG\n", 5);
351639beb93cSSam Leffler 		reply_len = 5;
3517f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
3518f05cddf9SRui Paulo 		if (wpa_debug_reopen_file() < 0)
3519f05cddf9SRui Paulo 			reply_len = -1;
352085732ac8SCy Schubert 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
352185732ac8SCy Schubert 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
35225b9c547cSRui Paulo 	} else if (os_strcmp(buf, "STATUS") == 0) {
35235b9c547cSRui Paulo 		reply_len = hostapd_ctrl_iface_status(hapd, reply,
35245b9c547cSRui Paulo 						      reply_size);
35255b9c547cSRui Paulo 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
35265b9c547cSRui Paulo 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
352739beb93cSSam Leffler 	} else if (os_strcmp(buf, "MIB") == 0) {
352839beb93cSSam Leffler 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
352939beb93cSSam Leffler 		if (reply_len >= 0) {
353039beb93cSSam Leffler 			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
353139beb93cSSam Leffler 					  reply_size - reply_len);
353239beb93cSSam Leffler 			if (res < 0)
353339beb93cSSam Leffler 				reply_len = -1;
353439beb93cSSam Leffler 			else
353539beb93cSSam Leffler 				reply_len += res;
353639beb93cSSam Leffler 		}
353739beb93cSSam Leffler 		if (reply_len >= 0) {
353839beb93cSSam Leffler 			res = ieee802_1x_get_mib(hapd, reply + reply_len,
353939beb93cSSam Leffler 						 reply_size - reply_len);
354039beb93cSSam Leffler 			if (res < 0)
354139beb93cSSam Leffler 				reply_len = -1;
354239beb93cSSam Leffler 			else
354339beb93cSSam Leffler 				reply_len += res;
354439beb93cSSam Leffler 		}
3545e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
354639beb93cSSam Leffler 		if (reply_len >= 0) {
354739beb93cSSam Leffler 			res = radius_client_get_mib(hapd->radius,
354839beb93cSSam Leffler 						    reply + reply_len,
354939beb93cSSam Leffler 						    reply_size - reply_len);
355039beb93cSSam Leffler 			if (res < 0)
355139beb93cSSam Leffler 				reply_len = -1;
355239beb93cSSam Leffler 			else
355339beb93cSSam Leffler 				reply_len += res;
355439beb93cSSam Leffler 		}
3555e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
35565b9c547cSRui Paulo 	} else if (os_strncmp(buf, "MIB ", 4) == 0) {
35575b9c547cSRui Paulo 		reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
35585b9c547cSRui Paulo 						   buf + 4);
355939beb93cSSam Leffler 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
356039beb93cSSam Leffler 		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
356139beb93cSSam Leffler 							 reply_size);
356239beb93cSSam Leffler 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
356339beb93cSSam Leffler 		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
356439beb93cSSam Leffler 						   reply_size);
356539beb93cSSam Leffler 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
356639beb93cSSam Leffler 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
356739beb93cSSam Leffler 							reply_size);
356839beb93cSSam Leffler 	} else if (os_strcmp(buf, "ATTACH") == 0) {
356985732ac8SCy Schubert 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
357085732ac8SCy Schubert 			reply_len = -1;
357185732ac8SCy Schubert 	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
357285732ac8SCy Schubert 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7))
357339beb93cSSam Leffler 			reply_len = -1;
357439beb93cSSam Leffler 	} else if (os_strcmp(buf, "DETACH") == 0) {
3575325151a3SRui Paulo 		if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
357639beb93cSSam Leffler 			reply_len = -1;
357739beb93cSSam Leffler 	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
3578325151a3SRui Paulo 		if (hostapd_ctrl_iface_level(hapd, from, fromlen,
357939beb93cSSam Leffler 						    buf + 6))
358039beb93cSSam Leffler 			reply_len = -1;
358139beb93cSSam Leffler 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
358239beb93cSSam Leffler 		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
358339beb93cSSam Leffler 			reply_len = -1;
3584e28a4053SRui Paulo 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
3585e28a4053SRui Paulo 		if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
3586e28a4053SRui Paulo 			reply_len = -1;
3587e28a4053SRui Paulo 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
3588e28a4053SRui Paulo 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
3589e28a4053SRui Paulo 			reply_len = -1;
3590780fb4a2SCy Schubert #ifdef CONFIG_TAXONOMY
3591780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
3592780fb4a2SCy Schubert 		reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
3593780fb4a2SCy Schubert 							 reply, reply_size);
3594780fb4a2SCy Schubert #endif /* CONFIG_TAXONOMY */
3595780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
3596780fb4a2SCy Schubert 		if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
3597780fb4a2SCy Schubert 			reply_len = -1;
35985b9c547cSRui Paulo 	} else if (os_strcmp(buf, "STOP_AP") == 0) {
35995b9c547cSRui Paulo 		if (hostapd_ctrl_iface_stop_ap(hapd))
36005b9c547cSRui Paulo 			reply_len = -1;
3601e28a4053SRui Paulo #ifdef NEED_AP_MLME
360239beb93cSSam Leffler 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
360339beb93cSSam Leffler 		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
360439beb93cSSam Leffler 			reply_len = -1;
3605e28a4053SRui Paulo #endif /* NEED_AP_MLME */
360639beb93cSSam Leffler #ifdef CONFIG_WPS
360739beb93cSSam Leffler 	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
360839beb93cSSam Leffler 		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
360939beb93cSSam Leffler 			reply_len = -1;
3610f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
3611f05cddf9SRui Paulo 		reply_len = hostapd_ctrl_iface_wps_check_pin(
3612f05cddf9SRui Paulo 			hapd, buf + 14, reply, reply_size);
361339beb93cSSam Leffler 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
3614f05cddf9SRui Paulo 		if (hostapd_wps_button_pushed(hapd, NULL))
361539beb93cSSam Leffler 			reply_len = -1;
3616f05cddf9SRui Paulo 	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
3617f05cddf9SRui Paulo 		if (hostapd_wps_cancel(hapd))
3618e28a4053SRui Paulo 			reply_len = -1;
3619e28a4053SRui Paulo 	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
3620e28a4053SRui Paulo 		reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
3621e28a4053SRui Paulo 							  reply, reply_size);
3622f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
3623f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
3624f05cddf9SRui Paulo 			reply_len = -1;
36255b9c547cSRui Paulo 	} else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
36265b9c547cSRui Paulo 		reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
36275b9c547cSRui Paulo 							      reply_size);
3628f05cddf9SRui Paulo #ifdef CONFIG_WPS_NFC
3629f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
3630f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
3631f05cddf9SRui Paulo 			reply_len = -1;
3632f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
3633f05cddf9SRui Paulo 		reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
3634f05cddf9SRui Paulo 			hapd, buf + 21, reply, reply_size);
3635f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
3636f05cddf9SRui Paulo 		reply_len = hostapd_ctrl_iface_wps_nfc_token(
3637f05cddf9SRui Paulo 			hapd, buf + 14, reply, reply_size);
36385b9c547cSRui Paulo 	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
36395b9c547cSRui Paulo 		reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
36405b9c547cSRui Paulo 			hapd, buf + 21, reply, reply_size);
36415b9c547cSRui Paulo 	} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
36425b9c547cSRui Paulo 		if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
36435b9c547cSRui Paulo 			reply_len = -1;
3644f05cddf9SRui Paulo #endif /* CONFIG_WPS_NFC */
364539beb93cSSam Leffler #endif /* CONFIG_WPS */
36465b9c547cSRui Paulo #ifdef CONFIG_INTERWORKING
36475b9c547cSRui Paulo 	} else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
36485b9c547cSRui Paulo 		if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
36495b9c547cSRui Paulo 			reply_len = -1;
36505b9c547cSRui Paulo 	} else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
36515b9c547cSRui Paulo 		if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
36525b9c547cSRui Paulo 			reply_len = -1;
36535b9c547cSRui Paulo #endif /* CONFIG_INTERWORKING */
36545b9c547cSRui Paulo #ifdef CONFIG_HS20
36555b9c547cSRui Paulo 	} else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
36565b9c547cSRui Paulo 		if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
36575b9c547cSRui Paulo 			reply_len = -1;
36585b9c547cSRui Paulo 	} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
36595b9c547cSRui Paulo 		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
36605b9c547cSRui Paulo 			reply_len = -1;
36615b9c547cSRui Paulo #endif /* CONFIG_HS20 */
366285732ac8SCy Schubert #ifdef CONFIG_WNM_AP
3663f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
3664f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
3665f05cddf9SRui Paulo 			reply_len = -1;
3666f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
3667f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
3668f05cddf9SRui Paulo 			reply_len = -1;
36695b9c547cSRui Paulo 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
36705b9c547cSRui Paulo 		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
36715b9c547cSRui Paulo 			reply_len = -1;
367285732ac8SCy Schubert 	} else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
367385732ac8SCy Schubert 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
367485732ac8SCy Schubert 			reply_len = -1;
367585732ac8SCy Schubert #endif /* CONFIG_WNM_AP */
3676f05cddf9SRui Paulo 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
3677f05cddf9SRui Paulo 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
3678f05cddf9SRui Paulo 							  reply_size);
3679f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
3680f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_set(hapd, buf + 4))
3681f05cddf9SRui Paulo 			reply_len = -1;
3682f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "GET ", 4) == 0) {
3683f05cddf9SRui Paulo 		reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
3684f05cddf9SRui Paulo 						   reply_size);
3685f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
3686f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_enable(hapd->iface))
3687f05cddf9SRui Paulo 			reply_len = -1;
36884bc52338SCy Schubert 	} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
36894bc52338SCy Schubert 		if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
36904bc52338SCy Schubert 			reply_len = -1;
3691f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
3692f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_reload(hapd->iface))
3693f05cddf9SRui Paulo 			reply_len = -1;
3694f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
3695f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_disable(hapd->iface))
3696f05cddf9SRui Paulo 			reply_len = -1;
36975b9c547cSRui Paulo 	} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
36985b9c547cSRui Paulo 		if (ieee802_11_set_beacon(hapd))
36995b9c547cSRui Paulo 			reply_len = -1;
37005b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
37015b9c547cSRui Paulo 	} else if (os_strncmp(buf, "RADAR ", 6) == 0) {
37025b9c547cSRui Paulo 		if (hostapd_ctrl_iface_radar(hapd, buf + 6))
37035b9c547cSRui Paulo 			reply_len = -1;
37045b9c547cSRui Paulo 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
37055b9c547cSRui Paulo 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
37065b9c547cSRui Paulo 			reply_len = -1;
370785732ac8SCy Schubert 	} else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
370885732ac8SCy Schubert 		if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
370985732ac8SCy Schubert 							      buf + 23) < 0)
371085732ac8SCy Schubert 			reply_len = -1;
371185732ac8SCy Schubert 	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
371285732ac8SCy Schubert 		if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
371385732ac8SCy Schubert 			reply_len = -1;
37145b9c547cSRui Paulo 	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
37155b9c547cSRui Paulo 		if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
37165b9c547cSRui Paulo 			reply_len = -1;
3717c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "EAPOL_TX ", 9) == 0) {
3718c1d255d3SCy Schubert 		if (hostapd_ctrl_iface_eapol_tx(hapd, buf + 9) < 0)
3719c1d255d3SCy Schubert 			reply_len = -1;
37205b9c547cSRui Paulo 	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
37215b9c547cSRui Paulo 		if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
37225b9c547cSRui Paulo 			reply_len = -1;
37235b9c547cSRui Paulo 	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
37245b9c547cSRui Paulo 		if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
37255b9c547cSRui Paulo 			reply_len = -1;
37265b9c547cSRui Paulo 	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
37275b9c547cSRui Paulo 		if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
37285b9c547cSRui Paulo 			reply_len = -1;
37295b9c547cSRui Paulo 	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
37305b9c547cSRui Paulo 		if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
37315b9c547cSRui Paulo 			reply_len = -1;
37325b9c547cSRui Paulo 	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
37335b9c547cSRui Paulo 		reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
37345b9c547cSRui Paulo 							reply_size);
3735325151a3SRui Paulo 	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
3736325151a3SRui Paulo 		if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
3737325151a3SRui Paulo 			reply_len = -1;
3738325151a3SRui Paulo 	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
3739325151a3SRui Paulo 		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
374085732ac8SCy Schubert 	} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
374185732ac8SCy Schubert 		if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
374285732ac8SCy Schubert 			reply_len = -1;
374385732ac8SCy Schubert 	} else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
374485732ac8SCy Schubert 		if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
374585732ac8SCy Schubert 			reply_len = -1;
374685732ac8SCy Schubert 	} else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
374785732ac8SCy Schubert 		if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
374885732ac8SCy Schubert 			reply_len = -1;
374985732ac8SCy Schubert 	} else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
375085732ac8SCy Schubert 		if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
375185732ac8SCy Schubert 			reply_len = -1;
375285732ac8SCy Schubert 	} else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
375385732ac8SCy Schubert 		if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
375485732ac8SCy Schubert 			reply_len = -1;
3755c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "REKEY_PTK ", 10) == 0) {
3756c1d255d3SCy Schubert 		if (hostapd_ctrl_rekey_ptk(hapd, buf + 10) < 0)
3757c1d255d3SCy Schubert 			reply_len = -1;
375885732ac8SCy Schubert 	} else if (os_strcmp(buf, "REKEY_GTK") == 0) {
375985732ac8SCy Schubert 		if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
376085732ac8SCy Schubert 			reply_len = -1;
3761c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "GET_PMK ", 8) == 0) {
3762c1d255d3SCy Schubert 		reply_len = hostapd_ctrl_get_pmk(hapd, buf + 8, reply,
3763c1d255d3SCy Schubert 						 reply_size);
3764c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) {
3765c1d255d3SCy Schubert 		if (hostapd_ctrl_register_frame(hapd, buf + 16) < 0)
3766c1d255d3SCy Schubert 			reply_len = -1;
37675b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
37685b9c547cSRui Paulo 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
37695b9c547cSRui Paulo 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
37705b9c547cSRui Paulo 			reply_len = -1;
37715b9c547cSRui Paulo 	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
37725b9c547cSRui Paulo 		reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
37735b9c547cSRui Paulo 						      reply_size);
37745b9c547cSRui Paulo 	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
37755b9c547cSRui Paulo 		ieee802_1x_erp_flush(hapd);
37765b9c547cSRui Paulo #ifdef RADIUS_SERVER
37775b9c547cSRui Paulo 		radius_server_erp_flush(hapd->radius_srv);
37785b9c547cSRui Paulo #endif /* RADIUS_SERVER */
3779325151a3SRui Paulo 	} else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
3780325151a3SRui Paulo 		if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
3781325151a3SRui Paulo 			reply_len = -1;
3782325151a3SRui Paulo 	} else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
3783325151a3SRui Paulo 		if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
3784325151a3SRui Paulo 			reply_len = -1;
3785325151a3SRui Paulo 	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
3786325151a3SRui Paulo 		reply_len = hostapd_ctrl_iface_log_level(
3787325151a3SRui Paulo 			hapd, buf + 9, reply, reply_size);
3788325151a3SRui Paulo #ifdef NEED_AP_MLME
3789325151a3SRui Paulo 	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
3790325151a3SRui Paulo 		reply_len = hostapd_ctrl_iface_track_sta_list(
3791325151a3SRui Paulo 			hapd, reply, reply_size);
3792325151a3SRui Paulo #endif /* NEED_AP_MLME */
3793780fb4a2SCy Schubert 	} else if (os_strcmp(buf, "PMKSA") == 0) {
3794780fb4a2SCy Schubert 		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
3795780fb4a2SCy Schubert 							  reply_size);
3796780fb4a2SCy Schubert 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
3797780fb4a2SCy Schubert 		hostapd_ctrl_iface_pmksa_flush(hapd);
379885732ac8SCy Schubert 	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
379985732ac8SCy Schubert 		if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
380085732ac8SCy Schubert 			reply_len = -1;
3801780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
3802780fb4a2SCy Schubert 		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
3803780fb4a2SCy Schubert 			reply_len = -1;
3804c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "SHOW_NEIGHBOR") == 0) {
3805c1d255d3SCy Schubert 		reply_len = hostapd_ctrl_iface_show_neighbor(hapd, reply,
3806c1d255d3SCy Schubert 							     reply_size);
3807780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
3808780fb4a2SCy Schubert 		if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
3809780fb4a2SCy Schubert 			reply_len = -1;
3810780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
3811780fb4a2SCy Schubert 		if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
3812780fb4a2SCy Schubert 			reply_len = -1;
3813780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
3814780fb4a2SCy Schubert 		if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
3815780fb4a2SCy Schubert 			reply_len = -1;
381685732ac8SCy Schubert 	} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
381785732ac8SCy Schubert 		reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
381885732ac8SCy Schubert 							  reply, reply_size);
3819780fb4a2SCy Schubert 	} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
3820780fb4a2SCy Schubert 		reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
3821780fb4a2SCy Schubert 						      reply_size);
3822c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "DRIVER_FLAGS2") == 0) {
3823c1d255d3SCy Schubert 		reply_len = hostapd_ctrl_driver_flags2(hapd->iface, reply,
3824c1d255d3SCy Schubert 						       reply_size);
382585732ac8SCy Schubert 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
382685732ac8SCy Schubert 		eloop_terminate();
382785732ac8SCy Schubert 	} else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
382885732ac8SCy Schubert 		if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
3829c1d255d3SCy Schubert 			if (hostapd_ctrl_iface_acl_add_mac(
3830c1d255d3SCy Schubert 				    &hapd->conf->accept_mac,
3831c1d255d3SCy Schubert 				    &hapd->conf->num_accept_mac, buf + 19))
3832c1d255d3SCy Schubert 				reply_len = -1;
3833c1d255d3SCy Schubert 		} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
3834c1d255d3SCy Schubert 			if (!hostapd_ctrl_iface_acl_del_mac(
383585732ac8SCy Schubert 				    &hapd->conf->accept_mac,
383685732ac8SCy Schubert 				    &hapd->conf->num_accept_mac, buf + 19))
383785732ac8SCy Schubert 				hostapd_disassoc_accept_mac(hapd);
383885732ac8SCy Schubert 			else
383985732ac8SCy Schubert 				reply_len = -1;
384085732ac8SCy Schubert 		} else if (os_strcmp(buf + 11, "SHOW") == 0) {
384185732ac8SCy Schubert 			reply_len = hostapd_ctrl_iface_acl_show_mac(
384285732ac8SCy Schubert 				hapd->conf->accept_mac,
384385732ac8SCy Schubert 				hapd->conf->num_accept_mac, reply, reply_size);
384485732ac8SCy Schubert 		} else if (os_strcmp(buf + 11, "CLEAR") == 0) {
384585732ac8SCy Schubert 			hostapd_ctrl_iface_acl_clear_list(
384685732ac8SCy Schubert 				&hapd->conf->accept_mac,
384785732ac8SCy Schubert 				&hapd->conf->num_accept_mac);
3848c1d255d3SCy Schubert 			hostapd_disassoc_accept_mac(hapd);
384985732ac8SCy Schubert 		}
385085732ac8SCy Schubert 	} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
385185732ac8SCy Schubert 		if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
385285732ac8SCy Schubert 			if (!hostapd_ctrl_iface_acl_add_mac(
385385732ac8SCy Schubert 				    &hapd->conf->deny_mac,
385485732ac8SCy Schubert 				    &hapd->conf->num_deny_mac, buf + 17))
385585732ac8SCy Schubert 				hostapd_disassoc_deny_mac(hapd);
3856c1d255d3SCy Schubert 			else
3857c1d255d3SCy Schubert 				reply_len = -1;
385885732ac8SCy Schubert 		} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
3859c1d255d3SCy Schubert 			if (hostapd_ctrl_iface_acl_del_mac(
386085732ac8SCy Schubert 				    &hapd->conf->deny_mac,
3861c1d255d3SCy Schubert 				    &hapd->conf->num_deny_mac, buf + 17))
3862c1d255d3SCy Schubert 				reply_len = -1;
386385732ac8SCy Schubert 		} else if (os_strcmp(buf + 9, "SHOW") == 0) {
386485732ac8SCy Schubert 			reply_len = hostapd_ctrl_iface_acl_show_mac(
386585732ac8SCy Schubert 				hapd->conf->deny_mac,
386685732ac8SCy Schubert 				hapd->conf->num_deny_mac, reply, reply_size);
386785732ac8SCy Schubert 		} else if (os_strcmp(buf + 9, "CLEAR") == 0) {
386885732ac8SCy Schubert 			hostapd_ctrl_iface_acl_clear_list(
386985732ac8SCy Schubert 				&hapd->conf->deny_mac,
387085732ac8SCy Schubert 				&hapd->conf->num_deny_mac);
387185732ac8SCy Schubert 		}
387285732ac8SCy Schubert #ifdef CONFIG_DPP
387385732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
387485732ac8SCy Schubert 		res = hostapd_dpp_qr_code(hapd, buf + 12);
387585732ac8SCy Schubert 		if (res < 0) {
387685732ac8SCy Schubert 			reply_len = -1;
387785732ac8SCy Schubert 		} else {
387885732ac8SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
387985732ac8SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
388085732ac8SCy Schubert 				reply_len = -1;
388185732ac8SCy Schubert 		}
3882c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) {
3883c1d255d3SCy Schubert 		res = hostapd_dpp_nfc_uri(hapd, buf + 12);
3884c1d255d3SCy Schubert 		if (res < 0) {
3885c1d255d3SCy Schubert 			reply_len = -1;
3886c1d255d3SCy Schubert 		} else {
3887c1d255d3SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3888c1d255d3SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
3889c1d255d3SCy Schubert 				reply_len = -1;
3890c1d255d3SCy Schubert 		}
3891c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_NFC_HANDOVER_REQ ", 21) == 0) {
3892c1d255d3SCy Schubert 		res = hostapd_dpp_nfc_handover_req(hapd, buf + 20);
3893c1d255d3SCy Schubert 		if (res < 0) {
3894c1d255d3SCy Schubert 			reply_len = -1;
3895c1d255d3SCy Schubert 		} else {
3896c1d255d3SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3897c1d255d3SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
3898c1d255d3SCy Schubert 				reply_len = -1;
3899c1d255d3SCy Schubert 		}
3900c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_NFC_HANDOVER_SEL ", 21) == 0) {
3901c1d255d3SCy Schubert 		res = hostapd_dpp_nfc_handover_sel(hapd, buf + 20);
3902c1d255d3SCy Schubert 		if (res < 0) {
3903c1d255d3SCy Schubert 			reply_len = -1;
3904c1d255d3SCy Schubert 		} else {
3905c1d255d3SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3906c1d255d3SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
3907c1d255d3SCy Schubert 				reply_len = -1;
3908c1d255d3SCy Schubert 		}
390985732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
39104bc52338SCy Schubert 		res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18);
391185732ac8SCy Schubert 		if (res < 0) {
391285732ac8SCy Schubert 			reply_len = -1;
391385732ac8SCy Schubert 		} else {
391485732ac8SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
391585732ac8SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
391685732ac8SCy Schubert 				reply_len = -1;
391785732ac8SCy Schubert 		}
391885732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
39194bc52338SCy Schubert 		if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp,
39204bc52338SCy Schubert 					 buf + 21) < 0)
392185732ac8SCy Schubert 			reply_len = -1;
392285732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
392385732ac8SCy Schubert 		const char *uri;
392485732ac8SCy Schubert 
39254bc52338SCy Schubert 		uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp,
39264bc52338SCy Schubert 					    atoi(buf + 22));
392785732ac8SCy Schubert 		if (!uri) {
392885732ac8SCy Schubert 			reply_len = -1;
392985732ac8SCy Schubert 		} else {
393085732ac8SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%s", uri);
393185732ac8SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
393285732ac8SCy Schubert 				reply_len = -1;
393385732ac8SCy Schubert 		}
393485732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
39354bc52338SCy Schubert 		reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp,
39364bc52338SCy Schubert 					       atoi(buf + 19),
393785732ac8SCy Schubert 			reply, reply_size);
3938c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_SET ", 18) == 0) {
3939c1d255d3SCy Schubert 		if (dpp_bootstrap_set(hapd->iface->interfaces->dpp,
3940c1d255d3SCy Schubert 				      atoi(buf + 18),
3941c1d255d3SCy Schubert 				      os_strchr(buf + 18, ' ')) < 0)
3942c1d255d3SCy Schubert 			reply_len = -1;
394385732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
394485732ac8SCy Schubert 		if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
394585732ac8SCy Schubert 			reply_len = -1;
394685732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
394785732ac8SCy Schubert 		if (hostapd_dpp_listen(hapd, buf + 11) < 0)
394885732ac8SCy Schubert 			reply_len = -1;
394985732ac8SCy Schubert 	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
395085732ac8SCy Schubert 		hostapd_dpp_stop(hapd);
395185732ac8SCy Schubert 		hostapd_dpp_listen_stop(hapd);
395285732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
39534bc52338SCy Schubert 		res = dpp_configurator_add(hapd->iface->interfaces->dpp,
39544bc52338SCy Schubert 					   buf + 20);
395585732ac8SCy Schubert 		if (res < 0) {
395685732ac8SCy Schubert 			reply_len = -1;
395785732ac8SCy Schubert 		} else {
395885732ac8SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
395985732ac8SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
396085732ac8SCy Schubert 				reply_len = -1;
396185732ac8SCy Schubert 		}
396285732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
39634bc52338SCy Schubert 		if (dpp_configurator_remove(hapd->iface->interfaces->dpp,
39644bc52338SCy Schubert 					    buf + 24) < 0)
396585732ac8SCy Schubert 			reply_len = -1;
396685732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
39674bc52338SCy Schubert 		if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0)
396885732ac8SCy Schubert 			reply_len = -1;
396985732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
39704bc52338SCy Schubert 		reply_len = dpp_configurator_get_key_id(
39714bc52338SCy Schubert 			hapd->iface->interfaces->dpp,
397285732ac8SCy Schubert 			atoi(buf + 25),
397385732ac8SCy Schubert 			reply, reply_size);
397485732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
397585732ac8SCy Schubert 		res = hostapd_dpp_pkex_add(hapd, buf + 12);
397685732ac8SCy Schubert 		if (res < 0) {
397785732ac8SCy Schubert 			reply_len = -1;
397885732ac8SCy Schubert 		} else {
397985732ac8SCy Schubert 			reply_len = os_snprintf(reply, reply_size, "%d", res);
398085732ac8SCy Schubert 			if (os_snprintf_error(reply_size, reply_len))
398185732ac8SCy Schubert 				reply_len = -1;
398285732ac8SCy Schubert 		}
398385732ac8SCy Schubert 	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
398485732ac8SCy Schubert 		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
398585732ac8SCy Schubert 			reply_len = -1;
3986c1d255d3SCy Schubert #ifdef CONFIG_DPP2
3987c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
3988c1d255d3SCy Schubert 		if (hostapd_dpp_controller_start(hapd, buf + 20) < 0)
3989c1d255d3SCy Schubert 			reply_len = -1;
3990c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
3991c1d255d3SCy Schubert 		if (hostapd_dpp_controller_start(hapd, NULL) < 0)
3992c1d255d3SCy Schubert 			reply_len = -1;
3993c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
3994c1d255d3SCy Schubert 		dpp_controller_stop(hapd->iface->interfaces->dpp);
3995c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
3996c1d255d3SCy Schubert 		if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
3997c1d255d3SCy Schubert 			reply_len = -1;
3998c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
3999c1d255d3SCy Schubert 		hostapd_dpp_chirp_stop(hapd);
4000c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */
400185732ac8SCy Schubert #endif /* CONFIG_DPP */
400285732ac8SCy Schubert #ifdef RADIUS_SERVER
400385732ac8SCy Schubert 	} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
400485732ac8SCy Schubert 		if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
400585732ac8SCy Schubert 			reply_len = -1;
400685732ac8SCy Schubert #endif /* RADIUS_SERVER */
40074bc52338SCy Schubert 	} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
40084bc52338SCy Schubert 		reply_len = hostapd_ctrl_iface_get_capability(
40094bc52338SCy Schubert 			hapd, buf + 15, reply, reply_size);
4010c1d255d3SCy Schubert #ifdef CONFIG_PASN
4011c1d255d3SCy Schubert 	} else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) {
4012c1d255d3SCy Schubert 		reply_len = ptksa_cache_list(hapd->ptksa, reply, reply_size);
4013c1d255d3SCy Schubert #endif /* CONFIG_PASN */
4014c1d255d3SCy Schubert #ifdef ANDROID
4015c1d255d3SCy Schubert 	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
4016c1d255d3SCy Schubert 		reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
4017c1d255d3SCy Schubert 							  reply_size);
4018c1d255d3SCy Schubert #endif /* ANDROID */
401939beb93cSSam Leffler 	} else {
402039beb93cSSam Leffler 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
402139beb93cSSam Leffler 		reply_len = 16;
402239beb93cSSam Leffler 	}
402339beb93cSSam Leffler 
402439beb93cSSam Leffler 	if (reply_len < 0) {
402539beb93cSSam Leffler 		os_memcpy(reply, "FAIL\n", 5);
402639beb93cSSam Leffler 		reply_len = 5;
402739beb93cSSam Leffler 	}
4028325151a3SRui Paulo 
4029325151a3SRui Paulo 	return reply_len;
4030325151a3SRui Paulo }
4031325151a3SRui Paulo 
4032325151a3SRui Paulo 
hostapd_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)4033325151a3SRui Paulo static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
4034325151a3SRui Paulo 				       void *sock_ctx)
4035325151a3SRui Paulo {
4036325151a3SRui Paulo 	struct hostapd_data *hapd = eloop_ctx;
4037325151a3SRui Paulo 	char buf[4096];
4038325151a3SRui Paulo 	int res;
4039780fb4a2SCy Schubert 	struct sockaddr_storage from;
4040325151a3SRui Paulo 	socklen_t fromlen = sizeof(from);
4041780fb4a2SCy Schubert 	char *reply, *pos = buf;
4042325151a3SRui Paulo 	const int reply_size = 4096;
4043325151a3SRui Paulo 	int reply_len;
4044325151a3SRui Paulo 	int level = MSG_DEBUG;
4045780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4046c1d255d3SCy Schubert 	unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
4047780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
4048325151a3SRui Paulo 
4049325151a3SRui Paulo 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
4050325151a3SRui Paulo 		       (struct sockaddr *) &from, &fromlen);
4051325151a3SRui Paulo 	if (res < 0) {
4052325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
4053325151a3SRui Paulo 			   strerror(errno));
4054325151a3SRui Paulo 		return;
4055325151a3SRui Paulo 	}
4056325151a3SRui Paulo 	buf[res] = '\0';
4057325151a3SRui Paulo 
4058325151a3SRui Paulo 	reply = os_malloc(reply_size);
4059325151a3SRui Paulo 	if (reply == NULL) {
4060325151a3SRui Paulo 		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
4061325151a3SRui Paulo 			   fromlen) < 0) {
4062325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
4063325151a3SRui Paulo 				   strerror(errno));
4064325151a3SRui Paulo 		}
4065325151a3SRui Paulo 		return;
4066325151a3SRui Paulo 	}
4067325151a3SRui Paulo 
4068780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4069780fb4a2SCy Schubert 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
4070780fb4a2SCy Schubert 		os_memcpy(reply, "COOKIE=", 7);
4071c1d255d3SCy Schubert 		wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
4072c1d255d3SCy Schubert 				 hapd->ctrl_iface_cookie,
4073c1d255d3SCy Schubert 				 CTRL_IFACE_COOKIE_LEN);
4074c1d255d3SCy Schubert 		reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
4075780fb4a2SCy Schubert 		goto done;
4076780fb4a2SCy Schubert 	}
4077780fb4a2SCy Schubert 
4078780fb4a2SCy Schubert 	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
4079c1d255d3SCy Schubert 	    hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
4080780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
4081780fb4a2SCy Schubert 			   "CTRL: No cookie in the request - drop request");
4082780fb4a2SCy Schubert 		os_free(reply);
4083780fb4a2SCy Schubert 		return;
4084780fb4a2SCy Schubert 	}
4085780fb4a2SCy Schubert 
4086c1d255d3SCy Schubert 	if (os_memcmp(hapd->ctrl_iface_cookie, lcookie,
4087c1d255d3SCy Schubert 		      CTRL_IFACE_COOKIE_LEN) != 0) {
4088780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
4089780fb4a2SCy Schubert 			   "CTRL: Invalid cookie in the request - drop request");
4090780fb4a2SCy Schubert 		os_free(reply);
4091780fb4a2SCy Schubert 		return;
4092780fb4a2SCy Schubert 	}
4093780fb4a2SCy Schubert 
4094c1d255d3SCy Schubert 	pos = buf + 7 + 2 * CTRL_IFACE_COOKIE_LEN;
4095780fb4a2SCy Schubert 	while (*pos == ' ')
4096780fb4a2SCy Schubert 		pos++;
4097780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
4098780fb4a2SCy Schubert 
4099780fb4a2SCy Schubert 	if (os_strcmp(pos, "PING") == 0)
4100780fb4a2SCy Schubert 		level = MSG_EXCESSIVE;
4101780fb4a2SCy Schubert 	wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res);
4102780fb4a2SCy Schubert 
4103780fb4a2SCy Schubert 	reply_len = hostapd_ctrl_iface_receive_process(hapd, pos,
4104325151a3SRui Paulo 						       reply, reply_size,
4105325151a3SRui Paulo 						       &from, fromlen);
4106325151a3SRui Paulo 
4107780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4108780fb4a2SCy Schubert done:
4109780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
41105b9c547cSRui Paulo 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
41115b9c547cSRui Paulo 		   fromlen) < 0) {
41125b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
41135b9c547cSRui Paulo 			   strerror(errno));
41145b9c547cSRui Paulo 	}
411539beb93cSSam Leffler 	os_free(reply);
411639beb93cSSam Leffler }
411739beb93cSSam Leffler 
411839beb93cSSam Leffler 
4119780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
hostapd_ctrl_iface_path(struct hostapd_data * hapd)412039beb93cSSam Leffler static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
412139beb93cSSam Leffler {
412239beb93cSSam Leffler 	char *buf;
412339beb93cSSam Leffler 	size_t len;
412439beb93cSSam Leffler 
412539beb93cSSam Leffler 	if (hapd->conf->ctrl_interface == NULL)
412639beb93cSSam Leffler 		return NULL;
412739beb93cSSam Leffler 
412839beb93cSSam Leffler 	len = os_strlen(hapd->conf->ctrl_interface) +
412939beb93cSSam Leffler 		os_strlen(hapd->conf->iface) + 2;
413039beb93cSSam Leffler 	buf = os_malloc(len);
413139beb93cSSam Leffler 	if (buf == NULL)
413239beb93cSSam Leffler 		return NULL;
413339beb93cSSam Leffler 
413439beb93cSSam Leffler 	os_snprintf(buf, len, "%s/%s",
413539beb93cSSam Leffler 		    hapd->conf->ctrl_interface, hapd->conf->iface);
413639beb93cSSam Leffler 	buf[len - 1] = '\0';
413739beb93cSSam Leffler 	return buf;
413839beb93cSSam Leffler }
4139780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
414039beb93cSSam Leffler 
414139beb93cSSam Leffler 
hostapd_ctrl_iface_msg_cb(void * ctx,int level,enum wpa_msg_type type,const char * txt,size_t len)4142325151a3SRui Paulo static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
4143325151a3SRui Paulo 				      enum wpa_msg_type type,
414439beb93cSSam Leffler 				      const char *txt, size_t len)
414539beb93cSSam Leffler {
414639beb93cSSam Leffler 	struct hostapd_data *hapd = ctx;
414739beb93cSSam Leffler 	if (hapd == NULL)
414839beb93cSSam Leffler 		return;
4149325151a3SRui Paulo 	hostapd_ctrl_iface_send(hapd, level, type, txt, len);
415039beb93cSSam Leffler }
415139beb93cSSam Leffler 
415239beb93cSSam Leffler 
hostapd_ctrl_iface_init(struct hostapd_data * hapd)415339beb93cSSam Leffler int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
415439beb93cSSam Leffler {
4155780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4156780fb4a2SCy Schubert 	int port = HOSTAPD_CTRL_IFACE_PORT;
4157780fb4a2SCy Schubert 	char p[32] = { 0 };
4158780fb4a2SCy Schubert 	char port_str[40], *tmp;
4159780fb4a2SCy Schubert 	char *pos;
4160780fb4a2SCy Schubert 	struct addrinfo hints = { 0 }, *res, *saveres;
4161780fb4a2SCy Schubert 	int n;
4162780fb4a2SCy Schubert 
4163780fb4a2SCy Schubert 	if (hapd->ctrl_sock > -1) {
4164780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
4165780fb4a2SCy Schubert 		return 0;
4166780fb4a2SCy Schubert 	}
4167780fb4a2SCy Schubert 
4168780fb4a2SCy Schubert 	if (hapd->conf->ctrl_interface == NULL)
4169780fb4a2SCy Schubert 		return 0;
4170780fb4a2SCy Schubert 
4171780fb4a2SCy Schubert 	pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
4172780fb4a2SCy Schubert 	if (pos) {
4173780fb4a2SCy Schubert 		pos += 4;
4174780fb4a2SCy Schubert 		port = atoi(pos);
4175780fb4a2SCy Schubert 		if (port <= 0) {
4176780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
4177780fb4a2SCy Schubert 			goto fail;
4178780fb4a2SCy Schubert 		}
4179780fb4a2SCy Schubert 	}
4180780fb4a2SCy Schubert 
4181780fb4a2SCy Schubert 	dl_list_init(&hapd->ctrl_dst);
4182780fb4a2SCy Schubert 	hapd->ctrl_sock = -1;
4183c1d255d3SCy Schubert 	os_get_random(hapd->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
4184780fb4a2SCy Schubert 
4185780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
4186780fb4a2SCy Schubert 	hints.ai_flags = AI_PASSIVE;
4187780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4188780fb4a2SCy Schubert 
4189780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4190780fb4a2SCy Schubert 	hints.ai_family = AF_INET6;
4191780fb4a2SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4192780fb4a2SCy Schubert 	hints.ai_family = AF_INET;
4193780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4194780fb4a2SCy Schubert 	hints.ai_socktype = SOCK_DGRAM;
4195780fb4a2SCy Schubert 
4196780fb4a2SCy Schubert try_again:
4197780fb4a2SCy Schubert 	os_snprintf(p, sizeof(p), "%d", port);
4198780fb4a2SCy Schubert 	n = getaddrinfo(NULL, p, &hints, &res);
4199780fb4a2SCy Schubert 	if (n) {
4200780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
4201780fb4a2SCy Schubert 		goto fail;
4202780fb4a2SCy Schubert 	}
4203780fb4a2SCy Schubert 
4204780fb4a2SCy Schubert 	saveres = res;
4205780fb4a2SCy Schubert 	hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype,
4206780fb4a2SCy Schubert 				 res->ai_protocol);
4207780fb4a2SCy Schubert 	if (hapd->ctrl_sock < 0) {
4208780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
4209780fb4a2SCy Schubert 		goto fail;
4210780fb4a2SCy Schubert 	}
4211780fb4a2SCy Schubert 
4212780fb4a2SCy Schubert 	if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
4213780fb4a2SCy Schubert 		port--;
4214780fb4a2SCy Schubert 		if ((HOSTAPD_CTRL_IFACE_PORT - port) <
4215780fb4a2SCy Schubert 		    HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
4216780fb4a2SCy Schubert 			goto try_again;
4217780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
4218780fb4a2SCy Schubert 		goto fail;
4219780fb4a2SCy Schubert 	}
4220780fb4a2SCy Schubert 
4221780fb4a2SCy Schubert 	freeaddrinfo(saveres);
4222780fb4a2SCy Schubert 
4223780fb4a2SCy Schubert 	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
4224780fb4a2SCy Schubert 	tmp = os_strdup(port_str);
4225780fb4a2SCy Schubert 	if (tmp) {
4226780fb4a2SCy Schubert 		os_free(hapd->conf->ctrl_interface);
4227780fb4a2SCy Schubert 		hapd->conf->ctrl_interface = tmp;
4228780fb4a2SCy Schubert 	}
4229780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
4230780fb4a2SCy Schubert 
4231780fb4a2SCy Schubert 	if (eloop_register_read_sock(hapd->ctrl_sock,
4232780fb4a2SCy Schubert 				     hostapd_ctrl_iface_receive, hapd, NULL) <
4233780fb4a2SCy Schubert 	    0) {
4234780fb4a2SCy Schubert 		hostapd_ctrl_iface_deinit(hapd);
4235780fb4a2SCy Schubert 		return -1;
4236780fb4a2SCy Schubert 	}
4237780fb4a2SCy Schubert 
4238780fb4a2SCy Schubert 	hapd->msg_ctx = hapd;
4239780fb4a2SCy Schubert 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
4240780fb4a2SCy Schubert 
4241780fb4a2SCy Schubert 	return 0;
4242780fb4a2SCy Schubert 
4243780fb4a2SCy Schubert fail:
4244780fb4a2SCy Schubert 	if (hapd->ctrl_sock >= 0)
4245780fb4a2SCy Schubert 		close(hapd->ctrl_sock);
4246780fb4a2SCy Schubert 	return -1;
4247780fb4a2SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP */
424839beb93cSSam Leffler 	struct sockaddr_un addr;
424939beb93cSSam Leffler 	int s = -1;
425039beb93cSSam Leffler 	char *fname = NULL;
425139beb93cSSam Leffler 
4252f05cddf9SRui Paulo 	if (hapd->ctrl_sock > -1) {
4253f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
4254f05cddf9SRui Paulo 		return 0;
4255f05cddf9SRui Paulo 	}
425639beb93cSSam Leffler 
4257780fb4a2SCy Schubert 	dl_list_init(&hapd->ctrl_dst);
4258780fb4a2SCy Schubert 
425939beb93cSSam Leffler 	if (hapd->conf->ctrl_interface == NULL)
426039beb93cSSam Leffler 		return 0;
426139beb93cSSam Leffler 
426239beb93cSSam Leffler 	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
426339beb93cSSam Leffler 		if (errno == EEXIST) {
426439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "Using existing control "
426539beb93cSSam Leffler 				   "interface directory.");
426639beb93cSSam Leffler 		} else {
42675b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
42685b9c547cSRui Paulo 				   strerror(errno));
426939beb93cSSam Leffler 			goto fail;
427039beb93cSSam Leffler 		}
427139beb93cSSam Leffler 	}
427239beb93cSSam Leffler 
427339beb93cSSam Leffler 	if (hapd->conf->ctrl_interface_gid_set &&
42744bc52338SCy Schubert 	    lchown(hapd->conf->ctrl_interface, -1,
427539beb93cSSam Leffler 		   hapd->conf->ctrl_interface_gid) < 0) {
42764bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
42775b9c547cSRui Paulo 			   strerror(errno));
42785b9c547cSRui Paulo 		return -1;
42795b9c547cSRui Paulo 	}
42805b9c547cSRui Paulo 
42815b9c547cSRui Paulo 	if (!hapd->conf->ctrl_interface_gid_set &&
42825b9c547cSRui Paulo 	    hapd->iface->interfaces->ctrl_iface_group &&
42834bc52338SCy Schubert 	    lchown(hapd->conf->ctrl_interface, -1,
42845b9c547cSRui Paulo 		   hapd->iface->interfaces->ctrl_iface_group) < 0) {
42854bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
42865b9c547cSRui Paulo 			   strerror(errno));
428739beb93cSSam Leffler 		return -1;
428839beb93cSSam Leffler 	}
428939beb93cSSam Leffler 
4290f05cddf9SRui Paulo #ifdef ANDROID
4291f05cddf9SRui Paulo 	/*
4292f05cddf9SRui Paulo 	 * Android is using umask 0077 which would leave the control interface
4293f05cddf9SRui Paulo 	 * directory without group access. This breaks things since Wi-Fi
4294f05cddf9SRui Paulo 	 * framework assumes that this directory can be accessed by other
4295f05cddf9SRui Paulo 	 * applications in the wifi group. Fix this by adding group access even
4296f05cddf9SRui Paulo 	 * if umask value would prevent this.
4297f05cddf9SRui Paulo 	 */
4298f05cddf9SRui Paulo 	if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
4299f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
4300f05cddf9SRui Paulo 			   strerror(errno));
4301f05cddf9SRui Paulo 		/* Try to continue anyway */
4302f05cddf9SRui Paulo 	}
4303f05cddf9SRui Paulo #endif /* ANDROID */
4304f05cddf9SRui Paulo 
430539beb93cSSam Leffler 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
430639beb93cSSam Leffler 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
430739beb93cSSam Leffler 		goto fail;
430839beb93cSSam Leffler 
430939beb93cSSam Leffler 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
431039beb93cSSam Leffler 	if (s < 0) {
43115b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
431239beb93cSSam Leffler 		goto fail;
431339beb93cSSam Leffler 	}
431439beb93cSSam Leffler 
431539beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
43163157ba21SRui Paulo #ifdef __FreeBSD__
43173157ba21SRui Paulo 	addr.sun_len = sizeof(addr);
43183157ba21SRui Paulo #endif /* __FreeBSD__ */
431939beb93cSSam Leffler 	addr.sun_family = AF_UNIX;
432039beb93cSSam Leffler 	fname = hostapd_ctrl_iface_path(hapd);
432139beb93cSSam Leffler 	if (fname == NULL)
432239beb93cSSam Leffler 		goto fail;
432339beb93cSSam Leffler 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
432439beb93cSSam Leffler 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
43253157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
43263157ba21SRui Paulo 			   strerror(errno));
43273157ba21SRui Paulo 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
43283157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
43293157ba21SRui Paulo 				   " allow connections - assuming it was left"
43303157ba21SRui Paulo 				   "over from forced program termination");
43313157ba21SRui Paulo 			if (unlink(fname) < 0) {
43325b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
43335b9c547cSRui Paulo 					   "Could not unlink existing ctrl_iface socket '%s': %s",
43345b9c547cSRui Paulo 					   fname, strerror(errno));
43353157ba21SRui Paulo 				goto fail;
43363157ba21SRui Paulo 			}
43373157ba21SRui Paulo 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
43383157ba21SRui Paulo 			    0) {
43395b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
43405b9c547cSRui Paulo 					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
43415b9c547cSRui Paulo 					   strerror(errno));
434239beb93cSSam Leffler 				goto fail;
434339beb93cSSam Leffler 			}
43443157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
43453157ba21SRui Paulo 				   "ctrl_iface socket '%s'", fname);
43463157ba21SRui Paulo 		} else {
43473157ba21SRui Paulo 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
43483157ba21SRui Paulo 				   "be in use - cannot override it");
43493157ba21SRui Paulo 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
43503157ba21SRui Paulo 				   "not used anymore", fname);
43513157ba21SRui Paulo 			os_free(fname);
43523157ba21SRui Paulo 			fname = NULL;
43533157ba21SRui Paulo 			goto fail;
43543157ba21SRui Paulo 		}
43553157ba21SRui Paulo 	}
435639beb93cSSam Leffler 
435739beb93cSSam Leffler 	if (hapd->conf->ctrl_interface_gid_set &&
43584bc52338SCy Schubert 	    lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
43594bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
43605b9c547cSRui Paulo 			   strerror(errno));
43615b9c547cSRui Paulo 		goto fail;
43625b9c547cSRui Paulo 	}
43635b9c547cSRui Paulo 
43645b9c547cSRui Paulo 	if (!hapd->conf->ctrl_interface_gid_set &&
43655b9c547cSRui Paulo 	    hapd->iface->interfaces->ctrl_iface_group &&
43664bc52338SCy Schubert 	    lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
43674bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
43685b9c547cSRui Paulo 			   strerror(errno));
436939beb93cSSam Leffler 		goto fail;
437039beb93cSSam Leffler 	}
437139beb93cSSam Leffler 
437239beb93cSSam Leffler 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
43735b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
43745b9c547cSRui Paulo 			   strerror(errno));
437539beb93cSSam Leffler 		goto fail;
437639beb93cSSam Leffler 	}
437739beb93cSSam Leffler 	os_free(fname);
437839beb93cSSam Leffler 
437939beb93cSSam Leffler 	hapd->ctrl_sock = s;
43805b9c547cSRui Paulo 	if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
43815b9c547cSRui Paulo 				     NULL) < 0) {
43825b9c547cSRui Paulo 		hostapd_ctrl_iface_deinit(hapd);
43835b9c547cSRui Paulo 		return -1;
43845b9c547cSRui Paulo 	}
4385e28a4053SRui Paulo 	hapd->msg_ctx = hapd;
438639beb93cSSam Leffler 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
438739beb93cSSam Leffler 
438839beb93cSSam Leffler 	return 0;
438939beb93cSSam Leffler 
439039beb93cSSam Leffler fail:
439139beb93cSSam Leffler 	if (s >= 0)
439239beb93cSSam Leffler 		close(s);
439339beb93cSSam Leffler 	if (fname) {
439439beb93cSSam Leffler 		unlink(fname);
439539beb93cSSam Leffler 		os_free(fname);
439639beb93cSSam Leffler 	}
439739beb93cSSam Leffler 	return -1;
4398780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
439939beb93cSSam Leffler }
440039beb93cSSam Leffler 
440139beb93cSSam Leffler 
hostapd_ctrl_iface_deinit(struct hostapd_data * hapd)440239beb93cSSam Leffler void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
440339beb93cSSam Leffler {
440439beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *prev;
440539beb93cSSam Leffler 
440639beb93cSSam Leffler 	if (hapd->ctrl_sock > -1) {
4407780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
440839beb93cSSam Leffler 		char *fname;
4409780fb4a2SCy Schubert #endif /* !CONFIG_CTRL_IFACE_UDP */
4410780fb4a2SCy Schubert 
441139beb93cSSam Leffler 		eloop_unregister_read_sock(hapd->ctrl_sock);
441239beb93cSSam Leffler 		close(hapd->ctrl_sock);
441339beb93cSSam Leffler 		hapd->ctrl_sock = -1;
4414780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
441539beb93cSSam Leffler 		fname = hostapd_ctrl_iface_path(hapd);
441639beb93cSSam Leffler 		if (fname)
441739beb93cSSam Leffler 			unlink(fname);
441839beb93cSSam Leffler 		os_free(fname);
441939beb93cSSam Leffler 
442039beb93cSSam Leffler 		if (hapd->conf->ctrl_interface &&
442139beb93cSSam Leffler 		    rmdir(hapd->conf->ctrl_interface) < 0) {
442239beb93cSSam Leffler 			if (errno == ENOTEMPTY) {
442339beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "Control interface "
442439beb93cSSam Leffler 					   "directory not empty - leaving it "
442539beb93cSSam Leffler 					   "behind");
442639beb93cSSam Leffler 			} else {
44275b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
44285b9c547cSRui Paulo 					   "rmdir[ctrl_interface=%s]: %s",
44295b9c547cSRui Paulo 					   hapd->conf->ctrl_interface,
44305b9c547cSRui Paulo 					   strerror(errno));
443139beb93cSSam Leffler 			}
443239beb93cSSam Leffler 		}
4433780fb4a2SCy Schubert #endif /* !CONFIG_CTRL_IFACE_UDP */
443439beb93cSSam Leffler 	}
443539beb93cSSam Leffler 
4436780fb4a2SCy Schubert 	dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst,
4437780fb4a2SCy Schubert 			      list)
4438780fb4a2SCy Schubert 		os_free(dst);
44395b9c547cSRui Paulo 
44405b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
44415b9c547cSRui Paulo 	l2_packet_deinit(hapd->l2_test);
44425b9c547cSRui Paulo 	hapd->l2_test = NULL;
44435b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
444439beb93cSSam Leffler }
444539beb93cSSam Leffler 
444639beb93cSSam Leffler 
hostapd_ctrl_iface_add(struct hapd_interfaces * interfaces,char * buf)4447f05cddf9SRui Paulo static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
4448f05cddf9SRui Paulo 				  char *buf)
4449f05cddf9SRui Paulo {
4450f05cddf9SRui Paulo 	if (hostapd_add_iface(interfaces, buf) < 0) {
4451f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
4452f05cddf9SRui Paulo 		return -1;
4453f05cddf9SRui Paulo 	}
4454f05cddf9SRui Paulo 	return 0;
4455f05cddf9SRui Paulo }
4456f05cddf9SRui Paulo 
4457f05cddf9SRui Paulo 
hostapd_ctrl_iface_remove(struct hapd_interfaces * interfaces,char * buf)4458f05cddf9SRui Paulo static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
4459f05cddf9SRui Paulo 				     char *buf)
4460f05cddf9SRui Paulo {
4461f05cddf9SRui Paulo 	if (hostapd_remove_iface(interfaces, buf) < 0) {
4462f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
4463f05cddf9SRui Paulo 		return -1;
4464f05cddf9SRui Paulo 	}
4465f05cddf9SRui Paulo 	return 0;
4466f05cddf9SRui Paulo }
4467f05cddf9SRui Paulo 
4468f05cddf9SRui Paulo 
hostapd_global_ctrl_iface_attach(struct hapd_interfaces * interfaces,struct sockaddr_storage * from,socklen_t fromlen,char * input)4469325151a3SRui Paulo static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
4470780fb4a2SCy Schubert 					    struct sockaddr_storage *from,
447185732ac8SCy Schubert 					    socklen_t fromlen, char *input)
4472325151a3SRui Paulo {
447385732ac8SCy Schubert 	return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen,
447485732ac8SCy Schubert 				 input);
4475325151a3SRui Paulo }
4476325151a3SRui Paulo 
4477325151a3SRui Paulo 
hostapd_global_ctrl_iface_detach(struct hapd_interfaces * interfaces,struct sockaddr_storage * from,socklen_t fromlen)4478325151a3SRui Paulo static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
4479780fb4a2SCy Schubert 					    struct sockaddr_storage *from,
4480325151a3SRui Paulo 					    socklen_t fromlen)
4481325151a3SRui Paulo {
4482780fb4a2SCy Schubert 	return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen);
4483325151a3SRui Paulo }
4484325151a3SRui Paulo 
4485325151a3SRui Paulo 
hostapd_ctrl_iface_flush(struct hapd_interfaces * interfaces)44865b9c547cSRui Paulo static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
44875b9c547cSRui Paulo {
44885b9c547cSRui Paulo #ifdef CONFIG_WPS_TESTING
44895b9c547cSRui Paulo 	wps_version_number = 0x20;
44904b72b91aSCy Schubert 	wps_testing_stub_cred = 0;
44915b9c547cSRui Paulo 	wps_corrupt_pkhash = 0;
44925b9c547cSRui Paulo #endif /* CONFIG_WPS_TESTING */
449385732ac8SCy Schubert 
449485732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
449585732ac8SCy Schubert #ifdef CONFIG_DPP
449685732ac8SCy Schubert 	dpp_test = DPP_TEST_DISABLED;
449732a95656SCy Schubert #ifdef CONFIG_DPP3
449832a95656SCy Schubert 	dpp_version_override = 3;
449932a95656SCy Schubert #elif defined(CONFIG_DPP2)
4500c1d255d3SCy Schubert 	dpp_version_override = 2;
4501c1d255d3SCy Schubert #else /* CONFIG_DPP2 */
4502c1d255d3SCy Schubert 	dpp_version_override = 1;
4503c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */
450485732ac8SCy Schubert #endif /* CONFIG_DPP */
450585732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
450685732ac8SCy Schubert 
450785732ac8SCy Schubert #ifdef CONFIG_DPP
45084bc52338SCy Schubert 	dpp_global_clear(interfaces->dpp);
450985732ac8SCy Schubert #endif /* CONFIG_DPP */
45105b9c547cSRui Paulo }
45115b9c547cSRui Paulo 
45125b9c547cSRui Paulo 
4513325151a3SRui Paulo #ifdef CONFIG_FST
4514325151a3SRui Paulo 
4515325151a3SRui Paulo static int
hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces * interfaces,const char * cmd)4516325151a3SRui Paulo hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
4517325151a3SRui Paulo 				     const char *cmd)
4518325151a3SRui Paulo {
4519325151a3SRui Paulo 	char ifname[IFNAMSIZ + 1];
4520325151a3SRui Paulo 	struct fst_iface_cfg cfg;
4521325151a3SRui Paulo 	struct hostapd_data *hapd;
4522325151a3SRui Paulo 	struct fst_wpa_obj iface_obj;
4523325151a3SRui Paulo 
4524325151a3SRui Paulo 	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
4525325151a3SRui Paulo 		hapd = hostapd_get_iface(interfaces, ifname);
4526325151a3SRui Paulo 		if (hapd) {
4527325151a3SRui Paulo 			if (hapd->iface->fst) {
4528325151a3SRui Paulo 				wpa_printf(MSG_INFO, "FST: Already attached");
4529325151a3SRui Paulo 				return -1;
4530325151a3SRui Paulo 			}
4531325151a3SRui Paulo 			fst_hostapd_fill_iface_obj(hapd, &iface_obj);
4532325151a3SRui Paulo 			hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
4533325151a3SRui Paulo 						      &iface_obj, &cfg);
4534325151a3SRui Paulo 			if (hapd->iface->fst)
4535325151a3SRui Paulo 				return 0;
4536325151a3SRui Paulo 		}
4537325151a3SRui Paulo 	}
4538325151a3SRui Paulo 
4539325151a3SRui Paulo 	return -EINVAL;
4540325151a3SRui Paulo }
4541325151a3SRui Paulo 
4542325151a3SRui Paulo 
4543325151a3SRui Paulo static int
hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces * interfaces,const char * cmd)4544325151a3SRui Paulo hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
4545325151a3SRui Paulo 				     const char *cmd)
4546325151a3SRui Paulo {
4547325151a3SRui Paulo 	char ifname[IFNAMSIZ + 1];
4548325151a3SRui Paulo 	struct hostapd_data * hapd;
4549325151a3SRui Paulo 
4550325151a3SRui Paulo 	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
4551325151a3SRui Paulo 		hapd = hostapd_get_iface(interfaces, ifname);
4552325151a3SRui Paulo 		if (hapd) {
4553325151a3SRui Paulo 			if (!fst_iface_detach(ifname)) {
4554325151a3SRui Paulo 				hapd->iface->fst = NULL;
4555325151a3SRui Paulo 				hapd->iface->fst_ies = NULL;
4556325151a3SRui Paulo 				return 0;
4557325151a3SRui Paulo 			}
4558325151a3SRui Paulo 		}
4559325151a3SRui Paulo 	}
4560325151a3SRui Paulo 
4561325151a3SRui Paulo 	return -EINVAL;
4562325151a3SRui Paulo }
4563325151a3SRui Paulo 
4564325151a3SRui Paulo #endif /* CONFIG_FST */
4565325151a3SRui Paulo 
4566325151a3SRui Paulo 
4567325151a3SRui Paulo static struct hostapd_data *
hostapd_interfaces_get_hapd(struct hapd_interfaces * interfaces,const char * ifname)4568325151a3SRui Paulo hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
4569325151a3SRui Paulo 			    const char *ifname)
4570325151a3SRui Paulo {
4571325151a3SRui Paulo 	size_t i, j;
4572325151a3SRui Paulo 
4573325151a3SRui Paulo 	for (i = 0; i < interfaces->count; i++) {
4574325151a3SRui Paulo 		struct hostapd_iface *iface = interfaces->iface[i];
4575325151a3SRui Paulo 
4576325151a3SRui Paulo 		for (j = 0; j < iface->num_bss; j++) {
4577325151a3SRui Paulo 			struct hostapd_data *hapd;
4578325151a3SRui Paulo 
4579325151a3SRui Paulo 			hapd = iface->bss[j];
4580325151a3SRui Paulo 			if (os_strcmp(ifname, hapd->conf->iface) == 0)
4581325151a3SRui Paulo 				return hapd;
4582325151a3SRui Paulo 		}
4583325151a3SRui Paulo 	}
4584325151a3SRui Paulo 
4585325151a3SRui Paulo 	return NULL;
4586325151a3SRui Paulo }
4587325151a3SRui Paulo 
4588325151a3SRui Paulo 
hostapd_ctrl_iface_dup_param(struct hostapd_data * src_hapd,struct hostapd_data * dst_hapd,const char * param)4589325151a3SRui Paulo static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
4590325151a3SRui Paulo 					struct hostapd_data *dst_hapd,
4591325151a3SRui Paulo 					const char *param)
4592325151a3SRui Paulo {
4593325151a3SRui Paulo 	int res;
4594325151a3SRui Paulo 	char *value;
4595325151a3SRui Paulo 
4596325151a3SRui Paulo 	value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
4597325151a3SRui Paulo 	if (!value) {
4598325151a3SRui Paulo 		wpa_printf(MSG_ERROR,
4599325151a3SRui Paulo 			   "DUP: cannot allocate buffer to stringify %s",
4600325151a3SRui Paulo 			   param);
4601325151a3SRui Paulo 		goto error_return;
4602325151a3SRui Paulo 	}
4603325151a3SRui Paulo 
4604325151a3SRui Paulo 	if (os_strcmp(param, "wpa") == 0) {
4605325151a3SRui Paulo 		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
4606325151a3SRui Paulo 			    src_hapd->conf->wpa);
4607325151a3SRui Paulo 	} else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
4608325151a3SRui Paulo 		   src_hapd->conf->wpa_key_mgmt) {
4609325151a3SRui Paulo 		res = hostapd_ctrl_iface_get_key_mgmt(
4610325151a3SRui Paulo 			src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
4611325151a3SRui Paulo 		if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
4612325151a3SRui Paulo 			goto error_stringify;
4613325151a3SRui Paulo 	} else if (os_strcmp(param, "wpa_pairwise") == 0 &&
4614325151a3SRui Paulo 		   src_hapd->conf->wpa_pairwise) {
4615325151a3SRui Paulo 		res = wpa_write_ciphers(value,
4616325151a3SRui Paulo 					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
4617325151a3SRui Paulo 					src_hapd->conf->wpa_pairwise, " ");
4618325151a3SRui Paulo 		if (res < 0)
4619325151a3SRui Paulo 			goto error_stringify;
4620325151a3SRui Paulo 	} else if (os_strcmp(param, "rsn_pairwise") == 0 &&
4621325151a3SRui Paulo 		   src_hapd->conf->rsn_pairwise) {
4622325151a3SRui Paulo 		res = wpa_write_ciphers(value,
4623325151a3SRui Paulo 					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
4624325151a3SRui Paulo 					src_hapd->conf->rsn_pairwise, " ");
4625325151a3SRui Paulo 		if (res < 0)
4626325151a3SRui Paulo 			goto error_stringify;
4627325151a3SRui Paulo 	} else if (os_strcmp(param, "wpa_passphrase") == 0 &&
4628325151a3SRui Paulo 		   src_hapd->conf->ssid.wpa_passphrase) {
4629325151a3SRui Paulo 		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
4630325151a3SRui Paulo 			    src_hapd->conf->ssid.wpa_passphrase);
4631325151a3SRui Paulo 	} else if (os_strcmp(param, "wpa_psk") == 0 &&
4632325151a3SRui Paulo 		   src_hapd->conf->ssid.wpa_psk_set) {
4633325151a3SRui Paulo 		wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
4634325151a3SRui Paulo 			src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
4635325151a3SRui Paulo 	} else {
4636325151a3SRui Paulo 		wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
4637325151a3SRui Paulo 		goto error_return;
4638325151a3SRui Paulo 	}
4639325151a3SRui Paulo 
4640325151a3SRui Paulo 	res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
4641325151a3SRui Paulo 	os_free(value);
4642325151a3SRui Paulo 	return res;
4643325151a3SRui Paulo 
4644325151a3SRui Paulo error_stringify:
4645325151a3SRui Paulo 	wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
4646325151a3SRui Paulo error_return:
4647325151a3SRui Paulo 	os_free(value);
4648325151a3SRui Paulo 	return -1;
4649325151a3SRui Paulo }
4650325151a3SRui Paulo 
4651325151a3SRui Paulo 
4652325151a3SRui Paulo static int
hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces * interfaces,const char * input,char * reply,int reply_size)4653780fb4a2SCy Schubert hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces,
4654780fb4a2SCy Schubert 				     const char *input,
4655780fb4a2SCy Schubert 				     char *reply, int reply_size)
4656780fb4a2SCy Schubert {
4657780fb4a2SCy Schubert 	size_t i, j;
4658780fb4a2SCy Schubert 	int res;
4659780fb4a2SCy Schubert 	char *pos, *end;
4660780fb4a2SCy Schubert 	struct hostapd_iface *iface;
4661780fb4a2SCy Schubert 	int show_ctrl = 0;
4662780fb4a2SCy Schubert 
4663780fb4a2SCy Schubert 	if (input)
4664780fb4a2SCy Schubert 		show_ctrl = !!os_strstr(input, "ctrl");
4665780fb4a2SCy Schubert 
4666780fb4a2SCy Schubert 	pos = reply;
4667780fb4a2SCy Schubert 	end = reply + reply_size;
4668780fb4a2SCy Schubert 
4669780fb4a2SCy Schubert 	for (i = 0; i < interfaces->count; i++) {
4670780fb4a2SCy Schubert 		iface = interfaces->iface[i];
4671780fb4a2SCy Schubert 
4672780fb4a2SCy Schubert 		for (j = 0; j < iface->num_bss; j++) {
4673780fb4a2SCy Schubert 			struct hostapd_bss_config *conf;
4674780fb4a2SCy Schubert 
4675780fb4a2SCy Schubert 			conf = iface->conf->bss[j];
4676780fb4a2SCy Schubert 			if (show_ctrl)
4677780fb4a2SCy Schubert 				res = os_snprintf(pos, end - pos,
4678780fb4a2SCy Schubert 						  "%s ctrl_iface=%s\n",
4679780fb4a2SCy Schubert 						  conf->iface,
4680780fb4a2SCy Schubert 						  conf->ctrl_interface ?
4681780fb4a2SCy Schubert 						  conf->ctrl_interface : "N/A");
4682780fb4a2SCy Schubert 			else
4683780fb4a2SCy Schubert 				res = os_snprintf(pos, end - pos, "%s\n",
4684780fb4a2SCy Schubert 						  conf->iface);
4685780fb4a2SCy Schubert 			if (os_snprintf_error(end - pos, res)) {
4686780fb4a2SCy Schubert 				*pos = '\0';
4687780fb4a2SCy Schubert 				return pos - reply;
4688780fb4a2SCy Schubert 			}
4689780fb4a2SCy Schubert 			pos += res;
4690780fb4a2SCy Schubert 		}
4691780fb4a2SCy Schubert 	}
4692780fb4a2SCy Schubert 
4693780fb4a2SCy Schubert 	return pos - reply;
4694780fb4a2SCy Schubert }
4695780fb4a2SCy Schubert 
4696780fb4a2SCy Schubert 
4697780fb4a2SCy Schubert static int
hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces * interfaces,char * cmd)4698325151a3SRui Paulo hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
4699325151a3SRui Paulo 				      char *cmd)
4700325151a3SRui Paulo {
4701325151a3SRui Paulo 	char *p_start = cmd, *p_end;
4702325151a3SRui Paulo 	struct hostapd_data *src_hapd, *dst_hapd;
4703325151a3SRui Paulo 
4704325151a3SRui Paulo 	/* cmd: "<src ifname> <dst ifname> <variable name> */
4705325151a3SRui Paulo 
4706325151a3SRui Paulo 	p_end = os_strchr(p_start, ' ');
4707325151a3SRui Paulo 	if (!p_end) {
4708325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
4709325151a3SRui Paulo 			   cmd);
4710325151a3SRui Paulo 		return -1;
4711325151a3SRui Paulo 	}
4712325151a3SRui Paulo 
4713325151a3SRui Paulo 	*p_end = '\0';
4714325151a3SRui Paulo 	src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
4715325151a3SRui Paulo 	if (!src_hapd) {
4716325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
4717325151a3SRui Paulo 			   p_start);
4718325151a3SRui Paulo 		return -1;
4719325151a3SRui Paulo 	}
4720325151a3SRui Paulo 
4721325151a3SRui Paulo 	p_start = p_end + 1;
4722325151a3SRui Paulo 	p_end = os_strchr(p_start, ' ');
4723325151a3SRui Paulo 	if (!p_end) {
4724325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
4725325151a3SRui Paulo 			   cmd);
4726325151a3SRui Paulo 		return -1;
4727325151a3SRui Paulo 	}
4728325151a3SRui Paulo 
4729325151a3SRui Paulo 	*p_end = '\0';
4730325151a3SRui Paulo 	dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
4731325151a3SRui Paulo 	if (!dst_hapd) {
4732325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
4733325151a3SRui Paulo 			   p_start);
4734325151a3SRui Paulo 		return -1;
4735325151a3SRui Paulo 	}
4736325151a3SRui Paulo 
4737325151a3SRui Paulo 	p_start = p_end + 1;
4738325151a3SRui Paulo 	return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
4739325151a3SRui Paulo }
4740325151a3SRui Paulo 
4741325151a3SRui Paulo 
hostapd_global_ctrl_iface_ifname(struct hapd_interfaces * interfaces,const char * ifname,char * buf,char * reply,int reply_size,struct sockaddr_storage * from,socklen_t fromlen)4742325151a3SRui Paulo static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
4743325151a3SRui Paulo 					    const char *ifname,
4744325151a3SRui Paulo 					    char *buf, char *reply,
4745325151a3SRui Paulo 					    int reply_size,
4746780fb4a2SCy Schubert 					    struct sockaddr_storage *from,
4747325151a3SRui Paulo 					    socklen_t fromlen)
4748325151a3SRui Paulo {
4749325151a3SRui Paulo 	struct hostapd_data *hapd;
4750325151a3SRui Paulo 
4751325151a3SRui Paulo 	hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
4752325151a3SRui Paulo 	if (hapd == NULL) {
4753325151a3SRui Paulo 		int res;
4754325151a3SRui Paulo 
4755325151a3SRui Paulo 		res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
4756325151a3SRui Paulo 		if (os_snprintf_error(reply_size, res))
4757325151a3SRui Paulo 			return -1;
4758325151a3SRui Paulo 		return res;
4759325151a3SRui Paulo 	}
4760325151a3SRui Paulo 
4761325151a3SRui Paulo 	return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
4762325151a3SRui Paulo 						  from, fromlen);
4763325151a3SRui Paulo }
4764325151a3SRui Paulo 
4765325151a3SRui Paulo 
hostapd_global_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)4766f05cddf9SRui Paulo static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
4767f05cddf9SRui Paulo 					      void *sock_ctx)
4768f05cddf9SRui Paulo {
4769c1d255d3SCy Schubert 	struct hapd_interfaces *interfaces = eloop_ctx;
4770780fb4a2SCy Schubert 	char buffer[256], *buf = buffer;
4771f05cddf9SRui Paulo 	int res;
4772780fb4a2SCy Schubert 	struct sockaddr_storage from;
4773f05cddf9SRui Paulo 	socklen_t fromlen = sizeof(from);
4774325151a3SRui Paulo 	char *reply;
4775f05cddf9SRui Paulo 	int reply_len;
4776325151a3SRui Paulo 	const int reply_size = 4096;
4777780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4778c1d255d3SCy Schubert 	unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
4779780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
4780f05cddf9SRui Paulo 
4781780fb4a2SCy Schubert 	res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
4782f05cddf9SRui Paulo 		       (struct sockaddr *) &from, &fromlen);
4783f05cddf9SRui Paulo 	if (res < 0) {
47845b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
47855b9c547cSRui Paulo 			   strerror(errno));
4786f05cddf9SRui Paulo 		return;
4787f05cddf9SRui Paulo 	}
4788f05cddf9SRui Paulo 	buf[res] = '\0';
47895b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
4790f05cddf9SRui Paulo 
4791325151a3SRui Paulo 	reply = os_malloc(reply_size);
4792325151a3SRui Paulo 	if (reply == NULL) {
4793325151a3SRui Paulo 		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
4794325151a3SRui Paulo 			   fromlen) < 0) {
4795325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
4796325151a3SRui Paulo 				   strerror(errno));
4797325151a3SRui Paulo 		}
4798325151a3SRui Paulo 		return;
4799325151a3SRui Paulo 	}
4800325151a3SRui Paulo 
4801f05cddf9SRui Paulo 	os_memcpy(reply, "OK\n", 3);
4802f05cddf9SRui Paulo 	reply_len = 3;
4803f05cddf9SRui Paulo 
4804780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4805780fb4a2SCy Schubert 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
4806780fb4a2SCy Schubert 		os_memcpy(reply, "COOKIE=", 7);
4807c1d255d3SCy Schubert 		wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
4808c1d255d3SCy Schubert 				 interfaces->ctrl_iface_cookie,
4809c1d255d3SCy Schubert 				 CTRL_IFACE_COOKIE_LEN);
4810c1d255d3SCy Schubert 		reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
4811780fb4a2SCy Schubert 		goto send_reply;
4812780fb4a2SCy Schubert 	}
4813780fb4a2SCy Schubert 
4814780fb4a2SCy Schubert 	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
4815c1d255d3SCy Schubert 	    hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
4816780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
4817780fb4a2SCy Schubert 			   "CTRL: No cookie in the request - drop request");
4818780fb4a2SCy Schubert 		os_free(reply);
4819780fb4a2SCy Schubert 		return;
4820780fb4a2SCy Schubert 	}
4821780fb4a2SCy Schubert 
4822c1d255d3SCy Schubert 	if (os_memcmp(interfaces->ctrl_iface_cookie, lcookie,
4823c1d255d3SCy Schubert 		      CTRL_IFACE_COOKIE_LEN) != 0) {
4824780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG,
4825780fb4a2SCy Schubert 			   "CTRL: Invalid cookie in the request - drop request");
4826780fb4a2SCy Schubert 		os_free(reply);
4827780fb4a2SCy Schubert 		return;
4828780fb4a2SCy Schubert 	}
4829780fb4a2SCy Schubert 
4830c1d255d3SCy Schubert 	buf += 7 + 2 * CTRL_IFACE_COOKIE_LEN;
4831780fb4a2SCy Schubert 	while (*buf == ' ')
4832780fb4a2SCy Schubert 		buf++;
4833780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
4834780fb4a2SCy Schubert 
4835325151a3SRui Paulo 	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
4836325151a3SRui Paulo 		char *pos = os_strchr(buf + 7, ' ');
4837325151a3SRui Paulo 
4838325151a3SRui Paulo 		if (pos) {
4839325151a3SRui Paulo 			*pos++ = '\0';
4840325151a3SRui Paulo 			reply_len = hostapd_global_ctrl_iface_ifname(
4841325151a3SRui Paulo 				interfaces, buf + 7, pos, reply, reply_size,
4842325151a3SRui Paulo 				&from, fromlen);
4843325151a3SRui Paulo 			goto send_reply;
4844325151a3SRui Paulo 		}
4845325151a3SRui Paulo 	}
4846325151a3SRui Paulo 
4847f05cddf9SRui Paulo 	if (os_strcmp(buf, "PING") == 0) {
4848f05cddf9SRui Paulo 		os_memcpy(reply, "PONG\n", 5);
4849f05cddf9SRui Paulo 		reply_len = 5;
48505b9c547cSRui Paulo 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
48515b9c547cSRui Paulo 		if (wpa_debug_reopen_file() < 0)
48525b9c547cSRui Paulo 			reply_len = -1;
48535b9c547cSRui Paulo 	} else if (os_strcmp(buf, "FLUSH") == 0) {
48545b9c547cSRui Paulo 		hostapd_ctrl_iface_flush(interfaces);
4855f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "ADD ", 4) == 0) {
4856f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
4857f05cddf9SRui Paulo 			reply_len = -1;
4858f05cddf9SRui Paulo 	} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
4859f05cddf9SRui Paulo 		if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
4860f05cddf9SRui Paulo 			reply_len = -1;
4861325151a3SRui Paulo 	} else if (os_strcmp(buf, "ATTACH") == 0) {
4862325151a3SRui Paulo 		if (hostapd_global_ctrl_iface_attach(interfaces, &from,
486385732ac8SCy Schubert 						     fromlen, NULL))
486485732ac8SCy Schubert 			reply_len = -1;
486585732ac8SCy Schubert 	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
486685732ac8SCy Schubert 		if (hostapd_global_ctrl_iface_attach(interfaces, &from,
486785732ac8SCy Schubert 						     fromlen, buf + 7))
4868325151a3SRui Paulo 			reply_len = -1;
4869325151a3SRui Paulo 	} else if (os_strcmp(buf, "DETACH") == 0) {
4870325151a3SRui Paulo 		if (hostapd_global_ctrl_iface_detach(interfaces, &from,
4871325151a3SRui Paulo 			fromlen))
4872325151a3SRui Paulo 			reply_len = -1;
48735b9c547cSRui Paulo #ifdef CONFIG_MODULE_TESTS
48745b9c547cSRui Paulo 	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
48755b9c547cSRui Paulo 		if (hapd_module_tests() < 0)
48765b9c547cSRui Paulo 			reply_len = -1;
48775b9c547cSRui Paulo #endif /* CONFIG_MODULE_TESTS */
4878325151a3SRui Paulo #ifdef CONFIG_FST
4879325151a3SRui Paulo 	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
4880325151a3SRui Paulo 		if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
4881325151a3SRui Paulo 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4882325151a3SRui Paulo 		else
4883325151a3SRui Paulo 			reply_len = -1;
4884325151a3SRui Paulo 	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
4885325151a3SRui Paulo 		if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
4886325151a3SRui Paulo 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4887325151a3SRui Paulo 		else
4888325151a3SRui Paulo 			reply_len = -1;
4889325151a3SRui Paulo 	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
4890325151a3SRui Paulo 		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
4891325151a3SRui Paulo #endif /* CONFIG_FST */
4892325151a3SRui Paulo 	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
4893325151a3SRui Paulo 		if (!hostapd_global_ctrl_iface_dup_network(interfaces,
4894325151a3SRui Paulo 							   buf + 12))
4895325151a3SRui Paulo 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4896325151a3SRui Paulo 		else
4897325151a3SRui Paulo 			reply_len = -1;
4898780fb4a2SCy Schubert 	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
4899780fb4a2SCy Schubert 		reply_len = hostapd_global_ctrl_iface_interfaces(
4900780fb4a2SCy Schubert 			interfaces, buf + 10, reply, sizeof(buffer));
4901780fb4a2SCy Schubert 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
4902780fb4a2SCy Schubert 		eloop_terminate();
4903f05cddf9SRui Paulo 	} else {
4904f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
4905f05cddf9SRui Paulo 			   "ignored");
4906f05cddf9SRui Paulo 		reply_len = -1;
4907f05cddf9SRui Paulo 	}
4908f05cddf9SRui Paulo 
4909325151a3SRui Paulo send_reply:
4910f05cddf9SRui Paulo 	if (reply_len < 0) {
4911f05cddf9SRui Paulo 		os_memcpy(reply, "FAIL\n", 5);
4912f05cddf9SRui Paulo 		reply_len = 5;
4913f05cddf9SRui Paulo 	}
4914f05cddf9SRui Paulo 
49155b9c547cSRui Paulo 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
49165b9c547cSRui Paulo 		   fromlen) < 0) {
49175b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
49185b9c547cSRui Paulo 			   strerror(errno));
49195b9c547cSRui Paulo 	}
4920325151a3SRui Paulo 	os_free(reply);
4921f05cddf9SRui Paulo }
4922f05cddf9SRui Paulo 
4923f05cddf9SRui Paulo 
4924780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
hostapd_global_ctrl_iface_path(struct hapd_interfaces * interface)4925f05cddf9SRui Paulo static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
4926f05cddf9SRui Paulo {
4927f05cddf9SRui Paulo 	char *buf;
4928f05cddf9SRui Paulo 	size_t len;
4929f05cddf9SRui Paulo 
4930f05cddf9SRui Paulo 	if (interface->global_iface_path == NULL)
4931f05cddf9SRui Paulo 		return NULL;
4932f05cddf9SRui Paulo 
4933f05cddf9SRui Paulo 	len = os_strlen(interface->global_iface_path) +
4934f05cddf9SRui Paulo 		os_strlen(interface->global_iface_name) + 2;
4935f05cddf9SRui Paulo 	buf = os_malloc(len);
4936f05cddf9SRui Paulo 	if (buf == NULL)
4937f05cddf9SRui Paulo 		return NULL;
4938f05cddf9SRui Paulo 
4939f05cddf9SRui Paulo 	os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
4940f05cddf9SRui Paulo 		    interface->global_iface_name);
4941f05cddf9SRui Paulo 	buf[len - 1] = '\0';
4942f05cddf9SRui Paulo 	return buf;
4943f05cddf9SRui Paulo }
4944780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
4945f05cddf9SRui Paulo 
4946f05cddf9SRui Paulo 
hostapd_global_ctrl_iface_init(struct hapd_interfaces * interface)4947f05cddf9SRui Paulo int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
4948f05cddf9SRui Paulo {
4949780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP
4950780fb4a2SCy Schubert 	int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
4951780fb4a2SCy Schubert 	char p[32] = { 0 };
4952780fb4a2SCy Schubert 	char *pos;
4953780fb4a2SCy Schubert 	struct addrinfo hints = { 0 }, *res, *saveres;
4954780fb4a2SCy Schubert 	int n;
4955780fb4a2SCy Schubert 
4956780fb4a2SCy Schubert 	if (interface->global_ctrl_sock > -1) {
4957780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
4958780fb4a2SCy Schubert 		return 0;
4959780fb4a2SCy Schubert 	}
4960780fb4a2SCy Schubert 
4961780fb4a2SCy Schubert 	if (interface->global_iface_path == NULL)
4962780fb4a2SCy Schubert 		return 0;
4963780fb4a2SCy Schubert 
4964780fb4a2SCy Schubert 	pos = os_strstr(interface->global_iface_path, "udp:");
4965780fb4a2SCy Schubert 	if (pos) {
4966780fb4a2SCy Schubert 		pos += 4;
4967780fb4a2SCy Schubert 		port = atoi(pos);
4968780fb4a2SCy Schubert 		if (port <= 0) {
4969780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
4970780fb4a2SCy Schubert 			goto fail;
4971780fb4a2SCy Schubert 		}
4972780fb4a2SCy Schubert 	}
4973780fb4a2SCy Schubert 
4974c1d255d3SCy Schubert 	os_get_random(interface->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
4975780fb4a2SCy Schubert 
4976780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
4977780fb4a2SCy Schubert 	hints.ai_flags = AI_PASSIVE;
4978780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4979780fb4a2SCy Schubert 
4980780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4981780fb4a2SCy Schubert 	hints.ai_family = AF_INET6;
4982780fb4a2SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4983780fb4a2SCy Schubert 	hints.ai_family = AF_INET;
4984780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4985780fb4a2SCy Schubert 	hints.ai_socktype = SOCK_DGRAM;
4986780fb4a2SCy Schubert 
4987780fb4a2SCy Schubert try_again:
4988780fb4a2SCy Schubert 	os_snprintf(p, sizeof(p), "%d", port);
4989780fb4a2SCy Schubert 	n = getaddrinfo(NULL, p, &hints, &res);
4990780fb4a2SCy Schubert 	if (n) {
4991780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
4992780fb4a2SCy Schubert 		goto fail;
4993780fb4a2SCy Schubert 	}
4994780fb4a2SCy Schubert 
4995780fb4a2SCy Schubert 	saveres = res;
4996780fb4a2SCy Schubert 	interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype,
4997780fb4a2SCy Schubert 					     res->ai_protocol);
4998780fb4a2SCy Schubert 	if (interface->global_ctrl_sock < 0) {
4999780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
5000780fb4a2SCy Schubert 		goto fail;
5001780fb4a2SCy Schubert 	}
5002780fb4a2SCy Schubert 
5003780fb4a2SCy Schubert 	if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) <
5004780fb4a2SCy Schubert 	    0) {
5005780fb4a2SCy Schubert 		port++;
5006780fb4a2SCy Schubert 		if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
5007780fb4a2SCy Schubert 		    HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
5008780fb4a2SCy Schubert 			goto try_again;
5009780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
5010780fb4a2SCy Schubert 		goto fail;
5011780fb4a2SCy Schubert 	}
5012780fb4a2SCy Schubert 
5013780fb4a2SCy Schubert 	freeaddrinfo(saveres);
5014780fb4a2SCy Schubert 
5015780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port);
5016780fb4a2SCy Schubert 
5017780fb4a2SCy Schubert 	if (eloop_register_read_sock(interface->global_ctrl_sock,
5018780fb4a2SCy Schubert 				     hostapd_global_ctrl_iface_receive,
5019780fb4a2SCy Schubert 				     interface, NULL) < 0) {
5020780fb4a2SCy Schubert 		hostapd_global_ctrl_iface_deinit(interface);
5021780fb4a2SCy Schubert 		return -1;
5022780fb4a2SCy Schubert 	}
5023780fb4a2SCy Schubert 
5024c1d255d3SCy Schubert 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
5025c1d255d3SCy Schubert 
5026780fb4a2SCy Schubert 	return 0;
5027780fb4a2SCy Schubert 
5028780fb4a2SCy Schubert fail:
5029780fb4a2SCy Schubert 	if (interface->global_ctrl_sock >= 0)
5030780fb4a2SCy Schubert 		close(interface->global_ctrl_sock);
5031780fb4a2SCy Schubert 	return -1;
5032780fb4a2SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP */
5033f05cddf9SRui Paulo 	struct sockaddr_un addr;
5034f05cddf9SRui Paulo 	int s = -1;
5035f05cddf9SRui Paulo 	char *fname = NULL;
5036f05cddf9SRui Paulo 
5037f05cddf9SRui Paulo 	if (interface->global_iface_path == NULL) {
5038f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
5039f05cddf9SRui Paulo 		return 0;
5040f05cddf9SRui Paulo 	}
5041f05cddf9SRui Paulo 
5042f05cddf9SRui Paulo 	if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
5043f05cddf9SRui Paulo 		if (errno == EEXIST) {
5044f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "Using existing control "
5045f05cddf9SRui Paulo 				   "interface directory.");
5046f05cddf9SRui Paulo 		} else {
50475b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
50485b9c547cSRui Paulo 				   strerror(errno));
5049f05cddf9SRui Paulo 			goto fail;
5050f05cddf9SRui Paulo 		}
50515b9c547cSRui Paulo 	} else if (interface->ctrl_iface_group &&
50524bc52338SCy Schubert 		   lchown(interface->global_iface_path, -1,
50535b9c547cSRui Paulo 			  interface->ctrl_iface_group) < 0) {
50544bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
50555b9c547cSRui Paulo 			   strerror(errno));
50565b9c547cSRui Paulo 		goto fail;
5057f05cddf9SRui Paulo 	}
5058f05cddf9SRui Paulo 
5059f05cddf9SRui Paulo 	if (os_strlen(interface->global_iface_path) + 1 +
5060f05cddf9SRui Paulo 	    os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
5061f05cddf9SRui Paulo 		goto fail;
5062f05cddf9SRui Paulo 
5063f05cddf9SRui Paulo 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
5064f05cddf9SRui Paulo 	if (s < 0) {
50655b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
5066f05cddf9SRui Paulo 		goto fail;
5067f05cddf9SRui Paulo 	}
5068f05cddf9SRui Paulo 
5069f05cddf9SRui Paulo 	os_memset(&addr, 0, sizeof(addr));
5070f05cddf9SRui Paulo #ifdef __FreeBSD__
5071f05cddf9SRui Paulo 	addr.sun_len = sizeof(addr);
5072f05cddf9SRui Paulo #endif /* __FreeBSD__ */
5073f05cddf9SRui Paulo 	addr.sun_family = AF_UNIX;
5074f05cddf9SRui Paulo 	fname = hostapd_global_ctrl_iface_path(interface);
5075f05cddf9SRui Paulo 	if (fname == NULL)
5076f05cddf9SRui Paulo 		goto fail;
5077f05cddf9SRui Paulo 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
5078f05cddf9SRui Paulo 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
5079f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
5080f05cddf9SRui Paulo 			   strerror(errno));
5081f05cddf9SRui Paulo 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
5082f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
5083f05cddf9SRui Paulo 				   " allow connections - assuming it was left"
5084f05cddf9SRui Paulo 				   "over from forced program termination");
5085f05cddf9SRui Paulo 			if (unlink(fname) < 0) {
50865b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
50875b9c547cSRui Paulo 					   "Could not unlink existing ctrl_iface socket '%s': %s",
50885b9c547cSRui Paulo 					   fname, strerror(errno));
5089f05cddf9SRui Paulo 				goto fail;
5090f05cddf9SRui Paulo 			}
5091f05cddf9SRui Paulo 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
5092f05cddf9SRui Paulo 			    0) {
50935b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
50945b9c547cSRui Paulo 					   strerror(errno));
5095f05cddf9SRui Paulo 				goto fail;
5096f05cddf9SRui Paulo 			}
5097f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
5098f05cddf9SRui Paulo 				   "ctrl_iface socket '%s'", fname);
5099f05cddf9SRui Paulo 		} else {
5100f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
5101f05cddf9SRui Paulo 				   "be in use - cannot override it");
5102f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
5103f05cddf9SRui Paulo 				   "not used anymore", fname);
5104f05cddf9SRui Paulo 			os_free(fname);
5105f05cddf9SRui Paulo 			fname = NULL;
5106f05cddf9SRui Paulo 			goto fail;
5107f05cddf9SRui Paulo 		}
5108f05cddf9SRui Paulo 	}
5109f05cddf9SRui Paulo 
51105b9c547cSRui Paulo 	if (interface->ctrl_iface_group &&
51114bc52338SCy Schubert 	    lchown(fname, -1, interface->ctrl_iface_group) < 0) {
51124bc52338SCy Schubert 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
51135b9c547cSRui Paulo 			   strerror(errno));
51145b9c547cSRui Paulo 		goto fail;
51155b9c547cSRui Paulo 	}
51165b9c547cSRui Paulo 
5117f05cddf9SRui Paulo 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
51185b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
51195b9c547cSRui Paulo 			   strerror(errno));
5120f05cddf9SRui Paulo 		goto fail;
5121f05cddf9SRui Paulo 	}
5122f05cddf9SRui Paulo 	os_free(fname);
5123f05cddf9SRui Paulo 
5124f05cddf9SRui Paulo 	interface->global_ctrl_sock = s;
5125f05cddf9SRui Paulo 	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
5126f05cddf9SRui Paulo 				 interface, NULL);
5127f05cddf9SRui Paulo 
5128c1d255d3SCy Schubert 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
5129c1d255d3SCy Schubert 
5130f05cddf9SRui Paulo 	return 0;
5131f05cddf9SRui Paulo 
5132f05cddf9SRui Paulo fail:
5133f05cddf9SRui Paulo 	if (s >= 0)
5134f05cddf9SRui Paulo 		close(s);
5135f05cddf9SRui Paulo 	if (fname) {
5136f05cddf9SRui Paulo 		unlink(fname);
5137f05cddf9SRui Paulo 		os_free(fname);
5138f05cddf9SRui Paulo 	}
5139f05cddf9SRui Paulo 	return -1;
5140780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
5141f05cddf9SRui Paulo }
5142f05cddf9SRui Paulo 
5143f05cddf9SRui Paulo 
hostapd_global_ctrl_iface_deinit(struct hapd_interfaces * interfaces)5144f05cddf9SRui Paulo void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
5145f05cddf9SRui Paulo {
5146780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
5147f05cddf9SRui Paulo 	char *fname = NULL;
5148780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
5149325151a3SRui Paulo 	struct wpa_ctrl_dst *dst, *prev;
5150f05cddf9SRui Paulo 
5151f05cddf9SRui Paulo 	if (interfaces->global_ctrl_sock > -1) {
5152f05cddf9SRui Paulo 		eloop_unregister_read_sock(interfaces->global_ctrl_sock);
5153f05cddf9SRui Paulo 		close(interfaces->global_ctrl_sock);
5154f05cddf9SRui Paulo 		interfaces->global_ctrl_sock = -1;
5155780fb4a2SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP
5156f05cddf9SRui Paulo 		fname = hostapd_global_ctrl_iface_path(interfaces);
5157f05cddf9SRui Paulo 		if (fname) {
5158f05cddf9SRui Paulo 			unlink(fname);
5159f05cddf9SRui Paulo 			os_free(fname);
5160f05cddf9SRui Paulo 		}
5161f05cddf9SRui Paulo 
5162f05cddf9SRui Paulo 		if (interfaces->global_iface_path &&
5163f05cddf9SRui Paulo 		    rmdir(interfaces->global_iface_path) < 0) {
5164f05cddf9SRui Paulo 			if (errno == ENOTEMPTY) {
5165f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "Control interface "
5166f05cddf9SRui Paulo 					   "directory not empty - leaving it "
5167f05cddf9SRui Paulo 					   "behind");
5168f05cddf9SRui Paulo 			} else {
51695b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
51705b9c547cSRui Paulo 					   "rmdir[ctrl_interface=%s]: %s",
51715b9c547cSRui Paulo 					   interfaces->global_iface_path,
51725b9c547cSRui Paulo 					   strerror(errno));
5173f05cddf9SRui Paulo 			}
5174f05cddf9SRui Paulo 		}
5175780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP */
5176325151a3SRui Paulo 	}
5177325151a3SRui Paulo 
5178f05cddf9SRui Paulo 	os_free(interfaces->global_iface_path);
5179f05cddf9SRui Paulo 	interfaces->global_iface_path = NULL;
5180325151a3SRui Paulo 
5181780fb4a2SCy Schubert 	dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst,
5182780fb4a2SCy Schubert 			      struct wpa_ctrl_dst, list)
5183780fb4a2SCy Schubert 		os_free(dst);
5184f05cddf9SRui Paulo }
5185f05cddf9SRui Paulo 
5186f05cddf9SRui Paulo 
hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst * dst,const char * buf)518785732ac8SCy Schubert static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
518885732ac8SCy Schubert 					    const char *buf)
518985732ac8SCy Schubert {
519085732ac8SCy Schubert 	/* Enable Probe Request events based on explicit request.
519185732ac8SCy Schubert 	 * Other events are enabled by default.
519285732ac8SCy Schubert 	 */
519385732ac8SCy Schubert 	if (str_starts(buf, RX_PROBE_REQUEST))
519485732ac8SCy Schubert 		return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST);
519585732ac8SCy Schubert 	return 1;
519685732ac8SCy Schubert }
519785732ac8SCy Schubert 
519885732ac8SCy Schubert 
hostapd_ctrl_iface_send_internal(int sock,struct dl_list * ctrl_dst,const char * ifname,int level,const char * buf,size_t len)5199c1d255d3SCy Schubert static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst,
5200c1d255d3SCy Schubert 					     const char *ifname, int level,
520139beb93cSSam Leffler 					     const char *buf, size_t len)
520239beb93cSSam Leffler {
520339beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *next;
520439beb93cSSam Leffler 	struct msghdr msg;
5205c1d255d3SCy Schubert 	int idx, res;
5206c1d255d3SCy Schubert 	struct iovec io[5];
520739beb93cSSam Leffler 	char levelstr[10];
520839beb93cSSam Leffler 
5209c1d255d3SCy Schubert 	if (sock < 0 || dl_list_empty(ctrl_dst))
521039beb93cSSam Leffler 		return;
521139beb93cSSam Leffler 
5212c1d255d3SCy Schubert 	res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
5213c1d255d3SCy Schubert 	if (os_snprintf_error(sizeof(levelstr), res))
5214c1d255d3SCy Schubert 		return;
5215c1d255d3SCy Schubert 	idx = 0;
5216c1d255d3SCy Schubert 	if (ifname) {
5217c1d255d3SCy Schubert 		io[idx].iov_base = "IFNAME=";
5218c1d255d3SCy Schubert 		io[idx].iov_len = 7;
5219c1d255d3SCy Schubert 		idx++;
5220c1d255d3SCy Schubert 		io[idx].iov_base = (char *) ifname;
5221c1d255d3SCy Schubert 		io[idx].iov_len = os_strlen(ifname);
5222c1d255d3SCy Schubert 		idx++;
5223c1d255d3SCy Schubert 		io[idx].iov_base = " ";
5224c1d255d3SCy Schubert 		io[idx].iov_len = 1;
5225c1d255d3SCy Schubert 		idx++;
5226c1d255d3SCy Schubert 	}
5227c1d255d3SCy Schubert 	io[idx].iov_base = levelstr;
5228c1d255d3SCy Schubert 	io[idx].iov_len = os_strlen(levelstr);
5229c1d255d3SCy Schubert 	idx++;
5230c1d255d3SCy Schubert 	io[idx].iov_base = (char *) buf;
5231c1d255d3SCy Schubert 	io[idx].iov_len = len;
5232c1d255d3SCy Schubert 	idx++;
523339beb93cSSam Leffler 	os_memset(&msg, 0, sizeof(msg));
523439beb93cSSam Leffler 	msg.msg_iov = io;
5235c1d255d3SCy Schubert 	msg.msg_iovlen = idx;
523639beb93cSSam Leffler 
523739beb93cSSam Leffler 	idx = 0;
5238780fb4a2SCy Schubert 	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
523985732ac8SCy Schubert 		if ((level >= dst->debug_level) &&
524085732ac8SCy Schubert 		     hostapd_ctrl_check_event_enabled(dst, buf)) {
5241780fb4a2SCy Schubert 			sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
5242780fb4a2SCy Schubert 				       &dst->addr, dst->addrlen);
524339beb93cSSam Leffler 			msg.msg_name = &dst->addr;
524439beb93cSSam Leffler 			msg.msg_namelen = dst->addrlen;
5245c1d255d3SCy Schubert 			if (sendmsg(sock, &msg, 0) < 0) {
52463157ba21SRui Paulo 				int _errno = errno;
52473157ba21SRui Paulo 				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
52483157ba21SRui Paulo 					   "%d - %s",
52493157ba21SRui Paulo 					   idx, errno, strerror(errno));
525039beb93cSSam Leffler 				dst->errors++;
52513157ba21SRui Paulo 				if (dst->errors > 10 || _errno == ENOENT) {
5252c1d255d3SCy Schubert 					ctrl_iface_detach(ctrl_dst,
5253325151a3SRui Paulo 							  &dst->addr,
5254325151a3SRui Paulo 							  dst->addrlen);
525539beb93cSSam Leffler 				}
525639beb93cSSam Leffler 			} else
525739beb93cSSam Leffler 				dst->errors = 0;
525839beb93cSSam Leffler 		}
525939beb93cSSam Leffler 		idx++;
526039beb93cSSam Leffler 	}
526139beb93cSSam Leffler }
526239beb93cSSam Leffler 
5263c1d255d3SCy Schubert 
hostapd_ctrl_iface_send(struct hostapd_data * hapd,int level,enum wpa_msg_type type,const char * buf,size_t len)5264c1d255d3SCy Schubert static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
5265c1d255d3SCy Schubert 				    enum wpa_msg_type type,
5266c1d255d3SCy Schubert 				    const char *buf, size_t len)
5267c1d255d3SCy Schubert {
5268c1d255d3SCy Schubert 	if (type != WPA_MSG_NO_GLOBAL) {
5269c1d255d3SCy Schubert 		hostapd_ctrl_iface_send_internal(
5270c1d255d3SCy Schubert 			hapd->iface->interfaces->global_ctrl_sock,
5271c1d255d3SCy Schubert 			&hapd->iface->interfaces->global_ctrl_dst,
5272c1d255d3SCy Schubert 			type != WPA_MSG_PER_INTERFACE ?
5273c1d255d3SCy Schubert 			NULL : hapd->conf->iface,
5274c1d255d3SCy Schubert 			level, buf, len);
5275c1d255d3SCy Schubert 	}
5276c1d255d3SCy Schubert 
5277c1d255d3SCy Schubert 	if (type != WPA_MSG_ONLY_GLOBAL) {
5278c1d255d3SCy Schubert 		hostapd_ctrl_iface_send_internal(
5279c1d255d3SCy Schubert 			hapd->ctrl_sock, &hapd->ctrl_dst,
5280c1d255d3SCy Schubert 			NULL, level, buf, len);
5281c1d255d3SCy Schubert 	}
5282c1d255d3SCy Schubert }
5283c1d255d3SCy Schubert 
528439beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
5285