18dbcf02cSchristos /*
28dbcf02cSchristos  * hostapd / UNIX domain socket -based control interface
3ebb5671cSchristos  * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
562a52023Schristos  * This software may be distributed under the terms of the BSD license.
662a52023Schristos  * See README for more details.
78dbcf02cSchristos  */
88dbcf02cSchristos 
98dbcf02cSchristos #include "utils/includes.h"
108dbcf02cSchristos 
118dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
128dbcf02cSchristos 
139a53cbbeSchristos #ifdef CONFIG_TESTING_OPTIONS
149a53cbbeSchristos #include <net/ethernet.h>
159a53cbbeSchristos #include <netinet/ip.h>
169a53cbbeSchristos #endif /* CONFIG_TESTING_OPTIONS */
179a53cbbeSchristos 
188dbcf02cSchristos #include <sys/un.h>
198dbcf02cSchristos #include <sys/stat.h>
208dbcf02cSchristos #include <stddef.h>
218dbcf02cSchristos 
22928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
23928750b6Schristos #include <netdb.h>
24928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
25928750b6Schristos 
268dbcf02cSchristos #include "utils/common.h"
278dbcf02cSchristos #include "utils/eloop.h"
28928750b6Schristos #include "utils/module_tests.h"
2942669be3Schristos #include "common/version.h"
308dbcf02cSchristos #include "common/ieee802_11_defs.h"
31928750b6Schristos #include "common/ctrl_iface_common.h"
32ebb5671cSchristos #ifdef CONFIG_DPP
33ebb5671cSchristos #include "common/dpp.h"
34ebb5671cSchristos #endif /* CONFIG_DPP */
35ebb5671cSchristos #include "common/wpa_ctrl.h"
369a53cbbeSchristos #include "crypto/tls.h"
378dbcf02cSchristos #include "drivers/driver.h"
38928750b6Schristos #include "eapol_auth/eapol_auth_sm.h"
398dbcf02cSchristos #include "radius/radius_client.h"
4036d97821Schristos #include "radius/radius_server.h"
419a53cbbeSchristos #include "l2_packet/l2_packet.h"
428dbcf02cSchristos #include "ap/hostapd.h"
438dbcf02cSchristos #include "ap/ap_config.h"
448dbcf02cSchristos #include "ap/ieee802_1x.h"
458dbcf02cSchristos #include "ap/wpa_auth.h"
468dbcf02cSchristos #include "ap/ieee802_11.h"
478dbcf02cSchristos #include "ap/sta_info.h"
488dbcf02cSchristos #include "ap/wps_hostapd.h"
498dbcf02cSchristos #include "ap/ctrl_iface_ap.h"
5042669be3Schristos #include "ap/ap_drv_ops.h"
5136d97821Schristos #include "ap/hs20.h"
5236d97821Schristos #include "ap/wnm_ap.h"
5336d97821Schristos #include "ap/wpa_auth.h"
549a53cbbeSchristos #include "ap/beacon.h"
55928750b6Schristos #include "ap/neighbor_db.h"
56928750b6Schristos #include "ap/rrm.h"
57ebb5671cSchristos #include "ap/dpp_hostapd.h"
5842669be3Schristos #include "wps/wps_defs.h"
5942669be3Schristos #include "wps/wps.h"
60928750b6Schristos #include "fst/fst_ctrl_iface.h"
6162a52023Schristos #include "config_file.h"
628dbcf02cSchristos #include "ctrl_iface.h"
638dbcf02cSchristos 
648dbcf02cSchristos 
65928750b6Schristos #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
668dbcf02cSchristos 
67928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
68928750b6Schristos #define COOKIE_LEN 8
69928750b6Schristos static unsigned char cookie[COOKIE_LEN];
70928750b6Schristos static unsigned char gcookie[COOKIE_LEN];
71928750b6Schristos #define HOSTAPD_CTRL_IFACE_PORT		8877
72928750b6Schristos #define HOSTAPD_CTRL_IFACE_PORT_LIMIT	50
73928750b6Schristos #define HOSTAPD_GLOBAL_CTRL_IFACE_PORT		8878
74928750b6Schristos #define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT	50
75928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
768dbcf02cSchristos 
778dbcf02cSchristos static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
78928750b6Schristos 				    enum wpa_msg_type type,
798dbcf02cSchristos 				    const char *buf, size_t len);
808dbcf02cSchristos 
818dbcf02cSchristos 
hostapd_ctrl_iface_attach(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen,const char * input)828dbcf02cSchristos static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
83928750b6Schristos 				     struct sockaddr_storage *from,
84ebb5671cSchristos 				     socklen_t fromlen, const char *input)
858dbcf02cSchristos {
86ebb5671cSchristos 	return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input);
878dbcf02cSchristos }
888dbcf02cSchristos 
898dbcf02cSchristos 
hostapd_ctrl_iface_detach(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen)908dbcf02cSchristos static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
91928750b6Schristos 				     struct sockaddr_storage *from,
928dbcf02cSchristos 				     socklen_t fromlen)
938dbcf02cSchristos {
94928750b6Schristos 	return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen);
958dbcf02cSchristos }
968dbcf02cSchristos 
978dbcf02cSchristos 
hostapd_ctrl_iface_level(struct hostapd_data * hapd,struct sockaddr_storage * from,socklen_t fromlen,char * level)988dbcf02cSchristos static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
99928750b6Schristos 				    struct sockaddr_storage *from,
1008dbcf02cSchristos 				    socklen_t fromlen,
1018dbcf02cSchristos 				    char *level)
1028dbcf02cSchristos {
103928750b6Schristos 	return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level);
1048dbcf02cSchristos }
1058dbcf02cSchristos 
1068dbcf02cSchristos 
hostapd_ctrl_iface_new_sta(struct hostapd_data * hapd,const char * txtaddr)1078dbcf02cSchristos static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
1088dbcf02cSchristos 				      const char *txtaddr)
1098dbcf02cSchristos {
1108dbcf02cSchristos 	u8 addr[ETH_ALEN];
1118dbcf02cSchristos 	struct sta_info *sta;
1128dbcf02cSchristos 
1138dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
1148dbcf02cSchristos 
1158dbcf02cSchristos 	if (hwaddr_aton(txtaddr, addr))
1168dbcf02cSchristos 		return -1;
1178dbcf02cSchristos 
1188dbcf02cSchristos 	sta = ap_get_sta(hapd, addr);
1198dbcf02cSchristos 	if (sta)
1208dbcf02cSchristos 		return 0;
1218dbcf02cSchristos 
1228dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
1238dbcf02cSchristos 		   "notification", MAC2STR(addr));
1248dbcf02cSchristos 	sta = ap_sta_add(hapd, addr);
1258dbcf02cSchristos 	if (sta == NULL)
1268dbcf02cSchristos 		return -1;
1278dbcf02cSchristos 
1288dbcf02cSchristos 	hostapd_new_assoc_sta(hapd, sta, 0);
1298dbcf02cSchristos 	return 0;
1308dbcf02cSchristos }
1318dbcf02cSchristos 
1328dbcf02cSchristos 
1338dbcf02cSchristos #ifdef CONFIG_IEEE80211W
1348dbcf02cSchristos #ifdef NEED_AP_MLME
hostapd_ctrl_iface_sa_query(struct hostapd_data * hapd,const char * txtaddr)1358dbcf02cSchristos static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
1368dbcf02cSchristos 				       const char *txtaddr)
1378dbcf02cSchristos {
1388dbcf02cSchristos 	u8 addr[ETH_ALEN];
1398dbcf02cSchristos 	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
1408dbcf02cSchristos 
1418dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
1428dbcf02cSchristos 
1438dbcf02cSchristos 	if (hwaddr_aton(txtaddr, addr) ||
1448dbcf02cSchristos 	    os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
1458dbcf02cSchristos 		return -1;
1468dbcf02cSchristos 
1478dbcf02cSchristos 	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
1488dbcf02cSchristos 
1498dbcf02cSchristos 	return 0;
1508dbcf02cSchristos }
1518dbcf02cSchristos #endif /* NEED_AP_MLME */
1528dbcf02cSchristos #endif /* CONFIG_IEEE80211W */
1538dbcf02cSchristos 
1548dbcf02cSchristos 
1558dbcf02cSchristos #ifdef CONFIG_WPS
hostapd_ctrl_iface_wps_pin(struct hostapd_data * hapd,char * txt)1568dbcf02cSchristos static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
1578dbcf02cSchristos {
1588dbcf02cSchristos 	char *pin = os_strchr(txt, ' ');
1598dbcf02cSchristos 	char *timeout_txt;
1608dbcf02cSchristos 	int timeout;
16142669be3Schristos 	u8 addr_buf[ETH_ALEN], *addr = NULL;
16242669be3Schristos 	char *pos;
1638dbcf02cSchristos 
1648dbcf02cSchristos 	if (pin == NULL)
1658dbcf02cSchristos 		return -1;
1668dbcf02cSchristos 	*pin++ = '\0';
1678dbcf02cSchristos 
1688dbcf02cSchristos 	timeout_txt = os_strchr(pin, ' ');
1698dbcf02cSchristos 	if (timeout_txt) {
1708dbcf02cSchristos 		*timeout_txt++ = '\0';
1718dbcf02cSchristos 		timeout = atoi(timeout_txt);
17242669be3Schristos 		pos = os_strchr(timeout_txt, ' ');
17342669be3Schristos 		if (pos) {
17442669be3Schristos 			*pos++ = '\0';
17542669be3Schristos 			if (hwaddr_aton(pos, addr_buf) == 0)
17642669be3Schristos 				addr = addr_buf;
17742669be3Schristos 		}
1788dbcf02cSchristos 	} else
1798dbcf02cSchristos 		timeout = 0;
1808dbcf02cSchristos 
18142669be3Schristos 	return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
18242669be3Schristos }
18342669be3Schristos 
18442669be3Schristos 
hostapd_ctrl_iface_wps_check_pin(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)18542669be3Schristos static int hostapd_ctrl_iface_wps_check_pin(
18642669be3Schristos 	struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
18742669be3Schristos {
18842669be3Schristos 	char pin[9];
18942669be3Schristos 	size_t len;
19042669be3Schristos 	char *pos;
19142669be3Schristos 	int ret;
19242669be3Schristos 
19342669be3Schristos 	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
19442669be3Schristos 			      (u8 *) cmd, os_strlen(cmd));
19542669be3Schristos 	for (pos = cmd, len = 0; *pos != '\0'; pos++) {
19642669be3Schristos 		if (*pos < '0' || *pos > '9')
19742669be3Schristos 			continue;
19842669be3Schristos 		pin[len++] = *pos;
19942669be3Schristos 		if (len == 9) {
20042669be3Schristos 			wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
20142669be3Schristos 			return -1;
20242669be3Schristos 		}
20342669be3Schristos 	}
20442669be3Schristos 	if (len != 4 && len != 8) {
20542669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
20642669be3Schristos 		return -1;
20742669be3Schristos 	}
20842669be3Schristos 	pin[len] = '\0';
20942669be3Schristos 
21042669be3Schristos 	if (len == 8) {
21142669be3Schristos 		unsigned int pin_val;
21242669be3Schristos 		pin_val = atoi(pin);
21342669be3Schristos 		if (!wps_pin_valid(pin_val)) {
21442669be3Schristos 			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
21542669be3Schristos 			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
2169a53cbbeSchristos 			if (os_snprintf_error(buflen, ret))
21742669be3Schristos 				return -1;
21842669be3Schristos 			return ret;
21942669be3Schristos 		}
22042669be3Schristos 	}
22142669be3Schristos 
22242669be3Schristos 	ret = os_snprintf(buf, buflen, "%s", pin);
2239a53cbbeSchristos 	if (os_snprintf_error(buflen, ret))
22442669be3Schristos 		return -1;
22542669be3Schristos 
22642669be3Schristos 	return ret;
2278dbcf02cSchristos }
2288dbcf02cSchristos 
2298dbcf02cSchristos 
23062a52023Schristos #ifdef CONFIG_WPS_NFC
hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data * hapd,char * pos)23162a52023Schristos static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
23262a52023Schristos 					       char *pos)
2338dbcf02cSchristos {
23462a52023Schristos 	size_t len;
23562a52023Schristos 	struct wpabuf *buf;
23662a52023Schristos 	int ret;
2378dbcf02cSchristos 
23862a52023Schristos 	len = os_strlen(pos);
23962a52023Schristos 	if (len & 0x01)
2408dbcf02cSchristos 		return -1;
24162a52023Schristos 	len /= 2;
2428dbcf02cSchristos 
24362a52023Schristos 	buf = wpabuf_alloc(len);
24462a52023Schristos 	if (buf == NULL)
2458dbcf02cSchristos 		return -1;
24662a52023Schristos 	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
24762a52023Schristos 		wpabuf_free(buf);
24862a52023Schristos 		return -1;
2498dbcf02cSchristos 	}
25062a52023Schristos 
25162a52023Schristos 	ret = hostapd_wps_nfc_tag_read(hapd, buf);
25262a52023Schristos 	wpabuf_free(buf);
25362a52023Schristos 
25462a52023Schristos 	return ret;
25562a52023Schristos }
25662a52023Schristos 
25762a52023Schristos 
hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)25862a52023Schristos static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
25962a52023Schristos 						   char *cmd, char *reply,
26062a52023Schristos 						   size_t max_len)
26162a52023Schristos {
26262a52023Schristos 	int ndef;
26362a52023Schristos 	struct wpabuf *buf;
26462a52023Schristos 	int res;
26562a52023Schristos 
26662a52023Schristos 	if (os_strcmp(cmd, "WPS") == 0)
26762a52023Schristos 		ndef = 0;
26862a52023Schristos 	else if (os_strcmp(cmd, "NDEF") == 0)
26962a52023Schristos 		ndef = 1;
27062a52023Schristos 	else
27162a52023Schristos 		return -1;
27262a52023Schristos 
27362a52023Schristos 	buf = hostapd_wps_nfc_config_token(hapd, ndef);
27462a52023Schristos 	if (buf == NULL)
27562a52023Schristos 		return -1;
27662a52023Schristos 
27762a52023Schristos 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
27862a52023Schristos 					 wpabuf_len(buf));
27962a52023Schristos 	reply[res++] = '\n';
28062a52023Schristos 	reply[res] = '\0';
28162a52023Schristos 
28262a52023Schristos 	wpabuf_free(buf);
28362a52023Schristos 
28462a52023Schristos 	return res;
28562a52023Schristos }
28662a52023Schristos 
28762a52023Schristos 
hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data * hapd,char * reply,size_t max_len,int ndef)28862a52023Schristos static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
28962a52023Schristos 						char *reply, size_t max_len,
29062a52023Schristos 						int ndef)
29162a52023Schristos {
29262a52023Schristos 	struct wpabuf *buf;
29362a52023Schristos 	int res;
29462a52023Schristos 
29562a52023Schristos 	buf = hostapd_wps_nfc_token_gen(hapd, ndef);
29662a52023Schristos 	if (buf == NULL)
29762a52023Schristos 		return -1;
29862a52023Schristos 
29962a52023Schristos 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
30062a52023Schristos 					 wpabuf_len(buf));
30162a52023Schristos 	reply[res++] = '\n';
30262a52023Schristos 	reply[res] = '\0';
30362a52023Schristos 
30462a52023Schristos 	wpabuf_free(buf);
30562a52023Schristos 
30662a52023Schristos 	return res;
30762a52023Schristos }
30862a52023Schristos 
30962a52023Schristos 
hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)31062a52023Schristos static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
31162a52023Schristos 					    char *cmd, char *reply,
31262a52023Schristos 					    size_t max_len)
31362a52023Schristos {
31462a52023Schristos 	if (os_strcmp(cmd, "WPS") == 0)
31562a52023Schristos 		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
31662a52023Schristos 							    max_len, 0);
31762a52023Schristos 
31862a52023Schristos 	if (os_strcmp(cmd, "NDEF") == 0)
31962a52023Schristos 		return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
32062a52023Schristos 							    max_len, 1);
32162a52023Schristos 
32262a52023Schristos 	if (os_strcmp(cmd, "enable") == 0)
32362a52023Schristos 		return hostapd_wps_nfc_token_enable(hapd);
32462a52023Schristos 
32562a52023Schristos 	if (os_strcmp(cmd, "disable") == 0) {
32662a52023Schristos 		hostapd_wps_nfc_token_disable(hapd);
32762a52023Schristos 		return 0;
32862a52023Schristos 	}
32962a52023Schristos 
33062a52023Schristos 	return -1;
33162a52023Schristos }
33236d97821Schristos 
33336d97821Schristos 
hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data * hapd,char * cmd,char * reply,size_t max_len)33436d97821Schristos static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
33536d97821Schristos 						   char *cmd, char *reply,
33636d97821Schristos 						   size_t max_len)
33736d97821Schristos {
33836d97821Schristos 	struct wpabuf *buf;
33936d97821Schristos 	int res;
34036d97821Schristos 	char *pos;
34136d97821Schristos 	int ndef;
34236d97821Schristos 
34336d97821Schristos 	pos = os_strchr(cmd, ' ');
34436d97821Schristos 	if (pos == NULL)
34536d97821Schristos 		return -1;
34636d97821Schristos 	*pos++ = '\0';
34736d97821Schristos 
34836d97821Schristos 	if (os_strcmp(cmd, "WPS") == 0)
34936d97821Schristos 		ndef = 0;
35036d97821Schristos 	else if (os_strcmp(cmd, "NDEF") == 0)
35136d97821Schristos 		ndef = 1;
35236d97821Schristos 	else
35336d97821Schristos 		return -1;
35436d97821Schristos 
35536d97821Schristos 	if (os_strcmp(pos, "WPS-CR") == 0)
35636d97821Schristos 		buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
35736d97821Schristos 	else
35836d97821Schristos 		buf = NULL;
35936d97821Schristos 	if (buf == NULL)
36036d97821Schristos 		return -1;
36136d97821Schristos 
36236d97821Schristos 	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
36336d97821Schristos 					 wpabuf_len(buf));
36436d97821Schristos 	reply[res++] = '\n';
36536d97821Schristos 	reply[res] = '\0';
36636d97821Schristos 
36736d97821Schristos 	wpabuf_free(buf);
36836d97821Schristos 
36936d97821Schristos 	return res;
37036d97821Schristos }
37136d97821Schristos 
37236d97821Schristos 
hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data * hapd,char * cmd)37336d97821Schristos static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
37436d97821Schristos 						  char *cmd)
37536d97821Schristos {
37636d97821Schristos 	size_t len;
37736d97821Schristos 	struct wpabuf *req, *sel;
37836d97821Schristos 	int ret;
37936d97821Schristos 	char *pos, *role, *type, *pos2;
38036d97821Schristos 
38136d97821Schristos 	role = cmd;
38236d97821Schristos 	pos = os_strchr(role, ' ');
38336d97821Schristos 	if (pos == NULL)
38436d97821Schristos 		return -1;
38536d97821Schristos 	*pos++ = '\0';
38636d97821Schristos 
38736d97821Schristos 	type = pos;
38836d97821Schristos 	pos = os_strchr(type, ' ');
38936d97821Schristos 	if (pos == NULL)
39036d97821Schristos 		return -1;
39136d97821Schristos 	*pos++ = '\0';
39236d97821Schristos 
39336d97821Schristos 	pos2 = os_strchr(pos, ' ');
39436d97821Schristos 	if (pos2 == NULL)
39536d97821Schristos 		return -1;
39636d97821Schristos 	*pos2++ = '\0';
39736d97821Schristos 
39836d97821Schristos 	len = os_strlen(pos);
39936d97821Schristos 	if (len & 0x01)
40036d97821Schristos 		return -1;
40136d97821Schristos 	len /= 2;
40236d97821Schristos 
40336d97821Schristos 	req = wpabuf_alloc(len);
40436d97821Schristos 	if (req == NULL)
40536d97821Schristos 		return -1;
40636d97821Schristos 	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
40736d97821Schristos 		wpabuf_free(req);
40836d97821Schristos 		return -1;
40936d97821Schristos 	}
41036d97821Schristos 
41136d97821Schristos 	len = os_strlen(pos2);
41236d97821Schristos 	if (len & 0x01) {
41336d97821Schristos 		wpabuf_free(req);
41436d97821Schristos 		return -1;
41536d97821Schristos 	}
41636d97821Schristos 	len /= 2;
41736d97821Schristos 
41836d97821Schristos 	sel = wpabuf_alloc(len);
41936d97821Schristos 	if (sel == NULL) {
42036d97821Schristos 		wpabuf_free(req);
42136d97821Schristos 		return -1;
42236d97821Schristos 	}
42336d97821Schristos 	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
42436d97821Schristos 		wpabuf_free(req);
42536d97821Schristos 		wpabuf_free(sel);
42636d97821Schristos 		return -1;
42736d97821Schristos 	}
42836d97821Schristos 
42936d97821Schristos 	if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
43036d97821Schristos 		ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
43136d97821Schristos 	} else {
43236d97821Schristos 		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
43336d97821Schristos 			   "reported: role=%s type=%s", role, type);
43436d97821Schristos 		ret = -1;
43536d97821Schristos 	}
43636d97821Schristos 	wpabuf_free(req);
43736d97821Schristos 	wpabuf_free(sel);
43836d97821Schristos 
43936d97821Schristos 	return ret;
44036d97821Schristos }
44136d97821Schristos 
44262a52023Schristos #endif /* CONFIG_WPS_NFC */
4431b7205bfSchristos 
4441b7205bfSchristos 
hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data * hapd,char * txt,char * buf,size_t buflen)4451b7205bfSchristos static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
4461b7205bfSchristos 					 char *buf, size_t buflen)
4471b7205bfSchristos {
4481b7205bfSchristos 	int timeout = 300;
4491b7205bfSchristos 	char *pos;
4501b7205bfSchristos 	const char *pin_txt;
4511b7205bfSchristos 
4521b7205bfSchristos 	pos = os_strchr(txt, ' ');
4531b7205bfSchristos 	if (pos)
4541b7205bfSchristos 		*pos++ = '\0';
4551b7205bfSchristos 
4561b7205bfSchristos 	if (os_strcmp(txt, "disable") == 0) {
4571b7205bfSchristos 		hostapd_wps_ap_pin_disable(hapd);
4581b7205bfSchristos 		return os_snprintf(buf, buflen, "OK\n");
4591b7205bfSchristos 	}
4601b7205bfSchristos 
4611b7205bfSchristos 	if (os_strcmp(txt, "random") == 0) {
4621b7205bfSchristos 		if (pos)
4631b7205bfSchristos 			timeout = atoi(pos);
4641b7205bfSchristos 		pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
4651b7205bfSchristos 		if (pin_txt == NULL)
4661b7205bfSchristos 			return -1;
4671b7205bfSchristos 		return os_snprintf(buf, buflen, "%s", pin_txt);
4681b7205bfSchristos 	}
4691b7205bfSchristos 
4701b7205bfSchristos 	if (os_strcmp(txt, "get") == 0) {
4711b7205bfSchristos 		pin_txt = hostapd_wps_ap_pin_get(hapd);
4721b7205bfSchristos 		if (pin_txt == NULL)
4731b7205bfSchristos 			return -1;
4741b7205bfSchristos 		return os_snprintf(buf, buflen, "%s", pin_txt);
4751b7205bfSchristos 	}
4761b7205bfSchristos 
4771b7205bfSchristos 	if (os_strcmp(txt, "set") == 0) {
4781b7205bfSchristos 		char *pin;
4791b7205bfSchristos 		if (pos == NULL)
4801b7205bfSchristos 			return -1;
4811b7205bfSchristos 		pin = pos;
4821b7205bfSchristos 		pos = os_strchr(pos, ' ');
4831b7205bfSchristos 		if (pos) {
4841b7205bfSchristos 			*pos++ = '\0';
4851b7205bfSchristos 			timeout = atoi(pos);
4861b7205bfSchristos 		}
4871b7205bfSchristos 		if (os_strlen(pin) > buflen)
4881b7205bfSchristos 			return -1;
4891b7205bfSchristos 		if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
4901b7205bfSchristos 			return -1;
4911b7205bfSchristos 		return os_snprintf(buf, buflen, "%s", pin);
4921b7205bfSchristos 	}
4931b7205bfSchristos 
4941b7205bfSchristos 	return -1;
4951b7205bfSchristos }
49642669be3Schristos 
49742669be3Schristos 
hostapd_ctrl_iface_wps_config(struct hostapd_data * hapd,char * txt)49842669be3Schristos static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
49942669be3Schristos {
50042669be3Schristos 	char *pos;
50142669be3Schristos 	char *ssid, *auth, *encr = NULL, *key = NULL;
50242669be3Schristos 
50342669be3Schristos 	ssid = txt;
50442669be3Schristos 	pos = os_strchr(txt, ' ');
50542669be3Schristos 	if (!pos)
50642669be3Schristos 		return -1;
50742669be3Schristos 	*pos++ = '\0';
50842669be3Schristos 
50942669be3Schristos 	auth = pos;
51042669be3Schristos 	pos = os_strchr(pos, ' ');
51142669be3Schristos 	if (pos) {
51242669be3Schristos 		*pos++ = '\0';
51342669be3Schristos 		encr = pos;
51442669be3Schristos 		pos = os_strchr(pos, ' ');
51542669be3Schristos 		if (pos) {
51642669be3Schristos 			*pos++ = '\0';
51742669be3Schristos 			key = pos;
51842669be3Schristos 		}
51942669be3Schristos 	}
52042669be3Schristos 
52142669be3Schristos 	return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
52242669be3Schristos }
52336d97821Schristos 
52436d97821Schristos 
pbc_status_str(enum pbc_status status)52536d97821Schristos static const char * pbc_status_str(enum pbc_status status)
52636d97821Schristos {
52736d97821Schristos 	switch (status) {
52836d97821Schristos 	case WPS_PBC_STATUS_DISABLE:
52936d97821Schristos 		return "Disabled";
53036d97821Schristos 	case WPS_PBC_STATUS_ACTIVE:
53136d97821Schristos 		return "Active";
53236d97821Schristos 	case WPS_PBC_STATUS_TIMEOUT:
53336d97821Schristos 		return "Timed-out";
53436d97821Schristos 	case WPS_PBC_STATUS_OVERLAP:
53536d97821Schristos 		return "Overlap";
53636d97821Schristos 	default:
53736d97821Schristos 		return "Unknown";
53836d97821Schristos 	}
53936d97821Schristos }
54036d97821Schristos 
54136d97821Schristos 
hostapd_ctrl_iface_wps_get_status(struct hostapd_data * hapd,char * buf,size_t buflen)54236d97821Schristos static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
54336d97821Schristos 					     char *buf, size_t buflen)
54436d97821Schristos {
54536d97821Schristos 	int ret;
54636d97821Schristos 	char *pos, *end;
54736d97821Schristos 
54836d97821Schristos 	pos = buf;
54936d97821Schristos 	end = buf + buflen;
55036d97821Schristos 
55136d97821Schristos 	ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
55236d97821Schristos 			  pbc_status_str(hapd->wps_stats.pbc_status));
55336d97821Schristos 
5549a53cbbeSchristos 	if (os_snprintf_error(end - pos, ret))
55536d97821Schristos 		return pos - buf;
55636d97821Schristos 	pos += ret;
55736d97821Schristos 
55836d97821Schristos 	ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
55936d97821Schristos 			  (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
56036d97821Schristos 			   "Success":
56136d97821Schristos 			   (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
56236d97821Schristos 			    "Failed" : "None")));
56336d97821Schristos 
5649a53cbbeSchristos 	if (os_snprintf_error(end - pos, ret))
56536d97821Schristos 		return pos - buf;
56636d97821Schristos 	pos += ret;
56736d97821Schristos 
56836d97821Schristos 	/* If status == Failure - Add possible Reasons */
56936d97821Schristos 	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
57036d97821Schristos 	   hapd->wps_stats.failure_reason > 0) {
57136d97821Schristos 		ret = os_snprintf(pos, end - pos,
57236d97821Schristos 				  "Failure Reason: %s\n",
57336d97821Schristos 				  wps_ei_str(hapd->wps_stats.failure_reason));
57436d97821Schristos 
5759a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
57636d97821Schristos 			return pos - buf;
57736d97821Schristos 		pos += ret;
57836d97821Schristos 	}
57936d97821Schristos 
58036d97821Schristos 	if (hapd->wps_stats.status) {
58136d97821Schristos 		ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
58236d97821Schristos 				  MAC2STR(hapd->wps_stats.peer_addr));
58336d97821Schristos 
5849a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
58536d97821Schristos 			return pos - buf;
58636d97821Schristos 		pos += ret;
58736d97821Schristos 	}
58836d97821Schristos 
58936d97821Schristos 	return pos - buf;
59036d97821Schristos }
59136d97821Schristos 
5928dbcf02cSchristos #endif /* CONFIG_WPS */
5938dbcf02cSchristos 
59436d97821Schristos #ifdef CONFIG_HS20
59536d97821Schristos 
hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data * hapd,const char * cmd)59636d97821Schristos static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
59736d97821Schristos 					     const char *cmd)
59836d97821Schristos {
59936d97821Schristos 	u8 addr[ETH_ALEN];
60036d97821Schristos 	const char *url;
60136d97821Schristos 
60236d97821Schristos 	if (hwaddr_aton(cmd, addr))
60336d97821Schristos 		return -1;
60436d97821Schristos 	url = cmd + 17;
60536d97821Schristos 	if (*url == '\0') {
60636d97821Schristos 		url = NULL;
60736d97821Schristos 	} else {
60836d97821Schristos 		if (*url != ' ')
60936d97821Schristos 			return -1;
61036d97821Schristos 		url++;
61136d97821Schristos 		if (*url == '\0')
61236d97821Schristos 			url = NULL;
61336d97821Schristos 	}
61436d97821Schristos 
61536d97821Schristos 	return hs20_send_wnm_notification(hapd, addr, 1, url);
61636d97821Schristos }
61736d97821Schristos 
61836d97821Schristos 
hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data * hapd,const char * cmd)61936d97821Schristos static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
62036d97821Schristos 					      const char *cmd)
62136d97821Schristos {
62236d97821Schristos 	u8 addr[ETH_ALEN];
62336d97821Schristos 	int code, reauth_delay, ret;
62436d97821Schristos 	const char *pos;
62536d97821Schristos 	size_t url_len;
62636d97821Schristos 	struct wpabuf *req;
62736d97821Schristos 
62836d97821Schristos 	/* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
62936d97821Schristos 	if (hwaddr_aton(cmd, addr))
63036d97821Schristos 		return -1;
63136d97821Schristos 
63236d97821Schristos 	pos = os_strchr(cmd, ' ');
63336d97821Schristos 	if (pos == NULL)
63436d97821Schristos 		return -1;
63536d97821Schristos 	pos++;
63636d97821Schristos 	code = atoi(pos);
63736d97821Schristos 
63836d97821Schristos 	pos = os_strchr(pos, ' ');
63936d97821Schristos 	if (pos == NULL)
64036d97821Schristos 		return -1;
64136d97821Schristos 	pos++;
64236d97821Schristos 	reauth_delay = atoi(pos);
64336d97821Schristos 
64436d97821Schristos 	url_len = 0;
64536d97821Schristos 	pos = os_strchr(pos, ' ');
64636d97821Schristos 	if (pos) {
64736d97821Schristos 		pos++;
64836d97821Schristos 		url_len = os_strlen(pos);
64936d97821Schristos 	}
65036d97821Schristos 
65136d97821Schristos 	req = wpabuf_alloc(4 + url_len);
65236d97821Schristos 	if (req == NULL)
65336d97821Schristos 		return -1;
65436d97821Schristos 	wpabuf_put_u8(req, code);
65536d97821Schristos 	wpabuf_put_le16(req, reauth_delay);
65636d97821Schristos 	wpabuf_put_u8(req, url_len);
65736d97821Schristos 	if (pos)
65836d97821Schristos 		wpabuf_put_data(req, pos, url_len);
65936d97821Schristos 
66036d97821Schristos 	wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
66136d97821Schristos 		   " to indicate imminent deauthentication (code=%d "
66236d97821Schristos 		   "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
66336d97821Schristos 	ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
66436d97821Schristos 	wpabuf_free(req);
66536d97821Schristos 	return ret;
66636d97821Schristos }
66736d97821Schristos 
66836d97821Schristos #endif /* CONFIG_HS20 */
66936d97821Schristos 
67036d97821Schristos 
67136d97821Schristos #ifdef CONFIG_INTERWORKING
67236d97821Schristos 
hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data * hapd,const char * cmd)67336d97821Schristos static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
67436d97821Schristos 					      const char *cmd)
67536d97821Schristos {
67636d97821Schristos 	u8 qos_map_set[16 + 2 * 21], count = 0;
67736d97821Schristos 	const char *pos = cmd;
67836d97821Schristos 	int val, ret;
67936d97821Schristos 
68036d97821Schristos 	for (;;) {
68136d97821Schristos 		if (count == sizeof(qos_map_set)) {
68236d97821Schristos 			wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
68336d97821Schristos 			return -1;
68436d97821Schristos 		}
68536d97821Schristos 
68636d97821Schristos 		val = atoi(pos);
68736d97821Schristos 		if (val < 0 || val > 255) {
68836d97821Schristos 			wpa_printf(MSG_INFO, "Invalid QoS Map Set");
68936d97821Schristos 			return -1;
69036d97821Schristos 		}
69136d97821Schristos 
69236d97821Schristos 		qos_map_set[count++] = val;
69336d97821Schristos 		pos = os_strchr(pos, ',');
69436d97821Schristos 		if (!pos)
69536d97821Schristos 			break;
69636d97821Schristos 		pos++;
69736d97821Schristos 	}
69836d97821Schristos 
69936d97821Schristos 	if (count < 16 || count & 1) {
70036d97821Schristos 		wpa_printf(MSG_INFO, "Invalid QoS Map Set");
70136d97821Schristos 		return -1;
70236d97821Schristos 	}
70336d97821Schristos 
70436d97821Schristos 	ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
70536d97821Schristos 	if (ret) {
70636d97821Schristos 		wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
70736d97821Schristos 		return -1;
70836d97821Schristos 	}
70936d97821Schristos 
71036d97821Schristos 	os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
71136d97821Schristos 	hapd->conf->qos_map_set_len = count;
71236d97821Schristos 
71336d97821Schristos 	return 0;
71436d97821Schristos }
71536d97821Schristos 
71636d97821Schristos 
hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data * hapd,const char * cmd)71736d97821Schristos static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
71836d97821Schristos 						const char *cmd)
71936d97821Schristos {
72036d97821Schristos 	u8 addr[ETH_ALEN];
72136d97821Schristos 	struct sta_info *sta;
72236d97821Schristos 	struct wpabuf *buf;
72336d97821Schristos 	u8 *qos_map_set = hapd->conf->qos_map_set;
72436d97821Schristos 	u8 qos_map_set_len = hapd->conf->qos_map_set_len;
72536d97821Schristos 	int ret;
72636d97821Schristos 
72736d97821Schristos 	if (!qos_map_set_len) {
72836d97821Schristos 		wpa_printf(MSG_INFO, "QoS Map Set is not set");
72936d97821Schristos 		return -1;
73036d97821Schristos 	}
73136d97821Schristos 
73236d97821Schristos 	if (hwaddr_aton(cmd, addr))
73336d97821Schristos 		return -1;
73436d97821Schristos 
73536d97821Schristos 	sta = ap_get_sta(hapd, addr);
73636d97821Schristos 	if (sta == NULL) {
73736d97821Schristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
73836d97821Schristos 			   "for QoS Map Configuration message",
73936d97821Schristos 			   MAC2STR(addr));
74036d97821Schristos 		return -1;
74136d97821Schristos 	}
74236d97821Schristos 
74336d97821Schristos 	if (!sta->qos_map_enabled) {
74436d97821Schristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
74536d97821Schristos 			   "support for QoS Map", MAC2STR(addr));
74636d97821Schristos 		return -1;
74736d97821Schristos 	}
74836d97821Schristos 
74936d97821Schristos 	buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
75036d97821Schristos 	if (buf == NULL)
75136d97821Schristos 		return -1;
75236d97821Schristos 
75336d97821Schristos 	wpabuf_put_u8(buf, WLAN_ACTION_QOS);
75436d97821Schristos 	wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
75536d97821Schristos 
75636d97821Schristos 	/* QoS Map Set Element */
75736d97821Schristos 	wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
75836d97821Schristos 	wpabuf_put_u8(buf, qos_map_set_len);
75936d97821Schristos 	wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
76036d97821Schristos 
76136d97821Schristos 	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
76236d97821Schristos 				      wpabuf_head(buf), wpabuf_len(buf));
76336d97821Schristos 	wpabuf_free(buf);
76436d97821Schristos 
76536d97821Schristos 	return ret;
76636d97821Schristos }
76736d97821Schristos 
76836d97821Schristos #endif /* CONFIG_INTERWORKING */
76936d97821Schristos 
7708dbcf02cSchristos 
771ebb5671cSchristos #ifdef CONFIG_WNM_AP
77262a52023Schristos 
hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data * hapd,const char * cmd)77362a52023Schristos static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
77462a52023Schristos 						const char *cmd)
77562a52023Schristos {
77662a52023Schristos 	u8 addr[ETH_ALEN];
77762a52023Schristos 	int disassoc_timer;
77836d97821Schristos 	struct sta_info *sta;
77962a52023Schristos 
78062a52023Schristos 	if (hwaddr_aton(cmd, addr))
78162a52023Schristos 		return -1;
78262a52023Schristos 	if (cmd[17] != ' ')
78362a52023Schristos 		return -1;
78462a52023Schristos 	disassoc_timer = atoi(cmd + 17);
78562a52023Schristos 
78636d97821Schristos 	sta = ap_get_sta(hapd, addr);
78736d97821Schristos 	if (sta == NULL) {
78836d97821Schristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR
78936d97821Schristos 			   " not found for disassociation imminent message",
79036d97821Schristos 			   MAC2STR(addr));
79162a52023Schristos 		return -1;
79262a52023Schristos 	}
79362a52023Schristos 
79436d97821Schristos 	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
79562a52023Schristos }
79662a52023Schristos 
79762a52023Schristos 
hostapd_ctrl_iface_ess_disassoc(struct hostapd_data * hapd,const char * cmd)79842669be3Schristos static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
79942669be3Schristos 					   const char *cmd)
80042669be3Schristos {
80142669be3Schristos 	u8 addr[ETH_ALEN];
80236d97821Schristos 	const char *url, *timerstr;
80336d97821Schristos 	int disassoc_timer;
80436d97821Schristos 	struct sta_info *sta;
80542669be3Schristos 
80642669be3Schristos 	if (hwaddr_aton(cmd, addr))
80742669be3Schristos 		return -1;
80842669be3Schristos 
80936d97821Schristos 	sta = ap_get_sta(hapd, addr);
81036d97821Schristos 	if (sta == NULL) {
81136d97821Schristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR
81236d97821Schristos 			   " not found for ESS disassociation imminent message",
81336d97821Schristos 			   MAC2STR(addr));
81442669be3Schristos 		return -1;
81542669be3Schristos 	}
81642669be3Schristos 
81736d97821Schristos 	timerstr = cmd + 17;
81836d97821Schristos 	if (*timerstr != ' ')
81936d97821Schristos 		return -1;
82036d97821Schristos 	timerstr++;
82136d97821Schristos 	disassoc_timer = atoi(timerstr);
82236d97821Schristos 	if (disassoc_timer < 0 || disassoc_timer > 65535)
82336d97821Schristos 		return -1;
82436d97821Schristos 
82536d97821Schristos 	url = os_strchr(timerstr, ' ');
82636d97821Schristos 	if (url == NULL)
82736d97821Schristos 		return -1;
82836d97821Schristos 	url++;
82936d97821Schristos 
83036d97821Schristos 	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
83142669be3Schristos }
83242669be3Schristos 
8339a53cbbeSchristos 
hostapd_ctrl_iface_bss_tm_req(struct hostapd_data * hapd,const char * cmd)8349a53cbbeSchristos static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
8359a53cbbeSchristos 					 const char *cmd)
8369a53cbbeSchristos {
8379a53cbbeSchristos 	u8 addr[ETH_ALEN];
8389a53cbbeSchristos 	const char *pos, *end;
8399a53cbbeSchristos 	int disassoc_timer = 0;
8409a53cbbeSchristos 	struct sta_info *sta;
8419a53cbbeSchristos 	u8 req_mode = 0, valid_int = 0x01;
8429a53cbbeSchristos 	u8 bss_term_dur[12];
8439a53cbbeSchristos 	char *url = NULL;
8449a53cbbeSchristos 	int ret;
8459a53cbbeSchristos 	u8 nei_rep[1000];
846ebb5671cSchristos 	int nei_len;
847928750b6Schristos 	u8 mbo[10];
848928750b6Schristos 	size_t mbo_len = 0;
8499a53cbbeSchristos 
8509a53cbbeSchristos 	if (hwaddr_aton(cmd, addr)) {
8519a53cbbeSchristos 		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
8529a53cbbeSchristos 		return -1;
8539a53cbbeSchristos 	}
8549a53cbbeSchristos 
8559a53cbbeSchristos 	sta = ap_get_sta(hapd, addr);
8569a53cbbeSchristos 	if (sta == NULL) {
8579a53cbbeSchristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR
8589a53cbbeSchristos 			   " not found for BSS TM Request message",
8599a53cbbeSchristos 			   MAC2STR(addr));
8609a53cbbeSchristos 		return -1;
8619a53cbbeSchristos 	}
8629a53cbbeSchristos 
8639a53cbbeSchristos 	pos = os_strstr(cmd, " disassoc_timer=");
8649a53cbbeSchristos 	if (pos) {
8659a53cbbeSchristos 		pos += 16;
8669a53cbbeSchristos 		disassoc_timer = atoi(pos);
8679a53cbbeSchristos 		if (disassoc_timer < 0 || disassoc_timer > 65535) {
8689a53cbbeSchristos 			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
8699a53cbbeSchristos 			return -1;
8709a53cbbeSchristos 		}
8719a53cbbeSchristos 	}
8729a53cbbeSchristos 
8739a53cbbeSchristos 	pos = os_strstr(cmd, " valid_int=");
8749a53cbbeSchristos 	if (pos) {
8759a53cbbeSchristos 		pos += 11;
8769a53cbbeSchristos 		valid_int = atoi(pos);
8779a53cbbeSchristos 	}
8789a53cbbeSchristos 
8799a53cbbeSchristos 	pos = os_strstr(cmd, " bss_term=");
8809a53cbbeSchristos 	if (pos) {
8819a53cbbeSchristos 		pos += 10;
8829a53cbbeSchristos 		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
8839a53cbbeSchristos 		/* TODO: TSF configurable/learnable */
8849a53cbbeSchristos 		bss_term_dur[0] = 4; /* Subelement ID */
8859a53cbbeSchristos 		bss_term_dur[1] = 10; /* Length */
886*0d69f216Schristos 		os_memset(&bss_term_dur[2], 0, 8);
8879a53cbbeSchristos 		end = os_strchr(pos, ',');
8889a53cbbeSchristos 		if (end == NULL) {
8899a53cbbeSchristos 			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
8909a53cbbeSchristos 			return -1;
8919a53cbbeSchristos 		}
8929a53cbbeSchristos 		end++;
8939a53cbbeSchristos 		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
8949a53cbbeSchristos 	}
8959a53cbbeSchristos 
896ebb5671cSchristos 	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
897ebb5671cSchristos 						  sizeof(nei_rep));
898ebb5671cSchristos 	if (nei_len < 0)
8999a53cbbeSchristos 		return -1;
9009a53cbbeSchristos 
9019a53cbbeSchristos 	pos = os_strstr(cmd, " url=");
9029a53cbbeSchristos 	if (pos) {
9039a53cbbeSchristos 		size_t len;
9049a53cbbeSchristos 		pos += 5;
9059a53cbbeSchristos 		end = os_strchr(pos, ' ');
9069a53cbbeSchristos 		if (end)
9079a53cbbeSchristos 			len = end - pos;
9089a53cbbeSchristos 		else
9099a53cbbeSchristos 			len = os_strlen(pos);
9109a53cbbeSchristos 		url = os_malloc(len + 1);
9119a53cbbeSchristos 		if (url == NULL)
9129a53cbbeSchristos 			return -1;
9139a53cbbeSchristos 		os_memcpy(url, pos, len);
9149a53cbbeSchristos 		url[len] = '\0';
9159a53cbbeSchristos 		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
9169a53cbbeSchristos 	}
9179a53cbbeSchristos 
9189a53cbbeSchristos 	if (os_strstr(cmd, " pref=1"))
9199a53cbbeSchristos 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
9209a53cbbeSchristos 	if (os_strstr(cmd, " abridged=1"))
9219a53cbbeSchristos 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
9229a53cbbeSchristos 	if (os_strstr(cmd, " disassoc_imminent=1"))
9239a53cbbeSchristos 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
9249a53cbbeSchristos 
925928750b6Schristos #ifdef CONFIG_MBO
926928750b6Schristos 	pos = os_strstr(cmd, "mbo=");
927928750b6Schristos 	if (pos) {
928928750b6Schristos 		unsigned int mbo_reason, cell_pref, reassoc_delay;
929928750b6Schristos 		u8 *mbo_pos = mbo;
930928750b6Schristos 
931928750b6Schristos 		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
932928750b6Schristos 			     &reassoc_delay, &cell_pref);
933928750b6Schristos 		if (ret != 3) {
934928750b6Schristos 			wpa_printf(MSG_DEBUG,
935928750b6Schristos 				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
936ebb5671cSchristos 			ret = -1;
937ebb5671cSchristos 			goto fail;
938928750b6Schristos 		}
939928750b6Schristos 
940928750b6Schristos 		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
941928750b6Schristos 			wpa_printf(MSG_DEBUG,
942928750b6Schristos 				   "Invalid MBO transition reason code %u",
943928750b6Schristos 				   mbo_reason);
944ebb5671cSchristos 			ret = -1;
945ebb5671cSchristos 			goto fail;
946928750b6Schristos 		}
947928750b6Schristos 
948928750b6Schristos 		/* Valid values for Cellular preference are: 0, 1, 255 */
949928750b6Schristos 		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
950928750b6Schristos 			wpa_printf(MSG_DEBUG,
951928750b6Schristos 				   "Invalid MBO cellular capability %u",
952928750b6Schristos 				   cell_pref);
953ebb5671cSchristos 			ret = -1;
954ebb5671cSchristos 			goto fail;
955928750b6Schristos 		}
956928750b6Schristos 
957928750b6Schristos 		if (reassoc_delay > 65535 ||
958928750b6Schristos 		    (reassoc_delay &&
959928750b6Schristos 		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
960928750b6Schristos 			wpa_printf(MSG_DEBUG,
961928750b6Schristos 				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
962ebb5671cSchristos 			ret = -1;
963ebb5671cSchristos 			goto fail;
964928750b6Schristos 		}
965928750b6Schristos 
966928750b6Schristos 		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
967928750b6Schristos 		*mbo_pos++ = 1;
968928750b6Schristos 		*mbo_pos++ = mbo_reason;
969928750b6Schristos 		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
970928750b6Schristos 		*mbo_pos++ = 1;
971928750b6Schristos 		*mbo_pos++ = cell_pref;
972928750b6Schristos 
973928750b6Schristos 		if (reassoc_delay) {
974928750b6Schristos 			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
975928750b6Schristos 			*mbo_pos++ = 2;
976928750b6Schristos 			WPA_PUT_LE16(mbo_pos, reassoc_delay);
977928750b6Schristos 			mbo_pos += 2;
978928750b6Schristos 		}
979928750b6Schristos 
980928750b6Schristos 		mbo_len = mbo_pos - mbo;
981928750b6Schristos 	}
982928750b6Schristos #endif /* CONFIG_MBO */
983928750b6Schristos 
9849a53cbbeSchristos 	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
9859a53cbbeSchristos 				  valid_int, bss_term_dur, url,
986ebb5671cSchristos 				  nei_len ? nei_rep : NULL, nei_len,
987ebb5671cSchristos 				  mbo_len ? mbo : NULL, mbo_len);
988ebb5671cSchristos #ifdef CONFIG_MBO
989ebb5671cSchristos fail:
990ebb5671cSchristos #endif /* CONFIG_MBO */
9919a53cbbeSchristos 	os_free(url);
9929a53cbbeSchristos 	return ret;
9939a53cbbeSchristos }
9949a53cbbeSchristos 
995ebb5671cSchristos 
hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data * hapd,const char * cmd)996ebb5671cSchristos static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
997ebb5671cSchristos 					     const char *cmd)
998ebb5671cSchristos {
999ebb5671cSchristos 	u8 addr[ETH_ALEN];
1000ebb5671cSchristos 	struct sta_info *sta;
1001ebb5671cSchristos 	const char *pos;
1002ebb5671cSchristos 	unsigned int auto_report, timeout;
1003ebb5671cSchristos 
1004ebb5671cSchristos 	if (hwaddr_aton(cmd, addr)) {
1005ebb5671cSchristos 		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
1006ebb5671cSchristos 		return -1;
1007ebb5671cSchristos 	}
1008ebb5671cSchristos 
1009ebb5671cSchristos 	sta = ap_get_sta(hapd, addr);
1010ebb5671cSchristos 	if (!sta) {
1011ebb5671cSchristos 		wpa_printf(MSG_DEBUG, "Station " MACSTR
1012ebb5671cSchristos 			   " not found for Collocated Interference Request",
1013ebb5671cSchristos 			   MAC2STR(addr));
1014ebb5671cSchristos 		return -1;
1015ebb5671cSchristos 	}
1016ebb5671cSchristos 
1017ebb5671cSchristos 	pos = cmd + 17;
1018ebb5671cSchristos 	if (*pos != ' ')
1019ebb5671cSchristos 		return -1;
1020ebb5671cSchristos 	pos++;
1021ebb5671cSchristos 	auto_report = atoi(pos);
1022ebb5671cSchristos 	pos = os_strchr(pos, ' ');
1023ebb5671cSchristos 	if (!pos)
1024ebb5671cSchristos 		return -1;
1025ebb5671cSchristos 	pos++;
1026ebb5671cSchristos 	timeout = atoi(pos);
1027ebb5671cSchristos 
1028ebb5671cSchristos 	return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
1029ebb5671cSchristos }
1030ebb5671cSchristos 
1031ebb5671cSchristos #endif /* CONFIG_WNM_AP */
103262a52023Schristos 
103342669be3Schristos 
hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data * hapd,char * buf,size_t buflen)1034928750b6Schristos static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
103542669be3Schristos 					   char *buf, size_t buflen)
103642669be3Schristos {
1037928750b6Schristos 	int ret = 0;
103842669be3Schristos 	char *pos, *end;
103942669be3Schristos 
104042669be3Schristos 	pos = buf;
104142669be3Schristos 	end = buf + buflen;
104242669be3Schristos 
1043928750b6Schristos 	WPA_ASSERT(hapd->conf->wpa_key_mgmt);
104442669be3Schristos 
104542669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
104642669be3Schristos 		ret = os_snprintf(pos, end - pos, "WPA-PSK ");
10479a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
104842669be3Schristos 			return pos - buf;
104942669be3Schristos 		pos += ret;
105042669be3Schristos 	}
105142669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
105242669be3Schristos 		ret = os_snprintf(pos, end - pos, "WPA-EAP ");
10539a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
105442669be3Schristos 			return pos - buf;
105542669be3Schristos 		pos += ret;
105642669be3Schristos 	}
1057ebb5671cSchristos #ifdef CONFIG_IEEE80211R_AP
105842669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
105942669be3Schristos 		ret = os_snprintf(pos, end - pos, "FT-PSK ");
10609a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
106142669be3Schristos 			return pos - buf;
106242669be3Schristos 		pos += ret;
106342669be3Schristos 	}
106442669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
106542669be3Schristos 		ret = os_snprintf(pos, end - pos, "FT-EAP ");
10669a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
106742669be3Schristos 			return pos - buf;
106842669be3Schristos 		pos += ret;
106942669be3Schristos 	}
1070ebb5671cSchristos #ifdef CONFIG_SHA384
1071ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
1072ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 ");
1073ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1074ebb5671cSchristos 			return pos - buf;
1075ebb5671cSchristos 		pos += ret;
1076ebb5671cSchristos 	}
1077ebb5671cSchristos #endif /* CONFIG_SHA384 */
107836d97821Schristos #ifdef CONFIG_SAE
107936d97821Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
108036d97821Schristos 		ret = os_snprintf(pos, end - pos, "FT-SAE ");
10819a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
108236d97821Schristos 			return pos - buf;
108336d97821Schristos 		pos += ret;
108436d97821Schristos 	}
108536d97821Schristos #endif /* CONFIG_SAE */
1086ebb5671cSchristos #ifdef CONFIG_FILS
1087ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
1088ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
1089ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1090ebb5671cSchristos 			return pos - buf;
1091ebb5671cSchristos 		pos += ret;
1092ebb5671cSchristos 	}
1093ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
1094ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
1095ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1096ebb5671cSchristos 			return pos - buf;
1097ebb5671cSchristos 		pos += ret;
1098ebb5671cSchristos 	}
1099ebb5671cSchristos #endif /* CONFIG_FILS */
1100ebb5671cSchristos #endif /* CONFIG_IEEE80211R_AP */
110142669be3Schristos #ifdef CONFIG_IEEE80211W
110242669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
110342669be3Schristos 		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
11049a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
110542669be3Schristos 			return pos - buf;
110642669be3Schristos 		pos += ret;
110742669be3Schristos 	}
110842669be3Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
110942669be3Schristos 		ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
11109a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
111142669be3Schristos 			return pos - buf;
111242669be3Schristos 		pos += ret;
111342669be3Schristos 	}
111442669be3Schristos #endif /* CONFIG_IEEE80211W */
111536d97821Schristos #ifdef CONFIG_SAE
111636d97821Schristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
111736d97821Schristos 		ret = os_snprintf(pos, end - pos, "SAE ");
11189a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
111936d97821Schristos 			return pos - buf;
112036d97821Schristos 		pos += ret;
112136d97821Schristos 	}
112236d97821Schristos #endif /* CONFIG_SAE */
11239a53cbbeSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
11249a53cbbeSchristos 		ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
11259a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
11269a53cbbeSchristos 			return pos - buf;
11279a53cbbeSchristos 		pos += ret;
11289a53cbbeSchristos 	}
11299a53cbbeSchristos 	if (hapd->conf->wpa_key_mgmt &
11309a53cbbeSchristos 	    WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
11319a53cbbeSchristos 		ret = os_snprintf(pos, end - pos,
11329a53cbbeSchristos 				  "WPA-EAP-SUITE-B-192 ");
11339a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
11349a53cbbeSchristos 			return pos - buf;
11359a53cbbeSchristos 		pos += ret;
11369a53cbbeSchristos 	}
1137ebb5671cSchristos #ifdef CONFIG_FILS
1138ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
1139ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
1140ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1141ebb5671cSchristos 			return pos - buf;
1142ebb5671cSchristos 		pos += ret;
1143ebb5671cSchristos 	}
1144ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
1145ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
1146ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1147ebb5671cSchristos 			return pos - buf;
1148ebb5671cSchristos 		pos += ret;
1149ebb5671cSchristos 	}
1150ebb5671cSchristos #endif /* CONFIG_FILS */
1151ebb5671cSchristos 
1152ebb5671cSchristos #ifdef CONFIG_OWE
1153ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
1154ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "OWE ");
1155ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1156ebb5671cSchristos 			return pos - buf;
1157ebb5671cSchristos 		pos += ret;
1158ebb5671cSchristos 	}
1159ebb5671cSchristos #endif /* CONFIG_OWE */
1160ebb5671cSchristos 
1161ebb5671cSchristos #ifdef CONFIG_DPP
1162ebb5671cSchristos 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
1163ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, "DPP ");
1164ebb5671cSchristos 		if (os_snprintf_error(end - pos, ret))
1165ebb5671cSchristos 			return pos - buf;
1166ebb5671cSchristos 		pos += ret;
1167ebb5671cSchristos 	}
1168ebb5671cSchristos #endif /* CONFIG_DPP */
116942669be3Schristos 
1170928750b6Schristos 	if (pos > buf && *(pos - 1) == ' ') {
1171928750b6Schristos 		*(pos - 1) = '\0';
1172928750b6Schristos 		pos--;
1173928750b6Schristos 	}
1174928750b6Schristos 
1175928750b6Schristos 	return pos - buf;
1176928750b6Schristos }
1177928750b6Schristos 
1178928750b6Schristos 
hostapd_ctrl_iface_get_config(struct hostapd_data * hapd,char * buf,size_t buflen)1179928750b6Schristos static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
1180928750b6Schristos 					 char *buf, size_t buflen)
1181928750b6Schristos {
1182928750b6Schristos 	int ret;
1183928750b6Schristos 	char *pos, *end;
1184928750b6Schristos 
1185928750b6Schristos 	pos = buf;
1186928750b6Schristos 	end = buf + buflen;
1187928750b6Schristos 
1188928750b6Schristos 	ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
1189928750b6Schristos 			  "ssid=%s\n",
1190928750b6Schristos 			  MAC2STR(hapd->own_addr),
1191928750b6Schristos 			  wpa_ssid_txt(hapd->conf->ssid.ssid,
1192928750b6Schristos 				       hapd->conf->ssid.ssid_len));
1193928750b6Schristos 	if (os_snprintf_error(end - pos, ret))
1194928750b6Schristos 		return pos - buf;
1195928750b6Schristos 	pos += ret;
1196928750b6Schristos 
1197928750b6Schristos #ifdef CONFIG_WPS
1198928750b6Schristos 	ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
1199928750b6Schristos 			  hapd->conf->wps_state == 0 ? "disabled" :
1200928750b6Schristos 			  (hapd->conf->wps_state == 1 ? "not configured" :
1201928750b6Schristos 			   "configured"));
1202928750b6Schristos 	if (os_snprintf_error(end - pos, ret))
1203928750b6Schristos 		return pos - buf;
1204928750b6Schristos 	pos += ret;
1205928750b6Schristos 
1206928750b6Schristos 	if (hapd->conf->wps_state && hapd->conf->wpa &&
1207928750b6Schristos 	    hapd->conf->ssid.wpa_passphrase) {
1208928750b6Schristos 		ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
1209928750b6Schristos 				  hapd->conf->ssid.wpa_passphrase);
1210928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
1211928750b6Schristos 			return pos - buf;
1212928750b6Schristos 		pos += ret;
1213928750b6Schristos 	}
1214928750b6Schristos 
1215928750b6Schristos 	if (hapd->conf->wps_state && hapd->conf->wpa &&
1216928750b6Schristos 	    hapd->conf->ssid.wpa_psk &&
1217928750b6Schristos 	    hapd->conf->ssid.wpa_psk->group) {
1218928750b6Schristos 		char hex[PMK_LEN * 2 + 1];
1219928750b6Schristos 		wpa_snprintf_hex(hex, sizeof(hex),
1220928750b6Schristos 				 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
1221928750b6Schristos 		ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
1222928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
1223928750b6Schristos 			return pos - buf;
1224928750b6Schristos 		pos += ret;
1225928750b6Schristos 	}
1226928750b6Schristos #endif /* CONFIG_WPS */
1227928750b6Schristos 
1228928750b6Schristos 	if (hapd->conf->wpa) {
1229928750b6Schristos 		ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
1230928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
1231928750b6Schristos 			return pos - buf;
1232928750b6Schristos 		pos += ret;
1233928750b6Schristos 	}
1234928750b6Schristos 
1235928750b6Schristos 	if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
1236928750b6Schristos 		ret = os_snprintf(pos, end - pos, "key_mgmt=");
1237928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
1238928750b6Schristos 			return pos - buf;
1239928750b6Schristos 		pos += ret;
1240928750b6Schristos 
1241928750b6Schristos 		pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
1242928750b6Schristos 
124342669be3Schristos 		ret = os_snprintf(pos, end - pos, "\n");
12449a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
124542669be3Schristos 			return pos - buf;
124642669be3Schristos 		pos += ret;
124742669be3Schristos 	}
124842669be3Schristos 
124936d97821Schristos 	if (hapd->conf->wpa) {
125036d97821Schristos 		ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
125136d97821Schristos 				  wpa_cipher_txt(hapd->conf->wpa_group));
12529a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
125342669be3Schristos 			return pos - buf;
125442669be3Schristos 		pos += ret;
125542669be3Schristos 	}
125642669be3Schristos 
125742669be3Schristos 	if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
125842669be3Schristos 		ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
12599a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
126042669be3Schristos 			return pos - buf;
126142669be3Schristos 		pos += ret;
126242669be3Schristos 
126336d97821Schristos 		ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
126436d97821Schristos 					" ");
126536d97821Schristos 		if (ret < 0)
126642669be3Schristos 			return pos - buf;
126742669be3Schristos 		pos += ret;
126842669be3Schristos 
126942669be3Schristos 		ret = os_snprintf(pos, end - pos, "\n");
12709a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
127142669be3Schristos 			return pos - buf;
127242669be3Schristos 		pos += ret;
127342669be3Schristos 	}
127442669be3Schristos 
127542669be3Schristos 	if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
127642669be3Schristos 		ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
12779a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
127842669be3Schristos 			return pos - buf;
127942669be3Schristos 		pos += ret;
128042669be3Schristos 
128136d97821Schristos 		ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
128236d97821Schristos 					" ");
128336d97821Schristos 		if (ret < 0)
128442669be3Schristos 			return pos - buf;
128542669be3Schristos 		pos += ret;
128642669be3Schristos 
128742669be3Schristos 		ret = os_snprintf(pos, end - pos, "\n");
12889a53cbbeSchristos 		if (os_snprintf_error(end - pos, ret))
128942669be3Schristos 			return pos - buf;
129042669be3Schristos 		pos += ret;
129142669be3Schristos 	}
129242669be3Schristos 
129342669be3Schristos 	return pos - buf;
129442669be3Schristos }
129542669be3Schristos 
129642669be3Schristos 
hostapd_disassoc_accept_mac(struct hostapd_data * hapd)1297ebb5671cSchristos static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
1298ebb5671cSchristos {
1299ebb5671cSchristos 	struct sta_info *sta;
1300ebb5671cSchristos 	struct vlan_description vlan_id;
1301ebb5671cSchristos 
1302ebb5671cSchristos 	if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
1303ebb5671cSchristos 		return;
1304ebb5671cSchristos 
1305ebb5671cSchristos 	for (sta = hapd->sta_list; sta; sta = sta->next) {
1306ebb5671cSchristos 		if (!hostapd_maclist_found(hapd->conf->accept_mac,
1307ebb5671cSchristos 					   hapd->conf->num_accept_mac,
1308ebb5671cSchristos 					   sta->addr, &vlan_id) ||
1309ebb5671cSchristos 		    (vlan_id.notempty &&
1310ebb5671cSchristos 		     vlan_compare(&vlan_id, sta->vlan_desc)))
1311ebb5671cSchristos 			ap_sta_disconnect(hapd, sta, sta->addr,
1312ebb5671cSchristos 					  WLAN_REASON_UNSPECIFIED);
1313ebb5671cSchristos 	}
1314ebb5671cSchristos }
1315ebb5671cSchristos 
1316ebb5671cSchristos 
hostapd_disassoc_deny_mac(struct hostapd_data * hapd)1317ebb5671cSchristos static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
1318ebb5671cSchristos {
1319ebb5671cSchristos 	struct sta_info *sta;
1320ebb5671cSchristos 	struct vlan_description vlan_id;
1321ebb5671cSchristos 
1322ebb5671cSchristos 	for (sta = hapd->sta_list; sta; sta = sta->next) {
1323ebb5671cSchristos 		if (hostapd_maclist_found(hapd->conf->deny_mac,
1324ebb5671cSchristos 					  hapd->conf->num_deny_mac, sta->addr,
1325ebb5671cSchristos 					  &vlan_id) &&
1326ebb5671cSchristos 		    (!vlan_id.notempty ||
1327ebb5671cSchristos 		     !vlan_compare(&vlan_id, sta->vlan_desc)))
1328ebb5671cSchristos 			ap_sta_disconnect(hapd, sta, sta->addr,
1329ebb5671cSchristos 					  WLAN_REASON_UNSPECIFIED);
1330ebb5671cSchristos 	}
1331ebb5671cSchristos }
1332ebb5671cSchristos 
hostapd_ctrl_iface_set(struct hostapd_data * hapd,char * cmd)133342669be3Schristos static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
133442669be3Schristos {
133542669be3Schristos 	char *value;
133642669be3Schristos 	int ret = 0;
133742669be3Schristos 
133842669be3Schristos 	value = os_strchr(cmd, ' ');
133942669be3Schristos 	if (value == NULL)
134042669be3Schristos 		return -1;
134142669be3Schristos 	*value++ = '\0';
134242669be3Schristos 
134342669be3Schristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
134442669be3Schristos 	if (0) {
134542669be3Schristos #ifdef CONFIG_WPS_TESTING
134642669be3Schristos 	} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
134742669be3Schristos 		long int val;
134842669be3Schristos 		val = strtol(value, NULL, 0);
134942669be3Schristos 		if (val < 0 || val > 0xff) {
135042669be3Schristos 			ret = -1;
135142669be3Schristos 			wpa_printf(MSG_DEBUG, "WPS: Invalid "
135242669be3Schristos 				   "wps_version_number %ld", val);
135342669be3Schristos 		} else {
135442669be3Schristos 			wps_version_number = val;
135542669be3Schristos 			wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
135642669be3Schristos 				   "version %u.%u",
135742669be3Schristos 				   (wps_version_number & 0xf0) >> 4,
135842669be3Schristos 				   wps_version_number & 0x0f);
135942669be3Schristos 			hostapd_wps_update_ie(hapd);
136042669be3Schristos 		}
136142669be3Schristos 	} else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
136242669be3Schristos 		wps_testing_dummy_cred = atoi(value);
136342669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
136442669be3Schristos 			   wps_testing_dummy_cred);
136536d97821Schristos 	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
136636d97821Schristos 		wps_corrupt_pkhash = atoi(value);
136736d97821Schristos 		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
136836d97821Schristos 			   wps_corrupt_pkhash);
136942669be3Schristos #endif /* CONFIG_WPS_TESTING */
137036d97821Schristos #ifdef CONFIG_TESTING_OPTIONS
137136d97821Schristos 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
137236d97821Schristos 		hapd->ext_mgmt_frame_handling = atoi(value);
13739a53cbbeSchristos 	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
13749a53cbbeSchristos 		hapd->ext_eapol_frame_io = atoi(value);
1375ebb5671cSchristos #ifdef CONFIG_DPP
1376ebb5671cSchristos 	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
1377ebb5671cSchristos 		os_free(hapd->dpp_config_obj_override);
1378ebb5671cSchristos 		hapd->dpp_config_obj_override = os_strdup(value);
1379ebb5671cSchristos 	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
1380ebb5671cSchristos 		os_free(hapd->dpp_discovery_override);
1381ebb5671cSchristos 		hapd->dpp_discovery_override = os_strdup(value);
1382ebb5671cSchristos 	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
1383ebb5671cSchristos 		os_free(hapd->dpp_groups_override);
1384ebb5671cSchristos 		hapd->dpp_groups_override = os_strdup(value);
1385ebb5671cSchristos 	} else if (os_strcasecmp(cmd,
1386ebb5671cSchristos 				 "dpp_ignore_netaccesskey_mismatch") == 0) {
1387ebb5671cSchristos 		hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
1388ebb5671cSchristos 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
1389ebb5671cSchristos 		dpp_test = atoi(value);
1390ebb5671cSchristos #endif /* CONFIG_DPP */
139136d97821Schristos #endif /* CONFIG_TESTING_OPTIONS */
1392928750b6Schristos #ifdef CONFIG_MBO
1393928750b6Schristos 	} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
1394928750b6Schristos 		int val;
1395928750b6Schristos 
1396928750b6Schristos 		if (!hapd->conf->mbo_enabled)
1397928750b6Schristos 			return -1;
1398928750b6Schristos 
1399928750b6Schristos 		val = atoi(value);
1400928750b6Schristos 		if (val < 0 || val > 1)
1401928750b6Schristos 			return -1;
1402928750b6Schristos 
1403928750b6Schristos 		hapd->mbo_assoc_disallow = val;
1404928750b6Schristos 		ieee802_11_update_beacons(hapd->iface);
1405928750b6Schristos 
1406928750b6Schristos 		/*
1407928750b6Schristos 		 * TODO: Need to configure drivers that do AP MLME offload with
1408928750b6Schristos 		 * disallowing station logic.
1409928750b6Schristos 		 */
1410928750b6Schristos #endif /* CONFIG_MBO */
1411ebb5671cSchristos #ifdef CONFIG_DPP
1412ebb5671cSchristos 	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
1413ebb5671cSchristos 		os_free(hapd->dpp_configurator_params);
1414ebb5671cSchristos 		hapd->dpp_configurator_params = os_strdup(value);
1415ebb5671cSchristos #endif /* CONFIG_DPP */
141662a52023Schristos 	} else {
141762a52023Schristos 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
141836d97821Schristos 		if (ret)
141936d97821Schristos 			return ret;
142036d97821Schristos 
142136d97821Schristos 		if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
1422ebb5671cSchristos 			hostapd_disassoc_deny_mac(hapd);
1423ebb5671cSchristos 		} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
1424ebb5671cSchristos 			hostapd_disassoc_accept_mac(hapd);
1425ebb5671cSchristos 		} else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
1426ebb5671cSchristos 			   os_strncmp(cmd, "wmm_ac_", 7) == 0) {
1427ebb5671cSchristos 			hapd->parameter_set_count++;
1428ebb5671cSchristos 			if (ieee802_11_update_beacons(hapd->iface))
1429ebb5671cSchristos 				wpa_printf(MSG_DEBUG,
1430ebb5671cSchristos 					   "Failed to update beacons with WMM parameters");
143136d97821Schristos 		}
143242669be3Schristos 	}
143342669be3Schristos 
143442669be3Schristos 	return ret;
143542669be3Schristos }
143642669be3Schristos 
143742669be3Schristos 
hostapd_ctrl_iface_get(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)143842669be3Schristos static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
143942669be3Schristos 				  char *buf, size_t buflen)
144042669be3Schristos {
144142669be3Schristos 	int res;
144242669be3Schristos 
144342669be3Schristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
144442669be3Schristos 
144542669be3Schristos 	if (os_strcmp(cmd, "version") == 0) {
144642669be3Schristos 		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
14479a53cbbeSchristos 		if (os_snprintf_error(buflen, res))
14489a53cbbeSchristos 			return -1;
14499a53cbbeSchristos 		return res;
14509a53cbbeSchristos 	} else if (os_strcmp(cmd, "tls_library") == 0) {
14519a53cbbeSchristos 		res = tls_get_library_version(buf, buflen);
14529a53cbbeSchristos 		if (os_snprintf_error(buflen, res))
145342669be3Schristos 			return -1;
145442669be3Schristos 		return res;
145542669be3Schristos 	}
145642669be3Schristos 
145742669be3Schristos 	return -1;
145842669be3Schristos }
145942669be3Schristos 
146042669be3Schristos 
hostapd_ctrl_iface_enable(struct hostapd_iface * iface)146162a52023Schristos static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
146262a52023Schristos {
146362a52023Schristos 	if (hostapd_enable_iface(iface) < 0) {
146462a52023Schristos 		wpa_printf(MSG_ERROR, "Enabling of interface failed");
146562a52023Schristos 		return -1;
146662a52023Schristos 	}
146762a52023Schristos 	return 0;
146862a52023Schristos }
146962a52023Schristos 
147062a52023Schristos 
hostapd_ctrl_iface_reload(struct hostapd_iface * iface)147162a52023Schristos static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
147262a52023Schristos {
147362a52023Schristos 	if (hostapd_reload_iface(iface) < 0) {
147462a52023Schristos 		wpa_printf(MSG_ERROR, "Reloading of interface failed");
147562a52023Schristos 		return -1;
147662a52023Schristos 	}
147762a52023Schristos 	return 0;
147862a52023Schristos }
147962a52023Schristos 
148062a52023Schristos 
hostapd_ctrl_iface_disable(struct hostapd_iface * iface)148162a52023Schristos static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
148262a52023Schristos {
148362a52023Schristos 	if (hostapd_disable_iface(iface) < 0) {
148462a52023Schristos 		wpa_printf(MSG_ERROR, "Disabling of interface failed");
148562a52023Schristos 		return -1;
148662a52023Schristos 	}
148762a52023Schristos 	return 0;
148862a52023Schristos }
148962a52023Schristos 
149062a52023Schristos 
1491*0d69f216Schristos static int
hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data * hapd,struct sta_info * sta,void * ctx)1492*0d69f216Schristos hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd,
1493*0d69f216Schristos 					      struct sta_info *sta, void *ctx)
1494*0d69f216Schristos {
1495*0d69f216Schristos 	struct hostapd_wpa_psk *psk;
1496*0d69f216Schristos 	const u8 *pmk;
1497*0d69f216Schristos 	int pmk_len;
1498*0d69f216Schristos 	int pmk_match;
1499*0d69f216Schristos 	int sta_match;
1500*0d69f216Schristos 	int bss_match;
1501*0d69f216Schristos 	int reason;
1502*0d69f216Schristos 
1503*0d69f216Schristos 	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
1504*0d69f216Schristos 
1505*0d69f216Schristos 	for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) {
1506*0d69f216Schristos 		pmk_match = PMK_LEN == pmk_len &&
1507*0d69f216Schristos 			os_memcmp(psk->psk, pmk, pmk_len) == 0;
1508*0d69f216Schristos 		sta_match = psk->group == 0 &&
1509*0d69f216Schristos 			os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0;
1510*0d69f216Schristos 		bss_match = psk->group == 1;
1511*0d69f216Schristos 
1512*0d69f216Schristos 		if (pmk_match && (sta_match || bss_match))
1513*0d69f216Schristos 			return 0;
1514*0d69f216Schristos 	}
1515*0d69f216Schristos 
1516*0d69f216Schristos 	wpa_printf(MSG_INFO, "STA " MACSTR
1517*0d69f216Schristos 		   " PSK/passphrase no longer valid - disconnect",
1518*0d69f216Schristos 		   MAC2STR(sta->addr));
1519*0d69f216Schristos 	reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
1520*0d69f216Schristos 	hostapd_drv_sta_deauth(hapd, sta->addr, reason);
1521*0d69f216Schristos 	ap_sta_deauthenticate(hapd, sta, reason);
1522*0d69f216Schristos 
1523*0d69f216Schristos 	return 0;
1524*0d69f216Schristos }
1525*0d69f216Schristos 
1526*0d69f216Schristos 
hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data * hapd)1527*0d69f216Schristos static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd)
1528*0d69f216Schristos {
1529*0d69f216Schristos 	struct hostapd_bss_config *conf = hapd->conf;
1530*0d69f216Schristos 	int err;
1531*0d69f216Schristos 
1532*0d69f216Schristos 	hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
1533*0d69f216Schristos 
1534*0d69f216Schristos 	err = hostapd_setup_wpa_psk(conf);
1535*0d69f216Schristos 	if (err < 0) {
1536*0d69f216Schristos 		wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d",
1537*0d69f216Schristos 			   err);
1538*0d69f216Schristos 		return -1;
1539*0d69f216Schristos 	}
1540*0d69f216Schristos 
1541*0d69f216Schristos 	ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter,
1542*0d69f216Schristos 			NULL);
1543*0d69f216Schristos 
1544*0d69f216Schristos 	return 0;
1545*0d69f216Schristos }
1546*0d69f216Schristos 
1547*0d69f216Schristos 
154836d97821Schristos #ifdef CONFIG_TESTING_OPTIONS
154936d97821Schristos 
hostapd_ctrl_iface_radar(struct hostapd_data * hapd,char * cmd)155036d97821Schristos static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
155136d97821Schristos {
155236d97821Schristos 	union wpa_event_data data;
155336d97821Schristos 	char *pos, *param;
155436d97821Schristos 	enum wpa_event_type event;
155536d97821Schristos 
155636d97821Schristos 	wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
155736d97821Schristos 
155836d97821Schristos 	os_memset(&data, 0, sizeof(data));
155936d97821Schristos 
156036d97821Schristos 	param = os_strchr(cmd, ' ');
156136d97821Schristos 	if (param == NULL)
156236d97821Schristos 		return -1;
156336d97821Schristos 	*param++ = '\0';
156436d97821Schristos 
156536d97821Schristos 	if (os_strcmp(cmd, "DETECTED") == 0)
156636d97821Schristos 		event = EVENT_DFS_RADAR_DETECTED;
156736d97821Schristos 	else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
156836d97821Schristos 		event = EVENT_DFS_CAC_FINISHED;
156936d97821Schristos 	else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
157036d97821Schristos 		event = EVENT_DFS_CAC_ABORTED;
157136d97821Schristos 	else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
157236d97821Schristos 		event = EVENT_DFS_NOP_FINISHED;
157336d97821Schristos 	else {
157436d97821Schristos 		wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
157536d97821Schristos 			   cmd);
157636d97821Schristos 		return -1;
157736d97821Schristos 	}
157836d97821Schristos 
157936d97821Schristos 	pos = os_strstr(param, "freq=");
158036d97821Schristos 	if (pos)
158136d97821Schristos 		data.dfs_event.freq = atoi(pos + 5);
158236d97821Schristos 
158336d97821Schristos 	pos = os_strstr(param, "ht_enabled=1");
158436d97821Schristos 	if (pos)
158536d97821Schristos 		data.dfs_event.ht_enabled = 1;
158636d97821Schristos 
158736d97821Schristos 	pos = os_strstr(param, "chan_offset=");
158836d97821Schristos 	if (pos)
158936d97821Schristos 		data.dfs_event.chan_offset = atoi(pos + 12);
159036d97821Schristos 
159136d97821Schristos 	pos = os_strstr(param, "chan_width=");
159236d97821Schristos 	if (pos)
159336d97821Schristos 		data.dfs_event.chan_width = atoi(pos + 11);
159436d97821Schristos 
159536d97821Schristos 	pos = os_strstr(param, "cf1=");
159636d97821Schristos 	if (pos)
159736d97821Schristos 		data.dfs_event.cf1 = atoi(pos + 4);
159836d97821Schristos 
159936d97821Schristos 	pos = os_strstr(param, "cf2=");
160036d97821Schristos 	if (pos)
160136d97821Schristos 		data.dfs_event.cf2 = atoi(pos + 4);
160236d97821Schristos 
160336d97821Schristos 	wpa_supplicant_event(hapd, event, &data);
160436d97821Schristos 
160536d97821Schristos 	return 0;
160636d97821Schristos }
160736d97821Schristos 
160836d97821Schristos 
hostapd_ctrl_iface_mgmt_tx(struct hostapd_data * hapd,char * cmd)160936d97821Schristos static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
161036d97821Schristos {
161136d97821Schristos 	size_t len;
161236d97821Schristos 	u8 *buf;
161336d97821Schristos 	int res;
161436d97821Schristos 
161536d97821Schristos 	wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
161636d97821Schristos 
161736d97821Schristos 	len = os_strlen(cmd);
161836d97821Schristos 	if (len & 1)
161936d97821Schristos 		return -1;
162036d97821Schristos 	len /= 2;
162136d97821Schristos 
162236d97821Schristos 	buf = os_malloc(len);
162336d97821Schristos 	if (buf == NULL)
162436d97821Schristos 		return -1;
162536d97821Schristos 
162636d97821Schristos 	if (hexstr2bin(cmd, buf, len) < 0) {
162736d97821Schristos 		os_free(buf);
162836d97821Schristos 		return -1;
162936d97821Schristos 	}
163036d97821Schristos 
163136d97821Schristos 	res = hostapd_drv_send_mlme(hapd, buf, len, 0);
163236d97821Schristos 	os_free(buf);
163336d97821Schristos 	return res;
163436d97821Schristos }
163536d97821Schristos 
16369a53cbbeSchristos 
hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data * hapd,char * cmd)1637ebb5671cSchristos static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
1638ebb5671cSchristos 						     char *cmd)
1639ebb5671cSchristos {
1640ebb5671cSchristos 	char *pos, *param;
1641ebb5671cSchristos 	size_t len;
1642ebb5671cSchristos 	u8 *buf;
1643ebb5671cSchristos 	int stype = 0, ok = 0;
1644ebb5671cSchristos 	union wpa_event_data event;
1645ebb5671cSchristos 
1646ebb5671cSchristos 	if (!hapd->ext_mgmt_frame_handling)
1647ebb5671cSchristos 		return -1;
1648ebb5671cSchristos 
1649ebb5671cSchristos 	/* stype=<val> ok=<0/1> buf=<frame hexdump> */
1650ebb5671cSchristos 
1651ebb5671cSchristos 	wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
1652ebb5671cSchristos 
1653ebb5671cSchristos 	pos = cmd;
1654ebb5671cSchristos 	param = os_strstr(pos, "stype=");
1655ebb5671cSchristos 	if (param) {
1656ebb5671cSchristos 		param += 6;
1657ebb5671cSchristos 		stype = atoi(param);
1658ebb5671cSchristos 	}
1659ebb5671cSchristos 
1660ebb5671cSchristos 	param = os_strstr(pos, " ok=");
1661ebb5671cSchristos 	if (param) {
1662ebb5671cSchristos 		param += 4;
1663ebb5671cSchristos 		ok = atoi(param);
1664ebb5671cSchristos 	}
1665ebb5671cSchristos 
1666ebb5671cSchristos 	param = os_strstr(pos, " buf=");
1667ebb5671cSchristos 	if (!param)
1668ebb5671cSchristos 		return -1;
1669ebb5671cSchristos 	param += 5;
1670ebb5671cSchristos 
1671ebb5671cSchristos 	len = os_strlen(param);
1672ebb5671cSchristos 	if (len & 1)
1673ebb5671cSchristos 		return -1;
1674ebb5671cSchristos 	len /= 2;
1675ebb5671cSchristos 
1676ebb5671cSchristos 	buf = os_malloc(len);
1677ebb5671cSchristos 	if (!buf || hexstr2bin(param, buf, len) < 0) {
1678ebb5671cSchristos 		os_free(buf);
1679ebb5671cSchristos 		return -1;
1680ebb5671cSchristos 	}
1681ebb5671cSchristos 
1682ebb5671cSchristos 	os_memset(&event, 0, sizeof(event));
1683ebb5671cSchristos 	event.tx_status.type = WLAN_FC_TYPE_MGMT;
1684ebb5671cSchristos 	event.tx_status.data = buf;
1685ebb5671cSchristos 	event.tx_status.data_len = len;
1686ebb5671cSchristos 	event.tx_status.stype = stype;
1687ebb5671cSchristos 	event.tx_status.ack = ok;
1688ebb5671cSchristos 	hapd->ext_mgmt_frame_handling = 0;
1689ebb5671cSchristos 	wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
1690ebb5671cSchristos 	hapd->ext_mgmt_frame_handling = 1;
1691ebb5671cSchristos 
1692ebb5671cSchristos 	os_free(buf);
1693ebb5671cSchristos 
1694ebb5671cSchristos 	return 0;
1695ebb5671cSchristos }
1696ebb5671cSchristos 
1697ebb5671cSchristos 
hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data * hapd,char * cmd)1698ebb5671cSchristos static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
1699ebb5671cSchristos 					      char *cmd)
1700ebb5671cSchristos {
1701ebb5671cSchristos 	char *pos, *param;
1702ebb5671cSchristos 	size_t len;
1703ebb5671cSchristos 	u8 *buf;
1704ebb5671cSchristos 	int freq = 0, datarate = 0, ssi_signal = 0;
1705ebb5671cSchristos 	union wpa_event_data event;
1706ebb5671cSchristos 
1707ebb5671cSchristos 	if (!hapd->ext_mgmt_frame_handling)
1708ebb5671cSchristos 		return -1;
1709ebb5671cSchristos 
1710ebb5671cSchristos 	/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
1711ebb5671cSchristos 
1712ebb5671cSchristos 	wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
1713ebb5671cSchristos 
1714ebb5671cSchristos 	pos = cmd;
1715ebb5671cSchristos 	param = os_strstr(pos, "freq=");
1716ebb5671cSchristos 	if (param) {
1717ebb5671cSchristos 		param += 5;
1718ebb5671cSchristos 		freq = atoi(param);
1719ebb5671cSchristos 	}
1720ebb5671cSchristos 
1721ebb5671cSchristos 	param = os_strstr(pos, " datarate=");
1722ebb5671cSchristos 	if (param) {
1723ebb5671cSchristos 		param += 10;
1724ebb5671cSchristos 		datarate = atoi(param);
1725ebb5671cSchristos 	}
1726ebb5671cSchristos 
1727ebb5671cSchristos 	param = os_strstr(pos, " ssi_signal=");
1728ebb5671cSchristos 	if (param) {
1729ebb5671cSchristos 		param += 12;
1730ebb5671cSchristos 		ssi_signal = atoi(param);
1731ebb5671cSchristos 	}
1732ebb5671cSchristos 
1733ebb5671cSchristos 	param = os_strstr(pos, " frame=");
1734ebb5671cSchristos 	if (param == NULL)
1735ebb5671cSchristos 		return -1;
1736ebb5671cSchristos 	param += 7;
1737ebb5671cSchristos 
1738ebb5671cSchristos 	len = os_strlen(param);
1739ebb5671cSchristos 	if (len & 1)
1740ebb5671cSchristos 		return -1;
1741ebb5671cSchristos 	len /= 2;
1742ebb5671cSchristos 
1743ebb5671cSchristos 	buf = os_malloc(len);
1744ebb5671cSchristos 	if (buf == NULL)
1745ebb5671cSchristos 		return -1;
1746ebb5671cSchristos 
1747ebb5671cSchristos 	if (hexstr2bin(param, buf, len) < 0) {
1748ebb5671cSchristos 		os_free(buf);
1749ebb5671cSchristos 		return -1;
1750ebb5671cSchristos 	}
1751ebb5671cSchristos 
1752ebb5671cSchristos 	os_memset(&event, 0, sizeof(event));
1753ebb5671cSchristos 	event.rx_mgmt.freq = freq;
1754ebb5671cSchristos 	event.rx_mgmt.frame = buf;
1755ebb5671cSchristos 	event.rx_mgmt.frame_len = len;
1756ebb5671cSchristos 	event.rx_mgmt.ssi_signal = ssi_signal;
1757ebb5671cSchristos 	event.rx_mgmt.datarate = datarate;
1758ebb5671cSchristos 	hapd->ext_mgmt_frame_handling = 0;
1759ebb5671cSchristos 	wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
1760ebb5671cSchristos 	hapd->ext_mgmt_frame_handling = 1;
1761ebb5671cSchristos 
1762ebb5671cSchristos 	os_free(buf);
1763ebb5671cSchristos 
1764ebb5671cSchristos 	return 0;
1765ebb5671cSchristos }
1766ebb5671cSchristos 
1767ebb5671cSchristos 
hostapd_ctrl_iface_eapol_rx(struct hostapd_data * hapd,char * cmd)17689a53cbbeSchristos static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
17699a53cbbeSchristos {
17709a53cbbeSchristos 	char *pos;
17719a53cbbeSchristos 	u8 src[ETH_ALEN], *buf;
17729a53cbbeSchristos 	int used;
17739a53cbbeSchristos 	size_t len;
17749a53cbbeSchristos 
17759a53cbbeSchristos 	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
17769a53cbbeSchristos 
17779a53cbbeSchristos 	pos = cmd;
17789a53cbbeSchristos 	used = hwaddr_aton2(pos, src);
17799a53cbbeSchristos 	if (used < 0)
17809a53cbbeSchristos 		return -1;
17819a53cbbeSchristos 	pos += used;
17829a53cbbeSchristos 	while (*pos == ' ')
17839a53cbbeSchristos 		pos++;
17849a53cbbeSchristos 
17859a53cbbeSchristos 	len = os_strlen(pos);
17869a53cbbeSchristos 	if (len & 1)
17879a53cbbeSchristos 		return -1;
17889a53cbbeSchristos 	len /= 2;
17899a53cbbeSchristos 
17909a53cbbeSchristos 	buf = os_malloc(len);
17919a53cbbeSchristos 	if (buf == NULL)
17929a53cbbeSchristos 		return -1;
17939a53cbbeSchristos 
17949a53cbbeSchristos 	if (hexstr2bin(pos, buf, len) < 0) {
17959a53cbbeSchristos 		os_free(buf);
17969a53cbbeSchristos 		return -1;
17979a53cbbeSchristos 	}
17989a53cbbeSchristos 
17999a53cbbeSchristos 	ieee802_1x_receive(hapd, src, buf, len);
18009a53cbbeSchristos 	os_free(buf);
18019a53cbbeSchristos 
18029a53cbbeSchristos 	return 0;
18039a53cbbeSchristos }
18049a53cbbeSchristos 
18059a53cbbeSchristos 
ipv4_hdr_checksum(const void * buf,size_t len)18069a53cbbeSchristos static u16 ipv4_hdr_checksum(const void *buf, size_t len)
18079a53cbbeSchristos {
18089a53cbbeSchristos 	size_t i;
18099a53cbbeSchristos 	u32 sum = 0;
18109a53cbbeSchristos 	const u16 *pos = buf;
18119a53cbbeSchristos 
18129a53cbbeSchristos 	for (i = 0; i < len / 2; i++)
18139a53cbbeSchristos 		sum += *pos++;
18149a53cbbeSchristos 
18159a53cbbeSchristos 	while (sum >> 16)
18169a53cbbeSchristos 		sum = (sum & 0xffff) + (sum >> 16);
18179a53cbbeSchristos 
18189a53cbbeSchristos 	return sum ^ 0xffff;
18199a53cbbeSchristos }
18209a53cbbeSchristos 
18219a53cbbeSchristos 
18229a53cbbeSchristos #define HWSIM_PACKETLEN 1500
18239a53cbbeSchristos #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
18249a53cbbeSchristos 
hostapd_data_test_rx(void * ctx,const u8 * src_addr,const u8 * buf,size_t len)1825928750b6Schristos static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
18269a53cbbeSchristos 				 size_t len)
18279a53cbbeSchristos {
18289a53cbbeSchristos 	struct hostapd_data *hapd = ctx;
18299a53cbbeSchristos 	const struct ether_header *eth;
1830928750b6Schristos 	struct iphdr ip;
18319a53cbbeSchristos 	const u8 *pos;
18329a53cbbeSchristos 	unsigned int i;
1833*0d69f216Schristos 	char extra[30];
18349a53cbbeSchristos 
1835*0d69f216Schristos 	if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) {
1836*0d69f216Schristos 		wpa_printf(MSG_DEBUG,
1837*0d69f216Schristos 			   "test data: RX - ignore unexpected length %d",
1838*0d69f216Schristos 			   (int) len);
18399a53cbbeSchristos 		return;
1840*0d69f216Schristos 	}
18419a53cbbeSchristos 
18429a53cbbeSchristos 	eth = (const struct ether_header *) buf;
1843928750b6Schristos 	os_memcpy(&ip, eth + 1, sizeof(ip));
1844928750b6Schristos 	pos = &buf[sizeof(*eth) + sizeof(ip)];
18459a53cbbeSchristos 
1846928750b6Schristos 	if (ip.ihl != 5 || ip.version != 4 ||
1847*0d69f216Schristos 	    ntohs(ip.tot_len) > HWSIM_IP_LEN) {
1848*0d69f216Schristos 		wpa_printf(MSG_DEBUG,
1849*0d69f216Schristos 			   "test data: RX - ignore unexpect IP header");
18509a53cbbeSchristos 		return;
1851*0d69f216Schristos 	}
18529a53cbbeSchristos 
1853*0d69f216Schristos 	for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) {
1854*0d69f216Schristos 		if (*pos != (u8) i) {
1855*0d69f216Schristos 			wpa_printf(MSG_DEBUG,
1856*0d69f216Schristos 				   "test data: RX - ignore mismatching payload");
18579a53cbbeSchristos 			return;
1858*0d69f216Schristos 		}
18599a53cbbeSchristos 		pos++;
18609a53cbbeSchristos 	}
18619a53cbbeSchristos 
1862*0d69f216Schristos 	extra[0] = '\0';
1863*0d69f216Schristos 	if (ntohs(ip.tot_len) != HWSIM_IP_LEN)
1864*0d69f216Schristos 		os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len));
1865*0d69f216Schristos 	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
1866*0d69f216Schristos 		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
18679a53cbbeSchristos }
18689a53cbbeSchristos 
18699a53cbbeSchristos 
hostapd_ctrl_iface_data_test_config(struct hostapd_data * hapd,char * cmd)18709a53cbbeSchristos static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
18719a53cbbeSchristos 					       char *cmd)
18729a53cbbeSchristos {
18739a53cbbeSchristos 	int enabled = atoi(cmd);
18749a53cbbeSchristos 	char *pos;
18759a53cbbeSchristos 	const char *ifname;
18769a53cbbeSchristos 
18779a53cbbeSchristos 	if (!enabled) {
18789a53cbbeSchristos 		if (hapd->l2_test) {
18799a53cbbeSchristos 			l2_packet_deinit(hapd->l2_test);
18809a53cbbeSchristos 			hapd->l2_test = NULL;
18819a53cbbeSchristos 			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
18829a53cbbeSchristos 				"test data: Disabled");
18839a53cbbeSchristos 		}
18849a53cbbeSchristos 		return 0;
18859a53cbbeSchristos 	}
18869a53cbbeSchristos 
18879a53cbbeSchristos 	if (hapd->l2_test)
18889a53cbbeSchristos 		return 0;
18899a53cbbeSchristos 
18909a53cbbeSchristos 	pos = os_strstr(cmd, " ifname=");
18919a53cbbeSchristos 	if (pos)
18929a53cbbeSchristos 		ifname = pos + 8;
18939a53cbbeSchristos 	else
18949a53cbbeSchristos 		ifname = hapd->conf->iface;
18959a53cbbeSchristos 
18969a53cbbeSchristos 	hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
18979a53cbbeSchristos 					ETHERTYPE_IP, hostapd_data_test_rx,
18989a53cbbeSchristos 					hapd, 1);
18999a53cbbeSchristos 	if (hapd->l2_test == NULL)
19009a53cbbeSchristos 		return -1;
19019a53cbbeSchristos 
19029a53cbbeSchristos 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
19039a53cbbeSchristos 
19049a53cbbeSchristos 	return 0;
19059a53cbbeSchristos }
19069a53cbbeSchristos 
19079a53cbbeSchristos 
hostapd_ctrl_iface_data_test_tx(struct hostapd_data * hapd,char * cmd)19089a53cbbeSchristos static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
19099a53cbbeSchristos {
19109a53cbbeSchristos 	u8 dst[ETH_ALEN], src[ETH_ALEN];
1911*0d69f216Schristos 	char *pos, *pos2;
19129a53cbbeSchristos 	int used;
19139a53cbbeSchristos 	long int val;
19149a53cbbeSchristos 	u8 tos;
1915928750b6Schristos 	u8 buf[2 + HWSIM_PACKETLEN];
19169a53cbbeSchristos 	struct ether_header *eth;
19179a53cbbeSchristos 	struct iphdr *ip;
19189a53cbbeSchristos 	u8 *dpos;
19199a53cbbeSchristos 	unsigned int i;
1920*0d69f216Schristos 	size_t send_len = HWSIM_IP_LEN;
19219a53cbbeSchristos 
19229a53cbbeSchristos 	if (hapd->l2_test == NULL)
19239a53cbbeSchristos 		return -1;
19249a53cbbeSchristos 
1925*0d69f216Schristos 	/* format: <dst> <src> <tos> [len=<length>] */
19269a53cbbeSchristos 
19279a53cbbeSchristos 	pos = cmd;
19289a53cbbeSchristos 	used = hwaddr_aton2(pos, dst);
19299a53cbbeSchristos 	if (used < 0)
19309a53cbbeSchristos 		return -1;
19319a53cbbeSchristos 	pos += used;
19329a53cbbeSchristos 	while (*pos == ' ')
19339a53cbbeSchristos 		pos++;
19349a53cbbeSchristos 	used = hwaddr_aton2(pos, src);
19359a53cbbeSchristos 	if (used < 0)
19369a53cbbeSchristos 		return -1;
19379a53cbbeSchristos 	pos += used;
19389a53cbbeSchristos 
1939*0d69f216Schristos 	val = strtol(pos, &pos2, 0);
19409a53cbbeSchristos 	if (val < 0 || val > 0xff)
19419a53cbbeSchristos 		return -1;
19429a53cbbeSchristos 	tos = val;
19439a53cbbeSchristos 
1944*0d69f216Schristos 	pos = os_strstr(pos2, " len=");
1945*0d69f216Schristos 	if (pos) {
1946*0d69f216Schristos 		i = atoi(pos + 5);
1947*0d69f216Schristos 		if (i < sizeof(*ip) || i > HWSIM_IP_LEN)
1948*0d69f216Schristos 			return -1;
1949*0d69f216Schristos 		send_len = i;
1950*0d69f216Schristos 	}
1951*0d69f216Schristos 
1952928750b6Schristos 	eth = (struct ether_header *) &buf[2];
19539a53cbbeSchristos 	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
19549a53cbbeSchristos 	os_memcpy(eth->ether_shost, src, ETH_ALEN);
19559a53cbbeSchristos 	eth->ether_type = htons(ETHERTYPE_IP);
19569a53cbbeSchristos 	ip = (struct iphdr *) (eth + 1);
19579a53cbbeSchristos 	os_memset(ip, 0, sizeof(*ip));
19589a53cbbeSchristos 	ip->ihl = 5;
19599a53cbbeSchristos 	ip->version = 4;
19609a53cbbeSchristos 	ip->ttl = 64;
19619a53cbbeSchristos 	ip->tos = tos;
1962*0d69f216Schristos 	ip->tot_len = htons(send_len);
19639a53cbbeSchristos 	ip->protocol = 1;
1964928750b6Schristos 	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
1965928750b6Schristos 	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
19669a53cbbeSchristos 	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
19679a53cbbeSchristos 	dpos = (u8 *) (ip + 1);
1968*0d69f216Schristos 	for (i = 0; i < send_len - sizeof(*ip); i++)
19699a53cbbeSchristos 		*dpos++ = i;
19709a53cbbeSchristos 
1971928750b6Schristos 	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
1972*0d69f216Schristos 			   sizeof(struct ether_header) + send_len) < 0)
19739a53cbbeSchristos 		return -1;
19749a53cbbeSchristos 
19759a53cbbeSchristos 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
19769a53cbbeSchristos 		" src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
19779a53cbbeSchristos 
19789a53cbbeSchristos 	return 0;
19799a53cbbeSchristos }
19809a53cbbeSchristos 
19819a53cbbeSchristos 
hostapd_ctrl_iface_data_test_frame(struct hostapd_data * hapd,char * cmd)19829a53cbbeSchristos static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
19839a53cbbeSchristos 					      char *cmd)
19849a53cbbeSchristos {
19859a53cbbeSchristos 	u8 *buf;
19869a53cbbeSchristos 	struct ether_header *eth;
19879a53cbbeSchristos 	struct l2_packet_data *l2 = NULL;
19889a53cbbeSchristos 	size_t len;
19899a53cbbeSchristos 	u16 ethertype;
19909a53cbbeSchristos 	int res = -1;
19919a53cbbeSchristos 	const char *ifname = hapd->conf->iface;
19929a53cbbeSchristos 
19939a53cbbeSchristos 	if (os_strncmp(cmd, "ifname=", 7) == 0) {
19949a53cbbeSchristos 		cmd += 7;
19959a53cbbeSchristos 		ifname = cmd;
19969a53cbbeSchristos 		cmd = os_strchr(cmd, ' ');
19979a53cbbeSchristos 		if (cmd == NULL)
19989a53cbbeSchristos 			return -1;
19999a53cbbeSchristos 		*cmd++ = '\0';
20009a53cbbeSchristos 	}
20019a53cbbeSchristos 
20029a53cbbeSchristos 	len = os_strlen(cmd);
20039a53cbbeSchristos 	if (len & 1 || len < ETH_HLEN * 2)
20049a53cbbeSchristos 		return -1;
20059a53cbbeSchristos 	len /= 2;
20069a53cbbeSchristos 
20079a53cbbeSchristos 	buf = os_malloc(len);
20089a53cbbeSchristos 	if (buf == NULL)
20099a53cbbeSchristos 		return -1;
20109a53cbbeSchristos 
20119a53cbbeSchristos 	if (hexstr2bin(cmd, buf, len) < 0)
20129a53cbbeSchristos 		goto done;
20139a53cbbeSchristos 
20149a53cbbeSchristos 	eth = (struct ether_header *) buf;
20159a53cbbeSchristos 	ethertype = ntohs(eth->ether_type);
20169a53cbbeSchristos 
20179a53cbbeSchristos 	l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
20189a53cbbeSchristos 			    hostapd_data_test_rx, hapd, 1);
20199a53cbbeSchristos 	if (l2 == NULL)
20209a53cbbeSchristos 		goto done;
20219a53cbbeSchristos 
20229a53cbbeSchristos 	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
20239a53cbbeSchristos 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
20249a53cbbeSchristos done:
20259a53cbbeSchristos 	if (l2)
20269a53cbbeSchristos 		l2_packet_deinit(l2);
20279a53cbbeSchristos 	os_free(buf);
20289a53cbbeSchristos 
20299a53cbbeSchristos 	return res < 0 ? -1 : 0;
20309a53cbbeSchristos }
20319a53cbbeSchristos 
20329a53cbbeSchristos 
hostapd_ctrl_test_alloc_fail(struct hostapd_data * hapd,char * cmd)20339a53cbbeSchristos static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
20349a53cbbeSchristos {
20359a53cbbeSchristos #ifdef WPA_TRACE_BFD
20369a53cbbeSchristos 	char *pos;
20379a53cbbeSchristos 
20389a53cbbeSchristos 	wpa_trace_fail_after = atoi(cmd);
20399a53cbbeSchristos 	pos = os_strchr(cmd, ':');
20409a53cbbeSchristos 	if (pos) {
20419a53cbbeSchristos 		pos++;
20429a53cbbeSchristos 		os_strlcpy(wpa_trace_fail_func, pos,
20439a53cbbeSchristos 			   sizeof(wpa_trace_fail_func));
20449a53cbbeSchristos 	} else {
20459a53cbbeSchristos 		wpa_trace_fail_after = 0;
20469a53cbbeSchristos 	}
20479a53cbbeSchristos 
20489a53cbbeSchristos 	return 0;
20499a53cbbeSchristos #else /* WPA_TRACE_BFD */
20509a53cbbeSchristos 	return -1;
20519a53cbbeSchristos #endif /* WPA_TRACE_BFD */
20529a53cbbeSchristos }
20539a53cbbeSchristos 
20549a53cbbeSchristos 
hostapd_ctrl_get_alloc_fail(struct hostapd_data * hapd,char * buf,size_t buflen)20559a53cbbeSchristos static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
20569a53cbbeSchristos 				       char *buf, size_t buflen)
20579a53cbbeSchristos {
20589a53cbbeSchristos #ifdef WPA_TRACE_BFD
20599a53cbbeSchristos 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
20609a53cbbeSchristos 			   wpa_trace_fail_func);
20619a53cbbeSchristos #else /* WPA_TRACE_BFD */
20629a53cbbeSchristos 	return -1;
20639a53cbbeSchristos #endif /* WPA_TRACE_BFD */
20649a53cbbeSchristos }
20659a53cbbeSchristos 
2066928750b6Schristos 
hostapd_ctrl_test_fail(struct hostapd_data * hapd,char * cmd)2067928750b6Schristos static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
2068928750b6Schristos {
2069928750b6Schristos #ifdef WPA_TRACE_BFD
2070928750b6Schristos 	char *pos;
2071928750b6Schristos 
2072928750b6Schristos 	wpa_trace_test_fail_after = atoi(cmd);
2073928750b6Schristos 	pos = os_strchr(cmd, ':');
2074928750b6Schristos 	if (pos) {
2075928750b6Schristos 		pos++;
2076928750b6Schristos 		os_strlcpy(wpa_trace_test_fail_func, pos,
2077928750b6Schristos 			   sizeof(wpa_trace_test_fail_func));
2078928750b6Schristos 	} else {
2079928750b6Schristos 		wpa_trace_test_fail_after = 0;
2080928750b6Schristos 	}
2081928750b6Schristos 
2082928750b6Schristos 	return 0;
2083928750b6Schristos #else /* WPA_TRACE_BFD */
2084928750b6Schristos 	return -1;
2085928750b6Schristos #endif /* WPA_TRACE_BFD */
2086928750b6Schristos }
2087928750b6Schristos 
2088928750b6Schristos 
hostapd_ctrl_get_fail(struct hostapd_data * hapd,char * buf,size_t buflen)2089928750b6Schristos static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
2090928750b6Schristos 				 char *buf, size_t buflen)
2091928750b6Schristos {
2092928750b6Schristos #ifdef WPA_TRACE_BFD
2093928750b6Schristos 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
2094928750b6Schristos 			   wpa_trace_test_fail_func);
2095928750b6Schristos #else /* WPA_TRACE_BFD */
2096928750b6Schristos 	return -1;
2097928750b6Schristos #endif /* WPA_TRACE_BFD */
2098928750b6Schristos }
2099928750b6Schristos 
2100ebb5671cSchristos 
hostapd_ctrl_reset_pn(struct hostapd_data * hapd,const char * cmd)2101ebb5671cSchristos static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
2102ebb5671cSchristos {
2103ebb5671cSchristos 	struct sta_info *sta;
2104ebb5671cSchristos 	u8 addr[ETH_ALEN];
2105ebb5671cSchristos 	u8 zero[WPA_TK_MAX_LEN];
2106ebb5671cSchristos 
2107ebb5671cSchristos 	os_memset(zero, 0, sizeof(zero));
2108ebb5671cSchristos 
2109ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2110ebb5671cSchristos 		return -1;
2111ebb5671cSchristos 
2112ebb5671cSchristos #ifdef CONFIG_IEEE80211W
2113ebb5671cSchristos 	if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
2114ebb5671cSchristos 		if (hapd->last_igtk_alg == WPA_ALG_NONE)
2115ebb5671cSchristos 			return -1;
2116ebb5671cSchristos 
2117ebb5671cSchristos 		wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
2118ebb5671cSchristos 
2119ebb5671cSchristos 		/* First, use a zero key to avoid any possible duplicate key
2120ebb5671cSchristos 		 * avoidance in the driver. */
2121ebb5671cSchristos 		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
2122ebb5671cSchristos 					hapd->last_igtk_alg,
2123ebb5671cSchristos 					broadcast_ether_addr,
2124ebb5671cSchristos 					hapd->last_igtk_key_idx, 1, NULL, 0,
2125ebb5671cSchristos 					zero, hapd->last_igtk_len) < 0)
2126ebb5671cSchristos 			return -1;
2127ebb5671cSchristos 
2128ebb5671cSchristos 		/* Set the previously configured key to reset its TSC */
2129ebb5671cSchristos 		return hostapd_drv_set_key(hapd->conf->iface, hapd,
2130ebb5671cSchristos 					   hapd->last_igtk_alg,
2131ebb5671cSchristos 					   broadcast_ether_addr,
2132ebb5671cSchristos 					   hapd->last_igtk_key_idx, 1, NULL, 0,
2133ebb5671cSchristos 					   hapd->last_igtk,
2134ebb5671cSchristos 					   hapd->last_igtk_len);
2135ebb5671cSchristos 	}
2136ebb5671cSchristos #endif /* CONFIG_IEEE80211W */
2137ebb5671cSchristos 
2138ebb5671cSchristos 	if (is_broadcast_ether_addr(addr)) {
2139ebb5671cSchristos 		if (hapd->last_gtk_alg == WPA_ALG_NONE)
2140ebb5671cSchristos 			return -1;
2141ebb5671cSchristos 
2142ebb5671cSchristos 		wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
2143ebb5671cSchristos 
2144ebb5671cSchristos 		/* First, use a zero key to avoid any possible duplicate key
2145ebb5671cSchristos 		 * avoidance in the driver. */
2146ebb5671cSchristos 		if (hostapd_drv_set_key(hapd->conf->iface, hapd,
2147ebb5671cSchristos 					hapd->last_gtk_alg,
2148ebb5671cSchristos 					broadcast_ether_addr,
2149ebb5671cSchristos 					hapd->last_gtk_key_idx, 1, NULL, 0,
2150ebb5671cSchristos 					zero, hapd->last_gtk_len) < 0)
2151ebb5671cSchristos 			return -1;
2152ebb5671cSchristos 
2153ebb5671cSchristos 		/* Set the previously configured key to reset its TSC */
2154ebb5671cSchristos 		return hostapd_drv_set_key(hapd->conf->iface, hapd,
2155ebb5671cSchristos 					   hapd->last_gtk_alg,
2156ebb5671cSchristos 					   broadcast_ether_addr,
2157ebb5671cSchristos 					   hapd->last_gtk_key_idx, 1, NULL, 0,
2158ebb5671cSchristos 					   hapd->last_gtk, hapd->last_gtk_len);
2159ebb5671cSchristos 	}
2160ebb5671cSchristos 
2161ebb5671cSchristos 	sta = ap_get_sta(hapd, addr);
2162ebb5671cSchristos 	if (!sta)
2163ebb5671cSchristos 		return -1;
2164ebb5671cSchristos 
2165ebb5671cSchristos 	if (sta->last_tk_alg == WPA_ALG_NONE)
2166ebb5671cSchristos 		return -1;
2167ebb5671cSchristos 
2168ebb5671cSchristos 	wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
2169ebb5671cSchristos 		   MAC2STR(sta->addr));
2170ebb5671cSchristos 
2171ebb5671cSchristos 	/* First, use a zero key to avoid any possible duplicate key avoidance
2172ebb5671cSchristos 	 * in the driver. */
2173ebb5671cSchristos 	if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2174ebb5671cSchristos 				sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
2175ebb5671cSchristos 				zero, sta->last_tk_len) < 0)
2176ebb5671cSchristos 		return -1;
2177ebb5671cSchristos 
2178ebb5671cSchristos 	/* Set the previously configured key to reset its TSC/RSC */
2179ebb5671cSchristos 	return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2180ebb5671cSchristos 				   sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
2181ebb5671cSchristos 				   sta->last_tk, sta->last_tk_len);
2182ebb5671cSchristos }
2183ebb5671cSchristos 
2184ebb5671cSchristos 
hostapd_ctrl_set_key(struct hostapd_data * hapd,const char * cmd)2185ebb5671cSchristos static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
2186ebb5671cSchristos {
2187ebb5671cSchristos 	u8 addr[ETH_ALEN];
2188ebb5671cSchristos 	const char *pos = cmd;
2189ebb5671cSchristos 	enum wpa_alg alg;
2190ebb5671cSchristos 	int idx, set_tx;
2191ebb5671cSchristos 	u8 seq[6], key[WPA_TK_MAX_LEN];
2192ebb5671cSchristos 	size_t key_len;
2193ebb5671cSchristos 
2194ebb5671cSchristos 	/* parameters: alg addr idx set_tx seq key */
2195ebb5671cSchristos 
2196ebb5671cSchristos 	alg = atoi(pos);
2197ebb5671cSchristos 	pos = os_strchr(pos, ' ');
2198ebb5671cSchristos 	if (!pos)
2199ebb5671cSchristos 		return -1;
2200ebb5671cSchristos 	pos++;
2201ebb5671cSchristos 	if (hwaddr_aton(pos, addr))
2202ebb5671cSchristos 		return -1;
2203ebb5671cSchristos 	pos += 17;
2204ebb5671cSchristos 	if (*pos != ' ')
2205ebb5671cSchristos 		return -1;
2206ebb5671cSchristos 	pos++;
2207ebb5671cSchristos 	idx = atoi(pos);
2208ebb5671cSchristos 	pos = os_strchr(pos, ' ');
2209ebb5671cSchristos 	if (!pos)
2210ebb5671cSchristos 		return -1;
2211ebb5671cSchristos 	pos++;
2212ebb5671cSchristos 	set_tx = atoi(pos);
2213ebb5671cSchristos 	pos = os_strchr(pos, ' ');
2214ebb5671cSchristos 	if (!pos)
2215ebb5671cSchristos 		return -1;
2216ebb5671cSchristos 	pos++;
2217ebb5671cSchristos 	if (hexstr2bin(pos, seq, sizeof(seq)) < 0)
2218ebb5671cSchristos 		return -1;
2219ebb5671cSchristos 	pos += 2 * 6;
2220ebb5671cSchristos 	if (*pos != ' ')
2221ebb5671cSchristos 		return -1;
2222ebb5671cSchristos 	pos++;
2223ebb5671cSchristos 	key_len = os_strlen(pos) / 2;
2224ebb5671cSchristos 	if (hexstr2bin(pos, key, key_len) < 0)
2225ebb5671cSchristos 		return -1;
2226ebb5671cSchristos 
2227ebb5671cSchristos 	wpa_printf(MSG_INFO, "TESTING: Set key");
2228ebb5671cSchristos 	return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
2229ebb5671cSchristos 				   set_tx, seq, 6, key, key_len);
2230ebb5671cSchristos }
2231ebb5671cSchristos 
2232ebb5671cSchristos 
restore_tk(void * ctx1,void * ctx2)2233ebb5671cSchristos static void restore_tk(void *ctx1, void *ctx2)
2234ebb5671cSchristos {
2235ebb5671cSchristos 	struct hostapd_data *hapd = ctx1;
2236ebb5671cSchristos 	struct sta_info *sta = ctx2;
2237ebb5671cSchristos 
2238ebb5671cSchristos 	wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
2239ebb5671cSchristos 		   MAC2STR(sta->addr));
2240ebb5671cSchristos 	/* This does not really restore the TSC properly, so this will result
2241ebb5671cSchristos 	 * in replay protection issues for now since there is no clean way of
2242ebb5671cSchristos 	 * preventing encryption of a single EAPOL frame. */
2243ebb5671cSchristos 	hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
2244ebb5671cSchristos 			    sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
2245ebb5671cSchristos 			    sta->last_tk, sta->last_tk_len);
2246ebb5671cSchristos }
2247ebb5671cSchristos 
2248ebb5671cSchristos 
hostapd_ctrl_resend_m1(struct hostapd_data * hapd,const char * cmd)2249ebb5671cSchristos static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
2250ebb5671cSchristos {
2251ebb5671cSchristos 	struct sta_info *sta;
2252ebb5671cSchristos 	u8 addr[ETH_ALEN];
2253ebb5671cSchristos 	int plain = os_strstr(cmd, "plaintext") != NULL;
2254ebb5671cSchristos 
2255ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2256ebb5671cSchristos 		return -1;
2257ebb5671cSchristos 
2258ebb5671cSchristos 	sta = ap_get_sta(hapd, addr);
2259ebb5671cSchristos 	if (!sta || !sta->wpa_sm)
2260ebb5671cSchristos 		return -1;
2261ebb5671cSchristos 
2262ebb5671cSchristos 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
2263ebb5671cSchristos 		plain = 0; /* no need for special processing */
2264ebb5671cSchristos 	if (plain) {
2265ebb5671cSchristos 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
2266ebb5671cSchristos 			   MAC2STR(sta->addr));
2267ebb5671cSchristos 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2268ebb5671cSchristos 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
2269ebb5671cSchristos 				    NULL, 0);
2270ebb5671cSchristos 	}
2271ebb5671cSchristos 
2272ebb5671cSchristos 	wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
2273ebb5671cSchristos 	return wpa_auth_resend_m1(sta->wpa_sm,
2274ebb5671cSchristos 				  os_strstr(cmd, "change-anonce") != NULL,
2275ebb5671cSchristos 				  plain ? restore_tk : NULL, hapd, sta);
2276ebb5671cSchristos }
2277ebb5671cSchristos 
2278ebb5671cSchristos 
hostapd_ctrl_resend_m3(struct hostapd_data * hapd,const char * cmd)2279ebb5671cSchristos static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
2280ebb5671cSchristos {
2281ebb5671cSchristos 	struct sta_info *sta;
2282ebb5671cSchristos 	u8 addr[ETH_ALEN];
2283ebb5671cSchristos 	int plain = os_strstr(cmd, "plaintext") != NULL;
2284ebb5671cSchristos 
2285ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2286ebb5671cSchristos 		return -1;
2287ebb5671cSchristos 
2288ebb5671cSchristos 	sta = ap_get_sta(hapd, addr);
2289ebb5671cSchristos 	if (!sta || !sta->wpa_sm)
2290ebb5671cSchristos 		return -1;
2291ebb5671cSchristos 
2292ebb5671cSchristos 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
2293ebb5671cSchristos 		plain = 0; /* no need for special processing */
2294ebb5671cSchristos 	if (plain) {
2295ebb5671cSchristos 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
2296ebb5671cSchristos 			   MAC2STR(sta->addr));
2297ebb5671cSchristos 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2298ebb5671cSchristos 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
2299ebb5671cSchristos 				    NULL, 0);
2300ebb5671cSchristos 	}
2301ebb5671cSchristos 
2302ebb5671cSchristos 	wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
2303ebb5671cSchristos 	return wpa_auth_resend_m3(sta->wpa_sm,
2304ebb5671cSchristos 				  plain ? restore_tk : NULL, hapd, sta);
2305ebb5671cSchristos }
2306ebb5671cSchristos 
2307ebb5671cSchristos 
hostapd_ctrl_resend_group_m1(struct hostapd_data * hapd,const char * cmd)2308ebb5671cSchristos static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
2309ebb5671cSchristos 					const char *cmd)
2310ebb5671cSchristos {
2311ebb5671cSchristos 	struct sta_info *sta;
2312ebb5671cSchristos 	u8 addr[ETH_ALEN];
2313ebb5671cSchristos 	int plain = os_strstr(cmd, "plaintext") != NULL;
2314ebb5671cSchristos 
2315ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2316ebb5671cSchristos 		return -1;
2317ebb5671cSchristos 
2318ebb5671cSchristos 	sta = ap_get_sta(hapd, addr);
2319ebb5671cSchristos 	if (!sta || !sta->wpa_sm)
2320ebb5671cSchristos 		return -1;
2321ebb5671cSchristos 
2322ebb5671cSchristos 	if (plain && sta->last_tk_alg == WPA_ALG_NONE)
2323ebb5671cSchristos 		plain = 0; /* no need for special processing */
2324ebb5671cSchristos 	if (plain) {
2325ebb5671cSchristos 		wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
2326ebb5671cSchristos 			   MAC2STR(sta->addr));
2327ebb5671cSchristos 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
2328ebb5671cSchristos 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
2329ebb5671cSchristos 				    NULL, 0);
2330ebb5671cSchristos 	}
2331ebb5671cSchristos 
2332ebb5671cSchristos 	wpa_printf(MSG_INFO,
2333ebb5671cSchristos 		   "TESTING: Send group M1 for the same GTK and zero RSC to "
2334ebb5671cSchristos 		   MACSTR, MAC2STR(sta->addr));
2335ebb5671cSchristos 	return wpa_auth_resend_group_m1(sta->wpa_sm,
2336ebb5671cSchristos 					plain ? restore_tk : NULL, hapd, sta);
2337ebb5671cSchristos }
2338ebb5671cSchristos 
233936d97821Schristos #endif /* CONFIG_TESTING_OPTIONS */
234036d97821Schristos 
234136d97821Schristos 
hostapd_ctrl_iface_chan_switch(struct hostapd_iface * iface,char * pos)234236d97821Schristos static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
234336d97821Schristos 					  char *pos)
234436d97821Schristos {
234536d97821Schristos #ifdef NEED_AP_MLME
234636d97821Schristos 	struct csa_settings settings;
234736d97821Schristos 	int ret;
234836d97821Schristos 	unsigned int i;
234936d97821Schristos 
235036d97821Schristos 	ret = hostapd_parse_csa_settings(pos, &settings);
235136d97821Schristos 	if (ret)
235236d97821Schristos 		return ret;
235336d97821Schristos 
235436d97821Schristos 	for (i = 0; i < iface->num_bss; i++) {
2355ebb5671cSchristos 
2356ebb5671cSchristos 		/* Save CHAN_SWITCH VHT config */
2357ebb5671cSchristos 		hostapd_chan_switch_vht_config(
2358ebb5671cSchristos 			iface->bss[i], settings.freq_params.vht_enabled);
2359ebb5671cSchristos 
236036d97821Schristos 		ret = hostapd_switch_channel(iface->bss[i], &settings);
236136d97821Schristos 		if (ret) {
236236d97821Schristos 			/* FIX: What do we do if CSA fails in the middle of
236336d97821Schristos 			 * submitting multi-BSS CSA requests? */
236436d97821Schristos 			return ret;
236536d97821Schristos 		}
236636d97821Schristos 	}
236736d97821Schristos 
236836d97821Schristos 	return 0;
236936d97821Schristos #else /* NEED_AP_MLME */
237036d97821Schristos 	return -1;
237136d97821Schristos #endif /* NEED_AP_MLME */
237236d97821Schristos }
237336d97821Schristos 
237436d97821Schristos 
hostapd_ctrl_iface_mib(struct hostapd_data * hapd,char * reply,int reply_size,const char * param)237536d97821Schristos static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
237636d97821Schristos 				  int reply_size, const char *param)
237736d97821Schristos {
237836d97821Schristos #ifdef RADIUS_SERVER
237936d97821Schristos 	if (os_strcmp(param, "radius_server") == 0) {
238036d97821Schristos 		return radius_server_get_mib(hapd->radius_srv, reply,
238136d97821Schristos 					     reply_size);
238236d97821Schristos 	}
238336d97821Schristos #endif /* RADIUS_SERVER */
238436d97821Schristos 	return -1;
238536d97821Schristos }
238636d97821Schristos 
238736d97821Schristos 
hostapd_ctrl_iface_vendor(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)238836d97821Schristos static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
238936d97821Schristos 				     char *buf, size_t buflen)
239036d97821Schristos {
239136d97821Schristos 	int ret;
239236d97821Schristos 	char *pos;
239336d97821Schristos 	u8 *data = NULL;
239436d97821Schristos 	unsigned int vendor_id, subcmd;
239536d97821Schristos 	struct wpabuf *reply;
239636d97821Schristos 	size_t data_len = 0;
239736d97821Schristos 
239836d97821Schristos 	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
239936d97821Schristos 	vendor_id = strtoul(cmd, &pos, 16);
2400928750b6Schristos 	if (!isblank((unsigned char) *pos))
240136d97821Schristos 		return -EINVAL;
240236d97821Schristos 
240336d97821Schristos 	subcmd = strtoul(pos, &pos, 10);
240436d97821Schristos 
240536d97821Schristos 	if (*pos != '\0') {
2406928750b6Schristos 		if (!isblank((unsigned char) *pos++))
240736d97821Schristos 			return -EINVAL;
240836d97821Schristos 		data_len = os_strlen(pos);
240936d97821Schristos 	}
241036d97821Schristos 
241136d97821Schristos 	if (data_len) {
241236d97821Schristos 		data_len /= 2;
241336d97821Schristos 		data = os_malloc(data_len);
241436d97821Schristos 		if (!data)
241536d97821Schristos 			return -ENOBUFS;
241636d97821Schristos 
241736d97821Schristos 		if (hexstr2bin(pos, data, data_len)) {
241836d97821Schristos 			wpa_printf(MSG_DEBUG,
241936d97821Schristos 				   "Vendor command: wrong parameter format");
242036d97821Schristos 			os_free(data);
242136d97821Schristos 			return -EINVAL;
242236d97821Schristos 		}
242336d97821Schristos 	}
242436d97821Schristos 
242536d97821Schristos 	reply = wpabuf_alloc((buflen - 1) / 2);
242636d97821Schristos 	if (!reply) {
242736d97821Schristos 		os_free(data);
242836d97821Schristos 		return -ENOBUFS;
242936d97821Schristos 	}
243036d97821Schristos 
243136d97821Schristos 	ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
243236d97821Schristos 				     reply);
243336d97821Schristos 
243436d97821Schristos 	if (ret == 0)
243536d97821Schristos 		ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
243636d97821Schristos 				       wpabuf_len(reply));
243736d97821Schristos 
243836d97821Schristos 	wpabuf_free(reply);
243936d97821Schristos 	os_free(data);
244036d97821Schristos 
244136d97821Schristos 	return ret;
244236d97821Schristos }
244336d97821Schristos 
244436d97821Schristos 
hostapd_ctrl_iface_eapol_reauth(struct hostapd_data * hapd,const char * cmd)2445928750b6Schristos static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
2446928750b6Schristos 					   const char *cmd)
24478dbcf02cSchristos {
2448928750b6Schristos 	u8 addr[ETH_ALEN];
2449928750b6Schristos 	struct sta_info *sta;
24508dbcf02cSchristos 
2451928750b6Schristos 	if (hwaddr_aton(cmd, addr))
2452928750b6Schristos 		return -1;
24538dbcf02cSchristos 
2454928750b6Schristos 	sta = ap_get_sta(hapd, addr);
2455928750b6Schristos 	if (!sta || !sta->eapol_sm)
2456928750b6Schristos 		return -1;
2457928750b6Schristos 
2458928750b6Schristos 	eapol_auth_reauthenticate(sta->eapol_sm);
2459928750b6Schristos 	return 0;
24609a53cbbeSchristos }
2461928750b6Schristos 
2462928750b6Schristos 
hostapd_ctrl_iface_eapol_set(struct hostapd_data * hapd,char * cmd)2463928750b6Schristos static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
2464928750b6Schristos {
2465928750b6Schristos 	u8 addr[ETH_ALEN];
2466928750b6Schristos 	struct sta_info *sta;
2467928750b6Schristos 	char *pos = cmd, *param;
2468928750b6Schristos 
2469928750b6Schristos 	if (hwaddr_aton(pos, addr) || pos[17] != ' ')
2470928750b6Schristos 		return -1;
2471928750b6Schristos 	pos += 18;
2472928750b6Schristos 	param = pos;
2473928750b6Schristos 	pos = os_strchr(pos, ' ');
2474928750b6Schristos 	if (!pos)
2475928750b6Schristos 		return -1;
2476928750b6Schristos 	*pos++ = '\0';
2477928750b6Schristos 
2478928750b6Schristos 	sta = ap_get_sta(hapd, addr);
2479928750b6Schristos 	if (!sta || !sta->eapol_sm)
2480928750b6Schristos 		return -1;
2481928750b6Schristos 
2482928750b6Schristos 	return eapol_auth_set_conf(sta->eapol_sm, param, pos);
24838dbcf02cSchristos }
24848dbcf02cSchristos 
2485928750b6Schristos 
hostapd_ctrl_iface_log_level(struct hostapd_data * hapd,char * cmd,char * buf,size_t buflen)2486928750b6Schristos static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
2487928750b6Schristos 					char *buf, size_t buflen)
2488928750b6Schristos {
2489928750b6Schristos 	char *pos, *end, *stamp;
2490928750b6Schristos 	int ret;
2491928750b6Schristos 
2492928750b6Schristos 	/* cmd: "LOG_LEVEL [<level>]" */
2493928750b6Schristos 	if (*cmd == '\0') {
2494928750b6Schristos 		pos = buf;
2495928750b6Schristos 		end = buf + buflen;
2496928750b6Schristos 		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
2497928750b6Schristos 				  "Timestamp: %d\n",
2498928750b6Schristos 				  debug_level_str(wpa_debug_level),
2499928750b6Schristos 				  wpa_debug_timestamp);
2500928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
2501928750b6Schristos 			ret = 0;
2502928750b6Schristos 
2503928750b6Schristos 		return ret;
2504928750b6Schristos 	}
2505928750b6Schristos 
2506928750b6Schristos 	while (*cmd == ' ')
2507928750b6Schristos 		cmd++;
2508928750b6Schristos 
2509928750b6Schristos 	stamp = os_strchr(cmd, ' ');
2510928750b6Schristos 	if (stamp) {
2511928750b6Schristos 		*stamp++ = '\0';
2512928750b6Schristos 		while (*stamp == ' ') {
2513928750b6Schristos 			stamp++;
2514928750b6Schristos 		}
2515928750b6Schristos 	}
2516928750b6Schristos 
2517928750b6Schristos 	if (os_strlen(cmd)) {
2518928750b6Schristos 		int level = str_to_debug_level(cmd);
2519928750b6Schristos 		if (level < 0)
2520928750b6Schristos 			return -1;
2521928750b6Schristos 		wpa_debug_level = level;
2522928750b6Schristos 	}
2523928750b6Schristos 
2524928750b6Schristos 	if (stamp && os_strlen(stamp))
2525928750b6Schristos 		wpa_debug_timestamp = atoi(stamp);
2526928750b6Schristos 
2527928750b6Schristos 	os_memcpy(buf, "OK\n", 3);
2528928750b6Schristos 	return 3;
2529928750b6Schristos }
2530928750b6Schristos 
2531928750b6Schristos 
2532928750b6Schristos #ifdef NEED_AP_MLME
hostapd_ctrl_iface_track_sta_list(struct hostapd_data * hapd,char * buf,size_t buflen)2533928750b6Schristos static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
2534928750b6Schristos 					     char *buf, size_t buflen)
2535928750b6Schristos {
2536928750b6Schristos 	struct hostapd_iface *iface = hapd->iface;
2537928750b6Schristos 	char *pos, *end;
2538928750b6Schristos 	struct hostapd_sta_info *info;
2539928750b6Schristos 	struct os_reltime now;
2540928750b6Schristos 
2541928750b6Schristos 	if (!iface->num_sta_seen)
2542928750b6Schristos 		return 0;
2543928750b6Schristos 
2544928750b6Schristos 	sta_track_expire(iface, 0);
2545928750b6Schristos 
2546928750b6Schristos 	pos = buf;
2547928750b6Schristos 	end = buf + buflen;
2548928750b6Schristos 
2549928750b6Schristos 	os_get_reltime(&now);
2550928750b6Schristos 	dl_list_for_each_reverse(info, &iface->sta_seen,
2551928750b6Schristos 				 struct hostapd_sta_info, list) {
2552928750b6Schristos 		struct os_reltime age;
2553928750b6Schristos 		int ret;
2554928750b6Schristos 
2555928750b6Schristos 		os_reltime_sub(&now, &info->last_seen, &age);
2556ebb5671cSchristos 		ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
2557ebb5671cSchristos 				  MAC2STR(info->addr), (unsigned int) age.sec,
2558ebb5671cSchristos 				  info->ssi_signal);
2559928750b6Schristos 		if (os_snprintf_error(end - pos, ret))
2560928750b6Schristos 			break;
2561928750b6Schristos 		pos += ret;
2562928750b6Schristos 	}
2563928750b6Schristos 
2564928750b6Schristos 	return pos - buf;
2565928750b6Schristos }
2566928750b6Schristos #endif /* NEED_AP_MLME */
2567928750b6Schristos 
2568928750b6Schristos 
hostapd_ctrl_iface_req_lci(struct hostapd_data * hapd,const char * cmd)2569928750b6Schristos static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
2570928750b6Schristos 				      const char *cmd)
2571928750b6Schristos {
2572928750b6Schristos 	u8 addr[ETH_ALEN];
2573928750b6Schristos 
2574928750b6Schristos 	if (hwaddr_aton(cmd, addr)) {
2575928750b6Schristos 		wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
2576928750b6Schristos 		return -1;
2577928750b6Schristos 	}
2578928750b6Schristos 
2579928750b6Schristos 	return hostapd_send_lci_req(hapd, addr);
2580928750b6Schristos }
2581928750b6Schristos 
2582928750b6Schristos 
hostapd_ctrl_iface_req_range(struct hostapd_data * hapd,char * cmd)2583928750b6Schristos static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
2584928750b6Schristos {
2585928750b6Schristos 	u8 addr[ETH_ALEN];
2586928750b6Schristos 	char *token, *context = NULL;
2587928750b6Schristos 	int random_interval, min_ap;
2588928750b6Schristos 	u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
2589928750b6Schristos 	unsigned int n_responders;
2590928750b6Schristos 
2591928750b6Schristos 	token = str_token(cmd, " ", &context);
2592928750b6Schristos 	if (!token || hwaddr_aton(token, addr)) {
2593928750b6Schristos 		wpa_printf(MSG_INFO,
2594928750b6Schristos 			   "CTRL: REQ_RANGE - Bad destination address");
2595928750b6Schristos 		return -1;
2596928750b6Schristos 	}
2597928750b6Schristos 
2598928750b6Schristos 	token = str_token(cmd, " ", &context);
2599928750b6Schristos 	if (!token)
2600928750b6Schristos 		return -1;
2601928750b6Schristos 
2602928750b6Schristos 	random_interval = atoi(token);
2603928750b6Schristos 	if (random_interval < 0 || random_interval > 0xffff)
2604928750b6Schristos 		return -1;
2605928750b6Schristos 
2606928750b6Schristos 	token = str_token(cmd, " ", &context);
2607928750b6Schristos 	if (!token)
2608928750b6Schristos 		return -1;
2609928750b6Schristos 
2610928750b6Schristos 	min_ap = atoi(token);
2611928750b6Schristos 	if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
2612928750b6Schristos 		return -1;
2613928750b6Schristos 
2614928750b6Schristos 	n_responders = 0;
2615928750b6Schristos 	while ((token = str_token(cmd, " ", &context))) {
2616928750b6Schristos 		if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
2617928750b6Schristos 			wpa_printf(MSG_INFO,
2618928750b6Schristos 				   "CTRL: REQ_RANGE: Too many responders");
2619928750b6Schristos 			return -1;
2620928750b6Schristos 		}
2621928750b6Schristos 
2622928750b6Schristos 		if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
2623928750b6Schristos 			wpa_printf(MSG_INFO,
2624928750b6Schristos 				   "CTRL: REQ_RANGE: Bad responder address");
2625928750b6Schristos 			return -1;
2626928750b6Schristos 		}
2627928750b6Schristos 
2628928750b6Schristos 		n_responders++;
2629928750b6Schristos 	}
2630928750b6Schristos 
2631928750b6Schristos 	if (!n_responders) {
2632928750b6Schristos 		wpa_printf(MSG_INFO,
2633928750b6Schristos 			   "CTRL: REQ_RANGE - No FTM responder address");
2634928750b6Schristos 		return -1;
2635928750b6Schristos 	}
2636928750b6Schristos 
2637928750b6Schristos 	return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
2638928750b6Schristos 				      responders, n_responders);
2639928750b6Schristos }
2640928750b6Schristos 
2641928750b6Schristos 
hostapd_ctrl_iface_req_beacon(struct hostapd_data * hapd,const char * cmd,char * reply,size_t reply_size)2642ebb5671cSchristos static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
2643ebb5671cSchristos 					 const char *cmd, char *reply,
2644ebb5671cSchristos 					 size_t reply_size)
2645ebb5671cSchristos {
2646ebb5671cSchristos 	u8 addr[ETH_ALEN];
2647ebb5671cSchristos 	const char *pos;
2648ebb5671cSchristos 	struct wpabuf *req;
2649ebb5671cSchristos 	int ret;
2650ebb5671cSchristos 	u8 req_mode = 0;
2651ebb5671cSchristos 
2652ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2653ebb5671cSchristos 		return -1;
2654ebb5671cSchristos 	pos = os_strchr(cmd, ' ');
2655ebb5671cSchristos 	if (!pos)
2656ebb5671cSchristos 		return -1;
2657ebb5671cSchristos 	pos++;
2658ebb5671cSchristos 	if (os_strncmp(pos, "req_mode=", 9) == 0) {
2659ebb5671cSchristos 		int val = hex2byte(pos + 9);
2660ebb5671cSchristos 
2661ebb5671cSchristos 		if (val < 0)
2662ebb5671cSchristos 			return -1;
2663ebb5671cSchristos 		req_mode = val;
2664ebb5671cSchristos 		pos += 11;
2665ebb5671cSchristos 		pos = os_strchr(pos, ' ');
2666ebb5671cSchristos 		if (!pos)
2667ebb5671cSchristos 			return -1;
2668ebb5671cSchristos 		pos++;
2669ebb5671cSchristos 	}
2670ebb5671cSchristos 	req = wpabuf_parse_bin(pos);
2671ebb5671cSchristos 	if (!req)
2672ebb5671cSchristos 		return -1;
2673ebb5671cSchristos 
2674ebb5671cSchristos 	ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
2675ebb5671cSchristos 	wpabuf_free(req);
2676ebb5671cSchristos 	if (ret >= 0)
2677ebb5671cSchristos 		ret = os_snprintf(reply, reply_size, "%d", ret);
2678ebb5671cSchristos 	return ret;
2679ebb5671cSchristos }
2680ebb5671cSchristos 
2681ebb5671cSchristos 
hostapd_ctrl_iface_set_neighbor(struct hostapd_data * hapd,char * buf)2682928750b6Schristos static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
2683928750b6Schristos {
2684928750b6Schristos 	struct wpa_ssid_value ssid;
2685928750b6Schristos 	u8 bssid[ETH_ALEN];
2686928750b6Schristos 	struct wpabuf *nr, *lci = NULL, *civic = NULL;
2687ebb5671cSchristos 	int stationary = 0;
2688928750b6Schristos 	char *tmp;
2689928750b6Schristos 	int ret;
2690928750b6Schristos 
2691928750b6Schristos 	if (!(hapd->conf->radio_measurements[0] &
2692928750b6Schristos 	      WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
2693928750b6Schristos 		wpa_printf(MSG_ERROR,
2694928750b6Schristos 			   "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
2695928750b6Schristos 		return -1;
2696928750b6Schristos 	}
2697928750b6Schristos 
2698928750b6Schristos 	if (hwaddr_aton(buf, bssid)) {
2699928750b6Schristos 		wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
2700928750b6Schristos 		return -1;
2701928750b6Schristos 	}
2702928750b6Schristos 
2703928750b6Schristos 	tmp = os_strstr(buf, "ssid=");
2704928750b6Schristos 	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
2705928750b6Schristos 		wpa_printf(MSG_ERROR,
2706928750b6Schristos 			   "CTRL: SET_NEIGHBOR: Bad or missing SSID");
2707928750b6Schristos 		return -1;
2708928750b6Schristos 	}
2709928750b6Schristos 	buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
2710928750b6Schristos 	if (!buf)
2711928750b6Schristos 		return -1;
2712928750b6Schristos 
2713928750b6Schristos 	tmp = os_strstr(buf, "nr=");
2714928750b6Schristos 	if (!tmp) {
2715928750b6Schristos 		wpa_printf(MSG_ERROR,
2716928750b6Schristos 			   "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
2717928750b6Schristos 		return -1;
2718928750b6Schristos 	}
2719928750b6Schristos 
2720928750b6Schristos 	buf = os_strchr(tmp, ' ');
2721928750b6Schristos 	if (buf)
2722928750b6Schristos 		*buf++ = '\0';
2723928750b6Schristos 
2724928750b6Schristos 	nr = wpabuf_parse_bin(tmp + 3);
2725928750b6Schristos 	if (!nr) {
2726928750b6Schristos 		wpa_printf(MSG_ERROR,
2727928750b6Schristos 			   "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
2728928750b6Schristos 		return -1;
2729928750b6Schristos 	}
2730928750b6Schristos 
2731928750b6Schristos 	if (!buf)
2732928750b6Schristos 		goto set;
2733928750b6Schristos 
2734928750b6Schristos 	tmp = os_strstr(buf, "lci=");
2735928750b6Schristos 	if (tmp) {
2736928750b6Schristos 		buf = os_strchr(tmp, ' ');
2737928750b6Schristos 		if (buf)
2738928750b6Schristos 			*buf++ = '\0';
2739928750b6Schristos 		lci = wpabuf_parse_bin(tmp + 4);
2740928750b6Schristos 		if (!lci) {
2741928750b6Schristos 			wpa_printf(MSG_ERROR,
2742928750b6Schristos 				   "CTRL: SET_NEIGHBOR: Bad LCI subelement");
2743928750b6Schristos 			wpabuf_free(nr);
2744928750b6Schristos 			return -1;
2745928750b6Schristos 		}
2746928750b6Schristos 	}
2747928750b6Schristos 
2748928750b6Schristos 	if (!buf)
2749928750b6Schristos 		goto set;
2750928750b6Schristos 
2751928750b6Schristos 	tmp = os_strstr(buf, "civic=");
2752928750b6Schristos 	if (tmp) {
2753928750b6Schristos 		buf = os_strchr(tmp, ' ');
2754928750b6Schristos 		if (buf)
2755928750b6Schristos 			*buf++ = '\0';
2756928750b6Schristos 		civic = wpabuf_parse_bin(tmp + 6);
2757928750b6Schristos 		if (!civic) {
2758928750b6Schristos 			wpa_printf(MSG_ERROR,
2759928750b6Schristos 				   "CTRL: SET_NEIGHBOR: Bad civic subelement");
2760928750b6Schristos 			wpabuf_free(nr);
2761928750b6Schristos 			wpabuf_free(lci);
2762928750b6Schristos 			return -1;
2763928750b6Schristos 		}
2764928750b6Schristos 	}
2765928750b6Schristos 
2766ebb5671cSchristos 	if (!buf)
2767ebb5671cSchristos 		goto set;
2768ebb5671cSchristos 
2769ebb5671cSchristos 	if (os_strstr(buf, "stat"))
2770ebb5671cSchristos 		stationary = 1;
2771ebb5671cSchristos 
2772928750b6Schristos set:
2773ebb5671cSchristos 	ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
2774ebb5671cSchristos 				   stationary);
2775928750b6Schristos 
2776928750b6Schristos 	wpabuf_free(nr);
2777928750b6Schristos 	wpabuf_free(lci);
2778928750b6Schristos 	wpabuf_free(civic);
2779928750b6Schristos 
2780928750b6Schristos 	return ret;
2781928750b6Schristos }
2782928750b6Schristos 
2783928750b6Schristos 
hostapd_ctrl_iface_remove_neighbor(struct hostapd_data * hapd,char * buf)2784928750b6Schristos static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
2785928750b6Schristos 					      char *buf)
2786928750b6Schristos {
2787928750b6Schristos 	struct wpa_ssid_value ssid;
2788928750b6Schristos 	u8 bssid[ETH_ALEN];
2789928750b6Schristos 	char *tmp;
2790928750b6Schristos 
2791928750b6Schristos 	if (hwaddr_aton(buf, bssid)) {
2792928750b6Schristos 		wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
2793928750b6Schristos 		return -1;
2794928750b6Schristos 	}
2795928750b6Schristos 
2796928750b6Schristos 	tmp = os_strstr(buf, "ssid=");
2797928750b6Schristos 	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
2798928750b6Schristos 		wpa_printf(MSG_ERROR,
2799928750b6Schristos 			   "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
2800928750b6Schristos 		return -1;
2801928750b6Schristos 	}
2802928750b6Schristos 
2803928750b6Schristos 	return hostapd_neighbor_remove(hapd, bssid, &ssid);
2804928750b6Schristos }
2805928750b6Schristos 
2806928750b6Schristos 
hostapd_ctrl_driver_flags(struct hostapd_iface * iface,char * buf,size_t buflen)2807928750b6Schristos static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
2808928750b6Schristos 				     size_t buflen)
2809928750b6Schristos {
2810928750b6Schristos 	int ret, i;
2811928750b6Schristos 	char *pos, *end;
2812928750b6Schristos 
2813928750b6Schristos 	ret = os_snprintf(buf, buflen, "%016llX:\n",
2814928750b6Schristos 			  (long long unsigned) iface->drv_flags);
2815928750b6Schristos 	if (os_snprintf_error(buflen, ret))
2816928750b6Schristos 		return -1;
2817928750b6Schristos 
2818928750b6Schristos 	pos = buf + ret;
2819928750b6Schristos 	end = buf + buflen;
2820928750b6Schristos 
2821928750b6Schristos 	for (i = 0; i < 64; i++) {
2822928750b6Schristos 		if (iface->drv_flags & (1LLU << i)) {
2823928750b6Schristos 			ret = os_snprintf(pos, end - pos, "%s\n",
2824928750b6Schristos 					  driver_flag_to_string(1LLU << i));
2825928750b6Schristos 			if (os_snprintf_error(end - pos, ret))
2826928750b6Schristos 				return -1;
2827928750b6Schristos 			pos += ret;
2828928750b6Schristos 		}
2829928750b6Schristos 	}
2830928750b6Schristos 
2831928750b6Schristos 	return pos - buf;
2832928750b6Schristos }
2833928750b6Schristos 
2834928750b6Schristos 
hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry ** acl,int * num,const char * txtaddr)2835ebb5671cSchristos static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
2836ebb5671cSchristos 					  const char *txtaddr)
2837ebb5671cSchristos {
2838ebb5671cSchristos 	u8 addr[ETH_ALEN];
2839ebb5671cSchristos 	struct vlan_description vlan_id;
2840ebb5671cSchristos 
2841ebb5671cSchristos 	if (!(*num))
2842ebb5671cSchristos 		return 0;
2843ebb5671cSchristos 
2844ebb5671cSchristos 	if (hwaddr_aton(txtaddr, addr))
2845ebb5671cSchristos 		return -1;
2846ebb5671cSchristos 
2847ebb5671cSchristos 	if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
2848ebb5671cSchristos 		hostapd_remove_acl_mac(acl, num, addr);
2849ebb5671cSchristos 
2850ebb5671cSchristos 	return 0;
2851ebb5671cSchristos }
2852ebb5671cSchristos 
2853ebb5671cSchristos 
hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry ** acl,int * num)2854ebb5671cSchristos static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
2855ebb5671cSchristos 					      int *num)
2856ebb5671cSchristos {
2857ebb5671cSchristos 	while (*num)
2858ebb5671cSchristos 		hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
2859ebb5671cSchristos }
2860ebb5671cSchristos 
2861ebb5671cSchristos 
hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry * acl,int num,char * buf,size_t buflen)2862ebb5671cSchristos static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
2863ebb5671cSchristos 					   char *buf, size_t buflen)
2864ebb5671cSchristos {
2865ebb5671cSchristos 	int i = 0, len = 0, ret = 0;
2866ebb5671cSchristos 
2867ebb5671cSchristos 	if (!acl)
2868ebb5671cSchristos 		return 0;
2869ebb5671cSchristos 
2870ebb5671cSchristos 	while (i < num) {
2871ebb5671cSchristos 		ret = os_snprintf(buf + len, buflen - len,
2872ebb5671cSchristos 				  MACSTR " VLAN_ID=%d\n",
2873ebb5671cSchristos 				  MAC2STR(acl[i].addr),
2874ebb5671cSchristos 				  acl[i].vlan_id.untagged);
2875ebb5671cSchristos 		if (ret < 0 || (size_t) ret >= buflen - len)
2876ebb5671cSchristos 			return len;
2877ebb5671cSchristos 		i++;
2878ebb5671cSchristos 		len += ret;
2879ebb5671cSchristos 	}
2880ebb5671cSchristos 	return len;
2881ebb5671cSchristos }
2882ebb5671cSchristos 
2883ebb5671cSchristos 
hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry ** acl,int * num,const char * cmd)2884ebb5671cSchristos static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
2885ebb5671cSchristos 					  const char *cmd)
2886ebb5671cSchristos {
2887ebb5671cSchristos 	u8 addr[ETH_ALEN];
2888ebb5671cSchristos 	struct vlan_description vlan_id;
2889ebb5671cSchristos 	int ret = 0, vlanid = 0;
2890ebb5671cSchristos 	const char *pos;
2891ebb5671cSchristos 
2892ebb5671cSchristos 	if (hwaddr_aton(cmd, addr))
2893ebb5671cSchristos 		return -1;
2894ebb5671cSchristos 
2895ebb5671cSchristos 	pos = os_strstr(cmd, "VLAN_ID=");
2896ebb5671cSchristos 	if (pos)
2897ebb5671cSchristos 		vlanid = atoi(pos + 8);
2898ebb5671cSchristos 
2899ebb5671cSchristos 	if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
2900ebb5671cSchristos 		ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
2901ebb5671cSchristos 		if (ret != -1 && *acl)
2902ebb5671cSchristos 			qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
2903ebb5671cSchristos 	}
2904ebb5671cSchristos 
2905ebb5671cSchristos 	return ret < 0 ? -1 : 0;
2906ebb5671cSchristos }
2907ebb5671cSchristos 
2908ebb5671cSchristos 
hostapd_ctrl_iface_get_capability(struct hostapd_data * hapd,const char * field,char * buf,size_t buflen)2909*0d69f216Schristos static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
2910*0d69f216Schristos 					     const char *field, char *buf,
2911*0d69f216Schristos 					     size_t buflen)
2912*0d69f216Schristos {
2913*0d69f216Schristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field);
2914*0d69f216Schristos 
2915*0d69f216Schristos #ifdef CONFIG_DPP
2916*0d69f216Schristos 	if (os_strcmp(field, "dpp") == 0) {
2917*0d69f216Schristos 		int res;
2918*0d69f216Schristos 
2919*0d69f216Schristos #ifdef CONFIG_DPP2
2920*0d69f216Schristos 		res = os_snprintf(buf, buflen, "DPP=2");
2921*0d69f216Schristos #else /* CONFIG_DPP2 */
2922*0d69f216Schristos 		res = os_snprintf(buf, buflen, "DPP=1");
2923*0d69f216Schristos #endif /* CONFIG_DPP2 */
2924*0d69f216Schristos 		if (os_snprintf_error(buflen, res))
2925*0d69f216Schristos 			return -1;
2926*0d69f216Schristos 		return res;
2927*0d69f216Schristos 	}
2928*0d69f216Schristos #endif /* CONFIG_DPP */
2929*0d69f216Schristos 
2930*0d69f216Schristos 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
2931*0d69f216Schristos 		   field);
2932*0d69f216Schristos 
2933*0d69f216Schristos 	return -1;
2934*0d69f216Schristos }
2935*0d69f216Schristos 
2936*0d69f216Schristos 
hostapd_ctrl_iface_receive_process(struct hostapd_data * hapd,char * buf,char * reply,int reply_size,struct sockaddr_storage * from,socklen_t fromlen)2937928750b6Schristos static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
2938928750b6Schristos 					      char *buf, char *reply,
2939928750b6Schristos 					      int reply_size,
2940928750b6Schristos 					      struct sockaddr_storage *from,
2941928750b6Schristos 					      socklen_t fromlen)
2942928750b6Schristos {
2943928750b6Schristos 	int reply_len, res;
2944928750b6Schristos 
29458dbcf02cSchristos 	os_memcpy(reply, "OK\n", 3);
29468dbcf02cSchristos 	reply_len = 3;
29478dbcf02cSchristos 
29488dbcf02cSchristos 	if (os_strcmp(buf, "PING") == 0) {
29498dbcf02cSchristos 		os_memcpy(reply, "PONG\n", 5);
29508dbcf02cSchristos 		reply_len = 5;
295142669be3Schristos 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
295242669be3Schristos 		if (wpa_debug_reopen_file() < 0)
295342669be3Schristos 			reply_len = -1;
2954ebb5671cSchristos 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
2955ebb5671cSchristos 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
295636d97821Schristos 	} else if (os_strcmp(buf, "STATUS") == 0) {
295736d97821Schristos 		reply_len = hostapd_ctrl_iface_status(hapd, reply,
295836d97821Schristos 						      reply_size);
295936d97821Schristos 	} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
296036d97821Schristos 		reply_len = hostapd_drv_status(hapd, reply, reply_size);
29618dbcf02cSchristos 	} else if (os_strcmp(buf, "MIB") == 0) {
29628dbcf02cSchristos 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
29638dbcf02cSchristos 		if (reply_len >= 0) {
29648dbcf02cSchristos 			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
29658dbcf02cSchristos 					  reply_size - reply_len);
29668dbcf02cSchristos 			if (res < 0)
29678dbcf02cSchristos 				reply_len = -1;
29688dbcf02cSchristos 			else
29698dbcf02cSchristos 				reply_len += res;
29708dbcf02cSchristos 		}
29718dbcf02cSchristos 		if (reply_len >= 0) {
29728dbcf02cSchristos 			res = ieee802_1x_get_mib(hapd, reply + reply_len,
29738dbcf02cSchristos 						 reply_size - reply_len);
29748dbcf02cSchristos 			if (res < 0)
29758dbcf02cSchristos 				reply_len = -1;
29768dbcf02cSchristos 			else
29778dbcf02cSchristos 				reply_len += res;
29788dbcf02cSchristos 		}
29798dbcf02cSchristos #ifndef CONFIG_NO_RADIUS
29808dbcf02cSchristos 		if (reply_len >= 0) {
29818dbcf02cSchristos 			res = radius_client_get_mib(hapd->radius,
29828dbcf02cSchristos 						    reply + reply_len,
29838dbcf02cSchristos 						    reply_size - reply_len);
29848dbcf02cSchristos 			if (res < 0)
29858dbcf02cSchristos 				reply_len = -1;
29868dbcf02cSchristos 			else
29878dbcf02cSchristos 				reply_len += res;
29888dbcf02cSchristos 		}
29898dbcf02cSchristos #endif /* CONFIG_NO_RADIUS */
299036d97821Schristos 	} else if (os_strncmp(buf, "MIB ", 4) == 0) {
299136d97821Schristos 		reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
299236d97821Schristos 						   buf + 4);
29938dbcf02cSchristos 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
29948dbcf02cSchristos 		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
29958dbcf02cSchristos 							 reply_size);
29968dbcf02cSchristos 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
29978dbcf02cSchristos 		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
29988dbcf02cSchristos 						   reply_size);
29998dbcf02cSchristos 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
30008dbcf02cSchristos 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
30018dbcf02cSchristos 							reply_size);
30028dbcf02cSchristos 	} else if (os_strcmp(buf, "ATTACH") == 0) {
3003ebb5671cSchristos 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
3004ebb5671cSchristos 			reply_len = -1;
3005ebb5671cSchristos 	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
3006ebb5671cSchristos 		if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7))
30078dbcf02cSchristos 			reply_len = -1;
30088dbcf02cSchristos 	} else if (os_strcmp(buf, "DETACH") == 0) {
3009928750b6Schristos 		if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
30108dbcf02cSchristos 			reply_len = -1;
30118dbcf02cSchristos 	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
3012928750b6Schristos 		if (hostapd_ctrl_iface_level(hapd, from, fromlen,
30138dbcf02cSchristos 						    buf + 6))
30148dbcf02cSchristos 			reply_len = -1;
30158dbcf02cSchristos 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
30168dbcf02cSchristos 		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
30178dbcf02cSchristos 			reply_len = -1;
30188dbcf02cSchristos 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
30198dbcf02cSchristos 		if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
30208dbcf02cSchristos 			reply_len = -1;
30218dbcf02cSchristos 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
30228dbcf02cSchristos 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
30238dbcf02cSchristos 			reply_len = -1;
3024928750b6Schristos #ifdef CONFIG_TAXONOMY
3025928750b6Schristos 	} else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
3026928750b6Schristos 		reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
3027928750b6Schristos 							 reply, reply_size);
3028928750b6Schristos #endif /* CONFIG_TAXONOMY */
3029928750b6Schristos 	} else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
3030928750b6Schristos 		if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
3031928750b6Schristos 			reply_len = -1;
30329a53cbbeSchristos 	} else if (os_strcmp(buf, "STOP_AP") == 0) {
30339a53cbbeSchristos 		if (hostapd_ctrl_iface_stop_ap(hapd))
30349a53cbbeSchristos 			reply_len = -1;
30358dbcf02cSchristos #ifdef CONFIG_IEEE80211W
30368dbcf02cSchristos #ifdef NEED_AP_MLME
30378dbcf02cSchristos 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
30388dbcf02cSchristos 		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
30398dbcf02cSchristos 			reply_len = -1;
30408dbcf02cSchristos #endif /* NEED_AP_MLME */
30418dbcf02cSchristos #endif /* CONFIG_IEEE80211W */
30428dbcf02cSchristos #ifdef CONFIG_WPS
30438dbcf02cSchristos 	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
30448dbcf02cSchristos 		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
30458dbcf02cSchristos 			reply_len = -1;
304642669be3Schristos 	} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
304742669be3Schristos 		reply_len = hostapd_ctrl_iface_wps_check_pin(
304842669be3Schristos 			hapd, buf + 14, reply, reply_size);
30498dbcf02cSchristos 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
305042669be3Schristos 		if (hostapd_wps_button_pushed(hapd, NULL))
30518dbcf02cSchristos 			reply_len = -1;
305262a52023Schristos 	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
305362a52023Schristos 		if (hostapd_wps_cancel(hapd))
30548dbcf02cSchristos 			reply_len = -1;
30551b7205bfSchristos 	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
30561b7205bfSchristos 		reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
30571b7205bfSchristos 							  reply, reply_size);
305842669be3Schristos 	} else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
305942669be3Schristos 		if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
306042669be3Schristos 			reply_len = -1;
306136d97821Schristos 	} else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
306236d97821Schristos 		reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
306336d97821Schristos 							      reply_size);
306462a52023Schristos #ifdef CONFIG_WPS_NFC
306562a52023Schristos 	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
306662a52023Schristos 		if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
306762a52023Schristos 			reply_len = -1;
306862a52023Schristos 	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
306962a52023Schristos 		reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
307062a52023Schristos 			hapd, buf + 21, reply, reply_size);
307162a52023Schristos 	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
307262a52023Schristos 		reply_len = hostapd_ctrl_iface_wps_nfc_token(
307362a52023Schristos 			hapd, buf + 14, reply, reply_size);
307436d97821Schristos 	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
307536d97821Schristos 		reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
307636d97821Schristos 			hapd, buf + 21, reply, reply_size);
307736d97821Schristos 	} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
307836d97821Schristos 		if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
307936d97821Schristos 			reply_len = -1;
308062a52023Schristos #endif /* CONFIG_WPS_NFC */
30818dbcf02cSchristos #endif /* CONFIG_WPS */
308236d97821Schristos #ifdef CONFIG_INTERWORKING
308336d97821Schristos 	} else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
308436d97821Schristos 		if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
308536d97821Schristos 			reply_len = -1;
308636d97821Schristos 	} else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
308736d97821Schristos 		if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
308836d97821Schristos 			reply_len = -1;
308936d97821Schristos #endif /* CONFIG_INTERWORKING */
309036d97821Schristos #ifdef CONFIG_HS20
309136d97821Schristos 	} else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
309236d97821Schristos 		if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
309336d97821Schristos 			reply_len = -1;
309436d97821Schristos 	} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
309536d97821Schristos 		if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
309636d97821Schristos 			reply_len = -1;
309736d97821Schristos #endif /* CONFIG_HS20 */
3098ebb5671cSchristos #ifdef CONFIG_WNM_AP
309962a52023Schristos 	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
310062a52023Schristos 		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
310162a52023Schristos 			reply_len = -1;
310242669be3Schristos 	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
310342669be3Schristos 		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
310442669be3Schristos 			reply_len = -1;
31059a53cbbeSchristos 	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
31069a53cbbeSchristos 		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
31079a53cbbeSchristos 			reply_len = -1;
3108ebb5671cSchristos 	} else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
3109ebb5671cSchristos 		if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
3110ebb5671cSchristos 			reply_len = -1;
3111ebb5671cSchristos #endif /* CONFIG_WNM_AP */
311242669be3Schristos 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
311342669be3Schristos 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
311442669be3Schristos 							  reply_size);
311542669be3Schristos 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
311642669be3Schristos 		if (hostapd_ctrl_iface_set(hapd, buf + 4))
311742669be3Schristos 			reply_len = -1;
311842669be3Schristos 	} else if (os_strncmp(buf, "GET ", 4) == 0) {
311942669be3Schristos 		reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
312042669be3Schristos 						   reply_size);
312162a52023Schristos 	} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
312262a52023Schristos 		if (hostapd_ctrl_iface_enable(hapd->iface))
312362a52023Schristos 			reply_len = -1;
3124*0d69f216Schristos 	} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
3125*0d69f216Schristos 		if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
3126*0d69f216Schristos 			reply_len = -1;
312762a52023Schristos 	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
312862a52023Schristos 		if (hostapd_ctrl_iface_reload(hapd->iface))
312962a52023Schristos 			reply_len = -1;
313062a52023Schristos 	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
313162a52023Schristos 		if (hostapd_ctrl_iface_disable(hapd->iface))
313262a52023Schristos 			reply_len = -1;
31339a53cbbeSchristos 	} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
31349a53cbbeSchristos 		if (ieee802_11_set_beacon(hapd))
31359a53cbbeSchristos 			reply_len = -1;
313636d97821Schristos #ifdef CONFIG_TESTING_OPTIONS
313736d97821Schristos 	} else if (os_strncmp(buf, "RADAR ", 6) == 0) {
313836d97821Schristos 		if (hostapd_ctrl_iface_radar(hapd, buf + 6))
313936d97821Schristos 			reply_len = -1;
314036d97821Schristos 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
314136d97821Schristos 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
314236d97821Schristos 			reply_len = -1;
3143ebb5671cSchristos 	} else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
3144ebb5671cSchristos 		if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
3145ebb5671cSchristos 							      buf + 23) < 0)
3146ebb5671cSchristos 			reply_len = -1;
3147ebb5671cSchristos 	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
3148ebb5671cSchristos 		if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
3149ebb5671cSchristos 			reply_len = -1;
31509a53cbbeSchristos 	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
31519a53cbbeSchristos 		if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
31529a53cbbeSchristos 			reply_len = -1;
31539a53cbbeSchristos 	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
31549a53cbbeSchristos 		if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
31559a53cbbeSchristos 			reply_len = -1;
31569a53cbbeSchristos 	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
31579a53cbbeSchristos 		if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
31589a53cbbeSchristos 			reply_len = -1;
31599a53cbbeSchristos 	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
31609a53cbbeSchristos 		if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
31619a53cbbeSchristos 			reply_len = -1;
31629a53cbbeSchristos 	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
31639a53cbbeSchristos 		if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
31649a53cbbeSchristos 			reply_len = -1;
31659a53cbbeSchristos 	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
31669a53cbbeSchristos 		reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
31679a53cbbeSchristos 							reply_size);
3168928750b6Schristos 	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
3169928750b6Schristos 		if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
3170928750b6Schristos 			reply_len = -1;
3171928750b6Schristos 	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
3172928750b6Schristos 		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
3173ebb5671cSchristos 	} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
3174ebb5671cSchristos 		if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
3175ebb5671cSchristos 			reply_len = -1;
3176ebb5671cSchristos 	} else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
3177ebb5671cSchristos 		if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
3178ebb5671cSchristos 			reply_len = -1;
3179ebb5671cSchristos 	} else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
3180ebb5671cSchristos 		if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
3181ebb5671cSchristos 			reply_len = -1;
3182ebb5671cSchristos 	} else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
3183ebb5671cSchristos 		if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
3184ebb5671cSchristos 			reply_len = -1;
3185ebb5671cSchristos 	} else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
3186ebb5671cSchristos 		if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
3187ebb5671cSchristos 			reply_len = -1;
3188ebb5671cSchristos 	} else if (os_strcmp(buf, "REKEY_GTK") == 0) {
3189ebb5671cSchristos 		if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
3190ebb5671cSchristos 			reply_len = -1;
319136d97821Schristos #endif /* CONFIG_TESTING_OPTIONS */
319236d97821Schristos 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
319336d97821Schristos 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
319436d97821Schristos 			reply_len = -1;
319536d97821Schristos 	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
319636d97821Schristos 		reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
319736d97821Schristos 						      reply_size);
31989a53cbbeSchristos 	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
31999a53cbbeSchristos 		ieee802_1x_erp_flush(hapd);
32009a53cbbeSchristos #ifdef RADIUS_SERVER
32019a53cbbeSchristos 		radius_server_erp_flush(hapd->radius_srv);
32029a53cbbeSchristos #endif /* RADIUS_SERVER */
3203928750b6Schristos 	} else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
3204928750b6Schristos 		if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
3205928750b6Schristos 			reply_len = -1;
3206928750b6Schristos 	} else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
3207928750b6Schristos 		if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
3208928750b6Schristos 			reply_len = -1;
3209928750b6Schristos 	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
3210928750b6Schristos 		reply_len = hostapd_ctrl_iface_log_level(
3211928750b6Schristos 			hapd, buf + 9, reply, reply_size);
3212928750b6Schristos #ifdef NEED_AP_MLME
3213928750b6Schristos 	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
3214928750b6Schristos 		reply_len = hostapd_ctrl_iface_track_sta_list(
3215928750b6Schristos 			hapd, reply, reply_size);
3216928750b6Schristos #endif /* NEED_AP_MLME */
3217928750b6Schristos 	} else if (os_strcmp(buf, "PMKSA") == 0) {
3218928750b6Schristos 		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
3219928750b6Schristos 							  reply_size);
3220928750b6Schristos 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
3221928750b6Schristos 		hostapd_ctrl_iface_pmksa_flush(hapd);
3222ebb5671cSchristos 	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
3223ebb5671cSchristos 		if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
3224ebb5671cSchristos 			reply_len = -1;
3225928750b6Schristos 	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
3226928750b6Schristos 		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
3227928750b6Schristos 			reply_len = -1;
3228928750b6Schristos 	} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
3229928750b6Schristos 		if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
3230928750b6Schristos 			reply_len = -1;
3231928750b6Schristos 	} else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
3232928750b6Schristos 		if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
3233928750b6Schristos 			reply_len = -1;
3234928750b6Schristos 	} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
3235928750b6Schristos 		if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
3236928750b6Schristos 			reply_len = -1;
3237ebb5671cSchristos 	} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
3238ebb5671cSchristos 		reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
3239ebb5671cSchristos 							  reply, reply_size);
3240928750b6Schristos 	} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
3241928750b6Schristos 		reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
3242928750b6Schristos 						      reply_size);
3243ebb5671cSchristos 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
3244ebb5671cSchristos 		eloop_terminate();
3245ebb5671cSchristos 	} else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
3246ebb5671cSchristos 		if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
3247ebb5671cSchristos 			if (!hostapd_ctrl_iface_acl_add_mac(
3248ebb5671cSchristos 				    &hapd->conf->accept_mac,
3249ebb5671cSchristos 				    &hapd->conf->num_accept_mac, buf + 19))
3250ebb5671cSchristos 				hostapd_disassoc_accept_mac(hapd);
3251ebb5671cSchristos 			else
3252ebb5671cSchristos 				reply_len = -1;
3253ebb5671cSchristos 		} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
3254ebb5671cSchristos 			hostapd_ctrl_iface_acl_del_mac(
3255ebb5671cSchristos 				&hapd->conf->accept_mac,
3256ebb5671cSchristos 				&hapd->conf->num_accept_mac, buf + 19);
3257ebb5671cSchristos 		} else if (os_strcmp(buf + 11, "SHOW") == 0) {
3258ebb5671cSchristos 			reply_len = hostapd_ctrl_iface_acl_show_mac(
3259ebb5671cSchristos 				hapd->conf->accept_mac,
3260ebb5671cSchristos 				hapd->conf->num_accept_mac, reply, reply_size);
3261ebb5671cSchristos 		} else if (os_strcmp(buf + 11, "CLEAR") == 0) {
3262ebb5671cSchristos 			hostapd_ctrl_iface_acl_clear_list(
3263ebb5671cSchristos 				&hapd->conf->accept_mac,
3264ebb5671cSchristos 				&hapd->conf->num_accept_mac);
3265ebb5671cSchristos 		}
3266ebb5671cSchristos 	} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
3267ebb5671cSchristos 		if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
3268ebb5671cSchristos 			if (!hostapd_ctrl_iface_acl_add_mac(
3269ebb5671cSchristos 				    &hapd->conf->deny_mac,
3270ebb5671cSchristos 				    &hapd->conf->num_deny_mac, buf + 17))
3271ebb5671cSchristos 				hostapd_disassoc_deny_mac(hapd);
3272ebb5671cSchristos 		} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
3273ebb5671cSchristos 			hostapd_ctrl_iface_acl_del_mac(
3274ebb5671cSchristos 				&hapd->conf->deny_mac,
3275ebb5671cSchristos 				&hapd->conf->num_deny_mac, buf + 17);
3276ebb5671cSchristos 		} else if (os_strcmp(buf + 9, "SHOW") == 0) {
3277ebb5671cSchristos 			reply_len = hostapd_ctrl_iface_acl_show_mac(
3278ebb5671cSchristos 				hapd->conf->deny_mac,
3279ebb5671cSchristos 				hapd->conf->num_deny_mac, reply, reply_size);
3280ebb5671cSchristos 		} else if (os_strcmp(buf + 9, "CLEAR") == 0) {
3281ebb5671cSchristos 			hostapd_ctrl_iface_acl_clear_list(
3282ebb5671cSchristos 				&hapd->conf->deny_mac,
3283ebb5671cSchristos 				&hapd->conf->num_deny_mac);
3284ebb5671cSchristos 		}
3285ebb5671cSchristos #ifdef CONFIG_DPP
3286ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
3287ebb5671cSchristos 		res = hostapd_dpp_qr_code(hapd, buf + 12);
3288ebb5671cSchristos 		if (res < 0) {
3289ebb5671cSchristos 			reply_len = -1;
3290ebb5671cSchristos 		} else {
3291ebb5671cSchristos 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3292ebb5671cSchristos 			if (os_snprintf_error(reply_size, reply_len))
3293ebb5671cSchristos 				reply_len = -1;
3294ebb5671cSchristos 		}
3295ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
3296*0d69f216Schristos 		res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18);
3297ebb5671cSchristos 		if (res < 0) {
3298ebb5671cSchristos 			reply_len = -1;
3299ebb5671cSchristos 		} else {
3300ebb5671cSchristos 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3301ebb5671cSchristos 			if (os_snprintf_error(reply_size, reply_len))
3302ebb5671cSchristos 				reply_len = -1;
3303ebb5671cSchristos 		}
3304ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
3305*0d69f216Schristos 		if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp,
3306*0d69f216Schristos 					 buf + 21) < 0)
3307ebb5671cSchristos 			reply_len = -1;
3308ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
3309ebb5671cSchristos 		const char *uri;
3310ebb5671cSchristos 
3311*0d69f216Schristos 		uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp,
3312*0d69f216Schristos 					    atoi(buf + 22));
3313ebb5671cSchristos 		if (!uri) {
3314ebb5671cSchristos 			reply_len = -1;
3315ebb5671cSchristos 		} else {
3316ebb5671cSchristos 			reply_len = os_snprintf(reply, reply_size, "%s", uri);
3317ebb5671cSchristos 			if (os_snprintf_error(reply_size, reply_len))
3318ebb5671cSchristos 				reply_len = -1;
3319ebb5671cSchristos 		}
3320ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
3321*0d69f216Schristos 		reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp,
3322*0d69f216Schristos 					       atoi(buf + 19),
3323ebb5671cSchristos 			reply, reply_size);
3324ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
3325ebb5671cSchristos 		if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
3326ebb5671cSchristos 			reply_len = -1;
3327ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
3328ebb5671cSchristos 		if (hostapd_dpp_listen(hapd, buf + 11) < 0)
3329ebb5671cSchristos 			reply_len = -1;
3330ebb5671cSchristos 	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
3331ebb5671cSchristos 		hostapd_dpp_stop(hapd);
3332ebb5671cSchristos 		hostapd_dpp_listen_stop(hapd);
3333ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
3334*0d69f216Schristos 		res = dpp_configurator_add(hapd->iface->interfaces->dpp,
3335*0d69f216Schristos 					   buf + 20);
3336ebb5671cSchristos 		if (res < 0) {
3337ebb5671cSchristos 			reply_len = -1;
3338ebb5671cSchristos 		} else {
3339ebb5671cSchristos 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3340ebb5671cSchristos 			if (os_snprintf_error(reply_size, reply_len))
3341ebb5671cSchristos 				reply_len = -1;
3342ebb5671cSchristos 		}
3343ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
3344*0d69f216Schristos 		if (dpp_configurator_remove(hapd->iface->interfaces->dpp,
3345*0d69f216Schristos 					    buf + 24) < 0)
3346ebb5671cSchristos 			reply_len = -1;
3347ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
3348*0d69f216Schristos 		if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0)
3349ebb5671cSchristos 			reply_len = -1;
3350ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
3351*0d69f216Schristos 		reply_len = dpp_configurator_get_key_id(
3352*0d69f216Schristos 			hapd->iface->interfaces->dpp,
3353ebb5671cSchristos 			atoi(buf + 25),
3354ebb5671cSchristos 			reply, reply_size);
3355ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
3356ebb5671cSchristos 		res = hostapd_dpp_pkex_add(hapd, buf + 12);
3357ebb5671cSchristos 		if (res < 0) {
3358ebb5671cSchristos 			reply_len = -1;
3359ebb5671cSchristos 		} else {
3360ebb5671cSchristos 			reply_len = os_snprintf(reply, reply_size, "%d", res);
3361ebb5671cSchristos 			if (os_snprintf_error(reply_size, reply_len))
3362ebb5671cSchristos 				reply_len = -1;
3363ebb5671cSchristos 		}
3364ebb5671cSchristos 	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
3365ebb5671cSchristos 		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
3366ebb5671cSchristos 			reply_len = -1;
3367ebb5671cSchristos #endif /* CONFIG_DPP */
3368ebb5671cSchristos #ifdef RADIUS_SERVER
3369ebb5671cSchristos 	} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
3370ebb5671cSchristos 		if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
3371ebb5671cSchristos 			reply_len = -1;
3372ebb5671cSchristos #endif /* RADIUS_SERVER */
3373*0d69f216Schristos 	} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
3374*0d69f216Schristos 		reply_len = hostapd_ctrl_iface_get_capability(
3375*0d69f216Schristos 			hapd, buf + 15, reply, reply_size);
33768dbcf02cSchristos 	} else {
33778dbcf02cSchristos 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
33788dbcf02cSchristos 		reply_len = 16;
33798dbcf02cSchristos 	}
33808dbcf02cSchristos 
33818dbcf02cSchristos 	if (reply_len < 0) {
33828dbcf02cSchristos 		os_memcpy(reply, "FAIL\n", 5);
33838dbcf02cSchristos 		reply_len = 5;
33848dbcf02cSchristos 	}
3385928750b6Schristos 
3386928750b6Schristos 	return reply_len;
3387928750b6Schristos }
3388928750b6Schristos 
3389928750b6Schristos 
hostapd_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)3390928750b6Schristos static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
3391928750b6Schristos 				       void *sock_ctx)
3392928750b6Schristos {
3393928750b6Schristos 	struct hostapd_data *hapd = eloop_ctx;
3394928750b6Schristos 	char buf[4096];
3395928750b6Schristos 	int res;
3396928750b6Schristos 	struct sockaddr_storage from;
3397928750b6Schristos 	socklen_t fromlen = sizeof(from);
3398928750b6Schristos 	char *reply, *pos = buf;
3399928750b6Schristos 	const int reply_size = 4096;
3400928750b6Schristos 	int reply_len;
3401928750b6Schristos 	int level = MSG_DEBUG;
3402928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
3403928750b6Schristos 	unsigned char lcookie[COOKIE_LEN];
3404928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
3405928750b6Schristos 
3406928750b6Schristos 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
3407928750b6Schristos 		       (struct sockaddr *) &from, &fromlen);
3408928750b6Schristos 	if (res < 0) {
3409928750b6Schristos 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
3410928750b6Schristos 			   strerror(errno));
3411928750b6Schristos 		return;
3412928750b6Schristos 	}
3413928750b6Schristos 	buf[res] = '\0';
3414928750b6Schristos 
3415928750b6Schristos 	reply = os_malloc(reply_size);
3416928750b6Schristos 	if (reply == NULL) {
3417928750b6Schristos 		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
3418928750b6Schristos 			   fromlen) < 0) {
3419928750b6Schristos 			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
3420928750b6Schristos 				   strerror(errno));
3421928750b6Schristos 		}
3422928750b6Schristos 		return;
3423928750b6Schristos 	}
3424928750b6Schristos 
3425928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
3426928750b6Schristos 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
3427928750b6Schristos 		os_memcpy(reply, "COOKIE=", 7);
3428928750b6Schristos 		wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
3429928750b6Schristos 				 cookie, COOKIE_LEN);
3430928750b6Schristos 		reply_len = 7 + 2 * COOKIE_LEN;
3431928750b6Schristos 		goto done;
3432928750b6Schristos 	}
3433928750b6Schristos 
3434928750b6Schristos 	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
3435928750b6Schristos 	    hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
3436928750b6Schristos 		wpa_printf(MSG_DEBUG,
3437928750b6Schristos 			   "CTRL: No cookie in the request - drop request");
3438928750b6Schristos 		os_free(reply);
3439928750b6Schristos 		return;
3440928750b6Schristos 	}
3441928750b6Schristos 
3442928750b6Schristos 	if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) {
3443928750b6Schristos 		wpa_printf(MSG_DEBUG,
3444928750b6Schristos 			   "CTRL: Invalid cookie in the request - drop request");
3445928750b6Schristos 		os_free(reply);
3446928750b6Schristos 		return;
3447928750b6Schristos 	}
3448928750b6Schristos 
3449928750b6Schristos 	pos = buf + 7 + 2 * COOKIE_LEN;
3450928750b6Schristos 	while (*pos == ' ')
3451928750b6Schristos 		pos++;
3452928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
3453928750b6Schristos 
3454928750b6Schristos 	if (os_strcmp(pos, "PING") == 0)
3455928750b6Schristos 		level = MSG_EXCESSIVE;
3456928750b6Schristos 	wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res);
3457928750b6Schristos 
3458928750b6Schristos 	reply_len = hostapd_ctrl_iface_receive_process(hapd, pos,
3459928750b6Schristos 						       reply, reply_size,
3460928750b6Schristos 						       &from, fromlen);
3461928750b6Schristos 
3462928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
3463928750b6Schristos done:
3464928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
34659a53cbbeSchristos 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
34669a53cbbeSchristos 		   fromlen) < 0) {
34679a53cbbeSchristos 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
34689a53cbbeSchristos 			   strerror(errno));
34699a53cbbeSchristos 	}
34708dbcf02cSchristos 	os_free(reply);
34718dbcf02cSchristos }
34728dbcf02cSchristos 
34738dbcf02cSchristos 
3474928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
hostapd_ctrl_iface_path(struct hostapd_data * hapd)34758dbcf02cSchristos static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
34768dbcf02cSchristos {
34778dbcf02cSchristos 	char *buf;
34788dbcf02cSchristos 	size_t len;
34798dbcf02cSchristos 
34808dbcf02cSchristos 	if (hapd->conf->ctrl_interface == NULL)
34818dbcf02cSchristos 		return NULL;
34828dbcf02cSchristos 
34838dbcf02cSchristos 	len = os_strlen(hapd->conf->ctrl_interface) +
34848dbcf02cSchristos 		os_strlen(hapd->conf->iface) + 2;
34858dbcf02cSchristos 	buf = os_malloc(len);
34868dbcf02cSchristos 	if (buf == NULL)
34878dbcf02cSchristos 		return NULL;
34888dbcf02cSchristos 
34898dbcf02cSchristos 	os_snprintf(buf, len, "%s/%s",
34908dbcf02cSchristos 		    hapd->conf->ctrl_interface, hapd->conf->iface);
34918dbcf02cSchristos 	buf[len - 1] = '\0';
34928dbcf02cSchristos 	return buf;
34938dbcf02cSchristos }
3494928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
34958dbcf02cSchristos 
34968dbcf02cSchristos 
hostapd_ctrl_iface_msg_cb(void * ctx,int level,enum wpa_msg_type type,const char * txt,size_t len)3497928750b6Schristos static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
3498928750b6Schristos 				      enum wpa_msg_type type,
34998dbcf02cSchristos 				      const char *txt, size_t len)
35008dbcf02cSchristos {
35018dbcf02cSchristos 	struct hostapd_data *hapd = ctx;
35028dbcf02cSchristos 	if (hapd == NULL)
35038dbcf02cSchristos 		return;
3504928750b6Schristos 	hostapd_ctrl_iface_send(hapd, level, type, txt, len);
35058dbcf02cSchristos }
35068dbcf02cSchristos 
35078dbcf02cSchristos 
hostapd_ctrl_iface_init(struct hostapd_data * hapd)35088dbcf02cSchristos int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
35098dbcf02cSchristos {
3510928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
3511928750b6Schristos 	int port = HOSTAPD_CTRL_IFACE_PORT;
3512928750b6Schristos 	char p[32] = { 0 };
3513928750b6Schristos 	char port_str[40], *tmp;
3514928750b6Schristos 	char *pos;
3515928750b6Schristos 	struct addrinfo hints = { 0 }, *res, *saveres;
3516928750b6Schristos 	int n;
3517928750b6Schristos 
3518928750b6Schristos 	if (hapd->ctrl_sock > -1) {
3519928750b6Schristos 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
3520928750b6Schristos 		return 0;
3521928750b6Schristos 	}
3522928750b6Schristos 
3523928750b6Schristos 	if (hapd->conf->ctrl_interface == NULL)
3524928750b6Schristos 		return 0;
3525928750b6Schristos 
3526928750b6Schristos 	pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
3527928750b6Schristos 	if (pos) {
3528928750b6Schristos 		pos += 4;
3529928750b6Schristos 		port = atoi(pos);
3530928750b6Schristos 		if (port <= 0) {
3531928750b6Schristos 			wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
3532928750b6Schristos 			goto fail;
3533928750b6Schristos 		}
3534928750b6Schristos 	}
3535928750b6Schristos 
3536928750b6Schristos 	dl_list_init(&hapd->ctrl_dst);
3537928750b6Schristos 	hapd->ctrl_sock = -1;
3538928750b6Schristos 	os_get_random(cookie, COOKIE_LEN);
3539928750b6Schristos 
3540928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
3541928750b6Schristos 	hints.ai_flags = AI_PASSIVE;
3542928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
3543928750b6Schristos 
3544928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
3545928750b6Schristos 	hints.ai_family = AF_INET6;
3546928750b6Schristos #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
3547928750b6Schristos 	hints.ai_family = AF_INET;
3548928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
3549928750b6Schristos 	hints.ai_socktype = SOCK_DGRAM;
3550928750b6Schristos 
3551928750b6Schristos try_again:
3552928750b6Schristos 	os_snprintf(p, sizeof(p), "%d", port);
3553928750b6Schristos 	n = getaddrinfo(NULL, p, &hints, &res);
3554928750b6Schristos 	if (n) {
3555928750b6Schristos 		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
3556928750b6Schristos 		goto fail;
3557928750b6Schristos 	}
3558928750b6Schristos 
3559928750b6Schristos 	saveres = res;
3560928750b6Schristos 	hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype,
3561928750b6Schristos 				 res->ai_protocol);
3562928750b6Schristos 	if (hapd->ctrl_sock < 0) {
3563928750b6Schristos 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
3564928750b6Schristos 		goto fail;
3565928750b6Schristos 	}
3566928750b6Schristos 
3567928750b6Schristos 	if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
3568928750b6Schristos 		port--;
3569928750b6Schristos 		if ((HOSTAPD_CTRL_IFACE_PORT - port) <
3570928750b6Schristos 		    HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
3571928750b6Schristos 			goto try_again;
3572928750b6Schristos 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
3573928750b6Schristos 		goto fail;
3574928750b6Schristos 	}
3575928750b6Schristos 
3576928750b6Schristos 	freeaddrinfo(saveres);
3577928750b6Schristos 
3578928750b6Schristos 	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
3579928750b6Schristos 	tmp = os_strdup(port_str);
3580928750b6Schristos 	if (tmp) {
3581928750b6Schristos 		os_free(hapd->conf->ctrl_interface);
3582928750b6Schristos 		hapd->conf->ctrl_interface = tmp;
3583928750b6Schristos 	}
3584928750b6Schristos 	wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
3585928750b6Schristos 
3586928750b6Schristos 	if (eloop_register_read_sock(hapd->ctrl_sock,
3587928750b6Schristos 				     hostapd_ctrl_iface_receive, hapd, NULL) <
3588928750b6Schristos 	    0) {
3589928750b6Schristos 		hostapd_ctrl_iface_deinit(hapd);
3590928750b6Schristos 		return -1;
3591928750b6Schristos 	}
3592928750b6Schristos 
3593928750b6Schristos 	hapd->msg_ctx = hapd;
3594928750b6Schristos 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
3595928750b6Schristos 
3596928750b6Schristos 	return 0;
3597928750b6Schristos 
3598928750b6Schristos fail:
3599928750b6Schristos 	if (hapd->ctrl_sock >= 0)
3600928750b6Schristos 		close(hapd->ctrl_sock);
3601928750b6Schristos 	return -1;
3602928750b6Schristos #else /* CONFIG_CTRL_IFACE_UDP */
36038dbcf02cSchristos 	struct sockaddr_un addr;
36048dbcf02cSchristos 	int s = -1;
36058dbcf02cSchristos 	char *fname = NULL;
36068dbcf02cSchristos 
360762a52023Schristos 	if (hapd->ctrl_sock > -1) {
360862a52023Schristos 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
360962a52023Schristos 		return 0;
361062a52023Schristos 	}
36118dbcf02cSchristos 
3612928750b6Schristos 	dl_list_init(&hapd->ctrl_dst);
3613928750b6Schristos 
36148dbcf02cSchristos 	if (hapd->conf->ctrl_interface == NULL)
36158dbcf02cSchristos 		return 0;
36168dbcf02cSchristos 
36178dbcf02cSchristos 	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
36188dbcf02cSchristos 		if (errno == EEXIST) {
36198dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "Using existing control "
36208dbcf02cSchristos 				   "interface directory.");
36218dbcf02cSchristos 		} else {
36229a53cbbeSchristos 			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
36239a53cbbeSchristos 				   strerror(errno));
36248dbcf02cSchristos 			goto fail;
36258dbcf02cSchristos 		}
36268dbcf02cSchristos 	}
36278dbcf02cSchristos 
36288dbcf02cSchristos 	if (hapd->conf->ctrl_interface_gid_set &&
3629*0d69f216Schristos 	    lchown(hapd->conf->ctrl_interface, -1,
36308dbcf02cSchristos 		   hapd->conf->ctrl_interface_gid) < 0) {
3631*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
36329a53cbbeSchristos 			   strerror(errno));
36338dbcf02cSchristos 		return -1;
36348dbcf02cSchristos 	}
36358dbcf02cSchristos 
363636d97821Schristos 	if (!hapd->conf->ctrl_interface_gid_set &&
363736d97821Schristos 	    hapd->iface->interfaces->ctrl_iface_group &&
3638*0d69f216Schristos 	    lchown(hapd->conf->ctrl_interface, -1,
363936d97821Schristos 		   hapd->iface->interfaces->ctrl_iface_group) < 0) {
3640*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
36419a53cbbeSchristos 			   strerror(errno));
364236d97821Schristos 		return -1;
364336d97821Schristos 	}
364436d97821Schristos 
364562a52023Schristos #ifdef ANDROID
364662a52023Schristos 	/*
364762a52023Schristos 	 * Android is using umask 0077 which would leave the control interface
364862a52023Schristos 	 * directory without group access. This breaks things since Wi-Fi
364962a52023Schristos 	 * framework assumes that this directory can be accessed by other
365062a52023Schristos 	 * applications in the wifi group. Fix this by adding group access even
365162a52023Schristos 	 * if umask value would prevent this.
365262a52023Schristos 	 */
365362a52023Schristos 	if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
365462a52023Schristos 		wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
365562a52023Schristos 			   strerror(errno));
365662a52023Schristos 		/* Try to continue anyway */
365762a52023Schristos 	}
365862a52023Schristos #endif /* ANDROID */
365962a52023Schristos 
36608dbcf02cSchristos 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
36618dbcf02cSchristos 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
36628dbcf02cSchristos 		goto fail;
36638dbcf02cSchristos 
36648dbcf02cSchristos 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
36658dbcf02cSchristos 	if (s < 0) {
36669a53cbbeSchristos 		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
36678dbcf02cSchristos 		goto fail;
36688dbcf02cSchristos 	}
36698dbcf02cSchristos 
36708dbcf02cSchristos 	os_memset(&addr, 0, sizeof(addr));
36718dbcf02cSchristos #ifdef __FreeBSD__
36728dbcf02cSchristos 	addr.sun_len = sizeof(addr);
36738dbcf02cSchristos #endif /* __FreeBSD__ */
36748dbcf02cSchristos 	addr.sun_family = AF_UNIX;
36758dbcf02cSchristos 	fname = hostapd_ctrl_iface_path(hapd);
36768dbcf02cSchristos 	if (fname == NULL)
36778dbcf02cSchristos 		goto fail;
36788dbcf02cSchristos 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
36798dbcf02cSchristos 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
36808dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
36818dbcf02cSchristos 			   strerror(errno));
36828dbcf02cSchristos 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
36838dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
36848dbcf02cSchristos 				   " allow connections - assuming it was left"
36858dbcf02cSchristos 				   "over from forced program termination");
36868dbcf02cSchristos 			if (unlink(fname) < 0) {
36879a53cbbeSchristos 				wpa_printf(MSG_ERROR,
36889a53cbbeSchristos 					   "Could not unlink existing ctrl_iface socket '%s': %s",
36899a53cbbeSchristos 					   fname, strerror(errno));
36908dbcf02cSchristos 				goto fail;
36918dbcf02cSchristos 			}
36928dbcf02cSchristos 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
36938dbcf02cSchristos 			    0) {
36949a53cbbeSchristos 				wpa_printf(MSG_ERROR,
36959a53cbbeSchristos 					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
36969a53cbbeSchristos 					   strerror(errno));
36978dbcf02cSchristos 				goto fail;
36988dbcf02cSchristos 			}
36998dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
37008dbcf02cSchristos 				   "ctrl_iface socket '%s'", fname);
37018dbcf02cSchristos 		} else {
37028dbcf02cSchristos 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
37038dbcf02cSchristos 				   "be in use - cannot override it");
37048dbcf02cSchristos 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
37058dbcf02cSchristos 				   "not used anymore", fname);
37068dbcf02cSchristos 			os_free(fname);
37078dbcf02cSchristos 			fname = NULL;
37088dbcf02cSchristos 			goto fail;
37098dbcf02cSchristos 		}
37108dbcf02cSchristos 	}
37118dbcf02cSchristos 
37128dbcf02cSchristos 	if (hapd->conf->ctrl_interface_gid_set &&
3713*0d69f216Schristos 	    lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
3714*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
37159a53cbbeSchristos 			   strerror(errno));
37168dbcf02cSchristos 		goto fail;
37178dbcf02cSchristos 	}
37188dbcf02cSchristos 
371936d97821Schristos 	if (!hapd->conf->ctrl_interface_gid_set &&
372036d97821Schristos 	    hapd->iface->interfaces->ctrl_iface_group &&
3721*0d69f216Schristos 	    lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
3722*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
37239a53cbbeSchristos 			   strerror(errno));
372436d97821Schristos 		goto fail;
372536d97821Schristos 	}
372636d97821Schristos 
37278dbcf02cSchristos 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
37289a53cbbeSchristos 		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
37299a53cbbeSchristos 			   strerror(errno));
37308dbcf02cSchristos 		goto fail;
37318dbcf02cSchristos 	}
37328dbcf02cSchristos 	os_free(fname);
37338dbcf02cSchristos 
37348dbcf02cSchristos 	hapd->ctrl_sock = s;
37359a53cbbeSchristos 	if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
37369a53cbbeSchristos 				     NULL) < 0) {
37379a53cbbeSchristos 		hostapd_ctrl_iface_deinit(hapd);
37389a53cbbeSchristos 		return -1;
37399a53cbbeSchristos 	}
37408dbcf02cSchristos 	hapd->msg_ctx = hapd;
37418dbcf02cSchristos 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
37428dbcf02cSchristos 
37438dbcf02cSchristos 	return 0;
37448dbcf02cSchristos 
37458dbcf02cSchristos fail:
37468dbcf02cSchristos 	if (s >= 0)
37478dbcf02cSchristos 		close(s);
37488dbcf02cSchristos 	if (fname) {
37498dbcf02cSchristos 		unlink(fname);
37508dbcf02cSchristos 		os_free(fname);
37518dbcf02cSchristos 	}
37528dbcf02cSchristos 	return -1;
3753928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
37548dbcf02cSchristos }
37558dbcf02cSchristos 
37568dbcf02cSchristos 
hostapd_ctrl_iface_deinit(struct hostapd_data * hapd)37578dbcf02cSchristos void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
37588dbcf02cSchristos {
37598dbcf02cSchristos 	struct wpa_ctrl_dst *dst, *prev;
37608dbcf02cSchristos 
37618dbcf02cSchristos 	if (hapd->ctrl_sock > -1) {
3762928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
37638dbcf02cSchristos 		char *fname;
3764928750b6Schristos #endif /* !CONFIG_CTRL_IFACE_UDP */
3765928750b6Schristos 
37668dbcf02cSchristos 		eloop_unregister_read_sock(hapd->ctrl_sock);
37678dbcf02cSchristos 		close(hapd->ctrl_sock);
37688dbcf02cSchristos 		hapd->ctrl_sock = -1;
3769928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
37708dbcf02cSchristos 		fname = hostapd_ctrl_iface_path(hapd);
37718dbcf02cSchristos 		if (fname)
37728dbcf02cSchristos 			unlink(fname);
37738dbcf02cSchristos 		os_free(fname);
37748dbcf02cSchristos 
37758dbcf02cSchristos 		if (hapd->conf->ctrl_interface &&
37768dbcf02cSchristos 		    rmdir(hapd->conf->ctrl_interface) < 0) {
37778dbcf02cSchristos 			if (errno == ENOTEMPTY) {
37788dbcf02cSchristos 				wpa_printf(MSG_DEBUG, "Control interface "
37798dbcf02cSchristos 					   "directory not empty - leaving it "
37808dbcf02cSchristos 					   "behind");
37818dbcf02cSchristos 			} else {
378236d97821Schristos 				wpa_printf(MSG_ERROR,
378336d97821Schristos 					   "rmdir[ctrl_interface=%s]: %s",
378436d97821Schristos 					   hapd->conf->ctrl_interface,
378536d97821Schristos 					   strerror(errno));
37868dbcf02cSchristos 			}
37878dbcf02cSchristos 		}
3788928750b6Schristos #endif /* !CONFIG_CTRL_IFACE_UDP */
37898dbcf02cSchristos 	}
37908dbcf02cSchristos 
3791928750b6Schristos 	dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst,
3792928750b6Schristos 			      list)
3793928750b6Schristos 		os_free(dst);
37949a53cbbeSchristos 
37959a53cbbeSchristos #ifdef CONFIG_TESTING_OPTIONS
37969a53cbbeSchristos 	l2_packet_deinit(hapd->l2_test);
37979a53cbbeSchristos 	hapd->l2_test = NULL;
37989a53cbbeSchristos #endif /* CONFIG_TESTING_OPTIONS */
37998dbcf02cSchristos }
38008dbcf02cSchristos 
38018dbcf02cSchristos 
hostapd_ctrl_iface_add(struct hapd_interfaces * interfaces,char * buf)380262a52023Schristos static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
380362a52023Schristos 				  char *buf)
380462a52023Schristos {
380562a52023Schristos 	if (hostapd_add_iface(interfaces, buf) < 0) {
380662a52023Schristos 		wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
380762a52023Schristos 		return -1;
380862a52023Schristos 	}
380962a52023Schristos 	return 0;
381062a52023Schristos }
381162a52023Schristos 
381262a52023Schristos 
hostapd_ctrl_iface_remove(struct hapd_interfaces * interfaces,char * buf)381362a52023Schristos static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
381462a52023Schristos 				     char *buf)
381562a52023Schristos {
381662a52023Schristos 	if (hostapd_remove_iface(interfaces, buf) < 0) {
381762a52023Schristos 		wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
381862a52023Schristos 		return -1;
381962a52023Schristos 	}
382062a52023Schristos 	return 0;
382162a52023Schristos }
382262a52023Schristos 
382362a52023Schristos 
hostapd_global_ctrl_iface_attach(struct hapd_interfaces * interfaces,struct sockaddr_storage * from,socklen_t fromlen,char * input)3824928750b6Schristos static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
3825928750b6Schristos 					    struct sockaddr_storage *from,
3826ebb5671cSchristos 					    socklen_t fromlen, char *input)
3827928750b6Schristos {
3828ebb5671cSchristos 	return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen,
3829ebb5671cSchristos 				 input);
3830928750b6Schristos }
3831928750b6Schristos 
3832928750b6Schristos 
hostapd_global_ctrl_iface_detach(struct hapd_interfaces * interfaces,struct sockaddr_storage * from,socklen_t fromlen)3833928750b6Schristos static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
3834928750b6Schristos 					    struct sockaddr_storage *from,
3835928750b6Schristos 					    socklen_t fromlen)
3836928750b6Schristos {
3837928750b6Schristos 	return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen);
3838928750b6Schristos }
3839928750b6Schristos 
3840928750b6Schristos 
hostapd_ctrl_iface_flush(struct hapd_interfaces * interfaces)384136d97821Schristos static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
384236d97821Schristos {
384336d97821Schristos #ifdef CONFIG_WPS_TESTING
384436d97821Schristos 	wps_version_number = 0x20;
384536d97821Schristos 	wps_testing_dummy_cred = 0;
384636d97821Schristos 	wps_corrupt_pkhash = 0;
384736d97821Schristos #endif /* CONFIG_WPS_TESTING */
3848ebb5671cSchristos 
3849ebb5671cSchristos #ifdef CONFIG_TESTING_OPTIONS
3850ebb5671cSchristos #ifdef CONFIG_DPP
3851ebb5671cSchristos 	dpp_test = DPP_TEST_DISABLED;
3852ebb5671cSchristos #endif /* CONFIG_DPP */
3853ebb5671cSchristos #endif /* CONFIG_TESTING_OPTIONS */
3854ebb5671cSchristos 
3855ebb5671cSchristos #ifdef CONFIG_DPP
3856*0d69f216Schristos 	dpp_global_clear(interfaces->dpp);
3857ebb5671cSchristos #endif /* CONFIG_DPP */
385836d97821Schristos }
385936d97821Schristos 
386036d97821Schristos 
3861928750b6Schristos #ifdef CONFIG_FST
3862928750b6Schristos 
3863928750b6Schristos static int
hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces * interfaces,const char * cmd)3864928750b6Schristos hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
3865928750b6Schristos 				     const char *cmd)
3866928750b6Schristos {
3867928750b6Schristos 	char ifname[IFNAMSIZ + 1];
3868928750b6Schristos 	struct fst_iface_cfg cfg;
3869928750b6Schristos 	struct hostapd_data *hapd;
3870928750b6Schristos 	struct fst_wpa_obj iface_obj;
3871928750b6Schristos 
3872928750b6Schristos 	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
3873928750b6Schristos 		hapd = hostapd_get_iface(interfaces, ifname);
3874928750b6Schristos 		if (hapd) {
3875928750b6Schristos 			if (hapd->iface->fst) {
3876928750b6Schristos 				wpa_printf(MSG_INFO, "FST: Already attached");
3877928750b6Schristos 				return -1;
3878928750b6Schristos 			}
3879928750b6Schristos 			fst_hostapd_fill_iface_obj(hapd, &iface_obj);
3880928750b6Schristos 			hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
3881928750b6Schristos 						      &iface_obj, &cfg);
3882928750b6Schristos 			if (hapd->iface->fst)
3883928750b6Schristos 				return 0;
3884928750b6Schristos 		}
3885928750b6Schristos 	}
3886928750b6Schristos 
3887928750b6Schristos 	return -EINVAL;
3888928750b6Schristos }
3889928750b6Schristos 
3890928750b6Schristos 
3891928750b6Schristos static int
hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces * interfaces,const char * cmd)3892928750b6Schristos hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
3893928750b6Schristos 				     const char *cmd)
3894928750b6Schristos {
3895928750b6Schristos 	char ifname[IFNAMSIZ + 1];
3896928750b6Schristos 	struct hostapd_data * hapd;
3897928750b6Schristos 
3898928750b6Schristos 	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
3899928750b6Schristos 		hapd = hostapd_get_iface(interfaces, ifname);
3900928750b6Schristos 		if (hapd) {
3901928750b6Schristos 			if (!fst_iface_detach(ifname)) {
3902928750b6Schristos 				hapd->iface->fst = NULL;
3903928750b6Schristos 				hapd->iface->fst_ies = NULL;
3904928750b6Schristos 				return 0;
3905928750b6Schristos 			}
3906928750b6Schristos 		}
3907928750b6Schristos 	}
3908928750b6Schristos 
3909928750b6Schristos 	return -EINVAL;
3910928750b6Schristos }
3911928750b6Schristos 
3912928750b6Schristos #endif /* CONFIG_FST */
3913928750b6Schristos 
3914928750b6Schristos 
3915928750b6Schristos static struct hostapd_data *
hostapd_interfaces_get_hapd(struct hapd_interfaces * interfaces,const char * ifname)3916928750b6Schristos hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
3917928750b6Schristos 			    const char *ifname)
3918928750b6Schristos {
3919928750b6Schristos 	size_t i, j;
3920928750b6Schristos 
3921928750b6Schristos 	for (i = 0; i < interfaces->count; i++) {
3922928750b6Schristos 		struct hostapd_iface *iface = interfaces->iface[i];
3923928750b6Schristos 
3924928750b6Schristos 		for (j = 0; j < iface->num_bss; j++) {
3925928750b6Schristos 			struct hostapd_data *hapd;
3926928750b6Schristos 
3927928750b6Schristos 			hapd = iface->bss[j];
3928928750b6Schristos 			if (os_strcmp(ifname, hapd->conf->iface) == 0)
3929928750b6Schristos 				return hapd;
3930928750b6Schristos 		}
3931928750b6Schristos 	}
3932928750b6Schristos 
3933928750b6Schristos 	return NULL;
3934928750b6Schristos }
3935928750b6Schristos 
3936928750b6Schristos 
hostapd_ctrl_iface_dup_param(struct hostapd_data * src_hapd,struct hostapd_data * dst_hapd,const char * param)3937928750b6Schristos static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
3938928750b6Schristos 					struct hostapd_data *dst_hapd,
3939928750b6Schristos 					const char *param)
3940928750b6Schristos {
3941928750b6Schristos 	int res;
3942928750b6Schristos 	char *value;
3943928750b6Schristos 
3944928750b6Schristos 	value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
3945928750b6Schristos 	if (!value) {
3946928750b6Schristos 		wpa_printf(MSG_ERROR,
3947928750b6Schristos 			   "DUP: cannot allocate buffer to stringify %s",
3948928750b6Schristos 			   param);
3949928750b6Schristos 		goto error_return;
3950928750b6Schristos 	}
3951928750b6Schristos 
3952928750b6Schristos 	if (os_strcmp(param, "wpa") == 0) {
3953928750b6Schristos 		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
3954928750b6Schristos 			    src_hapd->conf->wpa);
3955928750b6Schristos 	} else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
3956928750b6Schristos 		   src_hapd->conf->wpa_key_mgmt) {
3957928750b6Schristos 		res = hostapd_ctrl_iface_get_key_mgmt(
3958928750b6Schristos 			src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
3959928750b6Schristos 		if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
3960928750b6Schristos 			goto error_stringify;
3961928750b6Schristos 	} else if (os_strcmp(param, "wpa_pairwise") == 0 &&
3962928750b6Schristos 		   src_hapd->conf->wpa_pairwise) {
3963928750b6Schristos 		res = wpa_write_ciphers(value,
3964928750b6Schristos 					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
3965928750b6Schristos 					src_hapd->conf->wpa_pairwise, " ");
3966928750b6Schristos 		if (res < 0)
3967928750b6Schristos 			goto error_stringify;
3968928750b6Schristos 	} else if (os_strcmp(param, "rsn_pairwise") == 0 &&
3969928750b6Schristos 		   src_hapd->conf->rsn_pairwise) {
3970928750b6Schristos 		res = wpa_write_ciphers(value,
3971928750b6Schristos 					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
3972928750b6Schristos 					src_hapd->conf->rsn_pairwise, " ");
3973928750b6Schristos 		if (res < 0)
3974928750b6Schristos 			goto error_stringify;
3975928750b6Schristos 	} else if (os_strcmp(param, "wpa_passphrase") == 0 &&
3976928750b6Schristos 		   src_hapd->conf->ssid.wpa_passphrase) {
3977928750b6Schristos 		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
3978928750b6Schristos 			    src_hapd->conf->ssid.wpa_passphrase);
3979928750b6Schristos 	} else if (os_strcmp(param, "wpa_psk") == 0 &&
3980928750b6Schristos 		   src_hapd->conf->ssid.wpa_psk_set) {
3981928750b6Schristos 		wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
3982928750b6Schristos 			src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
3983928750b6Schristos 	} else {
3984928750b6Schristos 		wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
3985928750b6Schristos 		goto error_return;
3986928750b6Schristos 	}
3987928750b6Schristos 
3988928750b6Schristos 	res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
3989928750b6Schristos 	os_free(value);
3990928750b6Schristos 	return res;
3991928750b6Schristos 
3992928750b6Schristos error_stringify:
3993928750b6Schristos 	wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
3994928750b6Schristos error_return:
3995928750b6Schristos 	os_free(value);
3996928750b6Schristos 	return -1;
3997928750b6Schristos }
3998928750b6Schristos 
3999928750b6Schristos 
4000928750b6Schristos static int
hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces * interfaces,const char * input,char * reply,int reply_size)4001928750b6Schristos hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces,
4002928750b6Schristos 				     const char *input,
4003928750b6Schristos 				     char *reply, int reply_size)
4004928750b6Schristos {
4005928750b6Schristos 	size_t i, j;
4006928750b6Schristos 	int res;
4007928750b6Schristos 	char *pos, *end;
4008928750b6Schristos 	struct hostapd_iface *iface;
4009928750b6Schristos 	int show_ctrl = 0;
4010928750b6Schristos 
4011928750b6Schristos 	if (input)
4012928750b6Schristos 		show_ctrl = !!os_strstr(input, "ctrl");
4013928750b6Schristos 
4014928750b6Schristos 	pos = reply;
4015928750b6Schristos 	end = reply + reply_size;
4016928750b6Schristos 
4017928750b6Schristos 	for (i = 0; i < interfaces->count; i++) {
4018928750b6Schristos 		iface = interfaces->iface[i];
4019928750b6Schristos 
4020928750b6Schristos 		for (j = 0; j < iface->num_bss; j++) {
4021928750b6Schristos 			struct hostapd_bss_config *conf;
4022928750b6Schristos 
4023928750b6Schristos 			conf = iface->conf->bss[j];
4024928750b6Schristos 			if (show_ctrl)
4025928750b6Schristos 				res = os_snprintf(pos, end - pos,
4026928750b6Schristos 						  "%s ctrl_iface=%s\n",
4027928750b6Schristos 						  conf->iface,
4028928750b6Schristos 						  conf->ctrl_interface ?
4029928750b6Schristos 						  conf->ctrl_interface : "N/A");
4030928750b6Schristos 			else
4031928750b6Schristos 				res = os_snprintf(pos, end - pos, "%s\n",
4032928750b6Schristos 						  conf->iface);
4033928750b6Schristos 			if (os_snprintf_error(end - pos, res)) {
4034928750b6Schristos 				*pos = '\0';
4035928750b6Schristos 				return pos - reply;
4036928750b6Schristos 			}
4037928750b6Schristos 			pos += res;
4038928750b6Schristos 		}
4039928750b6Schristos 	}
4040928750b6Schristos 
4041928750b6Schristos 	return pos - reply;
4042928750b6Schristos }
4043928750b6Schristos 
4044928750b6Schristos 
4045928750b6Schristos static int
hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces * interfaces,char * cmd)4046928750b6Schristos hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
4047928750b6Schristos 				      char *cmd)
4048928750b6Schristos {
4049928750b6Schristos 	char *p_start = cmd, *p_end;
4050928750b6Schristos 	struct hostapd_data *src_hapd, *dst_hapd;
4051928750b6Schristos 
4052928750b6Schristos 	/* cmd: "<src ifname> <dst ifname> <variable name> */
4053928750b6Schristos 
4054928750b6Schristos 	p_end = os_strchr(p_start, ' ');
4055928750b6Schristos 	if (!p_end) {
4056928750b6Schristos 		wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
4057928750b6Schristos 			   cmd);
4058928750b6Schristos 		return -1;
4059928750b6Schristos 	}
4060928750b6Schristos 
4061928750b6Schristos 	*p_end = '\0';
4062928750b6Schristos 	src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
4063928750b6Schristos 	if (!src_hapd) {
4064928750b6Schristos 		wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
4065928750b6Schristos 			   p_start);
4066928750b6Schristos 		return -1;
4067928750b6Schristos 	}
4068928750b6Schristos 
4069928750b6Schristos 	p_start = p_end + 1;
4070928750b6Schristos 	p_end = os_strchr(p_start, ' ');
4071928750b6Schristos 	if (!p_end) {
4072928750b6Schristos 		wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
4073928750b6Schristos 			   cmd);
4074928750b6Schristos 		return -1;
4075928750b6Schristos 	}
4076928750b6Schristos 
4077928750b6Schristos 	*p_end = '\0';
4078928750b6Schristos 	dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
4079928750b6Schristos 	if (!dst_hapd) {
4080928750b6Schristos 		wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
4081928750b6Schristos 			   p_start);
4082928750b6Schristos 		return -1;
4083928750b6Schristos 	}
4084928750b6Schristos 
4085928750b6Schristos 	p_start = p_end + 1;
4086928750b6Schristos 	return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
4087928750b6Schristos }
4088928750b6Schristos 
4089928750b6Schristos 
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)4090928750b6Schristos static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
4091928750b6Schristos 					    const char *ifname,
4092928750b6Schristos 					    char *buf, char *reply,
4093928750b6Schristos 					    int reply_size,
4094928750b6Schristos 					    struct sockaddr_storage *from,
4095928750b6Schristos 					    socklen_t fromlen)
4096928750b6Schristos {
4097928750b6Schristos 	struct hostapd_data *hapd;
4098928750b6Schristos 
4099928750b6Schristos 	hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
4100928750b6Schristos 	if (hapd == NULL) {
4101928750b6Schristos 		int res;
4102928750b6Schristos 
4103928750b6Schristos 		res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
4104928750b6Schristos 		if (os_snprintf_error(reply_size, res))
4105928750b6Schristos 			return -1;
4106928750b6Schristos 		return res;
4107928750b6Schristos 	}
4108928750b6Schristos 
4109928750b6Schristos 	return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
4110928750b6Schristos 						  from, fromlen);
4111928750b6Schristos }
4112928750b6Schristos 
4113928750b6Schristos 
hostapd_global_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)411462a52023Schristos static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
411562a52023Schristos 					      void *sock_ctx)
411662a52023Schristos {
411762a52023Schristos 	void *interfaces = eloop_ctx;
4118928750b6Schristos 	char buffer[256], *buf = buffer;
411962a52023Schristos 	int res;
4120928750b6Schristos 	struct sockaddr_storage from;
412162a52023Schristos 	socklen_t fromlen = sizeof(from);
4122928750b6Schristos 	char *reply;
412362a52023Schristos 	int reply_len;
4124928750b6Schristos 	const int reply_size = 4096;
4125928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
4126928750b6Schristos 	unsigned char lcookie[COOKIE_LEN];
4127928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
412862a52023Schristos 
4129928750b6Schristos 	res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
413062a52023Schristos 		       (struct sockaddr *) &from, &fromlen);
413162a52023Schristos 	if (res < 0) {
41329a53cbbeSchristos 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
41339a53cbbeSchristos 			   strerror(errno));
413462a52023Schristos 		return;
413562a52023Schristos 	}
413662a52023Schristos 	buf[res] = '\0';
413736d97821Schristos 	wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
413862a52023Schristos 
4139928750b6Schristos 	reply = os_malloc(reply_size);
4140928750b6Schristos 	if (reply == NULL) {
4141928750b6Schristos 		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
4142928750b6Schristos 			   fromlen) < 0) {
4143928750b6Schristos 			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
4144928750b6Schristos 				   strerror(errno));
4145928750b6Schristos 		}
4146928750b6Schristos 		return;
4147928750b6Schristos 	}
4148928750b6Schristos 
414962a52023Schristos 	os_memcpy(reply, "OK\n", 3);
415062a52023Schristos 	reply_len = 3;
415162a52023Schristos 
4152928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
4153928750b6Schristos 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
4154928750b6Schristos 		os_memcpy(reply, "COOKIE=", 7);
4155928750b6Schristos 		wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
4156928750b6Schristos 				 gcookie, COOKIE_LEN);
4157928750b6Schristos 		reply_len = 7 + 2 * COOKIE_LEN;
4158928750b6Schristos 		goto send_reply;
4159928750b6Schristos 	}
4160928750b6Schristos 
4161928750b6Schristos 	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
4162928750b6Schristos 	    hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
4163928750b6Schristos 		wpa_printf(MSG_DEBUG,
4164928750b6Schristos 			   "CTRL: No cookie in the request - drop request");
4165928750b6Schristos 		os_free(reply);
4166928750b6Schristos 		return;
4167928750b6Schristos 	}
4168928750b6Schristos 
4169928750b6Schristos 	if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) {
4170928750b6Schristos 		wpa_printf(MSG_DEBUG,
4171928750b6Schristos 			   "CTRL: Invalid cookie in the request - drop request");
4172928750b6Schristos 		os_free(reply);
4173928750b6Schristos 		return;
4174928750b6Schristos 	}
4175928750b6Schristos 
4176928750b6Schristos 	buf += 7 + 2 * COOKIE_LEN;
4177928750b6Schristos 	while (*buf == ' ')
4178928750b6Schristos 		buf++;
4179928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
4180928750b6Schristos 
4181928750b6Schristos 	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
4182928750b6Schristos 		char *pos = os_strchr(buf + 7, ' ');
4183928750b6Schristos 
4184928750b6Schristos 		if (pos) {
4185928750b6Schristos 			*pos++ = '\0';
4186928750b6Schristos 			reply_len = hostapd_global_ctrl_iface_ifname(
4187928750b6Schristos 				interfaces, buf + 7, pos, reply, reply_size,
4188928750b6Schristos 				&from, fromlen);
4189928750b6Schristos 			goto send_reply;
4190928750b6Schristos 		}
4191928750b6Schristos 	}
4192928750b6Schristos 
419362a52023Schristos 	if (os_strcmp(buf, "PING") == 0) {
419462a52023Schristos 		os_memcpy(reply, "PONG\n", 5);
419562a52023Schristos 		reply_len = 5;
419636d97821Schristos 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
419736d97821Schristos 		if (wpa_debug_reopen_file() < 0)
419836d97821Schristos 			reply_len = -1;
419936d97821Schristos 	} else if (os_strcmp(buf, "FLUSH") == 0) {
420036d97821Schristos 		hostapd_ctrl_iface_flush(interfaces);
420162a52023Schristos 	} else if (os_strncmp(buf, "ADD ", 4) == 0) {
420262a52023Schristos 		if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
420362a52023Schristos 			reply_len = -1;
420462a52023Schristos 	} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
420562a52023Schristos 		if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
420662a52023Schristos 			reply_len = -1;
4207928750b6Schristos 	} else if (os_strcmp(buf, "ATTACH") == 0) {
4208928750b6Schristos 		if (hostapd_global_ctrl_iface_attach(interfaces, &from,
4209ebb5671cSchristos 						     fromlen, NULL))
4210ebb5671cSchristos 			reply_len = -1;
4211ebb5671cSchristos 	} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
4212ebb5671cSchristos 		if (hostapd_global_ctrl_iface_attach(interfaces, &from,
4213ebb5671cSchristos 						     fromlen, buf + 7))
4214928750b6Schristos 			reply_len = -1;
4215928750b6Schristos 	} else if (os_strcmp(buf, "DETACH") == 0) {
4216928750b6Schristos 		if (hostapd_global_ctrl_iface_detach(interfaces, &from,
4217928750b6Schristos 			fromlen))
4218928750b6Schristos 			reply_len = -1;
421936d97821Schristos #ifdef CONFIG_MODULE_TESTS
422036d97821Schristos 	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
422136d97821Schristos 		if (hapd_module_tests() < 0)
422236d97821Schristos 			reply_len = -1;
422336d97821Schristos #endif /* CONFIG_MODULE_TESTS */
4224928750b6Schristos #ifdef CONFIG_FST
4225928750b6Schristos 	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
4226928750b6Schristos 		if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
4227928750b6Schristos 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4228928750b6Schristos 		else
4229928750b6Schristos 			reply_len = -1;
4230928750b6Schristos 	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
4231928750b6Schristos 		if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
4232928750b6Schristos 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4233928750b6Schristos 		else
4234928750b6Schristos 			reply_len = -1;
4235928750b6Schristos 	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
4236928750b6Schristos 		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
4237928750b6Schristos #endif /* CONFIG_FST */
4238928750b6Schristos 	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
4239928750b6Schristos 		if (!hostapd_global_ctrl_iface_dup_network(interfaces,
4240928750b6Schristos 							   buf + 12))
4241928750b6Schristos 			reply_len = os_snprintf(reply, reply_size, "OK\n");
4242928750b6Schristos 		else
4243928750b6Schristos 			reply_len = -1;
4244928750b6Schristos 	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
4245928750b6Schristos 		reply_len = hostapd_global_ctrl_iface_interfaces(
4246928750b6Schristos 			interfaces, buf + 10, reply, sizeof(buffer));
4247928750b6Schristos 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
4248928750b6Schristos 		eloop_terminate();
424962a52023Schristos 	} else {
425062a52023Schristos 		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
425162a52023Schristos 			   "ignored");
425262a52023Schristos 		reply_len = -1;
425362a52023Schristos 	}
425462a52023Schristos 
4255928750b6Schristos send_reply:
425662a52023Schristos 	if (reply_len < 0) {
425762a52023Schristos 		os_memcpy(reply, "FAIL\n", 5);
425862a52023Schristos 		reply_len = 5;
425962a52023Schristos 	}
426062a52023Schristos 
42619a53cbbeSchristos 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
42629a53cbbeSchristos 		   fromlen) < 0) {
42639a53cbbeSchristos 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
42649a53cbbeSchristos 			   strerror(errno));
42659a53cbbeSchristos 	}
4266928750b6Schristos 	os_free(reply);
426762a52023Schristos }
426862a52023Schristos 
426962a52023Schristos 
4270928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
hostapd_global_ctrl_iface_path(struct hapd_interfaces * interface)427162a52023Schristos static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
427262a52023Schristos {
427362a52023Schristos 	char *buf;
427462a52023Schristos 	size_t len;
427562a52023Schristos 
427662a52023Schristos 	if (interface->global_iface_path == NULL)
427762a52023Schristos 		return NULL;
427862a52023Schristos 
427962a52023Schristos 	len = os_strlen(interface->global_iface_path) +
428062a52023Schristos 		os_strlen(interface->global_iface_name) + 2;
428162a52023Schristos 	buf = os_malloc(len);
428262a52023Schristos 	if (buf == NULL)
428362a52023Schristos 		return NULL;
428462a52023Schristos 
428562a52023Schristos 	os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
428662a52023Schristos 		    interface->global_iface_name);
428762a52023Schristos 	buf[len - 1] = '\0';
428862a52023Schristos 	return buf;
428962a52023Schristos }
4290928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
429162a52023Schristos 
429262a52023Schristos 
hostapd_global_ctrl_iface_init(struct hapd_interfaces * interface)429362a52023Schristos int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
429462a52023Schristos {
4295928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP
4296928750b6Schristos 	int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
4297928750b6Schristos 	char p[32] = { 0 };
4298928750b6Schristos 	char *pos;
4299928750b6Schristos 	struct addrinfo hints = { 0 }, *res, *saveres;
4300928750b6Schristos 	int n;
4301928750b6Schristos 
4302928750b6Schristos 	if (interface->global_ctrl_sock > -1) {
4303928750b6Schristos 		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
4304928750b6Schristos 		return 0;
4305928750b6Schristos 	}
4306928750b6Schristos 
4307928750b6Schristos 	if (interface->global_iface_path == NULL)
4308928750b6Schristos 		return 0;
4309928750b6Schristos 
4310928750b6Schristos 	pos = os_strstr(interface->global_iface_path, "udp:");
4311928750b6Schristos 	if (pos) {
4312928750b6Schristos 		pos += 4;
4313928750b6Schristos 		port = atoi(pos);
4314928750b6Schristos 		if (port <= 0) {
4315928750b6Schristos 			wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
4316928750b6Schristos 			goto fail;
4317928750b6Schristos 		}
4318928750b6Schristos 	}
4319928750b6Schristos 
4320928750b6Schristos 	os_get_random(gcookie, COOKIE_LEN);
4321928750b6Schristos 
4322928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
4323928750b6Schristos 	hints.ai_flags = AI_PASSIVE;
4324928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4325928750b6Schristos 
4326928750b6Schristos #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4327928750b6Schristos 	hints.ai_family = AF_INET6;
4328928750b6Schristos #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4329928750b6Schristos 	hints.ai_family = AF_INET;
4330928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4331928750b6Schristos 	hints.ai_socktype = SOCK_DGRAM;
4332928750b6Schristos 
4333928750b6Schristos try_again:
4334928750b6Schristos 	os_snprintf(p, sizeof(p), "%d", port);
4335928750b6Schristos 	n = getaddrinfo(NULL, p, &hints, &res);
4336928750b6Schristos 	if (n) {
4337928750b6Schristos 		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
4338928750b6Schristos 		goto fail;
4339928750b6Schristos 	}
4340928750b6Schristos 
4341928750b6Schristos 	saveres = res;
4342928750b6Schristos 	interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype,
4343928750b6Schristos 					     res->ai_protocol);
4344928750b6Schristos 	if (interface->global_ctrl_sock < 0) {
4345928750b6Schristos 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
4346928750b6Schristos 		goto fail;
4347928750b6Schristos 	}
4348928750b6Schristos 
4349928750b6Schristos 	if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) <
4350928750b6Schristos 	    0) {
4351928750b6Schristos 		port++;
4352928750b6Schristos 		if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
4353928750b6Schristos 		    HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
4354928750b6Schristos 			goto try_again;
4355928750b6Schristos 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
4356928750b6Schristos 		goto fail;
4357928750b6Schristos 	}
4358928750b6Schristos 
4359928750b6Schristos 	freeaddrinfo(saveres);
4360928750b6Schristos 
4361928750b6Schristos 	wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port);
4362928750b6Schristos 
4363928750b6Schristos 	if (eloop_register_read_sock(interface->global_ctrl_sock,
4364928750b6Schristos 				     hostapd_global_ctrl_iface_receive,
4365928750b6Schristos 				     interface, NULL) < 0) {
4366928750b6Schristos 		hostapd_global_ctrl_iface_deinit(interface);
4367928750b6Schristos 		return -1;
4368928750b6Schristos 	}
4369928750b6Schristos 
4370928750b6Schristos 	return 0;
4371928750b6Schristos 
4372928750b6Schristos fail:
4373928750b6Schristos 	if (interface->global_ctrl_sock >= 0)
4374928750b6Schristos 		close(interface->global_ctrl_sock);
4375928750b6Schristos 	return -1;
4376928750b6Schristos #else /* CONFIG_CTRL_IFACE_UDP */
437762a52023Schristos 	struct sockaddr_un addr;
437862a52023Schristos 	int s = -1;
437962a52023Schristos 	char *fname = NULL;
438062a52023Schristos 
438162a52023Schristos 	if (interface->global_iface_path == NULL) {
438262a52023Schristos 		wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
438362a52023Schristos 		return 0;
438462a52023Schristos 	}
438562a52023Schristos 
438662a52023Schristos 	if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
438762a52023Schristos 		if (errno == EEXIST) {
438862a52023Schristos 			wpa_printf(MSG_DEBUG, "Using existing control "
438962a52023Schristos 				   "interface directory.");
439062a52023Schristos 		} else {
43919a53cbbeSchristos 			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
43929a53cbbeSchristos 				   strerror(errno));
439362a52023Schristos 			goto fail;
439462a52023Schristos 		}
439536d97821Schristos 	} else if (interface->ctrl_iface_group &&
4396*0d69f216Schristos 		   lchown(interface->global_iface_path, -1,
439736d97821Schristos 			  interface->ctrl_iface_group) < 0) {
4398*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
43999a53cbbeSchristos 			   strerror(errno));
440036d97821Schristos 		goto fail;
440162a52023Schristos 	}
440262a52023Schristos 
440362a52023Schristos 	if (os_strlen(interface->global_iface_path) + 1 +
440462a52023Schristos 	    os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
440562a52023Schristos 		goto fail;
440662a52023Schristos 
440762a52023Schristos 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
440862a52023Schristos 	if (s < 0) {
44099a53cbbeSchristos 		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
441062a52023Schristos 		goto fail;
441162a52023Schristos 	}
441262a52023Schristos 
441362a52023Schristos 	os_memset(&addr, 0, sizeof(addr));
441462a52023Schristos #ifdef __FreeBSD__
441562a52023Schristos 	addr.sun_len = sizeof(addr);
441662a52023Schristos #endif /* __FreeBSD__ */
441762a52023Schristos 	addr.sun_family = AF_UNIX;
441862a52023Schristos 	fname = hostapd_global_ctrl_iface_path(interface);
441962a52023Schristos 	if (fname == NULL)
442062a52023Schristos 		goto fail;
442162a52023Schristos 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
442262a52023Schristos 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
442362a52023Schristos 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
442462a52023Schristos 			   strerror(errno));
442562a52023Schristos 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
442662a52023Schristos 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
442762a52023Schristos 				   " allow connections - assuming it was left"
442862a52023Schristos 				   "over from forced program termination");
442962a52023Schristos 			if (unlink(fname) < 0) {
44309a53cbbeSchristos 				wpa_printf(MSG_ERROR,
44319a53cbbeSchristos 					   "Could not unlink existing ctrl_iface socket '%s': %s",
44329a53cbbeSchristos 					   fname, strerror(errno));
443362a52023Schristos 				goto fail;
443462a52023Schristos 			}
443562a52023Schristos 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
443662a52023Schristos 			    0) {
44379a53cbbeSchristos 				wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
44389a53cbbeSchristos 					   strerror(errno));
443962a52023Schristos 				goto fail;
444062a52023Schristos 			}
444162a52023Schristos 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
444262a52023Schristos 				   "ctrl_iface socket '%s'", fname);
444362a52023Schristos 		} else {
444462a52023Schristos 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
444562a52023Schristos 				   "be in use - cannot override it");
444662a52023Schristos 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
444762a52023Schristos 				   "not used anymore", fname);
444862a52023Schristos 			os_free(fname);
444962a52023Schristos 			fname = NULL;
445062a52023Schristos 			goto fail;
445162a52023Schristos 		}
445262a52023Schristos 	}
445362a52023Schristos 
445436d97821Schristos 	if (interface->ctrl_iface_group &&
4455*0d69f216Schristos 	    lchown(fname, -1, interface->ctrl_iface_group) < 0) {
4456*0d69f216Schristos 		wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
44579a53cbbeSchristos 			   strerror(errno));
445836d97821Schristos 		goto fail;
445936d97821Schristos 	}
446036d97821Schristos 
446162a52023Schristos 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
44629a53cbbeSchristos 		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
44639a53cbbeSchristos 			   strerror(errno));
446462a52023Schristos 		goto fail;
446562a52023Schristos 	}
446662a52023Schristos 	os_free(fname);
446762a52023Schristos 
446862a52023Schristos 	interface->global_ctrl_sock = s;
446962a52023Schristos 	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
447062a52023Schristos 				 interface, NULL);
447162a52023Schristos 
447262a52023Schristos 	return 0;
447362a52023Schristos 
447462a52023Schristos fail:
447562a52023Schristos 	if (s >= 0)
447662a52023Schristos 		close(s);
447762a52023Schristos 	if (fname) {
447862a52023Schristos 		unlink(fname);
447962a52023Schristos 		os_free(fname);
448062a52023Schristos 	}
448162a52023Schristos 	return -1;
4482928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
448362a52023Schristos }
448462a52023Schristos 
448562a52023Schristos 
hostapd_global_ctrl_iface_deinit(struct hapd_interfaces * interfaces)448662a52023Schristos void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
448762a52023Schristos {
4488928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
448962a52023Schristos 	char *fname = NULL;
4490928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
4491928750b6Schristos 	struct wpa_ctrl_dst *dst, *prev;
449262a52023Schristos 
449362a52023Schristos 	if (interfaces->global_ctrl_sock > -1) {
449462a52023Schristos 		eloop_unregister_read_sock(interfaces->global_ctrl_sock);
449562a52023Schristos 		close(interfaces->global_ctrl_sock);
449662a52023Schristos 		interfaces->global_ctrl_sock = -1;
4497928750b6Schristos #ifndef CONFIG_CTRL_IFACE_UDP
449862a52023Schristos 		fname = hostapd_global_ctrl_iface_path(interfaces);
449962a52023Schristos 		if (fname) {
450062a52023Schristos 			unlink(fname);
450162a52023Schristos 			os_free(fname);
450262a52023Schristos 		}
450362a52023Schristos 
450462a52023Schristos 		if (interfaces->global_iface_path &&
450562a52023Schristos 		    rmdir(interfaces->global_iface_path) < 0) {
450662a52023Schristos 			if (errno == ENOTEMPTY) {
450762a52023Schristos 				wpa_printf(MSG_DEBUG, "Control interface "
450862a52023Schristos 					   "directory not empty - leaving it "
450962a52023Schristos 					   "behind");
451062a52023Schristos 			} else {
451136d97821Schristos 				wpa_printf(MSG_ERROR,
451236d97821Schristos 					   "rmdir[ctrl_interface=%s]: %s",
451336d97821Schristos 					   interfaces->global_iface_path,
451436d97821Schristos 					   strerror(errno));
451562a52023Schristos 			}
451662a52023Schristos 		}
4517928750b6Schristos #endif /* CONFIG_CTRL_IFACE_UDP */
4518928750b6Schristos 	}
4519928750b6Schristos 
452062a52023Schristos 	os_free(interfaces->global_iface_path);
452162a52023Schristos 	interfaces->global_iface_path = NULL;
4522928750b6Schristos 
4523928750b6Schristos 	dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst,
4524928750b6Schristos 			      struct wpa_ctrl_dst, list)
4525928750b6Schristos 		os_free(dst);
452662a52023Schristos }
452762a52023Schristos 
452862a52023Schristos 
hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst * dst,const char * buf)4529ebb5671cSchristos static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
4530ebb5671cSchristos 					    const char *buf)
4531ebb5671cSchristos {
4532ebb5671cSchristos 	/* Enable Probe Request events based on explicit request.
4533ebb5671cSchristos 	 * Other events are enabled by default.
4534ebb5671cSchristos 	 */
4535ebb5671cSchristos 	if (str_starts(buf, RX_PROBE_REQUEST))
4536ebb5671cSchristos 		return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST);
4537ebb5671cSchristos 	return 1;
4538ebb5671cSchristos }
4539ebb5671cSchristos 
4540ebb5671cSchristos 
hostapd_ctrl_iface_send(struct hostapd_data * hapd,int level,enum wpa_msg_type type,const char * buf,size_t len)45418dbcf02cSchristos static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
4542928750b6Schristos 				    enum wpa_msg_type type,
45438dbcf02cSchristos 				    const char *buf, size_t len)
45448dbcf02cSchristos {
45458dbcf02cSchristos 	struct wpa_ctrl_dst *dst, *next;
4546928750b6Schristos 	struct dl_list *ctrl_dst;
45478dbcf02cSchristos 	struct msghdr msg;
45488dbcf02cSchristos 	int idx;
45498dbcf02cSchristos 	struct iovec io[2];
45508dbcf02cSchristos 	char levelstr[10];
4551928750b6Schristos 	int s;
45528dbcf02cSchristos 
4553928750b6Schristos 	if (type != WPA_MSG_ONLY_GLOBAL) {
4554928750b6Schristos 		s = hapd->ctrl_sock;
4555928750b6Schristos 		ctrl_dst = &hapd->ctrl_dst;
4556928750b6Schristos 	} else {
4557928750b6Schristos 		s = hapd->iface->interfaces->global_ctrl_sock;
4558928750b6Schristos 		ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst;
4559928750b6Schristos 	}
4560928750b6Schristos 
4561928750b6Schristos 	if (s < 0 || dl_list_empty(ctrl_dst))
45628dbcf02cSchristos 		return;
45638dbcf02cSchristos 
45648dbcf02cSchristos 	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
45658dbcf02cSchristos 	io[0].iov_base = levelstr;
45668dbcf02cSchristos 	io[0].iov_len = os_strlen(levelstr);
45678dbcf02cSchristos 	io[1].iov_base = (char *) buf;
45688dbcf02cSchristos 	io[1].iov_len = len;
45698dbcf02cSchristos 	os_memset(&msg, 0, sizeof(msg));
45708dbcf02cSchristos 	msg.msg_iov = io;
45718dbcf02cSchristos 	msg.msg_iovlen = 2;
45728dbcf02cSchristos 
45738dbcf02cSchristos 	idx = 0;
4574928750b6Schristos 	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
4575ebb5671cSchristos 		if ((level >= dst->debug_level) &&
4576ebb5671cSchristos 		     hostapd_ctrl_check_event_enabled(dst, buf)) {
4577928750b6Schristos 			sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
4578928750b6Schristos 				       &dst->addr, dst->addrlen);
45798dbcf02cSchristos 			msg.msg_name = &dst->addr;
45808dbcf02cSchristos 			msg.msg_namelen = dst->addrlen;
4581928750b6Schristos 			if (sendmsg(s, &msg, 0) < 0) {
45828dbcf02cSchristos 				int _errno = errno;
45838dbcf02cSchristos 				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
45848dbcf02cSchristos 					   "%d - %s",
45858dbcf02cSchristos 					   idx, errno, strerror(errno));
45868dbcf02cSchristos 				dst->errors++;
45878dbcf02cSchristos 				if (dst->errors > 10 || _errno == ENOENT) {
4588928750b6Schristos 					if (type != WPA_MSG_ONLY_GLOBAL)
45898dbcf02cSchristos 						hostapd_ctrl_iface_detach(
45908dbcf02cSchristos 							hapd, &dst->addr,
45918dbcf02cSchristos 							dst->addrlen);
4592928750b6Schristos 					else
4593928750b6Schristos 						hostapd_global_ctrl_iface_detach(
4594928750b6Schristos 							hapd->iface->interfaces,
4595928750b6Schristos 							&dst->addr,
4596928750b6Schristos 							dst->addrlen);
45978dbcf02cSchristos 				}
45988dbcf02cSchristos 			} else
45998dbcf02cSchristos 				dst->errors = 0;
46008dbcf02cSchristos 		}
46018dbcf02cSchristos 		idx++;
46028dbcf02cSchristos 	}
46038dbcf02cSchristos }
46048dbcf02cSchristos 
46058dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
4606