1189251Ssam/*
2189251Ssam * WPA Supplicant / Control interface (shared code for all backends)
3346981Scy * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9214734Srpaulo#include "utils/includes.h"
10281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
11281806Srpaulo#include <netinet/ip.h>
12281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
13189251Ssam
14351611Scy#include <net/ethernet.h>
15214734Srpaulo#include "utils/common.h"
16214734Srpaulo#include "utils/eloop.h"
17281806Srpaulo#include "utils/uuid.h"
18337817Scy#include "utils/module_tests.h"
19252726Srpaulo#include "common/version.h"
20214734Srpaulo#include "common/ieee802_11_defs.h"
21252726Srpaulo#include "common/ieee802_11_common.h"
22214734Srpaulo#include "common/wpa_ctrl.h"
23346981Scy#ifdef CONFIG_DPP
24346981Scy#include "common/dpp.h"
25346981Scy#endif /* CONFIG_DPP */
26281806Srpaulo#include "crypto/tls.h"
27281806Srpaulo#include "ap/hostapd.h"
28214734Srpaulo#include "eap_peer/eap.h"
29214734Srpaulo#include "eapol_supp/eapol_supp_sm.h"
30214734Srpaulo#include "rsn_supp/wpa.h"
31214734Srpaulo#include "rsn_supp/preauth.h"
32214734Srpaulo#include "rsn_supp/pmksa_cache.h"
33214734Srpaulo#include "l2_packet/l2_packet.h"
34214734Srpaulo#include "wps/wps.h"
35289549Srpaulo#include "fst/fst.h"
36289549Srpaulo#include "fst/fst_ctrl_iface.h"
37189251Ssam#include "config.h"
38189251Ssam#include "wpa_supplicant_i.h"
39214734Srpaulo#include "driver_i.h"
40214734Srpaulo#include "wps_supplicant.h"
41214734Srpaulo#include "ibss_rsn.h"
42214734Srpaulo#include "ap.h"
43252726Srpaulo#include "p2p_supplicant.h"
44252726Srpaulo#include "p2p/p2p.h"
45252726Srpaulo#include "hs20_supplicant.h"
46252726Srpaulo#include "wifi_display.h"
47214734Srpaulo#include "notify.h"
48214734Srpaulo#include "bss.h"
49214734Srpaulo#include "scan.h"
50189251Ssam#include "ctrl_iface.h"
51252726Srpaulo#include "interworking.h"
52252726Srpaulo#include "blacklist.h"
53252726Srpaulo#include "autoscan.h"
54252726Srpaulo#include "wnm_sta.h"
55281806Srpaulo#include "offchannel.h"
56281806Srpaulo#include "drivers/driver.h"
57281806Srpaulo#include "mesh.h"
58346981Scy#include "dpp_supplicant.h"
59346981Scy#include "sme.h"
60189251Ssam
61189251Ssamstatic int wpa_supplicant_global_iface_list(struct wpa_global *global,
62189251Ssam					    char *buf, int len);
63189251Ssamstatic int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
64337817Scy						  const char *input,
65189251Ssam						  char *buf, int len);
66281806Srpaulostatic int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
67281806Srpaulo					char *val);
68189251Ssam
69346981Scy
70252726Srpaulostatic int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
71252726Srpaulo{
72252726Srpaulo	char *pos;
73252726Srpaulo	u8 addr[ETH_ALEN], *filter = NULL, *n;
74252726Srpaulo	size_t count = 0;
75252726Srpaulo
76252726Srpaulo	pos = val;
77252726Srpaulo	while (pos) {
78252726Srpaulo		if (*pos == '\0')
79252726Srpaulo			break;
80252726Srpaulo		if (hwaddr_aton(pos, addr)) {
81252726Srpaulo			os_free(filter);
82252726Srpaulo			return -1;
83252726Srpaulo		}
84252726Srpaulo		n = os_realloc_array(filter, count + 1, ETH_ALEN);
85252726Srpaulo		if (n == NULL) {
86252726Srpaulo			os_free(filter);
87252726Srpaulo			return -1;
88252726Srpaulo		}
89252726Srpaulo		filter = n;
90252726Srpaulo		os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
91252726Srpaulo		count++;
92252726Srpaulo
93252726Srpaulo		pos = os_strchr(pos, ' ');
94252726Srpaulo		if (pos)
95252726Srpaulo			pos++;
96252726Srpaulo	}
97252726Srpaulo
98252726Srpaulo	wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
99252726Srpaulo	os_free(wpa_s->bssid_filter);
100252726Srpaulo	wpa_s->bssid_filter = filter;
101252726Srpaulo	wpa_s->bssid_filter_count = count;
102252726Srpaulo
103252726Srpaulo	return 0;
104252726Srpaulo}
105252726Srpaulo
106252726Srpaulo
107252726Srpaulostatic int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
108252726Srpaulo{
109252726Srpaulo	char *pos;
110252726Srpaulo	u8 addr[ETH_ALEN], *bssid = NULL, *n;
111252726Srpaulo	struct wpa_ssid_value *ssid = NULL, *ns;
112252726Srpaulo	size_t count = 0, ssid_count = 0;
113252726Srpaulo	struct wpa_ssid *c;
114252726Srpaulo
115252726Srpaulo	/*
116281806Srpaulo	 * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
117252726Srpaulo	 * SSID_SPEC ::= ssid <SSID_HEX>
118252726Srpaulo	 * BSSID_SPEC ::= bssid <BSSID_HEX>
119252726Srpaulo	 */
120252726Srpaulo
121252726Srpaulo	pos = val;
122252726Srpaulo	while (pos) {
123252726Srpaulo		if (*pos == '\0')
124252726Srpaulo			break;
125252726Srpaulo		if (os_strncmp(pos, "bssid ", 6) == 0) {
126252726Srpaulo			int res;
127252726Srpaulo			pos += 6;
128252726Srpaulo			res = hwaddr_aton2(pos, addr);
129252726Srpaulo			if (res < 0) {
130252726Srpaulo				os_free(ssid);
131252726Srpaulo				os_free(bssid);
132252726Srpaulo				wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
133252726Srpaulo					   "BSSID value '%s'", pos);
134252726Srpaulo				return -1;
135252726Srpaulo			}
136252726Srpaulo			pos += res;
137252726Srpaulo			n = os_realloc_array(bssid, count + 1, ETH_ALEN);
138252726Srpaulo			if (n == NULL) {
139252726Srpaulo				os_free(ssid);
140252726Srpaulo				os_free(bssid);
141252726Srpaulo				return -1;
142252726Srpaulo			}
143252726Srpaulo			bssid = n;
144252726Srpaulo			os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
145252726Srpaulo			count++;
146252726Srpaulo		} else if (os_strncmp(pos, "ssid ", 5) == 0) {
147252726Srpaulo			char *end;
148252726Srpaulo			pos += 5;
149252726Srpaulo
150252726Srpaulo			end = pos;
151252726Srpaulo			while (*end) {
152252726Srpaulo				if (*end == '\0' || *end == ' ')
153252726Srpaulo					break;
154252726Srpaulo				end++;
155252726Srpaulo			}
156252726Srpaulo
157252726Srpaulo			ns = os_realloc_array(ssid, ssid_count + 1,
158252726Srpaulo					      sizeof(struct wpa_ssid_value));
159252726Srpaulo			if (ns == NULL) {
160252726Srpaulo				os_free(ssid);
161252726Srpaulo				os_free(bssid);
162252726Srpaulo				return -1;
163252726Srpaulo			}
164252726Srpaulo			ssid = ns;
165252726Srpaulo
166289549Srpaulo			if ((end - pos) & 0x01 ||
167289549Srpaulo			    end - pos > 2 * SSID_MAX_LEN ||
168252726Srpaulo			    hexstr2bin(pos, ssid[ssid_count].ssid,
169252726Srpaulo				       (end - pos) / 2) < 0) {
170252726Srpaulo				os_free(ssid);
171252726Srpaulo				os_free(bssid);
172252726Srpaulo				wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
173252726Srpaulo					   "SSID value '%s'", pos);
174252726Srpaulo				return -1;
175252726Srpaulo			}
176252726Srpaulo			ssid[ssid_count].ssid_len = (end - pos) / 2;
177252726Srpaulo			wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
178252726Srpaulo					  ssid[ssid_count].ssid,
179252726Srpaulo					  ssid[ssid_count].ssid_len);
180252726Srpaulo			ssid_count++;
181252726Srpaulo			pos = end;
182252726Srpaulo		} else {
183252726Srpaulo			wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
184252726Srpaulo				   "'%s'", pos);
185252726Srpaulo			os_free(ssid);
186252726Srpaulo			os_free(bssid);
187252726Srpaulo			return -1;
188252726Srpaulo		}
189252726Srpaulo
190252726Srpaulo		pos = os_strchr(pos, ' ');
191252726Srpaulo		if (pos)
192252726Srpaulo			pos++;
193252726Srpaulo	}
194252726Srpaulo
195252726Srpaulo	wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
196252726Srpaulo	os_free(wpa_s->disallow_aps_bssid);
197252726Srpaulo	wpa_s->disallow_aps_bssid = bssid;
198252726Srpaulo	wpa_s->disallow_aps_bssid_count = count;
199252726Srpaulo
200252726Srpaulo	wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
201252726Srpaulo	os_free(wpa_s->disallow_aps_ssid);
202252726Srpaulo	wpa_s->disallow_aps_ssid = ssid;
203252726Srpaulo	wpa_s->disallow_aps_ssid_count = ssid_count;
204252726Srpaulo
205252726Srpaulo	if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
206252726Srpaulo		return 0;
207252726Srpaulo
208252726Srpaulo	c = wpa_s->current_ssid;
209252726Srpaulo	if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
210252726Srpaulo		return 0;
211252726Srpaulo
212252726Srpaulo	if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
213252726Srpaulo	    !disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
214252726Srpaulo		return 0;
215252726Srpaulo
216252726Srpaulo	wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
217252726Srpaulo		   "because current AP was marked disallowed");
218252726Srpaulo
219252726Srpaulo#ifdef CONFIG_SME
220252726Srpaulo	wpa_s->sme.prev_bssid_set = 0;
221252726Srpaulo#endif /* CONFIG_SME */
222252726Srpaulo	wpa_s->reassociate = 1;
223281806Srpaulo	wpa_s->own_disconnect_req = 1;
224252726Srpaulo	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
225252726Srpaulo	wpa_supplicant_req_scan(wpa_s, 0, 0);
226252726Srpaulo
227252726Srpaulo	return 0;
228252726Srpaulo}
229252726Srpaulo
230252726Srpaulo
231281806Srpaulo#ifndef CONFIG_NO_CONFIG_BLOBS
232281806Srpaulostatic int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
233281806Srpaulo{
234281806Srpaulo	char *name = pos;
235281806Srpaulo	struct wpa_config_blob *blob;
236281806Srpaulo	size_t len;
237281806Srpaulo
238281806Srpaulo	pos = os_strchr(pos, ' ');
239281806Srpaulo	if (pos == NULL)
240281806Srpaulo		return -1;
241281806Srpaulo	*pos++ = '\0';
242281806Srpaulo	len = os_strlen(pos);
243281806Srpaulo	if (len & 1)
244281806Srpaulo		return -1;
245281806Srpaulo
246281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
247281806Srpaulo	blob = os_zalloc(sizeof(*blob));
248281806Srpaulo	if (blob == NULL)
249281806Srpaulo		return -1;
250281806Srpaulo	blob->name = os_strdup(name);
251281806Srpaulo	blob->data = os_malloc(len / 2);
252281806Srpaulo	if (blob->name == NULL || blob->data == NULL) {
253281806Srpaulo		wpa_config_free_blob(blob);
254281806Srpaulo		return -1;
255281806Srpaulo	}
256281806Srpaulo
257281806Srpaulo	if (hexstr2bin(pos, blob->data, len / 2) < 0) {
258281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
259281806Srpaulo		wpa_config_free_blob(blob);
260281806Srpaulo		return -1;
261281806Srpaulo	}
262281806Srpaulo	blob->len = len / 2;
263281806Srpaulo
264281806Srpaulo	wpa_config_set_blob(wpa_s->conf, blob);
265281806Srpaulo
266281806Srpaulo	return 0;
267281806Srpaulo}
268281806Srpaulo#endif /* CONFIG_NO_CONFIG_BLOBS */
269281806Srpaulo
270281806Srpaulo
271281806Srpaulostatic int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
272281806Srpaulo{
273281806Srpaulo	char *params;
274281806Srpaulo	char *pos;
275281806Srpaulo	int *freqs = NULL;
276281806Srpaulo	int ret;
277281806Srpaulo
278281806Srpaulo	if (atoi(cmd)) {
279281806Srpaulo		params = os_strchr(cmd, ' ');
280281806Srpaulo		os_free(wpa_s->manual_sched_scan_freqs);
281281806Srpaulo		if (params) {
282281806Srpaulo			params++;
283281806Srpaulo			pos = os_strstr(params, "freq=");
284281806Srpaulo			if (pos)
285281806Srpaulo				freqs = freq_range_to_channel_list(wpa_s,
286281806Srpaulo								   pos + 5);
287281806Srpaulo		}
288281806Srpaulo		wpa_s->manual_sched_scan_freqs = freqs;
289281806Srpaulo		ret = wpas_start_pno(wpa_s);
290281806Srpaulo	} else {
291281806Srpaulo		ret = wpas_stop_pno(wpa_s);
292281806Srpaulo	}
293281806Srpaulo	return ret;
294281806Srpaulo}
295281806Srpaulo
296281806Srpaulo
297289549Srpaulostatic int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
298289549Srpaulo{
299289549Srpaulo	union wpa_event_data event;
300289549Srpaulo
301289549Srpaulo	if (os_strcmp(band, "AUTO") == 0)
302289549Srpaulo		wpa_s->setband = WPA_SETBAND_AUTO;
303289549Srpaulo	else if (os_strcmp(band, "5G") == 0)
304289549Srpaulo		wpa_s->setband = WPA_SETBAND_5G;
305289549Srpaulo	else if (os_strcmp(band, "2G") == 0)
306289549Srpaulo		wpa_s->setband = WPA_SETBAND_2G;
307289549Srpaulo	else
308289549Srpaulo		return -1;
309289549Srpaulo
310289549Srpaulo	if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
311289549Srpaulo		os_memset(&event, 0, sizeof(event));
312289549Srpaulo		event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
313289549Srpaulo		event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
314289549Srpaulo		wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
315289549Srpaulo	}
316289549Srpaulo
317289549Srpaulo	return 0;
318289549Srpaulo}
319289549Srpaulo
320289549Srpaulo
321337817Scystatic int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s,
322337817Scy				   const char *cmd)
323337817Scy{
324337817Scy	struct wpabuf *lci;
325337817Scy
326337817Scy	if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
327337817Scy		wpabuf_free(wpa_s->lci);
328337817Scy		wpa_s->lci = NULL;
329337817Scy		return 0;
330337817Scy	}
331337817Scy
332337817Scy	lci = wpabuf_parse_bin(cmd);
333337817Scy	if (!lci)
334337817Scy		return -1;
335337817Scy
336337817Scy	if (os_get_reltime(&wpa_s->lci_time)) {
337337817Scy		wpabuf_free(lci);
338337817Scy		return -1;
339337817Scy	}
340337817Scy
341337817Scy	wpabuf_free(wpa_s->lci);
342337817Scy	wpa_s->lci = lci;
343337817Scy
344337817Scy	return 0;
345337817Scy}
346337817Scy
347337817Scy
348346981Scystatic int
349346981Scywpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd)
350346981Scy{
351346981Scy	int relative_rssi;
352346981Scy
353346981Scy	if (os_strcmp(cmd, "disable") == 0) {
354346981Scy		wpa_s->srp.relative_rssi_set = 0;
355346981Scy		return 0;
356346981Scy	}
357346981Scy
358346981Scy	relative_rssi = atoi(cmd);
359346981Scy	if (relative_rssi < 0 || relative_rssi > 100)
360346981Scy		return -1;
361346981Scy	wpa_s->srp.relative_rssi = relative_rssi;
362346981Scy	wpa_s->srp.relative_rssi_set = 1;
363346981Scy	return 0;
364346981Scy}
365346981Scy
366346981Scy
367346981Scystatic int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s,
368346981Scy					      const char *cmd)
369346981Scy{
370346981Scy	char *pos;
371346981Scy	int adjust_rssi;
372346981Scy
373346981Scy	/* <band>:adjust_value */
374346981Scy	pos = os_strchr(cmd, ':');
375346981Scy	if (!pos)
376346981Scy		return -1;
377346981Scy	pos++;
378346981Scy	adjust_rssi = atoi(pos);
379346981Scy	if (adjust_rssi < -100 || adjust_rssi > 100)
380346981Scy		return -1;
381346981Scy
382346981Scy	if (os_strncmp(cmd, "2G", 2) == 0)
383346981Scy		wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G;
384346981Scy	else if (os_strncmp(cmd, "5G", 2) == 0)
385346981Scy		wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G;
386346981Scy	else
387346981Scy		return -1;
388346981Scy
389346981Scy	wpa_s->srp.relative_adjust_rssi = adjust_rssi;
390346981Scy
391346981Scy	return 0;
392346981Scy}
393346981Scy
394346981Scy
395346981Scystatic int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s,
396346981Scy				   const char *cmd)
397346981Scy{
398346981Scy	struct wpabuf *ric_ies;
399346981Scy
400346981Scy	if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
401346981Scy		wpabuf_free(wpa_s->ric_ies);
402346981Scy		wpa_s->ric_ies = NULL;
403346981Scy		return 0;
404346981Scy	}
405346981Scy
406346981Scy	ric_ies = wpabuf_parse_bin(cmd);
407346981Scy	if (!ric_ies)
408346981Scy		return -1;
409346981Scy
410346981Scy	wpabuf_free(wpa_s->ric_ies);
411346981Scy	wpa_s->ric_ies = ric_ies;
412346981Scy
413346981Scy	return 0;
414346981Scy}
415346981Scy
416346981Scy
417189251Ssamstatic int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
418189251Ssam					 char *cmd)
419189251Ssam{
420189251Ssam	char *value;
421189251Ssam	int ret = 0;
422189251Ssam
423189251Ssam	value = os_strchr(cmd, ' ');
424189251Ssam	if (value == NULL)
425189251Ssam		return -1;
426189251Ssam	*value++ = '\0';
427189251Ssam
428189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
429189251Ssam	if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
430189251Ssam		eapol_sm_configure(wpa_s->eapol,
431189251Ssam				   atoi(value), -1, -1, -1);
432189251Ssam	} else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
433189251Ssam		eapol_sm_configure(wpa_s->eapol,
434189251Ssam				   -1, atoi(value), -1, -1);
435189251Ssam	} else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
436189251Ssam		eapol_sm_configure(wpa_s->eapol,
437189251Ssam				   -1, -1, atoi(value), -1);
438189251Ssam	} else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
439189251Ssam		eapol_sm_configure(wpa_s->eapol,
440189251Ssam				   -1, -1, -1, atoi(value));
441189251Ssam	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
442189251Ssam		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
443346981Scy				     atoi(value))) {
444189251Ssam			ret = -1;
445346981Scy		} else {
446346981Scy			value[-1] = '=';
447346981Scy			wpa_config_process_global(wpa_s->conf, cmd, -1);
448346981Scy		}
449189251Ssam	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
450189251Ssam		   0) {
451189251Ssam		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
452346981Scy				     atoi(value))) {
453189251Ssam			ret = -1;
454346981Scy		} else {
455346981Scy			value[-1] = '=';
456346981Scy			wpa_config_process_global(wpa_s->conf, cmd, -1);
457346981Scy		}
458189251Ssam	} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
459346981Scy		if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
460346981Scy				     atoi(value))) {
461189251Ssam			ret = -1;
462346981Scy		} else {
463346981Scy			value[-1] = '=';
464346981Scy			wpa_config_process_global(wpa_s->conf, cmd, -1);
465346981Scy		}
466252726Srpaulo	} else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
467252726Srpaulo		wpa_s->wps_fragment_size = atoi(value);
468252726Srpaulo#ifdef CONFIG_WPS_TESTING
469252726Srpaulo	} else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
470252726Srpaulo		long int val;
471252726Srpaulo		val = strtol(value, NULL, 0);
472252726Srpaulo		if (val < 0 || val > 0xff) {
473252726Srpaulo			ret = -1;
474252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid "
475252726Srpaulo				   "wps_version_number %ld", val);
476252726Srpaulo		} else {
477252726Srpaulo			wps_version_number = val;
478252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
479252726Srpaulo				   "version %u.%u",
480252726Srpaulo				   (wps_version_number & 0xf0) >> 4,
481252726Srpaulo				   wps_version_number & 0x0f);
482252726Srpaulo		}
483252726Srpaulo	} else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
484252726Srpaulo		wps_testing_dummy_cred = atoi(value);
485252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
486252726Srpaulo			   wps_testing_dummy_cred);
487281806Srpaulo	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
488281806Srpaulo		wps_corrupt_pkhash = atoi(value);
489281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
490281806Srpaulo			   wps_corrupt_pkhash);
491337817Scy	} else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) {
492337817Scy		if (value[0] == '\0') {
493337817Scy			wps_force_auth_types_in_use = 0;
494337817Scy		} else {
495337817Scy			wps_force_auth_types = strtol(value, NULL, 0);
496337817Scy			wps_force_auth_types_in_use = 1;
497337817Scy		}
498337817Scy	} else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) {
499337817Scy		if (value[0] == '\0') {
500337817Scy			wps_force_encr_types_in_use = 0;
501337817Scy		} else {
502337817Scy			wps_force_encr_types = strtol(value, NULL, 0);
503337817Scy			wps_force_encr_types_in_use = 1;
504337817Scy		}
505252726Srpaulo#endif /* CONFIG_WPS_TESTING */
506252726Srpaulo	} else if (os_strcasecmp(cmd, "ampdu") == 0) {
507252726Srpaulo		if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
508252726Srpaulo			ret = -1;
509281806Srpaulo#ifdef CONFIG_TDLS
510252726Srpaulo#ifdef CONFIG_TDLS_TESTING
511252726Srpaulo	} else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
512252726Srpaulo		tdls_testing = strtol(value, NULL, 0);
513252726Srpaulo		wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
514252726Srpaulo#endif /* CONFIG_TDLS_TESTING */
515252726Srpaulo	} else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
516252726Srpaulo		int disabled = atoi(value);
517252726Srpaulo		wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
518252726Srpaulo		if (disabled) {
519252726Srpaulo			if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
520252726Srpaulo				ret = -1;
521252726Srpaulo		} else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
522252726Srpaulo			ret = -1;
523252726Srpaulo		wpa_tdls_enable(wpa_s->wpa, !disabled);
524252726Srpaulo#endif /* CONFIG_TDLS */
525252726Srpaulo	} else if (os_strcasecmp(cmd, "pno") == 0) {
526281806Srpaulo		ret = wpas_ctrl_pno(wpa_s, value);
527252726Srpaulo	} else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
528252726Srpaulo		int disabled = atoi(value);
529252726Srpaulo		if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
530252726Srpaulo			ret = -1;
531252726Srpaulo		else if (disabled)
532252726Srpaulo			wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
533252726Srpaulo	} else if (os_strcasecmp(cmd, "uapsd") == 0) {
534252726Srpaulo		if (os_strcmp(value, "disable") == 0)
535252726Srpaulo			wpa_s->set_sta_uapsd = 0;
536252726Srpaulo		else {
537252726Srpaulo			int be, bk, vi, vo;
538252726Srpaulo			char *pos;
539252726Srpaulo			/* format: BE,BK,VI,VO;max SP Length */
540252726Srpaulo			be = atoi(value);
541252726Srpaulo			pos = os_strchr(value, ',');
542252726Srpaulo			if (pos == NULL)
543252726Srpaulo				return -1;
544252726Srpaulo			pos++;
545252726Srpaulo			bk = atoi(pos);
546252726Srpaulo			pos = os_strchr(pos, ',');
547252726Srpaulo			if (pos == NULL)
548252726Srpaulo				return -1;
549252726Srpaulo			pos++;
550252726Srpaulo			vi = atoi(pos);
551252726Srpaulo			pos = os_strchr(pos, ',');
552252726Srpaulo			if (pos == NULL)
553252726Srpaulo				return -1;
554252726Srpaulo			pos++;
555252726Srpaulo			vo = atoi(pos);
556252726Srpaulo			/* ignore max SP Length for now */
557189251Ssam
558252726Srpaulo			wpa_s->set_sta_uapsd = 1;
559252726Srpaulo			wpa_s->sta_uapsd = 0;
560252726Srpaulo			if (be)
561252726Srpaulo				wpa_s->sta_uapsd |= BIT(0);
562252726Srpaulo			if (bk)
563252726Srpaulo				wpa_s->sta_uapsd |= BIT(1);
564252726Srpaulo			if (vi)
565252726Srpaulo				wpa_s->sta_uapsd |= BIT(2);
566252726Srpaulo			if (vo)
567252726Srpaulo				wpa_s->sta_uapsd |= BIT(3);
568252726Srpaulo		}
569252726Srpaulo	} else if (os_strcasecmp(cmd, "ps") == 0) {
570252726Srpaulo		ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
571252726Srpaulo#ifdef CONFIG_WIFI_DISPLAY
572252726Srpaulo	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
573281806Srpaulo		int enabled = !!atoi(value);
574281806Srpaulo		if (enabled && !wpa_s->global->p2p)
575281806Srpaulo			ret = -1;
576281806Srpaulo		else
577281806Srpaulo			wifi_display_enable(wpa_s->global, enabled);
578252726Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
579252726Srpaulo	} else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
580252726Srpaulo		ret = set_bssid_filter(wpa_s, value);
581252726Srpaulo	} else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
582252726Srpaulo		ret = set_disallow_aps(wpa_s, value);
583252726Srpaulo	} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
584252726Srpaulo		wpa_s->no_keep_alive = !!atoi(value);
585346981Scy#ifdef CONFIG_DPP
586346981Scy	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
587346981Scy		os_free(wpa_s->dpp_configurator_params);
588346981Scy		wpa_s->dpp_configurator_params = os_strdup(value);
589346981Scy	} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
590346981Scy		wpa_s->dpp_init_max_tries = atoi(value);
591346981Scy	} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
592346981Scy		wpa_s->dpp_init_retry_time = atoi(value);
593346981Scy	} else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
594346981Scy		wpa_s->dpp_resp_wait_time = atoi(value);
595346981Scy	} else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
596346981Scy		wpa_s->dpp_resp_max_tries = atoi(value);
597346981Scy	} else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
598346981Scy		wpa_s->dpp_resp_retry_time = atoi(value);
599281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
600346981Scy	} else if (os_strcasecmp(cmd, "dpp_pkex_own_mac_override") == 0) {
601346981Scy		if (hwaddr_aton(value, dpp_pkex_own_mac_override))
602346981Scy			ret = -1;
603346981Scy	} else if (os_strcasecmp(cmd, "dpp_pkex_peer_mac_override") == 0) {
604346981Scy		if (hwaddr_aton(value, dpp_pkex_peer_mac_override))
605346981Scy			ret = -1;
606346981Scy	} else if (os_strcasecmp(cmd, "dpp_pkex_ephemeral_key_override") == 0) {
607346981Scy		size_t hex_len = os_strlen(value);
608346981Scy
609346981Scy		if (hex_len >
610346981Scy		    2 * sizeof(dpp_pkex_ephemeral_key_override))
611346981Scy			ret = -1;
612346981Scy		else if (hexstr2bin(value, dpp_pkex_ephemeral_key_override,
613346981Scy				    hex_len / 2))
614346981Scy			ret = -1;
615346981Scy		else
616346981Scy			dpp_pkex_ephemeral_key_override_len = hex_len / 2;
617346981Scy	} else if (os_strcasecmp(cmd, "dpp_protocol_key_override") == 0) {
618346981Scy		size_t hex_len = os_strlen(value);
619346981Scy
620346981Scy		if (hex_len > 2 * sizeof(dpp_protocol_key_override))
621346981Scy			ret = -1;
622346981Scy		else if (hexstr2bin(value, dpp_protocol_key_override,
623346981Scy				    hex_len / 2))
624346981Scy			ret = -1;
625346981Scy		else
626346981Scy			dpp_protocol_key_override_len = hex_len / 2;
627346981Scy	} else if (os_strcasecmp(cmd, "dpp_nonce_override") == 0) {
628346981Scy		size_t hex_len = os_strlen(value);
629346981Scy
630346981Scy		if (hex_len > 2 * sizeof(dpp_nonce_override))
631346981Scy			ret = -1;
632346981Scy		else if (hexstr2bin(value, dpp_nonce_override, hex_len / 2))
633346981Scy			ret = -1;
634346981Scy		else
635346981Scy			dpp_nonce_override_len = hex_len / 2;
636346981Scy#endif /* CONFIG_TESTING_OPTIONS */
637346981Scy#endif /* CONFIG_DPP */
638346981Scy#ifdef CONFIG_TESTING_OPTIONS
639281806Srpaulo	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
640281806Srpaulo		wpa_s->ext_mgmt_frame_handling = !!atoi(value);
641281806Srpaulo	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
642281806Srpaulo		wpa_s->ext_eapol_frame_io = !!atoi(value);
643281806Srpaulo#ifdef CONFIG_AP
644281806Srpaulo		if (wpa_s->ap_iface) {
645281806Srpaulo			wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
646281806Srpaulo				wpa_s->ext_eapol_frame_io;
647281806Srpaulo		}
648281806Srpaulo#endif /* CONFIG_AP */
649281806Srpaulo	} else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
650281806Srpaulo		wpa_s->extra_roc_dur = atoi(value);
651281806Srpaulo	} else if (os_strcasecmp(cmd, "test_failure") == 0) {
652281806Srpaulo		wpa_s->test_failure = atoi(value);
653337817Scy	} else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
654337817Scy		wpa_s->p2p_go_csa_on_inv = !!atoi(value);
655337817Scy	} else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) {
656337817Scy		wpa_s->ignore_auth_resp = !!atoi(value);
657337817Scy	} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
658337817Scy		wpa_s->ignore_assoc_disallow = !!atoi(value);
659346981Scy		wpa_drv_ignore_assoc_disallow(wpa_s,
660346981Scy					      wpa_s->ignore_assoc_disallow);
661337817Scy	} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
662337817Scy		wpa_s->reject_btm_req_reason = atoi(value);
663346981Scy	} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
664346981Scy		os_free(wpa_s->get_pref_freq_list_override);
665346981Scy		if (!value[0])
666346981Scy			wpa_s->get_pref_freq_list_override = NULL;
667346981Scy		else
668346981Scy			wpa_s->get_pref_freq_list_override = os_strdup(value);
669346981Scy	} else if (os_strcasecmp(cmd, "sae_commit_override") == 0) {
670346981Scy		wpabuf_free(wpa_s->sae_commit_override);
671346981Scy		if (value[0] == '\0')
672346981Scy			wpa_s->sae_commit_override = NULL;
673346981Scy		else
674346981Scy			wpa_s->sae_commit_override = wpabuf_parse_bin(value);
675346981Scy#ifdef CONFIG_DPP
676346981Scy	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
677346981Scy		os_free(wpa_s->dpp_config_obj_override);
678346981Scy		if (value[0] == '\0')
679346981Scy			wpa_s->dpp_config_obj_override = NULL;
680346981Scy		else
681346981Scy			wpa_s->dpp_config_obj_override = os_strdup(value);
682346981Scy	} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
683346981Scy		os_free(wpa_s->dpp_discovery_override);
684346981Scy		if (value[0] == '\0')
685346981Scy			wpa_s->dpp_discovery_override = NULL;
686346981Scy		else
687346981Scy			wpa_s->dpp_discovery_override = os_strdup(value);
688346981Scy	} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
689346981Scy		os_free(wpa_s->dpp_groups_override);
690346981Scy		if (value[0] == '\0')
691346981Scy			wpa_s->dpp_groups_override = NULL;
692346981Scy		else
693346981Scy			wpa_s->dpp_groups_override = os_strdup(value);
694346981Scy	} else if (os_strcasecmp(cmd,
695346981Scy				 "dpp_ignore_netaccesskey_mismatch") == 0) {
696346981Scy		wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value);
697346981Scy	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
698346981Scy		dpp_test = atoi(value);
699346981Scy#endif /* CONFIG_DPP */
700281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
701346981Scy#ifdef CONFIG_FILS
702346981Scy	} else if (os_strcasecmp(cmd, "disable_fils") == 0) {
703346981Scy		wpa_s->disable_fils = !!atoi(value);
704346981Scy		wpa_drv_disable_fils(wpa_s, wpa_s->disable_fils);
705346981Scy		wpa_supplicant_set_default_scan_ies(wpa_s);
706346981Scy#endif /* CONFIG_FILS */
707281806Srpaulo#ifndef CONFIG_NO_CONFIG_BLOBS
708281806Srpaulo	} else if (os_strcmp(cmd, "blob") == 0) {
709281806Srpaulo		ret = wpas_ctrl_set_blob(wpa_s, value);
710281806Srpaulo#endif /* CONFIG_NO_CONFIG_BLOBS */
711281806Srpaulo	} else if (os_strcasecmp(cmd, "setband") == 0) {
712289549Srpaulo		ret = wpas_ctrl_set_band(wpa_s, value);
713337817Scy#ifdef CONFIG_MBO
714337817Scy	} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
715337817Scy		ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
716346981Scy		if (ret == 0) {
717346981Scy			value[-1] = '=';
718346981Scy			wpa_config_process_global(wpa_s->conf, cmd, -1);
719346981Scy		}
720337817Scy	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
721337817Scy		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
722346981Scy	} else if (os_strcasecmp(cmd, "oce") == 0) {
723346981Scy		wpa_s->conf->oce = atoi(value);
724346981Scy		if (wpa_s->conf->oce) {
725346981Scy			if ((wpa_s->conf->oce & OCE_STA) &&
726346981Scy			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
727346981Scy				wpa_s->enable_oce = OCE_STA;
728346981Scy
729346981Scy			if ((wpa_s->conf->oce & OCE_STA_CFON) &&
730346981Scy			    (wpa_s->drv_flags &
731346981Scy			     WPA_DRIVER_FLAGS_OCE_STA_CFON)) {
732346981Scy				/* TODO: Need to add STA-CFON support */
733346981Scy				wpa_printf(MSG_ERROR,
734346981Scy					   "OCE STA-CFON feature is not yet supported");
735346981Scy				return -1;
736346981Scy			}
737346981Scy		} else {
738346981Scy			wpa_s->enable_oce = 0;
739346981Scy		}
740346981Scy		wpa_supplicant_set_default_scan_ies(wpa_s);
741337817Scy#endif /* CONFIG_MBO */
742337817Scy	} else if (os_strcasecmp(cmd, "lci") == 0) {
743337817Scy		ret = wpas_ctrl_iface_set_lci(wpa_s, value);
744346981Scy	} else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) {
745346981Scy		ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value));
746346981Scy	} else if (os_strcasecmp(cmd, "relative_rssi") == 0) {
747346981Scy		ret = wpas_ctrl_set_relative_rssi(wpa_s, value);
748346981Scy	} else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) {
749346981Scy		ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
750346981Scy	} else if (os_strcasecmp(cmd, "ric_ies") == 0) {
751346981Scy		ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
752346981Scy	} else if (os_strcasecmp(cmd, "roaming") == 0) {
753346981Scy		ret = wpa_drv_roaming(wpa_s, atoi(value), NULL);
754346981Scy#ifdef CONFIG_WNM
755346981Scy	} else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) {
756346981Scy		struct wpabuf *elems;
757346981Scy
758346981Scy		elems = wpabuf_parse_bin(value);
759346981Scy		if (!elems)
760346981Scy			return -1;
761346981Scy		wnm_set_coloc_intf_elems(wpa_s, elems);
762346981Scy#endif /* CONFIG_WNM */
763252726Srpaulo	} else {
764252726Srpaulo		value[-1] = '=';
765252726Srpaulo		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
766252726Srpaulo		if (ret == 0)
767252726Srpaulo			wpa_supplicant_update_config(wpa_s);
768252726Srpaulo	}
769252726Srpaulo
770189251Ssam	return ret;
771189251Ssam}
772189251Ssam
773189251Ssam
774252726Srpaulostatic int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
775252726Srpaulo					 char *cmd, char *buf, size_t buflen)
776252726Srpaulo{
777252726Srpaulo	int res = -1;
778252726Srpaulo
779252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
780252726Srpaulo
781252726Srpaulo	if (os_strcmp(cmd, "version") == 0) {
782252726Srpaulo		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
783252726Srpaulo	} else if (os_strcasecmp(cmd, "country") == 0) {
784252726Srpaulo		if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
785252726Srpaulo			res = os_snprintf(buf, buflen, "%c%c",
786252726Srpaulo					  wpa_s->conf->country[0],
787252726Srpaulo					  wpa_s->conf->country[1]);
788252726Srpaulo#ifdef CONFIG_WIFI_DISPLAY
789252726Srpaulo	} else if (os_strcasecmp(cmd, "wifi_display") == 0) {
790281806Srpaulo		int enabled;
791281806Srpaulo		if (wpa_s->global->p2p == NULL ||
792281806Srpaulo		    wpa_s->global->p2p_disabled)
793281806Srpaulo			enabled = 0;
794281806Srpaulo		else
795281806Srpaulo			enabled = wpa_s->global->wifi_display;
796281806Srpaulo		res = os_snprintf(buf, buflen, "%d", enabled);
797281806Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
798281806Srpaulo#ifdef CONFIG_TESTING_GET_GTK
799281806Srpaulo	} else if (os_strcmp(cmd, "gtk") == 0) {
800281806Srpaulo		if (wpa_s->last_gtk_len == 0)
801252726Srpaulo			return -1;
802281806Srpaulo		res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk,
803281806Srpaulo				       wpa_s->last_gtk_len);
804252726Srpaulo		return res;
805281806Srpaulo#endif /* CONFIG_TESTING_GET_GTK */
806281806Srpaulo	} else if (os_strcmp(cmd, "tls_library") == 0) {
807281806Srpaulo		res = tls_get_library_version(buf, buflen);
808346981Scy#ifdef CONFIG_TESTING_OPTIONS
809346981Scy	} else if (os_strcmp(cmd, "anonce") == 0) {
810346981Scy		return wpa_snprintf_hex(buf, buflen,
811346981Scy					wpa_sm_get_anonce(wpa_s->wpa),
812346981Scy					WPA_NONCE_LEN);
813346981Scy#endif /* CONFIG_TESTING_OPTIONS */
814281806Srpaulo	} else {
815281806Srpaulo		res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
816252726Srpaulo	}
817252726Srpaulo
818281806Srpaulo	if (os_snprintf_error(buflen, res))
819252726Srpaulo		return -1;
820252726Srpaulo	return res;
821252726Srpaulo}
822252726Srpaulo
823252726Srpaulo
824189251Ssam#ifdef IEEE8021X_EAPOL
825189251Ssamstatic int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
826189251Ssam					     char *addr)
827189251Ssam{
828189251Ssam	u8 bssid[ETH_ALEN];
829189251Ssam	struct wpa_ssid *ssid = wpa_s->current_ssid;
830189251Ssam
831189251Ssam	if (hwaddr_aton(addr, bssid)) {
832189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
833189251Ssam			   "'%s'", addr);
834189251Ssam		return -1;
835189251Ssam	}
836189251Ssam
837189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
838189251Ssam	rsn_preauth_deinit(wpa_s->wpa);
839189251Ssam	if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
840189251Ssam		return -1;
841189251Ssam
842189251Ssam	return 0;
843189251Ssam}
844189251Ssam#endif /* IEEE8021X_EAPOL */
845189251Ssam
846189251Ssam
847252726Srpaulo#ifdef CONFIG_TDLS
848252726Srpaulo
849252726Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_discover(
850252726Srpaulo	struct wpa_supplicant *wpa_s, char *addr)
851252726Srpaulo{
852252726Srpaulo	u8 peer[ETH_ALEN];
853252726Srpaulo	int ret;
854252726Srpaulo
855252726Srpaulo	if (hwaddr_aton(addr, peer)) {
856252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
857252726Srpaulo			   "address '%s'", addr);
858252726Srpaulo		return -1;
859252726Srpaulo	}
860252726Srpaulo
861252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
862252726Srpaulo		   MAC2STR(peer));
863252726Srpaulo
864252726Srpaulo	if (wpa_tdls_is_external_setup(wpa_s->wpa))
865252726Srpaulo		ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
866252726Srpaulo	else
867252726Srpaulo		ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
868252726Srpaulo
869252726Srpaulo	return ret;
870252726Srpaulo}
871252726Srpaulo
872252726Srpaulo
873252726Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_setup(
874252726Srpaulo	struct wpa_supplicant *wpa_s, char *addr)
875252726Srpaulo{
876252726Srpaulo	u8 peer[ETH_ALEN];
877252726Srpaulo	int ret;
878252726Srpaulo
879252726Srpaulo	if (hwaddr_aton(addr, peer)) {
880252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
881252726Srpaulo			   "address '%s'", addr);
882252726Srpaulo		return -1;
883252726Srpaulo	}
884252726Srpaulo
885252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
886252726Srpaulo		   MAC2STR(peer));
887252726Srpaulo
888281806Srpaulo	if ((wpa_s->conf->tdls_external_control) &&
889281806Srpaulo	    wpa_tdls_is_external_setup(wpa_s->wpa))
890281806Srpaulo		return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
891252726Srpaulo
892281806Srpaulo	wpa_tdls_remove(wpa_s->wpa, peer);
893281806Srpaulo
894281806Srpaulo	if (wpa_tdls_is_external_setup(wpa_s->wpa))
895281806Srpaulo		ret = wpa_tdls_start(wpa_s->wpa, peer);
896281806Srpaulo	else
897281806Srpaulo		ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
898281806Srpaulo
899252726Srpaulo	return ret;
900252726Srpaulo}
901252726Srpaulo
902252726Srpaulo
903252726Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_teardown(
904252726Srpaulo	struct wpa_supplicant *wpa_s, char *addr)
905252726Srpaulo{
906252726Srpaulo	u8 peer[ETH_ALEN];
907281806Srpaulo	int ret;
908252726Srpaulo
909281806Srpaulo	if (os_strcmp(addr, "*") == 0) {
910281806Srpaulo		/* remove everyone */
911281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
912281806Srpaulo		wpa_tdls_teardown_peers(wpa_s->wpa);
913281806Srpaulo		return 0;
914281806Srpaulo	}
915281806Srpaulo
916252726Srpaulo	if (hwaddr_aton(addr, peer)) {
917252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
918252726Srpaulo			   "address '%s'", addr);
919252726Srpaulo		return -1;
920252726Srpaulo	}
921252726Srpaulo
922252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
923252726Srpaulo		   MAC2STR(peer));
924252726Srpaulo
925281806Srpaulo	if ((wpa_s->conf->tdls_external_control) &&
926281806Srpaulo	    wpa_tdls_is_external_setup(wpa_s->wpa))
927281806Srpaulo		return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
928281806Srpaulo
929281806Srpaulo	if (wpa_tdls_is_external_setup(wpa_s->wpa))
930281806Srpaulo		ret = wpa_tdls_teardown_link(
931281806Srpaulo			wpa_s->wpa, peer,
932281806Srpaulo			WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
933281806Srpaulo	else
934281806Srpaulo		ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
935281806Srpaulo
936281806Srpaulo	return ret;
937252726Srpaulo}
938252726Srpaulo
939281806Srpaulo
940281806Srpaulostatic int ctrl_iface_get_capability_tdls(
941281806Srpaulo	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
942281806Srpaulo{
943281806Srpaulo	int ret;
944281806Srpaulo
945281806Srpaulo	ret = os_snprintf(buf, buflen, "%s\n",
946281806Srpaulo			  wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
947281806Srpaulo			  (wpa_s->drv_flags &
948281806Srpaulo			   WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
949281806Srpaulo			   "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
950281806Srpaulo	if (os_snprintf_error(buflen, ret))
951281806Srpaulo		return -1;
952281806Srpaulo	return ret;
953281806Srpaulo}
954281806Srpaulo
955281806Srpaulo
956281806Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_chan_switch(
957281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
958281806Srpaulo{
959281806Srpaulo	u8 peer[ETH_ALEN];
960281806Srpaulo	struct hostapd_freq_params freq_params;
961281806Srpaulo	u8 oper_class;
962281806Srpaulo	char *pos, *end;
963281806Srpaulo
964281806Srpaulo	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
965281806Srpaulo		wpa_printf(MSG_INFO,
966281806Srpaulo			   "tdls_chanswitch: Only supported with external setup");
967281806Srpaulo		return -1;
968281806Srpaulo	}
969281806Srpaulo
970281806Srpaulo	os_memset(&freq_params, 0, sizeof(freq_params));
971281806Srpaulo
972281806Srpaulo	pos = os_strchr(cmd, ' ');
973281806Srpaulo	if (pos == NULL)
974281806Srpaulo		return -1;
975281806Srpaulo	*pos++ = '\0';
976281806Srpaulo
977281806Srpaulo	oper_class = strtol(pos, &end, 10);
978281806Srpaulo	if (pos == end) {
979281806Srpaulo		wpa_printf(MSG_INFO,
980281806Srpaulo			   "tdls_chanswitch: Invalid op class provided");
981281806Srpaulo		return -1;
982281806Srpaulo	}
983281806Srpaulo
984281806Srpaulo	pos = end;
985281806Srpaulo	freq_params.freq = atoi(pos);
986281806Srpaulo	if (freq_params.freq == 0) {
987281806Srpaulo		wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
988281806Srpaulo		return -1;
989281806Srpaulo	}
990281806Srpaulo
991281806Srpaulo#define SET_FREQ_SETTING(str) \
992281806Srpaulo	do { \
993281806Srpaulo		const char *pos2 = os_strstr(pos, " " #str "="); \
994281806Srpaulo		if (pos2) { \
995281806Srpaulo			pos2 += sizeof(" " #str "=") - 1; \
996281806Srpaulo			freq_params.str = atoi(pos2); \
997281806Srpaulo		} \
998281806Srpaulo	} while (0)
999281806Srpaulo
1000281806Srpaulo	SET_FREQ_SETTING(center_freq1);
1001281806Srpaulo	SET_FREQ_SETTING(center_freq2);
1002281806Srpaulo	SET_FREQ_SETTING(bandwidth);
1003281806Srpaulo	SET_FREQ_SETTING(sec_channel_offset);
1004281806Srpaulo#undef SET_FREQ_SETTING
1005281806Srpaulo
1006281806Srpaulo	freq_params.ht_enabled = !!os_strstr(pos, " ht");
1007281806Srpaulo	freq_params.vht_enabled = !!os_strstr(pos, " vht");
1008281806Srpaulo
1009281806Srpaulo	if (hwaddr_aton(cmd, peer)) {
1010281806Srpaulo		wpa_printf(MSG_DEBUG,
1011281806Srpaulo			   "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
1012281806Srpaulo			   cmd);
1013281806Srpaulo		return -1;
1014281806Srpaulo	}
1015281806Srpaulo
1016281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
1017281806Srpaulo		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
1018281806Srpaulo		   MAC2STR(peer), oper_class, freq_params.freq,
1019281806Srpaulo		   freq_params.center_freq1, freq_params.center_freq2,
1020281806Srpaulo		   freq_params.bandwidth, freq_params.sec_channel_offset,
1021281806Srpaulo		   freq_params.ht_enabled ? " HT" : "",
1022281806Srpaulo		   freq_params.vht_enabled ? " VHT" : "");
1023281806Srpaulo
1024281806Srpaulo	return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
1025281806Srpaulo					   &freq_params);
1026281806Srpaulo}
1027281806Srpaulo
1028281806Srpaulo
1029281806Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
1030281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
1031281806Srpaulo{
1032281806Srpaulo	u8 peer[ETH_ALEN];
1033281806Srpaulo
1034281806Srpaulo	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
1035281806Srpaulo		wpa_printf(MSG_INFO,
1036281806Srpaulo			   "tdls_chanswitch: Only supported with external setup");
1037281806Srpaulo		return -1;
1038281806Srpaulo	}
1039281806Srpaulo
1040281806Srpaulo	if (hwaddr_aton(cmd, peer)) {
1041281806Srpaulo		wpa_printf(MSG_DEBUG,
1042281806Srpaulo			   "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
1043281806Srpaulo			   cmd);
1044281806Srpaulo		return -1;
1045281806Srpaulo	}
1046281806Srpaulo
1047281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
1048281806Srpaulo		   MAC2STR(peer));
1049281806Srpaulo
1050281806Srpaulo	return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
1051281806Srpaulo}
1052281806Srpaulo
1053289549Srpaulo
1054289549Srpaulostatic int wpa_supplicant_ctrl_iface_tdls_link_status(
1055289549Srpaulo	struct wpa_supplicant *wpa_s, const char *addr,
1056289549Srpaulo	char *buf, size_t buflen)
1057289549Srpaulo{
1058289549Srpaulo	u8 peer[ETH_ALEN];
1059289549Srpaulo	const char *tdls_status;
1060289549Srpaulo	int ret;
1061289549Srpaulo
1062289549Srpaulo	if (hwaddr_aton(addr, peer)) {
1063289549Srpaulo		wpa_printf(MSG_DEBUG,
1064289549Srpaulo			   "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
1065289549Srpaulo			   addr);
1066289549Srpaulo		return -1;
1067289549Srpaulo	}
1068289549Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
1069289549Srpaulo		   MAC2STR(peer));
1070289549Srpaulo
1071289549Srpaulo	tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
1072289549Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
1073289549Srpaulo	ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
1074289549Srpaulo	if (os_snprintf_error(buflen, ret))
1075289549Srpaulo		return -1;
1076289549Srpaulo
1077289549Srpaulo	return ret;
1078289549Srpaulo}
1079289549Srpaulo
1080252726Srpaulo#endif /* CONFIG_TDLS */
1081252726Srpaulo
1082252726Srpaulo
1083281806Srpaulostatic int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
1084281806Srpaulo{
1085281806Srpaulo	char *token, *context = NULL;
1086281806Srpaulo	struct wmm_ac_ts_setup_params params = {
1087281806Srpaulo		.tsid = 0xff,
1088281806Srpaulo		.direction = 0xff,
1089281806Srpaulo	};
1090281806Srpaulo
1091281806Srpaulo	while ((token = str_token(cmd, " ", &context))) {
1092281806Srpaulo		if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
1093281806Srpaulo		    sscanf(token, "up=%i", &params.user_priority) == 1 ||
1094281806Srpaulo		    sscanf(token, "nominal_msdu_size=%i",
1095281806Srpaulo			   &params.nominal_msdu_size) == 1 ||
1096281806Srpaulo		    sscanf(token, "mean_data_rate=%i",
1097281806Srpaulo			   &params.mean_data_rate) == 1 ||
1098281806Srpaulo		    sscanf(token, "min_phy_rate=%i",
1099281806Srpaulo			   &params.minimum_phy_rate) == 1 ||
1100281806Srpaulo		    sscanf(token, "sba=%i",
1101281806Srpaulo			   &params.surplus_bandwidth_allowance) == 1)
1102281806Srpaulo			continue;
1103281806Srpaulo
1104281806Srpaulo		if (os_strcasecmp(token, "downlink") == 0) {
1105281806Srpaulo			params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
1106281806Srpaulo		} else if (os_strcasecmp(token, "uplink") == 0) {
1107281806Srpaulo			params.direction = WMM_TSPEC_DIRECTION_UPLINK;
1108281806Srpaulo		} else if (os_strcasecmp(token, "bidi") == 0) {
1109281806Srpaulo			params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
1110281806Srpaulo		} else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
1111281806Srpaulo			params.fixed_nominal_msdu = 1;
1112281806Srpaulo		} else {
1113281806Srpaulo			wpa_printf(MSG_DEBUG,
1114281806Srpaulo				   "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
1115281806Srpaulo				   token);
1116281806Srpaulo			return -1;
1117281806Srpaulo		}
1118281806Srpaulo
1119281806Srpaulo	}
1120281806Srpaulo
1121281806Srpaulo	return wpas_wmm_ac_addts(wpa_s, &params);
1122281806Srpaulo}
1123281806Srpaulo
1124281806Srpaulo
1125281806Srpaulostatic int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
1126281806Srpaulo{
1127281806Srpaulo	u8 tsid = atoi(cmd);
1128281806Srpaulo
1129281806Srpaulo	return wpas_wmm_ac_delts(wpa_s, tsid);
1130281806Srpaulo}
1131281806Srpaulo
1132281806Srpaulo
1133189251Ssam#ifdef CONFIG_IEEE80211R
1134189251Ssamstatic int wpa_supplicant_ctrl_iface_ft_ds(
1135189251Ssam	struct wpa_supplicant *wpa_s, char *addr)
1136189251Ssam{
1137189251Ssam	u8 target_ap[ETH_ALEN];
1138214734Srpaulo	struct wpa_bss *bss;
1139214734Srpaulo	const u8 *mdie;
1140189251Ssam
1141189251Ssam	if (hwaddr_aton(addr, target_ap)) {
1142189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
1143214734Srpaulo			   "address '%s'", addr);
1144189251Ssam		return -1;
1145189251Ssam	}
1146189251Ssam
1147189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
1148189251Ssam
1149214734Srpaulo	bss = wpa_bss_get_bssid(wpa_s, target_ap);
1150214734Srpaulo	if (bss)
1151214734Srpaulo		mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
1152214734Srpaulo	else
1153214734Srpaulo		mdie = NULL;
1154214734Srpaulo
1155214734Srpaulo	return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
1156189251Ssam}
1157189251Ssam#endif /* CONFIG_IEEE80211R */
1158189251Ssam
1159189251Ssam
1160189251Ssam#ifdef CONFIG_WPS
1161189251Ssamstatic int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
1162189251Ssam					     char *cmd)
1163189251Ssam{
1164214734Srpaulo	u8 bssid[ETH_ALEN], *_bssid = bssid;
1165252726Srpaulo#ifdef CONFIG_P2P
1166252726Srpaulo	u8 p2p_dev_addr[ETH_ALEN];
1167252726Srpaulo#endif /* CONFIG_P2P */
1168252726Srpaulo#ifdef CONFIG_AP
1169252726Srpaulo	u8 *_p2p_dev_addr = NULL;
1170252726Srpaulo#endif /* CONFIG_AP */
1171346981Scy	char *pos;
1172346981Scy	int multi_ap = 0;
1173189251Ssam
1174346981Scy	if (!cmd || os_strcmp(cmd, "any") == 0 ||
1175346981Scy	    os_strncmp(cmd, "any ", 4) == 0) {
1176214734Srpaulo		_bssid = NULL;
1177252726Srpaulo#ifdef CONFIG_P2P
1178252726Srpaulo	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
1179252726Srpaulo		if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
1180252726Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
1181252726Srpaulo				   "P2P Device Address '%s'",
1182252726Srpaulo				   cmd + 13);
1183252726Srpaulo			return -1;
1184252726Srpaulo		}
1185252726Srpaulo		_p2p_dev_addr = p2p_dev_addr;
1186252726Srpaulo#endif /* CONFIG_P2P */
1187346981Scy	} else if (os_strncmp(cmd, "multi_ap=", 9) == 0) {
1188346981Scy		_bssid = NULL;
1189346981Scy		multi_ap = atoi(cmd + 9);
1190252726Srpaulo	} else if (hwaddr_aton(cmd, bssid)) {
1191189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
1192189251Ssam			   cmd);
1193189251Ssam		return -1;
1194189251Ssam	}
1195189251Ssam
1196346981Scy	if (cmd) {
1197346981Scy		pos = os_strstr(cmd, " multi_ap=");
1198346981Scy		if (pos) {
1199346981Scy			pos += 10;
1200346981Scy			multi_ap = atoi(pos);
1201346981Scy		}
1202346981Scy	}
1203346981Scy
1204214734Srpaulo#ifdef CONFIG_AP
1205214734Srpaulo	if (wpa_s->ap_iface)
1206252726Srpaulo		return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
1207214734Srpaulo#endif /* CONFIG_AP */
1208214734Srpaulo
1209346981Scy	return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap);
1210189251Ssam}
1211189251Ssam
1212189251Ssam
1213189251Ssamstatic int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
1214189251Ssam					     char *cmd, char *buf,
1215189251Ssam					     size_t buflen)
1216189251Ssam{
1217189251Ssam	u8 bssid[ETH_ALEN], *_bssid = bssid;
1218189251Ssam	char *pin;
1219189251Ssam	int ret;
1220189251Ssam
1221189251Ssam	pin = os_strchr(cmd, ' ');
1222189251Ssam	if (pin)
1223189251Ssam		*pin++ = '\0';
1224189251Ssam
1225189251Ssam	if (os_strcmp(cmd, "any") == 0)
1226189251Ssam		_bssid = NULL;
1227252726Srpaulo	else if (os_strcmp(cmd, "get") == 0) {
1228337817Scy		if (wps_generate_pin((unsigned int *) &ret) < 0)
1229337817Scy			return -1;
1230252726Srpaulo		goto done;
1231252726Srpaulo	} else if (hwaddr_aton(cmd, bssid)) {
1232189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
1233189251Ssam			   cmd);
1234189251Ssam		return -1;
1235189251Ssam	}
1236189251Ssam
1237214734Srpaulo#ifdef CONFIG_AP
1238252726Srpaulo	if (wpa_s->ap_iface) {
1239252726Srpaulo		int timeout = 0;
1240252726Srpaulo		char *pos;
1241252726Srpaulo
1242252726Srpaulo		if (pin) {
1243252726Srpaulo			pos = os_strchr(pin, ' ');
1244252726Srpaulo			if (pos) {
1245252726Srpaulo				*pos++ = '\0';
1246252726Srpaulo				timeout = atoi(pos);
1247252726Srpaulo			}
1248252726Srpaulo		}
1249252726Srpaulo
1250214734Srpaulo		return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
1251252726Srpaulo						 buf, buflen, timeout);
1252252726Srpaulo	}
1253214734Srpaulo#endif /* CONFIG_AP */
1254214734Srpaulo
1255189251Ssam	if (pin) {
1256252726Srpaulo		ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
1257252726Srpaulo					 DEV_PW_DEFAULT);
1258189251Ssam		if (ret < 0)
1259189251Ssam			return -1;
1260189251Ssam		ret = os_snprintf(buf, buflen, "%s", pin);
1261281806Srpaulo		if (os_snprintf_error(buflen, ret))
1262189251Ssam			return -1;
1263189251Ssam		return ret;
1264189251Ssam	}
1265189251Ssam
1266252726Srpaulo	ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
1267189251Ssam	if (ret < 0)
1268189251Ssam		return -1;
1269189251Ssam
1270252726Srpaulodone:
1271189251Ssam	/* Return the generated PIN */
1272189251Ssam	ret = os_snprintf(buf, buflen, "%08d", ret);
1273281806Srpaulo	if (os_snprintf_error(buflen, ret))
1274189251Ssam		return -1;
1275189251Ssam	return ret;
1276189251Ssam}
1277189251Ssam
1278189251Ssam
1279252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_check_pin(
1280252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
1281252726Srpaulo{
1282252726Srpaulo	char pin[9];
1283252726Srpaulo	size_t len;
1284252726Srpaulo	char *pos;
1285252726Srpaulo	int ret;
1286252726Srpaulo
1287252726Srpaulo	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
1288252726Srpaulo			      (u8 *) cmd, os_strlen(cmd));
1289252726Srpaulo	for (pos = cmd, len = 0; *pos != '\0'; pos++) {
1290252726Srpaulo		if (*pos < '0' || *pos > '9')
1291252726Srpaulo			continue;
1292252726Srpaulo		pin[len++] = *pos;
1293252726Srpaulo		if (len == 9) {
1294252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
1295252726Srpaulo			return -1;
1296252726Srpaulo		}
1297252726Srpaulo	}
1298252726Srpaulo	if (len != 4 && len != 8) {
1299252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
1300252726Srpaulo		return -1;
1301252726Srpaulo	}
1302252726Srpaulo	pin[len] = '\0';
1303252726Srpaulo
1304252726Srpaulo	if (len == 8) {
1305252726Srpaulo		unsigned int pin_val;
1306252726Srpaulo		pin_val = atoi(pin);
1307252726Srpaulo		if (!wps_pin_valid(pin_val)) {
1308252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
1309252726Srpaulo			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
1310281806Srpaulo			if (os_snprintf_error(buflen, ret))
1311252726Srpaulo				return -1;
1312252726Srpaulo			return ret;
1313252726Srpaulo		}
1314252726Srpaulo	}
1315252726Srpaulo
1316252726Srpaulo	ret = os_snprintf(buf, buflen, "%s", pin);
1317281806Srpaulo	if (os_snprintf_error(buflen, ret))
1318252726Srpaulo		return -1;
1319252726Srpaulo
1320252726Srpaulo	return ret;
1321252726Srpaulo}
1322252726Srpaulo
1323252726Srpaulo
1324252726Srpaulo#ifdef CONFIG_WPS_NFC
1325252726Srpaulo
1326252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
1327214734Srpaulo					     char *cmd)
1328214734Srpaulo{
1329252726Srpaulo	u8 bssid[ETH_ALEN], *_bssid = bssid;
1330214734Srpaulo
1331252726Srpaulo	if (cmd == NULL || cmd[0] == '\0')
1332252726Srpaulo		_bssid = NULL;
1333252726Srpaulo	else if (hwaddr_aton(cmd, bssid))
1334214734Srpaulo		return -1;
1335214734Srpaulo
1336281806Srpaulo	return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
1337281806Srpaulo				  0, 0);
1338252726Srpaulo}
1339252726Srpaulo
1340252726Srpaulo
1341281806Srpaulostatic int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
1342281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
1343281806Srpaulo{
1344281806Srpaulo	int ndef;
1345281806Srpaulo	struct wpabuf *buf;
1346281806Srpaulo	int res;
1347281806Srpaulo	char *pos;
1348281806Srpaulo
1349281806Srpaulo	pos = os_strchr(cmd, ' ');
1350281806Srpaulo	if (pos)
1351281806Srpaulo		*pos++ = '\0';
1352281806Srpaulo	if (os_strcmp(cmd, "WPS") == 0)
1353281806Srpaulo		ndef = 0;
1354281806Srpaulo	else if (os_strcmp(cmd, "NDEF") == 0)
1355281806Srpaulo		ndef = 1;
1356281806Srpaulo	else
1357281806Srpaulo		return -1;
1358281806Srpaulo
1359281806Srpaulo	buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos);
1360281806Srpaulo	if (buf == NULL)
1361281806Srpaulo		return -1;
1362281806Srpaulo
1363281806Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1364281806Srpaulo					 wpabuf_len(buf));
1365281806Srpaulo	reply[res++] = '\n';
1366281806Srpaulo	reply[res] = '\0';
1367281806Srpaulo
1368281806Srpaulo	wpabuf_free(buf);
1369281806Srpaulo
1370281806Srpaulo	return res;
1371281806Srpaulo}
1372281806Srpaulo
1373281806Srpaulo
1374252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_nfc_token(
1375252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
1376252726Srpaulo{
1377252726Srpaulo	int ndef;
1378252726Srpaulo	struct wpabuf *buf;
1379252726Srpaulo	int res;
1380252726Srpaulo
1381252726Srpaulo	if (os_strcmp(cmd, "WPS") == 0)
1382252726Srpaulo		ndef = 0;
1383252726Srpaulo	else if (os_strcmp(cmd, "NDEF") == 0)
1384252726Srpaulo		ndef = 1;
1385252726Srpaulo	else
1386214734Srpaulo		return -1;
1387214734Srpaulo
1388252726Srpaulo	buf = wpas_wps_nfc_token(wpa_s, ndef);
1389252726Srpaulo	if (buf == NULL)
1390252726Srpaulo		return -1;
1391214734Srpaulo
1392252726Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1393252726Srpaulo					 wpabuf_len(buf));
1394252726Srpaulo	reply[res++] = '\n';
1395252726Srpaulo	reply[res] = '\0';
1396252726Srpaulo
1397252726Srpaulo	wpabuf_free(buf);
1398252726Srpaulo
1399252726Srpaulo	return res;
1400214734Srpaulo}
1401214734Srpaulo
1402214734Srpaulo
1403252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
1404252726Srpaulo	struct wpa_supplicant *wpa_s, char *pos)
1405252726Srpaulo{
1406252726Srpaulo	size_t len;
1407252726Srpaulo	struct wpabuf *buf;
1408252726Srpaulo	int ret;
1409281806Srpaulo	char *freq;
1410281806Srpaulo	int forced_freq = 0;
1411252726Srpaulo
1412281806Srpaulo	freq = strstr(pos, " freq=");
1413281806Srpaulo	if (freq) {
1414281806Srpaulo		*freq = '\0';
1415281806Srpaulo		freq += 6;
1416281806Srpaulo		forced_freq = atoi(freq);
1417281806Srpaulo	}
1418281806Srpaulo
1419252726Srpaulo	len = os_strlen(pos);
1420252726Srpaulo	if (len & 0x01)
1421252726Srpaulo		return -1;
1422252726Srpaulo	len /= 2;
1423252726Srpaulo
1424252726Srpaulo	buf = wpabuf_alloc(len);
1425252726Srpaulo	if (buf == NULL)
1426252726Srpaulo		return -1;
1427252726Srpaulo	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
1428252726Srpaulo		wpabuf_free(buf);
1429252726Srpaulo		return -1;
1430252726Srpaulo	}
1431252726Srpaulo
1432281806Srpaulo	ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
1433252726Srpaulo	wpabuf_free(buf);
1434252726Srpaulo
1435252726Srpaulo	return ret;
1436252726Srpaulo}
1437252726Srpaulo
1438252726Srpaulo
1439252726Srpaulostatic int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
1440281806Srpaulo					      char *reply, size_t max_len,
1441281806Srpaulo					      int ndef)
1442252726Srpaulo{
1443252726Srpaulo	struct wpabuf *buf;
1444252726Srpaulo	int res;
1445252726Srpaulo
1446281806Srpaulo	buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
1447252726Srpaulo	if (buf == NULL)
1448252726Srpaulo		return -1;
1449252726Srpaulo
1450252726Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1451252726Srpaulo					 wpabuf_len(buf));
1452252726Srpaulo	reply[res++] = '\n';
1453252726Srpaulo	reply[res] = '\0';
1454252726Srpaulo
1455252726Srpaulo	wpabuf_free(buf);
1456252726Srpaulo
1457252726Srpaulo	return res;
1458252726Srpaulo}
1459252726Srpaulo
1460252726Srpaulo
1461281806Srpaulo#ifdef CONFIG_P2P
1462281806Srpaulostatic int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
1463281806Srpaulo					      char *reply, size_t max_len,
1464281806Srpaulo					      int ndef)
1465281806Srpaulo{
1466281806Srpaulo	struct wpabuf *buf;
1467281806Srpaulo	int res;
1468281806Srpaulo
1469281806Srpaulo	buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
1470281806Srpaulo	if (buf == NULL) {
1471281806Srpaulo		wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
1472281806Srpaulo		return -1;
1473281806Srpaulo	}
1474281806Srpaulo
1475281806Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1476281806Srpaulo					 wpabuf_len(buf));
1477281806Srpaulo	reply[res++] = '\n';
1478281806Srpaulo	reply[res] = '\0';
1479281806Srpaulo
1480281806Srpaulo	wpabuf_free(buf);
1481281806Srpaulo
1482281806Srpaulo	return res;
1483281806Srpaulo}
1484281806Srpaulo#endif /* CONFIG_P2P */
1485281806Srpaulo
1486281806Srpaulo
1487252726Srpaulostatic int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
1488252726Srpaulo					  char *cmd, char *reply,
1489252726Srpaulo					  size_t max_len)
1490252726Srpaulo{
1491252726Srpaulo	char *pos;
1492281806Srpaulo	int ndef;
1493252726Srpaulo
1494252726Srpaulo	pos = os_strchr(cmd, ' ');
1495252726Srpaulo	if (pos == NULL)
1496252726Srpaulo		return -1;
1497252726Srpaulo	*pos++ = '\0';
1498252726Srpaulo
1499281806Srpaulo	if (os_strcmp(cmd, "WPS") == 0)
1500281806Srpaulo		ndef = 0;
1501281806Srpaulo	else if (os_strcmp(cmd, "NDEF") == 0)
1502281806Srpaulo		ndef = 1;
1503281806Srpaulo	else
1504252726Srpaulo		return -1;
1505252726Srpaulo
1506281806Srpaulo	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
1507281806Srpaulo		if (!ndef)
1508281806Srpaulo			return -1;
1509281806Srpaulo		return wpas_ctrl_nfc_get_handover_req_wps(
1510281806Srpaulo			wpa_s, reply, max_len, ndef);
1511252726Srpaulo	}
1512252726Srpaulo
1513281806Srpaulo#ifdef CONFIG_P2P
1514281806Srpaulo	if (os_strcmp(pos, "P2P-CR") == 0) {
1515281806Srpaulo		return wpas_ctrl_nfc_get_handover_req_p2p(
1516281806Srpaulo			wpa_s, reply, max_len, ndef);
1517281806Srpaulo	}
1518281806Srpaulo#endif /* CONFIG_P2P */
1519281806Srpaulo
1520252726Srpaulo	return -1;
1521252726Srpaulo}
1522252726Srpaulo
1523252726Srpaulo
1524252726Srpaulostatic int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
1525281806Srpaulo					      char *reply, size_t max_len,
1526281806Srpaulo					      int ndef, int cr, char *uuid)
1527252726Srpaulo{
1528252726Srpaulo	struct wpabuf *buf;
1529252726Srpaulo	int res;
1530252726Srpaulo
1531281806Srpaulo	buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid);
1532252726Srpaulo	if (buf == NULL)
1533252726Srpaulo		return -1;
1534252726Srpaulo
1535252726Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1536252726Srpaulo					 wpabuf_len(buf));
1537252726Srpaulo	reply[res++] = '\n';
1538252726Srpaulo	reply[res] = '\0';
1539252726Srpaulo
1540252726Srpaulo	wpabuf_free(buf);
1541252726Srpaulo
1542252726Srpaulo	return res;
1543252726Srpaulo}
1544252726Srpaulo
1545252726Srpaulo
1546281806Srpaulo#ifdef CONFIG_P2P
1547281806Srpaulostatic int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
1548281806Srpaulo					      char *reply, size_t max_len,
1549281806Srpaulo					      int ndef, int tag)
1550281806Srpaulo{
1551281806Srpaulo	struct wpabuf *buf;
1552281806Srpaulo	int res;
1553281806Srpaulo
1554281806Srpaulo	buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
1555281806Srpaulo	if (buf == NULL)
1556281806Srpaulo		return -1;
1557281806Srpaulo
1558281806Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1559281806Srpaulo					 wpabuf_len(buf));
1560281806Srpaulo	reply[res++] = '\n';
1561281806Srpaulo	reply[res] = '\0';
1562281806Srpaulo
1563281806Srpaulo	wpabuf_free(buf);
1564281806Srpaulo
1565281806Srpaulo	return res;
1566281806Srpaulo}
1567281806Srpaulo#endif /* CONFIG_P2P */
1568281806Srpaulo
1569281806Srpaulo
1570252726Srpaulostatic int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
1571252726Srpaulo					  char *cmd, char *reply,
1572252726Srpaulo					  size_t max_len)
1573252726Srpaulo{
1574281806Srpaulo	char *pos, *pos2;
1575281806Srpaulo	int ndef;
1576252726Srpaulo
1577252726Srpaulo	pos = os_strchr(cmd, ' ');
1578252726Srpaulo	if (pos == NULL)
1579252726Srpaulo		return -1;
1580252726Srpaulo	*pos++ = '\0';
1581252726Srpaulo
1582281806Srpaulo	if (os_strcmp(cmd, "WPS") == 0)
1583281806Srpaulo		ndef = 0;
1584281806Srpaulo	else if (os_strcmp(cmd, "NDEF") == 0)
1585281806Srpaulo		ndef = 1;
1586281806Srpaulo	else
1587252726Srpaulo		return -1;
1588252726Srpaulo
1589281806Srpaulo	pos2 = os_strchr(pos, ' ');
1590281806Srpaulo	if (pos2)
1591281806Srpaulo		*pos2++ = '\0';
1592281806Srpaulo	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
1593281806Srpaulo		if (!ndef)
1594281806Srpaulo			return -1;
1595281806Srpaulo		return wpas_ctrl_nfc_get_handover_sel_wps(
1596281806Srpaulo			wpa_s, reply, max_len, ndef,
1597281806Srpaulo			os_strcmp(pos, "WPS-CR") == 0, pos2);
1598252726Srpaulo	}
1599252726Srpaulo
1600281806Srpaulo#ifdef CONFIG_P2P
1601281806Srpaulo	if (os_strcmp(pos, "P2P-CR") == 0) {
1602281806Srpaulo		return wpas_ctrl_nfc_get_handover_sel_p2p(
1603281806Srpaulo			wpa_s, reply, max_len, ndef, 0);
1604281806Srpaulo	}
1605281806Srpaulo
1606281806Srpaulo	if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
1607281806Srpaulo		return wpas_ctrl_nfc_get_handover_sel_p2p(
1608281806Srpaulo			wpa_s, reply, max_len, ndef, 1);
1609281806Srpaulo	}
1610281806Srpaulo#endif /* CONFIG_P2P */
1611281806Srpaulo
1612252726Srpaulo	return -1;
1613252726Srpaulo}
1614252726Srpaulo
1615252726Srpaulo
1616281806Srpaulostatic int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
1617281806Srpaulo					 char *cmd)
1618252726Srpaulo{
1619252726Srpaulo	size_t len;
1620281806Srpaulo	struct wpabuf *req, *sel;
1621252726Srpaulo	int ret;
1622281806Srpaulo	char *pos, *role, *type, *pos2;
1623281806Srpaulo#ifdef CONFIG_P2P
1624281806Srpaulo	char *freq;
1625281806Srpaulo	int forced_freq = 0;
1626252726Srpaulo
1627281806Srpaulo	freq = strstr(cmd, " freq=");
1628281806Srpaulo	if (freq) {
1629281806Srpaulo		*freq = '\0';
1630281806Srpaulo		freq += 6;
1631281806Srpaulo		forced_freq = atoi(freq);
1632281806Srpaulo	}
1633281806Srpaulo#endif /* CONFIG_P2P */
1634281806Srpaulo
1635281806Srpaulo	role = cmd;
1636281806Srpaulo	pos = os_strchr(role, ' ');
1637281806Srpaulo	if (pos == NULL) {
1638281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
1639252726Srpaulo		return -1;
1640281806Srpaulo	}
1641281806Srpaulo	*pos++ = '\0';
1642252726Srpaulo
1643281806Srpaulo	type = pos;
1644281806Srpaulo	pos = os_strchr(type, ' ');
1645281806Srpaulo	if (pos == NULL) {
1646281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
1647252726Srpaulo		return -1;
1648281806Srpaulo	}
1649281806Srpaulo	*pos++ = '\0';
1650281806Srpaulo
1651281806Srpaulo	pos2 = os_strchr(pos, ' ');
1652281806Srpaulo	if (pos2 == NULL) {
1653281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
1654252726Srpaulo		return -1;
1655252726Srpaulo	}
1656281806Srpaulo	*pos2++ = '\0';
1657252726Srpaulo
1658281806Srpaulo	len = os_strlen(pos);
1659281806Srpaulo	if (len & 0x01) {
1660281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
1661281806Srpaulo		return -1;
1662281806Srpaulo	}
1663281806Srpaulo	len /= 2;
1664252726Srpaulo
1665281806Srpaulo	req = wpabuf_alloc(len);
1666281806Srpaulo	if (req == NULL) {
1667281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
1668281806Srpaulo		return -1;
1669281806Srpaulo	}
1670281806Srpaulo	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
1671281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
1672281806Srpaulo		wpabuf_free(req);
1673281806Srpaulo		return -1;
1674281806Srpaulo	}
1675252726Srpaulo
1676281806Srpaulo	len = os_strlen(pos2);
1677281806Srpaulo	if (len & 0x01) {
1678281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
1679281806Srpaulo		wpabuf_free(req);
1680252726Srpaulo		return -1;
1681281806Srpaulo	}
1682252726Srpaulo	len /= 2;
1683252726Srpaulo
1684281806Srpaulo	sel = wpabuf_alloc(len);
1685281806Srpaulo	if (sel == NULL) {
1686281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
1687281806Srpaulo		wpabuf_free(req);
1688252726Srpaulo		return -1;
1689281806Srpaulo	}
1690281806Srpaulo	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
1691281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
1692281806Srpaulo		wpabuf_free(req);
1693281806Srpaulo		wpabuf_free(sel);
1694252726Srpaulo		return -1;
1695252726Srpaulo	}
1696252726Srpaulo
1697281806Srpaulo	wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
1698281806Srpaulo		   role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
1699252726Srpaulo
1700281806Srpaulo	if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
1701281806Srpaulo		ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
1702281806Srpaulo#ifdef CONFIG_AP
1703281806Srpaulo	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
1704281806Srpaulo	{
1705281806Srpaulo		ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
1706281806Srpaulo		if (ret < 0)
1707281806Srpaulo			ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
1708281806Srpaulo#endif /* CONFIG_AP */
1709281806Srpaulo#ifdef CONFIG_P2P
1710281806Srpaulo	} else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
1711281806Srpaulo	{
1712281806Srpaulo		ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
1713281806Srpaulo	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
1714281806Srpaulo	{
1715281806Srpaulo		ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
1716281806Srpaulo						   forced_freq);
1717281806Srpaulo#endif /* CONFIG_P2P */
1718281806Srpaulo	} else {
1719281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
1720281806Srpaulo			   "reported: role=%s type=%s", role, type);
1721281806Srpaulo		ret = -1;
1722281806Srpaulo	}
1723281806Srpaulo	wpabuf_free(req);
1724281806Srpaulo	wpabuf_free(sel);
1725281806Srpaulo
1726281806Srpaulo	if (ret)
1727281806Srpaulo		wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
1728281806Srpaulo
1729252726Srpaulo	return ret;
1730252726Srpaulo}
1731252726Srpaulo
1732252726Srpaulo#endif /* CONFIG_WPS_NFC */
1733252726Srpaulo
1734252726Srpaulo
1735189251Ssamstatic int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
1736189251Ssam					     char *cmd)
1737189251Ssam{
1738252726Srpaulo	u8 bssid[ETH_ALEN];
1739189251Ssam	char *pin;
1740214734Srpaulo	char *new_ssid;
1741214734Srpaulo	char *new_auth;
1742214734Srpaulo	char *new_encr;
1743214734Srpaulo	char *new_key;
1744214734Srpaulo	struct wps_new_ap_settings ap;
1745189251Ssam
1746189251Ssam	pin = os_strchr(cmd, ' ');
1747189251Ssam	if (pin == NULL)
1748189251Ssam		return -1;
1749189251Ssam	*pin++ = '\0';
1750189251Ssam
1751252726Srpaulo	if (hwaddr_aton(cmd, bssid)) {
1752189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
1753189251Ssam			   cmd);
1754189251Ssam		return -1;
1755189251Ssam	}
1756189251Ssam
1757214734Srpaulo	new_ssid = os_strchr(pin, ' ');
1758214734Srpaulo	if (new_ssid == NULL)
1759252726Srpaulo		return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
1760214734Srpaulo	*new_ssid++ = '\0';
1761214734Srpaulo
1762214734Srpaulo	new_auth = os_strchr(new_ssid, ' ');
1763214734Srpaulo	if (new_auth == NULL)
1764214734Srpaulo		return -1;
1765214734Srpaulo	*new_auth++ = '\0';
1766214734Srpaulo
1767214734Srpaulo	new_encr = os_strchr(new_auth, ' ');
1768214734Srpaulo	if (new_encr == NULL)
1769214734Srpaulo		return -1;
1770214734Srpaulo	*new_encr++ = '\0';
1771214734Srpaulo
1772214734Srpaulo	new_key = os_strchr(new_encr, ' ');
1773214734Srpaulo	if (new_key == NULL)
1774214734Srpaulo		return -1;
1775214734Srpaulo	*new_key++ = '\0';
1776214734Srpaulo
1777214734Srpaulo	os_memset(&ap, 0, sizeof(ap));
1778214734Srpaulo	ap.ssid_hex = new_ssid;
1779214734Srpaulo	ap.auth = new_auth;
1780214734Srpaulo	ap.encr = new_encr;
1781214734Srpaulo	ap.key_hex = new_key;
1782252726Srpaulo	return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
1783189251Ssam}
1784214734Srpaulo
1785214734Srpaulo
1786252726Srpaulo#ifdef CONFIG_AP
1787252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
1788252726Srpaulo						char *cmd, char *buf,
1789252726Srpaulo						size_t buflen)
1790252726Srpaulo{
1791252726Srpaulo	int timeout = 300;
1792252726Srpaulo	char *pos;
1793252726Srpaulo	const char *pin_txt;
1794252726Srpaulo
1795252726Srpaulo	if (!wpa_s->ap_iface)
1796252726Srpaulo		return -1;
1797252726Srpaulo
1798252726Srpaulo	pos = os_strchr(cmd, ' ');
1799252726Srpaulo	if (pos)
1800252726Srpaulo		*pos++ = '\0';
1801252726Srpaulo
1802252726Srpaulo	if (os_strcmp(cmd, "disable") == 0) {
1803252726Srpaulo		wpas_wps_ap_pin_disable(wpa_s);
1804252726Srpaulo		return os_snprintf(buf, buflen, "OK\n");
1805252726Srpaulo	}
1806252726Srpaulo
1807252726Srpaulo	if (os_strcmp(cmd, "random") == 0) {
1808252726Srpaulo		if (pos)
1809252726Srpaulo			timeout = atoi(pos);
1810252726Srpaulo		pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
1811252726Srpaulo		if (pin_txt == NULL)
1812252726Srpaulo			return -1;
1813252726Srpaulo		return os_snprintf(buf, buflen, "%s", pin_txt);
1814252726Srpaulo	}
1815252726Srpaulo
1816252726Srpaulo	if (os_strcmp(cmd, "get") == 0) {
1817252726Srpaulo		pin_txt = wpas_wps_ap_pin_get(wpa_s);
1818252726Srpaulo		if (pin_txt == NULL)
1819252726Srpaulo			return -1;
1820252726Srpaulo		return os_snprintf(buf, buflen, "%s", pin_txt);
1821252726Srpaulo	}
1822252726Srpaulo
1823252726Srpaulo	if (os_strcmp(cmd, "set") == 0) {
1824252726Srpaulo		char *pin;
1825252726Srpaulo		if (pos == NULL)
1826252726Srpaulo			return -1;
1827252726Srpaulo		pin = pos;
1828252726Srpaulo		pos = os_strchr(pos, ' ');
1829252726Srpaulo		if (pos) {
1830252726Srpaulo			*pos++ = '\0';
1831252726Srpaulo			timeout = atoi(pos);
1832252726Srpaulo		}
1833252726Srpaulo		if (os_strlen(pin) > buflen)
1834252726Srpaulo			return -1;
1835252726Srpaulo		if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
1836252726Srpaulo			return -1;
1837252726Srpaulo		return os_snprintf(buf, buflen, "%s", pin);
1838252726Srpaulo	}
1839252726Srpaulo
1840252726Srpaulo	return -1;
1841252726Srpaulo}
1842252726Srpaulo#endif /* CONFIG_AP */
1843252726Srpaulo
1844252726Srpaulo
1845214734Srpaulo#ifdef CONFIG_WPS_ER
1846214734Srpaulostatic int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
1847214734Srpaulo						char *cmd)
1848214734Srpaulo{
1849252726Srpaulo	char *uuid = cmd, *pin, *pos;
1850252726Srpaulo	u8 addr_buf[ETH_ALEN], *addr = NULL;
1851214734Srpaulo	pin = os_strchr(uuid, ' ');
1852214734Srpaulo	if (pin == NULL)
1853214734Srpaulo		return -1;
1854214734Srpaulo	*pin++ = '\0';
1855252726Srpaulo	pos = os_strchr(pin, ' ');
1856252726Srpaulo	if (pos) {
1857252726Srpaulo		*pos++ = '\0';
1858252726Srpaulo		if (hwaddr_aton(pos, addr_buf) == 0)
1859252726Srpaulo			addr = addr_buf;
1860252726Srpaulo	}
1861252726Srpaulo	return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
1862214734Srpaulo}
1863214734Srpaulo
1864214734Srpaulo
1865214734Srpaulostatic int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
1866214734Srpaulo						  char *cmd)
1867214734Srpaulo{
1868214734Srpaulo	char *uuid = cmd, *pin;
1869214734Srpaulo	pin = os_strchr(uuid, ' ');
1870214734Srpaulo	if (pin == NULL)
1871214734Srpaulo		return -1;
1872214734Srpaulo	*pin++ = '\0';
1873214734Srpaulo	return wpas_wps_er_learn(wpa_s, uuid, pin);
1874214734Srpaulo}
1875252726Srpaulo
1876252726Srpaulo
1877252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_er_set_config(
1878252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
1879252726Srpaulo{
1880252726Srpaulo	char *uuid = cmd, *id;
1881252726Srpaulo	id = os_strchr(uuid, ' ');
1882252726Srpaulo	if (id == NULL)
1883252726Srpaulo		return -1;
1884252726Srpaulo	*id++ = '\0';
1885252726Srpaulo	return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
1886252726Srpaulo}
1887252726Srpaulo
1888252726Srpaulo
1889252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_er_config(
1890252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
1891252726Srpaulo{
1892252726Srpaulo	char *pin;
1893252726Srpaulo	char *new_ssid;
1894252726Srpaulo	char *new_auth;
1895252726Srpaulo	char *new_encr;
1896252726Srpaulo	char *new_key;
1897252726Srpaulo	struct wps_new_ap_settings ap;
1898252726Srpaulo
1899252726Srpaulo	pin = os_strchr(cmd, ' ');
1900252726Srpaulo	if (pin == NULL)
1901252726Srpaulo		return -1;
1902252726Srpaulo	*pin++ = '\0';
1903252726Srpaulo
1904252726Srpaulo	new_ssid = os_strchr(pin, ' ');
1905252726Srpaulo	if (new_ssid == NULL)
1906252726Srpaulo		return -1;
1907252726Srpaulo	*new_ssid++ = '\0';
1908252726Srpaulo
1909252726Srpaulo	new_auth = os_strchr(new_ssid, ' ');
1910252726Srpaulo	if (new_auth == NULL)
1911252726Srpaulo		return -1;
1912252726Srpaulo	*new_auth++ = '\0';
1913252726Srpaulo
1914252726Srpaulo	new_encr = os_strchr(new_auth, ' ');
1915252726Srpaulo	if (new_encr == NULL)
1916252726Srpaulo		return -1;
1917252726Srpaulo	*new_encr++ = '\0';
1918252726Srpaulo
1919252726Srpaulo	new_key = os_strchr(new_encr, ' ');
1920252726Srpaulo	if (new_key == NULL)
1921252726Srpaulo		return -1;
1922252726Srpaulo	*new_key++ = '\0';
1923252726Srpaulo
1924252726Srpaulo	os_memset(&ap, 0, sizeof(ap));
1925252726Srpaulo	ap.ssid_hex = new_ssid;
1926252726Srpaulo	ap.auth = new_auth;
1927252726Srpaulo	ap.encr = new_encr;
1928252726Srpaulo	ap.key_hex = new_key;
1929252726Srpaulo	return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
1930252726Srpaulo}
1931252726Srpaulo
1932252726Srpaulo
1933252726Srpaulo#ifdef CONFIG_WPS_NFC
1934252726Srpaulostatic int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
1935252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
1936252726Srpaulo{
1937252726Srpaulo	int ndef;
1938252726Srpaulo	struct wpabuf *buf;
1939252726Srpaulo	int res;
1940252726Srpaulo	char *uuid;
1941252726Srpaulo
1942252726Srpaulo	uuid = os_strchr(cmd, ' ');
1943252726Srpaulo	if (uuid == NULL)
1944252726Srpaulo		return -1;
1945252726Srpaulo	*uuid++ = '\0';
1946252726Srpaulo
1947252726Srpaulo	if (os_strcmp(cmd, "WPS") == 0)
1948252726Srpaulo		ndef = 0;
1949252726Srpaulo	else if (os_strcmp(cmd, "NDEF") == 0)
1950252726Srpaulo		ndef = 1;
1951252726Srpaulo	else
1952252726Srpaulo		return -1;
1953252726Srpaulo
1954252726Srpaulo	buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
1955252726Srpaulo	if (buf == NULL)
1956252726Srpaulo		return -1;
1957252726Srpaulo
1958252726Srpaulo	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
1959252726Srpaulo					 wpabuf_len(buf));
1960252726Srpaulo	reply[res++] = '\n';
1961252726Srpaulo	reply[res] = '\0';
1962252726Srpaulo
1963252726Srpaulo	wpabuf_free(buf);
1964252726Srpaulo
1965252726Srpaulo	return res;
1966252726Srpaulo}
1967252726Srpaulo#endif /* CONFIG_WPS_NFC */
1968214734Srpaulo#endif /* CONFIG_WPS_ER */
1969214734Srpaulo
1970189251Ssam#endif /* CONFIG_WPS */
1971189251Ssam
1972189251Ssam
1973214734Srpaulo#ifdef CONFIG_IBSS_RSN
1974214734Srpaulostatic int wpa_supplicant_ctrl_iface_ibss_rsn(
1975214734Srpaulo	struct wpa_supplicant *wpa_s, char *addr)
1976214734Srpaulo{
1977214734Srpaulo	u8 peer[ETH_ALEN];
1978214734Srpaulo
1979214734Srpaulo	if (hwaddr_aton(addr, peer)) {
1980214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
1981214734Srpaulo			   "address '%s'", addr);
1982214734Srpaulo		return -1;
1983214734Srpaulo	}
1984214734Srpaulo
1985214734Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
1986214734Srpaulo		   MAC2STR(peer));
1987214734Srpaulo
1988214734Srpaulo	return ibss_rsn_start(wpa_s->ibss_rsn, peer);
1989214734Srpaulo}
1990214734Srpaulo#endif /* CONFIG_IBSS_RSN */
1991214734Srpaulo
1992214734Srpaulo
1993189251Ssamstatic int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
1994189251Ssam					      char *rsp)
1995189251Ssam{
1996189251Ssam#ifdef IEEE8021X_EAPOL
1997189251Ssam	char *pos, *id_pos;
1998189251Ssam	int id;
1999189251Ssam	struct wpa_ssid *ssid;
2000189251Ssam
2001189251Ssam	pos = os_strchr(rsp, '-');
2002189251Ssam	if (pos == NULL)
2003189251Ssam		return -1;
2004189251Ssam	*pos++ = '\0';
2005189251Ssam	id_pos = pos;
2006189251Ssam	pos = os_strchr(pos, ':');
2007189251Ssam	if (pos == NULL)
2008189251Ssam		return -1;
2009189251Ssam	*pos++ = '\0';
2010189251Ssam	id = atoi(id_pos);
2011189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
2012189251Ssam	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
2013189251Ssam			      (u8 *) pos, os_strlen(pos));
2014189251Ssam
2015189251Ssam	ssid = wpa_config_get_network(wpa_s->conf, id);
2016189251Ssam	if (ssid == NULL) {
2017189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
2018189251Ssam			   "to update", id);
2019189251Ssam		return -1;
2020189251Ssam	}
2021189251Ssam
2022252726Srpaulo	return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
2023252726Srpaulo							 pos);
2024189251Ssam#else /* IEEE8021X_EAPOL */
2025189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
2026189251Ssam	return -1;
2027189251Ssam#endif /* IEEE8021X_EAPOL */
2028189251Ssam}
2029189251Ssam
2030189251Ssam
2031189251Ssamstatic int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
2032189251Ssam					    const char *params,
2033189251Ssam					    char *buf, size_t buflen)
2034189251Ssam{
2035189251Ssam	char *pos, *end, tmp[30];
2036252726Srpaulo	int res, verbose, wps, ret;
2037281806Srpaulo#ifdef CONFIG_HS20
2038281806Srpaulo	const u8 *hs20;
2039281806Srpaulo#endif /* CONFIG_HS20 */
2040281806Srpaulo	const u8 *sess_id;
2041281806Srpaulo	size_t sess_id_len;
2042189251Ssam
2043281806Srpaulo	if (os_strcmp(params, "-DRIVER") == 0)
2044281806Srpaulo		return wpa_drv_status(wpa_s, buf, buflen);
2045189251Ssam	verbose = os_strcmp(params, "-VERBOSE") == 0;
2046252726Srpaulo	wps = os_strcmp(params, "-WPS") == 0;
2047189251Ssam	pos = buf;
2048189251Ssam	end = buf + buflen;
2049189251Ssam	if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
2050189251Ssam		struct wpa_ssid *ssid = wpa_s->current_ssid;
2051189251Ssam		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
2052189251Ssam				  MAC2STR(wpa_s->bssid));
2053281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2054189251Ssam			return pos - buf;
2055189251Ssam		pos += ret;
2056281806Srpaulo		ret = os_snprintf(pos, end - pos, "freq=%u\n",
2057281806Srpaulo				  wpa_s->assoc_freq);
2058281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2059281806Srpaulo			return pos - buf;
2060281806Srpaulo		pos += ret;
2061189251Ssam		if (ssid) {
2062189251Ssam			u8 *_ssid = ssid->ssid;
2063189251Ssam			size_t ssid_len = ssid->ssid_len;
2064289549Srpaulo			u8 ssid_buf[SSID_MAX_LEN];
2065189251Ssam			if (ssid_len == 0) {
2066189251Ssam				int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
2067189251Ssam				if (_res < 0)
2068189251Ssam					ssid_len = 0;
2069189251Ssam				else
2070189251Ssam					ssid_len = _res;
2071189251Ssam				_ssid = ssid_buf;
2072189251Ssam			}
2073189251Ssam			ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
2074189251Ssam					  wpa_ssid_txt(_ssid, ssid_len),
2075189251Ssam					  ssid->id);
2076281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2077189251Ssam				return pos - buf;
2078189251Ssam			pos += ret;
2079189251Ssam
2080252726Srpaulo			if (wps && ssid->passphrase &&
2081252726Srpaulo			    wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
2082252726Srpaulo			    (ssid->mode == WPAS_MODE_AP ||
2083252726Srpaulo			     ssid->mode == WPAS_MODE_P2P_GO)) {
2084252726Srpaulo				ret = os_snprintf(pos, end - pos,
2085252726Srpaulo						  "passphrase=%s\n",
2086252726Srpaulo						  ssid->passphrase);
2087281806Srpaulo				if (os_snprintf_error(end - pos, ret))
2088252726Srpaulo					return pos - buf;
2089252726Srpaulo				pos += ret;
2090252726Srpaulo			}
2091189251Ssam			if (ssid->id_str) {
2092189251Ssam				ret = os_snprintf(pos, end - pos,
2093189251Ssam						  "id_str=%s\n",
2094189251Ssam						  ssid->id_str);
2095281806Srpaulo				if (os_snprintf_error(end - pos, ret))
2096189251Ssam					return pos - buf;
2097189251Ssam				pos += ret;
2098189251Ssam			}
2099214734Srpaulo
2100214734Srpaulo			switch (ssid->mode) {
2101214734Srpaulo			case WPAS_MODE_INFRA:
2102214734Srpaulo				ret = os_snprintf(pos, end - pos,
2103214734Srpaulo						  "mode=station\n");
2104214734Srpaulo				break;
2105214734Srpaulo			case WPAS_MODE_IBSS:
2106214734Srpaulo				ret = os_snprintf(pos, end - pos,
2107214734Srpaulo						  "mode=IBSS\n");
2108214734Srpaulo				break;
2109214734Srpaulo			case WPAS_MODE_AP:
2110214734Srpaulo				ret = os_snprintf(pos, end - pos,
2111214734Srpaulo						  "mode=AP\n");
2112214734Srpaulo				break;
2113252726Srpaulo			case WPAS_MODE_P2P_GO:
2114252726Srpaulo				ret = os_snprintf(pos, end - pos,
2115252726Srpaulo						  "mode=P2P GO\n");
2116252726Srpaulo				break;
2117252726Srpaulo			case WPAS_MODE_P2P_GROUP_FORMATION:
2118252726Srpaulo				ret = os_snprintf(pos, end - pos,
2119252726Srpaulo						  "mode=P2P GO - group "
2120252726Srpaulo						  "formation\n");
2121252726Srpaulo				break;
2122337817Scy			case WPAS_MODE_MESH:
2123337817Scy				ret = os_snprintf(pos, end - pos,
2124337817Scy						  "mode=mesh\n");
2125337817Scy				break;
2126214734Srpaulo			default:
2127214734Srpaulo				ret = 0;
2128214734Srpaulo				break;
2129214734Srpaulo			}
2130281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2131214734Srpaulo				return pos - buf;
2132214734Srpaulo			pos += ret;
2133189251Ssam		}
2134189251Ssam
2135346981Scy		if (wpa_s->connection_set &&
2136346981Scy		    (wpa_s->connection_ht || wpa_s->connection_vht ||
2137346981Scy		     wpa_s->connection_he)) {
2138346981Scy			ret = os_snprintf(pos, end - pos,
2139346981Scy					  "wifi_generation=%u\n",
2140346981Scy					  wpa_s->connection_he ? 6 :
2141346981Scy					  (wpa_s->connection_vht ? 5 : 4));
2142346981Scy			if (os_snprintf_error(end - pos, ret))
2143346981Scy				return pos - buf;
2144346981Scy			pos += ret;
2145346981Scy		}
2146346981Scy
2147214734Srpaulo#ifdef CONFIG_AP
2148214734Srpaulo		if (wpa_s->ap_iface) {
2149214734Srpaulo			pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
2150214734Srpaulo							    end - pos,
2151214734Srpaulo							    verbose);
2152214734Srpaulo		} else
2153214734Srpaulo#endif /* CONFIG_AP */
2154189251Ssam		pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
2155189251Ssam	}
2156346981Scy#ifdef CONFIG_SME
2157281806Srpaulo#ifdef CONFIG_SAE
2158281806Srpaulo	if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
2159281806Srpaulo#ifdef CONFIG_AP
2160281806Srpaulo	    !wpa_s->ap_iface &&
2161281806Srpaulo#endif /* CONFIG_AP */
2162281806Srpaulo	    wpa_s->sme.sae.state == SAE_ACCEPTED) {
2163281806Srpaulo		ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
2164281806Srpaulo				  wpa_s->sme.sae.group);
2165281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2166281806Srpaulo			return pos - buf;
2167281806Srpaulo		pos += ret;
2168281806Srpaulo	}
2169281806Srpaulo#endif /* CONFIG_SAE */
2170346981Scy#endif /* CONFIG_SME */
2171189251Ssam	ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
2172189251Ssam			  wpa_supplicant_state_txt(wpa_s->wpa_state));
2173281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2174189251Ssam		return pos - buf;
2175189251Ssam	pos += ret;
2176189251Ssam
2177189251Ssam	if (wpa_s->l2 &&
2178189251Ssam	    l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
2179189251Ssam		ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
2180281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2181189251Ssam			return pos - buf;
2182189251Ssam		pos += ret;
2183189251Ssam	}
2184189251Ssam
2185252726Srpaulo#ifdef CONFIG_P2P
2186252726Srpaulo	if (wpa_s->global->p2p) {
2187252726Srpaulo		ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
2188252726Srpaulo				  "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
2189281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2190252726Srpaulo			return pos - buf;
2191252726Srpaulo		pos += ret;
2192252726Srpaulo	}
2193252726Srpaulo#endif /* CONFIG_P2P */
2194252726Srpaulo
2195252726Srpaulo	ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
2196252726Srpaulo			  MAC2STR(wpa_s->own_addr));
2197281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2198252726Srpaulo		return pos - buf;
2199252726Srpaulo	pos += ret;
2200252726Srpaulo
2201252726Srpaulo#ifdef CONFIG_HS20
2202252726Srpaulo	if (wpa_s->current_bss &&
2203281806Srpaulo	    (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
2204281806Srpaulo					  HS20_IE_VENDOR_TYPE)) &&
2205252726Srpaulo	    wpa_s->wpa_proto == WPA_PROTO_RSN &&
2206252726Srpaulo	    wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
2207281806Srpaulo		int release = 1;
2208281806Srpaulo		if (hs20[1] >= 5) {
2209281806Srpaulo			u8 rel_num = (hs20[6] & 0xf0) >> 4;
2210281806Srpaulo			release = rel_num + 1;
2211281806Srpaulo		}
2212281806Srpaulo		ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
2213281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2214252726Srpaulo			return pos - buf;
2215252726Srpaulo		pos += ret;
2216252726Srpaulo	}
2217252726Srpaulo
2218252726Srpaulo	if (wpa_s->current_ssid) {
2219252726Srpaulo		struct wpa_cred *cred;
2220252726Srpaulo		char *type;
2221252726Srpaulo
2222252726Srpaulo		for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
2223281806Srpaulo			size_t i;
2224281806Srpaulo
2225252726Srpaulo			if (wpa_s->current_ssid->parent_cred != cred)
2226252726Srpaulo				continue;
2227281806Srpaulo
2228281806Srpaulo			if (cred->provisioning_sp) {
2229281806Srpaulo				ret = os_snprintf(pos, end - pos,
2230281806Srpaulo						  "provisioning_sp=%s\n",
2231281806Srpaulo						  cred->provisioning_sp);
2232281806Srpaulo				if (os_snprintf_error(end - pos, ret))
2233281806Srpaulo					return pos - buf;
2234281806Srpaulo				pos += ret;
2235281806Srpaulo			}
2236281806Srpaulo
2237252726Srpaulo			if (!cred->domain)
2238281806Srpaulo				goto no_domain;
2239252726Srpaulo
2240281806Srpaulo			i = 0;
2241281806Srpaulo			if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
2242281806Srpaulo				struct wpabuf *names =
2243281806Srpaulo					wpa_s->current_bss->anqp->domain_name;
2244281806Srpaulo				for (i = 0; names && i < cred->num_domain; i++)
2245281806Srpaulo				{
2246281806Srpaulo					if (domain_name_list_contains(
2247281806Srpaulo						    names, cred->domain[i], 1))
2248281806Srpaulo						break;
2249281806Srpaulo				}
2250281806Srpaulo				if (i == cred->num_domain)
2251281806Srpaulo					i = 0; /* show first entry by default */
2252281806Srpaulo			}
2253252726Srpaulo			ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
2254281806Srpaulo					  cred->domain[i]);
2255281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2256252726Srpaulo				return pos - buf;
2257252726Srpaulo			pos += ret;
2258252726Srpaulo
2259281806Srpaulo		no_domain:
2260252726Srpaulo			if (wpa_s->current_bss == NULL ||
2261252726Srpaulo			    wpa_s->current_bss->anqp == NULL)
2262252726Srpaulo				res = -1;
2263252726Srpaulo			else
2264252726Srpaulo				res = interworking_home_sp_cred(
2265252726Srpaulo					wpa_s, cred,
2266252726Srpaulo					wpa_s->current_bss->anqp->domain_name);
2267252726Srpaulo			if (res > 0)
2268252726Srpaulo				type = "home";
2269252726Srpaulo			else if (res == 0)
2270252726Srpaulo				type = "roaming";
2271252726Srpaulo			else
2272252726Srpaulo				type = "unknown";
2273252726Srpaulo
2274252726Srpaulo			ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
2275281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2276252726Srpaulo				return pos - buf;
2277252726Srpaulo			pos += ret;
2278252726Srpaulo
2279252726Srpaulo			break;
2280252726Srpaulo		}
2281252726Srpaulo	}
2282252726Srpaulo#endif /* CONFIG_HS20 */
2283252726Srpaulo
2284189251Ssam	if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
2285189251Ssam	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
2286189251Ssam		res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
2287189251Ssam					  verbose);
2288189251Ssam		if (res >= 0)
2289189251Ssam			pos += res;
2290189251Ssam	}
2291189251Ssam
2292346981Scy#ifdef CONFIG_MACSEC
2293346981Scy	res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos);
2294346981Scy	if (res > 0)
2295346981Scy		pos += res;
2296346981Scy#endif /* CONFIG_MACSEC */
2297346981Scy
2298281806Srpaulo	sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
2299281806Srpaulo	if (sess_id) {
2300281806Srpaulo		char *start = pos;
2301281806Srpaulo
2302281806Srpaulo		ret = os_snprintf(pos, end - pos, "eap_session_id=");
2303281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2304281806Srpaulo			return start - buf;
2305281806Srpaulo		pos += ret;
2306281806Srpaulo		ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
2307281806Srpaulo		if (ret <= 0)
2308281806Srpaulo			return start - buf;
2309281806Srpaulo		pos += ret;
2310281806Srpaulo		ret = os_snprintf(pos, end - pos, "\n");
2311281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2312281806Srpaulo			return start - buf;
2313281806Srpaulo		pos += ret;
2314281806Srpaulo	}
2315281806Srpaulo
2316189251Ssam	res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
2317189251Ssam	if (res >= 0)
2318189251Ssam		pos += res;
2319189251Ssam
2320281806Srpaulo#ifdef CONFIG_WPS
2321281806Srpaulo	{
2322281806Srpaulo		char uuid_str[100];
2323281806Srpaulo		uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
2324281806Srpaulo		ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
2325281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2326281806Srpaulo			return pos - buf;
2327281806Srpaulo		pos += ret;
2328281806Srpaulo	}
2329281806Srpaulo#endif /* CONFIG_WPS */
2330281806Srpaulo
2331346981Scy	if (wpa_s->ieee80211ac) {
2332346981Scy		ret = os_snprintf(pos, end - pos, "ieee80211ac=1\n");
2333346981Scy		if (os_snprintf_error(end - pos, ret))
2334346981Scy			return pos - buf;
2335346981Scy		pos += ret;
2336346981Scy	}
2337346981Scy
2338281806Srpaulo#ifdef ANDROID
2339281806Srpaulo	/*
2340281806Srpaulo	 * Allow using the STATUS command with default behavior, say for debug,
2341281806Srpaulo	 * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
2342281806Srpaulo	 * events with STATUS-NO_EVENTS.
2343281806Srpaulo	 */
2344281806Srpaulo	if (os_strcmp(params, "-NO_EVENTS")) {
2345281806Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
2346281806Srpaulo			     "id=%d state=%d BSSID=" MACSTR " SSID=%s",
2347281806Srpaulo			     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
2348281806Srpaulo			     wpa_s->wpa_state,
2349281806Srpaulo			     MAC2STR(wpa_s->bssid),
2350281806Srpaulo			     wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
2351281806Srpaulo			     wpa_ssid_txt(wpa_s->current_ssid->ssid,
2352281806Srpaulo					  wpa_s->current_ssid->ssid_len) : "");
2353281806Srpaulo		if (wpa_s->wpa_state == WPA_COMPLETED) {
2354281806Srpaulo			struct wpa_ssid *ssid = wpa_s->current_ssid;
2355281806Srpaulo			wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
2356281806Srpaulo				     "- connection to " MACSTR
2357281806Srpaulo				     " completed %s [id=%d id_str=%s]",
2358281806Srpaulo				     MAC2STR(wpa_s->bssid), "(auth)",
2359281806Srpaulo				     ssid ? ssid->id : -1,
2360281806Srpaulo				     ssid && ssid->id_str ? ssid->id_str : "");
2361281806Srpaulo		}
2362281806Srpaulo	}
2363281806Srpaulo#endif /* ANDROID */
2364281806Srpaulo
2365189251Ssam	return pos - buf;
2366189251Ssam}
2367189251Ssam
2368189251Ssam
2369189251Ssamstatic int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
2370189251Ssam					   char *cmd)
2371189251Ssam{
2372189251Ssam	char *pos;
2373189251Ssam	int id;
2374189251Ssam	struct wpa_ssid *ssid;
2375189251Ssam	u8 bssid[ETH_ALEN];
2376189251Ssam
2377189251Ssam	/* cmd: "<network id> <BSSID>" */
2378189251Ssam	pos = os_strchr(cmd, ' ');
2379189251Ssam	if (pos == NULL)
2380189251Ssam		return -1;
2381189251Ssam	*pos++ = '\0';
2382189251Ssam	id = atoi(cmd);
2383189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
2384189251Ssam	if (hwaddr_aton(pos, bssid)) {
2385189251Ssam		wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
2386189251Ssam		return -1;
2387189251Ssam	}
2388189251Ssam
2389189251Ssam	ssid = wpa_config_get_network(wpa_s->conf, id);
2390189251Ssam	if (ssid == NULL) {
2391189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
2392189251Ssam			   "to update", id);
2393189251Ssam		return -1;
2394189251Ssam	}
2395189251Ssam
2396189251Ssam	os_memcpy(ssid->bssid, bssid, ETH_ALEN);
2397189251Ssam	ssid->bssid_set = !is_zero_ether_addr(bssid);
2398189251Ssam
2399189251Ssam	return 0;
2400189251Ssam}
2401189251Ssam
2402189251Ssam
2403252726Srpaulostatic int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
2404252726Srpaulo					       char *cmd, char *buf,
2405252726Srpaulo					       size_t buflen)
2406252726Srpaulo{
2407252726Srpaulo	u8 bssid[ETH_ALEN];
2408252726Srpaulo	struct wpa_blacklist *e;
2409252726Srpaulo	char *pos, *end;
2410252726Srpaulo	int ret;
2411252726Srpaulo
2412252726Srpaulo	/* cmd: "BLACKLIST [<BSSID>]" */
2413252726Srpaulo	if (*cmd == '\0') {
2414252726Srpaulo		pos = buf;
2415252726Srpaulo		end = buf + buflen;
2416252726Srpaulo		e = wpa_s->blacklist;
2417252726Srpaulo		while (e) {
2418252726Srpaulo			ret = os_snprintf(pos, end - pos, MACSTR "\n",
2419252726Srpaulo					  MAC2STR(e->bssid));
2420281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2421252726Srpaulo				return pos - buf;
2422252726Srpaulo			pos += ret;
2423252726Srpaulo			e = e->next;
2424252726Srpaulo		}
2425252726Srpaulo		return pos - buf;
2426252726Srpaulo	}
2427252726Srpaulo
2428252726Srpaulo	cmd++;
2429252726Srpaulo	if (os_strncmp(cmd, "clear", 5) == 0) {
2430252726Srpaulo		wpa_blacklist_clear(wpa_s);
2431252726Srpaulo		os_memcpy(buf, "OK\n", 3);
2432252726Srpaulo		return 3;
2433252726Srpaulo	}
2434252726Srpaulo
2435252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
2436252726Srpaulo	if (hwaddr_aton(cmd, bssid)) {
2437252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
2438252726Srpaulo		return -1;
2439252726Srpaulo	}
2440252726Srpaulo
2441252726Srpaulo	/*
2442252726Srpaulo	 * Add the BSSID twice, so its count will be 2, causing it to be
2443252726Srpaulo	 * skipped when processing scan results.
2444252726Srpaulo	 */
2445252726Srpaulo	ret = wpa_blacklist_add(wpa_s, bssid);
2446281806Srpaulo	if (ret < 0)
2447252726Srpaulo		return -1;
2448252726Srpaulo	ret = wpa_blacklist_add(wpa_s, bssid);
2449281806Srpaulo	if (ret < 0)
2450252726Srpaulo		return -1;
2451252726Srpaulo	os_memcpy(buf, "OK\n", 3);
2452252726Srpaulo	return 3;
2453252726Srpaulo}
2454252726Srpaulo
2455252726Srpaulo
2456252726Srpaulostatic int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
2457252726Srpaulo					       char *cmd, char *buf,
2458252726Srpaulo					       size_t buflen)
2459252726Srpaulo{
2460252726Srpaulo	char *pos, *end, *stamp;
2461252726Srpaulo	int ret;
2462252726Srpaulo
2463252726Srpaulo	/* cmd: "LOG_LEVEL [<level>]" */
2464252726Srpaulo	if (*cmd == '\0') {
2465252726Srpaulo		pos = buf;
2466252726Srpaulo		end = buf + buflen;
2467252726Srpaulo		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
2468252726Srpaulo				  "Timestamp: %d\n",
2469252726Srpaulo				  debug_level_str(wpa_debug_level),
2470252726Srpaulo				  wpa_debug_timestamp);
2471281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2472252726Srpaulo			ret = 0;
2473252726Srpaulo
2474252726Srpaulo		return ret;
2475252726Srpaulo	}
2476252726Srpaulo
2477252726Srpaulo	while (*cmd == ' ')
2478252726Srpaulo		cmd++;
2479252726Srpaulo
2480252726Srpaulo	stamp = os_strchr(cmd, ' ');
2481252726Srpaulo	if (stamp) {
2482252726Srpaulo		*stamp++ = '\0';
2483252726Srpaulo		while (*stamp == ' ') {
2484252726Srpaulo			stamp++;
2485252726Srpaulo		}
2486252726Srpaulo	}
2487252726Srpaulo
2488289549Srpaulo	if (os_strlen(cmd)) {
2489252726Srpaulo		int level = str_to_debug_level(cmd);
2490252726Srpaulo		if (level < 0)
2491252726Srpaulo			return -1;
2492252726Srpaulo		wpa_debug_level = level;
2493252726Srpaulo	}
2494252726Srpaulo
2495252726Srpaulo	if (stamp && os_strlen(stamp))
2496252726Srpaulo		wpa_debug_timestamp = atoi(stamp);
2497252726Srpaulo
2498252726Srpaulo	os_memcpy(buf, "OK\n", 3);
2499252726Srpaulo	return 3;
2500252726Srpaulo}
2501252726Srpaulo
2502252726Srpaulo
2503189251Ssamstatic int wpa_supplicant_ctrl_iface_list_networks(
2504281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
2505189251Ssam{
2506281806Srpaulo	char *pos, *end, *prev;
2507189251Ssam	struct wpa_ssid *ssid;
2508189251Ssam	int ret;
2509189251Ssam
2510189251Ssam	pos = buf;
2511189251Ssam	end = buf + buflen;
2512189251Ssam	ret = os_snprintf(pos, end - pos,
2513189251Ssam			  "network id / ssid / bssid / flags\n");
2514281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2515189251Ssam		return pos - buf;
2516189251Ssam	pos += ret;
2517189251Ssam
2518189251Ssam	ssid = wpa_s->conf->ssid;
2519281806Srpaulo
2520281806Srpaulo	/* skip over ssids until we find next one */
2521281806Srpaulo	if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) {
2522281806Srpaulo		int last_id = atoi(cmd + 8);
2523281806Srpaulo		if (last_id != -1) {
2524281806Srpaulo			while (ssid != NULL && ssid->id <= last_id) {
2525281806Srpaulo				ssid = ssid->next;
2526281806Srpaulo			}
2527281806Srpaulo		}
2528281806Srpaulo	}
2529281806Srpaulo
2530189251Ssam	while (ssid) {
2531281806Srpaulo		prev = pos;
2532189251Ssam		ret = os_snprintf(pos, end - pos, "%d\t%s",
2533189251Ssam				  ssid->id,
2534189251Ssam				  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
2535281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2536281806Srpaulo			return prev - buf;
2537189251Ssam		pos += ret;
2538189251Ssam		if (ssid->bssid_set) {
2539189251Ssam			ret = os_snprintf(pos, end - pos, "\t" MACSTR,
2540189251Ssam					  MAC2STR(ssid->bssid));
2541189251Ssam		} else {
2542189251Ssam			ret = os_snprintf(pos, end - pos, "\tany");
2543189251Ssam		}
2544281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2545281806Srpaulo			return prev - buf;
2546189251Ssam		pos += ret;
2547252726Srpaulo		ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
2548189251Ssam				  ssid == wpa_s->current_ssid ?
2549189251Ssam				  "[CURRENT]" : "",
2550252726Srpaulo				  ssid->disabled ? "[DISABLED]" : "",
2551252726Srpaulo				  ssid->disabled_until.sec ?
2552252726Srpaulo				  "[TEMP-DISABLED]" : "",
2553252726Srpaulo				  ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
2554252726Srpaulo				  "");
2555281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2556281806Srpaulo			return prev - buf;
2557189251Ssam		pos += ret;
2558189251Ssam		ret = os_snprintf(pos, end - pos, "\n");
2559281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2560281806Srpaulo			return prev - buf;
2561189251Ssam		pos += ret;
2562189251Ssam
2563189251Ssam		ssid = ssid->next;
2564189251Ssam	}
2565189251Ssam
2566189251Ssam	return pos - buf;
2567189251Ssam}
2568189251Ssam
2569189251Ssam
2570189251Ssamstatic char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
2571189251Ssam{
2572281806Srpaulo	int ret;
2573189251Ssam	ret = os_snprintf(pos, end - pos, "-");
2574281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2575189251Ssam		return pos;
2576189251Ssam	pos += ret;
2577281806Srpaulo	ret = wpa_write_ciphers(pos, end, cipher, "+");
2578281806Srpaulo	if (ret < 0)
2579281806Srpaulo		return pos;
2580281806Srpaulo	pos += ret;
2581189251Ssam	return pos;
2582189251Ssam}
2583189251Ssam
2584189251Ssam
2585189251Ssamstatic char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
2586189251Ssam				    const u8 *ie, size_t ie_len)
2587189251Ssam{
2588189251Ssam	struct wpa_ie_data data;
2589281806Srpaulo	char *start;
2590281806Srpaulo	int ret;
2591189251Ssam
2592189251Ssam	ret = os_snprintf(pos, end - pos, "[%s-", proto);
2593281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2594189251Ssam		return pos;
2595189251Ssam	pos += ret;
2596189251Ssam
2597189251Ssam	if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
2598189251Ssam		ret = os_snprintf(pos, end - pos, "?]");
2599281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2600189251Ssam			return pos;
2601189251Ssam		pos += ret;
2602189251Ssam		return pos;
2603189251Ssam	}
2604189251Ssam
2605281806Srpaulo	start = pos;
2606189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
2607281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sEAP",
2608281806Srpaulo				  pos == start ? "" : "+");
2609281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2610189251Ssam			return pos;
2611189251Ssam		pos += ret;
2612189251Ssam	}
2613189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
2614281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sPSK",
2615281806Srpaulo				  pos == start ? "" : "+");
2616281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2617189251Ssam			return pos;
2618189251Ssam		pos += ret;
2619189251Ssam	}
2620189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
2621281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sNone",
2622281806Srpaulo				  pos == start ? "" : "+");
2623281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2624189251Ssam			return pos;
2625189251Ssam		pos += ret;
2626189251Ssam	}
2627281806Srpaulo	if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
2628281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sSAE",
2629281806Srpaulo				  pos == start ? "" : "+");
2630281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2631281806Srpaulo			return pos;
2632281806Srpaulo		pos += ret;
2633281806Srpaulo	}
2634189251Ssam#ifdef CONFIG_IEEE80211R
2635189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
2636189251Ssam		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
2637281806Srpaulo				  pos == start ? "" : "+");
2638281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2639189251Ssam			return pos;
2640189251Ssam		pos += ret;
2641189251Ssam	}
2642189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
2643189251Ssam		ret = os_snprintf(pos, end - pos, "%sFT/PSK",
2644281806Srpaulo				  pos == start ? "" : "+");
2645281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2646189251Ssam			return pos;
2647189251Ssam		pos += ret;
2648189251Ssam	}
2649281806Srpaulo	if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
2650281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sFT/SAE",
2651281806Srpaulo				  pos == start ? "" : "+");
2652281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2653281806Srpaulo			return pos;
2654281806Srpaulo		pos += ret;
2655281806Srpaulo	}
2656189251Ssam#endif /* CONFIG_IEEE80211R */
2657189251Ssam#ifdef CONFIG_IEEE80211W
2658189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
2659189251Ssam		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
2660281806Srpaulo				  pos == start ? "" : "+");
2661281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2662189251Ssam			return pos;
2663189251Ssam		pos += ret;
2664189251Ssam	}
2665189251Ssam	if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
2666189251Ssam		ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
2667281806Srpaulo				  pos == start ? "" : "+");
2668281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2669189251Ssam			return pos;
2670189251Ssam		pos += ret;
2671189251Ssam	}
2672189251Ssam#endif /* CONFIG_IEEE80211W */
2673189251Ssam
2674281806Srpaulo#ifdef CONFIG_SUITEB
2675281806Srpaulo	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
2676281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
2677281806Srpaulo				  pos == start ? "" : "+");
2678281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2679281806Srpaulo			return pos;
2680281806Srpaulo		pos += ret;
2681281806Srpaulo	}
2682281806Srpaulo#endif /* CONFIG_SUITEB */
2683281806Srpaulo
2684281806Srpaulo#ifdef CONFIG_SUITEB192
2685281806Srpaulo	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
2686281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
2687281806Srpaulo				  pos == start ? "" : "+");
2688281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2689281806Srpaulo			return pos;
2690281806Srpaulo		pos += ret;
2691281806Srpaulo	}
2692281806Srpaulo#endif /* CONFIG_SUITEB192 */
2693281806Srpaulo
2694346981Scy#ifdef CONFIG_FILS
2695346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
2696346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
2697346981Scy				  pos == start ? "" : "+");
2698346981Scy		if (os_snprintf_error(end - pos, ret))
2699346981Scy			return pos;
2700346981Scy		pos += ret;
2701346981Scy	}
2702346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
2703346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
2704346981Scy				  pos == start ? "" : "+");
2705346981Scy		if (os_snprintf_error(end - pos, ret))
2706346981Scy			return pos;
2707346981Scy		pos += ret;
2708346981Scy	}
2709346981Scy#ifdef CONFIG_IEEE80211R
2710346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
2711346981Scy		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
2712346981Scy				  pos == start ? "" : "+");
2713346981Scy		if (os_snprintf_error(end - pos, ret))
2714346981Scy			return pos;
2715346981Scy		pos += ret;
2716346981Scy	}
2717346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
2718346981Scy		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
2719346981Scy				  pos == start ? "" : "+");
2720346981Scy		if (os_snprintf_error(end - pos, ret))
2721346981Scy			return pos;
2722346981Scy		pos += ret;
2723346981Scy	}
2724346981Scy#endif /* CONFIG_IEEE80211R */
2725346981Scy#endif /* CONFIG_FILS */
2726346981Scy
2727346981Scy#ifdef CONFIG_OWE
2728346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_OWE) {
2729346981Scy		ret = os_snprintf(pos, end - pos, "%sOWE",
2730346981Scy				  pos == start ? "" : "+");
2731346981Scy		if (os_snprintf_error(end - pos, ret))
2732346981Scy			return pos;
2733346981Scy		pos += ret;
2734346981Scy	}
2735346981Scy#endif /* CONFIG_OWE */
2736346981Scy
2737346981Scy#ifdef CONFIG_DPP
2738346981Scy	if (data.key_mgmt & WPA_KEY_MGMT_DPP) {
2739346981Scy		ret = os_snprintf(pos, end - pos, "%sDPP",
2740346981Scy				  pos == start ? "" : "+");
2741346981Scy		if (os_snprintf_error(end - pos, ret))
2742346981Scy			return pos;
2743346981Scy		pos += ret;
2744346981Scy	}
2745346981Scy#endif /* CONFIG_DPP */
2746346981Scy
2747289549Srpaulo	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
2748289549Srpaulo		ret = os_snprintf(pos, end - pos, "%sOSEN",
2749289549Srpaulo				  pos == start ? "" : "+");
2750289549Srpaulo		if (os_snprintf_error(end - pos, ret))
2751289549Srpaulo			return pos;
2752289549Srpaulo		pos += ret;
2753289549Srpaulo	}
2754289549Srpaulo
2755189251Ssam	pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
2756189251Ssam
2757189251Ssam	if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
2758189251Ssam		ret = os_snprintf(pos, end - pos, "-preauth");
2759281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2760189251Ssam			return pos;
2761189251Ssam		pos += ret;
2762189251Ssam	}
2763189251Ssam
2764189251Ssam	ret = os_snprintf(pos, end - pos, "]");
2765281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2766189251Ssam		return pos;
2767189251Ssam	pos += ret;
2768189251Ssam
2769189251Ssam	return pos;
2770189251Ssam}
2771189251Ssam
2772214734Srpaulo
2773214734Srpaulo#ifdef CONFIG_WPS
2774252726Srpaulostatic char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
2775252726Srpaulo					    char *pos, char *end,
2776214734Srpaulo					    struct wpabuf *wps_ie)
2777189251Ssam{
2778189251Ssam	int ret;
2779189251Ssam	const char *txt;
2780189251Ssam
2781189251Ssam	if (wps_ie == NULL)
2782189251Ssam		return pos;
2783189251Ssam	if (wps_is_selected_pbc_registrar(wps_ie))
2784189251Ssam		txt = "[WPS-PBC]";
2785252726Srpaulo	else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
2786252726Srpaulo		txt = "[WPS-AUTH]";
2787189251Ssam	else if (wps_is_selected_pin_registrar(wps_ie))
2788189251Ssam		txt = "[WPS-PIN]";
2789189251Ssam	else
2790189251Ssam		txt = "[WPS]";
2791189251Ssam
2792189251Ssam	ret = os_snprintf(pos, end - pos, "%s", txt);
2793281806Srpaulo	if (!os_snprintf_error(end - pos, ret))
2794189251Ssam		pos += ret;
2795189251Ssam	wpabuf_free(wps_ie);
2796214734Srpaulo	return pos;
2797214734Srpaulo}
2798189251Ssam#endif /* CONFIG_WPS */
2799189251Ssam
2800214734Srpaulo
2801252726Srpaulostatic char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
2802252726Srpaulo					char *pos, char *end,
2803214734Srpaulo					const struct wpa_bss *bss)
2804214734Srpaulo{
2805214734Srpaulo#ifdef CONFIG_WPS
2806214734Srpaulo	struct wpabuf *wps_ie;
2807214734Srpaulo	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
2808252726Srpaulo	return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
2809214734Srpaulo#else /* CONFIG_WPS */
2810189251Ssam	return pos;
2811214734Srpaulo#endif /* CONFIG_WPS */
2812189251Ssam}
2813189251Ssam
2814189251Ssam
2815189251Ssam/* Format one result on one text line into a buffer. */
2816189251Ssamstatic int wpa_supplicant_ctrl_iface_scan_result(
2817252726Srpaulo	struct wpa_supplicant *wpa_s,
2818214734Srpaulo	const struct wpa_bss *bss, char *buf, size_t buflen)
2819189251Ssam{
2820189251Ssam	char *pos, *end;
2821189251Ssam	int ret;
2822346981Scy	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
2823189251Ssam
2824281806Srpaulo	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
2825252726Srpaulo	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
2826281806Srpaulo	if (!p2p)
2827281806Srpaulo		p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
2828252726Srpaulo	if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
2829252726Srpaulo	    os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
2830252726Srpaulo	    0)
2831252726Srpaulo		return 0; /* Do not show P2P listen discovery results here */
2832252726Srpaulo
2833189251Ssam	pos = buf;
2834189251Ssam	end = buf + buflen;
2835189251Ssam
2836189251Ssam	ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
2837214734Srpaulo			  MAC2STR(bss->bssid), bss->freq, bss->level);
2838281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2839252726Srpaulo		return -1;
2840189251Ssam	pos += ret;
2841214734Srpaulo	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
2842189251Ssam	if (ie)
2843189251Ssam		pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
2844214734Srpaulo	ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
2845281806Srpaulo	if (ie2) {
2846281806Srpaulo		pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
2847281806Srpaulo					    ie2, 2 + ie2[1]);
2848281806Srpaulo	}
2849289549Srpaulo	osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
2850289549Srpaulo	if (osen_ie)
2851289549Srpaulo		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
2852289549Srpaulo					    osen_ie, 2 + osen_ie[1]);
2853346981Scy	owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
2854346981Scy	if (owe) {
2855346981Scy		ret = os_snprintf(pos, end - pos,
2856346981Scy				  ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
2857346981Scy		if (os_snprintf_error(end - pos, ret))
2858346981Scy			return -1;
2859346981Scy		pos += ret;
2860346981Scy	}
2861252726Srpaulo	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
2862289549Srpaulo	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
2863189251Ssam		ret = os_snprintf(pos, end - pos, "[WEP]");
2864281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2865252726Srpaulo			return -1;
2866189251Ssam		pos += ret;
2867189251Ssam	}
2868281806Srpaulo	if (mesh) {
2869281806Srpaulo		ret = os_snprintf(pos, end - pos, "[MESH]");
2870281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2871252726Srpaulo			return -1;
2872189251Ssam		pos += ret;
2873189251Ssam	}
2874281806Srpaulo	if (bss_is_dmg(bss)) {
2875281806Srpaulo		const char *s;
2876281806Srpaulo		ret = os_snprintf(pos, end - pos, "[DMG]");
2877281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2878252726Srpaulo			return -1;
2879214734Srpaulo		pos += ret;
2880281806Srpaulo		switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
2881281806Srpaulo		case IEEE80211_CAP_DMG_IBSS:
2882281806Srpaulo			s = "[IBSS]";
2883281806Srpaulo			break;
2884281806Srpaulo		case IEEE80211_CAP_DMG_AP:
2885281806Srpaulo			s = "[ESS]";
2886281806Srpaulo			break;
2887281806Srpaulo		case IEEE80211_CAP_DMG_PBSS:
2888281806Srpaulo			s = "[PBSS]";
2889281806Srpaulo			break;
2890281806Srpaulo		default:
2891281806Srpaulo			s = "";
2892281806Srpaulo			break;
2893281806Srpaulo		}
2894281806Srpaulo		ret = os_snprintf(pos, end - pos, "%s", s);
2895281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2896281806Srpaulo			return -1;
2897281806Srpaulo		pos += ret;
2898281806Srpaulo	} else {
2899281806Srpaulo		if (bss->caps & IEEE80211_CAP_IBSS) {
2900281806Srpaulo			ret = os_snprintf(pos, end - pos, "[IBSS]");
2901281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2902281806Srpaulo				return -1;
2903281806Srpaulo			pos += ret;
2904281806Srpaulo		}
2905281806Srpaulo		if (bss->caps & IEEE80211_CAP_ESS) {
2906281806Srpaulo			ret = os_snprintf(pos, end - pos, "[ESS]");
2907281806Srpaulo			if (os_snprintf_error(end - pos, ret))
2908281806Srpaulo				return -1;
2909281806Srpaulo			pos += ret;
2910281806Srpaulo		}
2911214734Srpaulo	}
2912252726Srpaulo	if (p2p) {
2913252726Srpaulo		ret = os_snprintf(pos, end - pos, "[P2P]");
2914281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2915252726Srpaulo			return -1;
2916252726Srpaulo		pos += ret;
2917252726Srpaulo	}
2918252726Srpaulo#ifdef CONFIG_HS20
2919252726Srpaulo	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
2920252726Srpaulo		ret = os_snprintf(pos, end - pos, "[HS20]");
2921281806Srpaulo		if (os_snprintf_error(end - pos, ret))
2922252726Srpaulo			return -1;
2923252726Srpaulo		pos += ret;
2924252726Srpaulo	}
2925252726Srpaulo#endif /* CONFIG_HS20 */
2926346981Scy#ifdef CONFIG_FILS
2927346981Scy	if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
2928346981Scy		ret = os_snprintf(pos, end - pos, "[FILS]");
2929346981Scy		if (os_snprintf_error(end - pos, ret))
2930346981Scy			return -1;
2931346981Scy		pos += ret;
2932346981Scy	}
2933346981Scy#endif /* CONFIG_FILS */
2934289549Srpaulo#ifdef CONFIG_FST
2935289549Srpaulo	if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
2936289549Srpaulo		ret = os_snprintf(pos, end - pos, "[FST]");
2937289549Srpaulo		if (os_snprintf_error(end - pos, ret))
2938289549Srpaulo			return -1;
2939289549Srpaulo		pos += ret;
2940289549Srpaulo	}
2941289549Srpaulo#endif /* CONFIG_FST */
2942346981Scy	if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) {
2943346981Scy		ret = os_snprintf(pos, end - pos, "[UTF-8]");
2944346981Scy		if (os_snprintf_error(end - pos, ret))
2945346981Scy			return -1;
2946346981Scy		pos += ret;
2947346981Scy	}
2948189251Ssam
2949189251Ssam	ret = os_snprintf(pos, end - pos, "\t%s",
2950214734Srpaulo			  wpa_ssid_txt(bss->ssid, bss->ssid_len));
2951281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2952252726Srpaulo		return -1;
2953189251Ssam	pos += ret;
2954189251Ssam
2955189251Ssam	ret = os_snprintf(pos, end - pos, "\n");
2956281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2957252726Srpaulo		return -1;
2958189251Ssam	pos += ret;
2959189251Ssam
2960189251Ssam	return pos - buf;
2961189251Ssam}
2962189251Ssam
2963189251Ssam
2964189251Ssamstatic int wpa_supplicant_ctrl_iface_scan_results(
2965189251Ssam	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
2966189251Ssam{
2967189251Ssam	char *pos, *end;
2968214734Srpaulo	struct wpa_bss *bss;
2969189251Ssam	int ret;
2970189251Ssam
2971189251Ssam	pos = buf;
2972189251Ssam	end = buf + buflen;
2973189251Ssam	ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
2974189251Ssam			  "flags / ssid\n");
2975281806Srpaulo	if (os_snprintf_error(end - pos, ret))
2976189251Ssam		return pos - buf;
2977189251Ssam	pos += ret;
2978189251Ssam
2979214734Srpaulo	dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
2980252726Srpaulo		ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
2981189251Ssam							    end - pos);
2982189251Ssam		if (ret < 0 || ret >= end - pos)
2983189251Ssam			return pos - buf;
2984189251Ssam		pos += ret;
2985189251Ssam	}
2986189251Ssam
2987189251Ssam	return pos - buf;
2988189251Ssam}
2989189251Ssam
2990189251Ssam
2991281806Srpaulo#ifdef CONFIG_MESH
2992281806Srpaulo
2993281806Srpaulostatic int wpa_supplicant_ctrl_iface_mesh_interface_add(
2994281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
2995281806Srpaulo{
2996281806Srpaulo	char *pos, ifname[IFNAMSIZ + 1];
2997281806Srpaulo
2998281806Srpaulo	ifname[0] = '\0';
2999281806Srpaulo
3000281806Srpaulo	pos = os_strstr(cmd, "ifname=");
3001281806Srpaulo	if (pos) {
3002281806Srpaulo		pos += 7;
3003281806Srpaulo		os_strlcpy(ifname, pos, sizeof(ifname));
3004281806Srpaulo	}
3005281806Srpaulo
3006281806Srpaulo	if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
3007281806Srpaulo		return -1;
3008281806Srpaulo
3009281806Srpaulo	os_strlcpy(reply, ifname, max_len);
3010281806Srpaulo	return os_strlen(ifname);
3011281806Srpaulo}
3012281806Srpaulo
3013281806Srpaulo
3014281806Srpaulostatic int wpa_supplicant_ctrl_iface_mesh_group_add(
3015281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
3016281806Srpaulo{
3017281806Srpaulo	int id;
3018281806Srpaulo	struct wpa_ssid *ssid;
3019281806Srpaulo
3020281806Srpaulo	id = atoi(cmd);
3021281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
3022281806Srpaulo
3023281806Srpaulo	ssid = wpa_config_get_network(wpa_s->conf, id);
3024281806Srpaulo	if (ssid == NULL) {
3025281806Srpaulo		wpa_printf(MSG_DEBUG,
3026281806Srpaulo			   "CTRL_IFACE: Could not find network id=%d", id);
3027281806Srpaulo		return -1;
3028281806Srpaulo	}
3029281806Srpaulo	if (ssid->mode != WPAS_MODE_MESH) {
3030281806Srpaulo		wpa_printf(MSG_DEBUG,
3031281806Srpaulo			   "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
3032281806Srpaulo		return -1;
3033281806Srpaulo	}
3034281806Srpaulo	if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
3035281806Srpaulo	    ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
3036281806Srpaulo		wpa_printf(MSG_ERROR,
3037281806Srpaulo			   "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
3038281806Srpaulo		return -1;
3039281806Srpaulo	}
3040281806Srpaulo
3041281806Srpaulo	/*
3042281806Srpaulo	 * TODO: If necessary write our own group_add function,
3043281806Srpaulo	 * for now we can reuse select_network
3044281806Srpaulo	 */
3045281806Srpaulo	wpa_supplicant_select_network(wpa_s, ssid);
3046281806Srpaulo
3047281806Srpaulo	return 0;
3048281806Srpaulo}
3049281806Srpaulo
3050281806Srpaulo
3051281806Srpaulostatic int wpa_supplicant_ctrl_iface_mesh_group_remove(
3052281806Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
3053281806Srpaulo{
3054281806Srpaulo	struct wpa_supplicant *orig;
3055281806Srpaulo	struct wpa_global *global;
3056281806Srpaulo	int found = 0;
3057281806Srpaulo
3058281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
3059281806Srpaulo
3060281806Srpaulo	global = wpa_s->global;
3061281806Srpaulo	orig = wpa_s;
3062281806Srpaulo
3063281806Srpaulo	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
3064281806Srpaulo		if (os_strcmp(wpa_s->ifname, cmd) == 0) {
3065281806Srpaulo			found = 1;
3066281806Srpaulo			break;
3067281806Srpaulo		}
3068281806Srpaulo	}
3069281806Srpaulo	if (!found) {
3070281806Srpaulo		wpa_printf(MSG_ERROR,
3071281806Srpaulo			   "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
3072281806Srpaulo			   cmd);
3073281806Srpaulo		return -1;
3074281806Srpaulo	}
3075281806Srpaulo	if (wpa_s->mesh_if_created && wpa_s == orig) {
3076281806Srpaulo		wpa_printf(MSG_ERROR,
3077281806Srpaulo			   "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
3078281806Srpaulo		return -1;
3079281806Srpaulo	}
3080281806Srpaulo
3081281806Srpaulo	wpa_s->reassociate = 0;
3082281806Srpaulo	wpa_s->disconnected = 1;
3083281806Srpaulo	wpa_supplicant_cancel_sched_scan(wpa_s);
3084281806Srpaulo	wpa_supplicant_cancel_scan(wpa_s);
3085281806Srpaulo
3086281806Srpaulo	/*
3087281806Srpaulo	 * TODO: If necessary write our own group_remove function,
3088281806Srpaulo	 * for now we can reuse deauthenticate
3089281806Srpaulo	 */
3090281806Srpaulo	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
3091281806Srpaulo
3092281806Srpaulo	if (wpa_s->mesh_if_created)
3093281806Srpaulo		wpa_supplicant_remove_iface(global, wpa_s, 0);
3094281806Srpaulo
3095281806Srpaulo	return 0;
3096281806Srpaulo}
3097281806Srpaulo
3098337817Scy
3099337817Scystatic int wpa_supplicant_ctrl_iface_mesh_peer_remove(
3100337817Scy	struct wpa_supplicant *wpa_s, char *cmd)
3101337817Scy{
3102337817Scy	u8 addr[ETH_ALEN];
3103337817Scy
3104337817Scy	if (hwaddr_aton(cmd, addr) < 0)
3105337817Scy		return -1;
3106337817Scy
3107337817Scy	return wpas_mesh_peer_remove(wpa_s, addr);
3108337817Scy}
3109337817Scy
3110337817Scy
3111337817Scystatic int wpa_supplicant_ctrl_iface_mesh_peer_add(
3112337817Scy	struct wpa_supplicant *wpa_s, char *cmd)
3113337817Scy{
3114337817Scy	u8 addr[ETH_ALEN];
3115337817Scy	int duration;
3116337817Scy	char *pos;
3117337817Scy
3118337817Scy	pos = os_strstr(cmd, " duration=");
3119337817Scy	if (pos) {
3120337817Scy		*pos = '\0';
3121337817Scy		duration = atoi(pos + 10);
3122337817Scy	} else {
3123337817Scy		duration = -1;
3124337817Scy	}
3125337817Scy
3126337817Scy	if (hwaddr_aton(cmd, addr))
3127337817Scy		return -1;
3128337817Scy
3129337817Scy	return wpas_mesh_peer_add(wpa_s, addr, duration);
3130337817Scy}
3131337817Scy
3132351611Scy
3133351611Scystatic int wpa_supplicant_ctrl_iface_mesh_link_probe(
3134351611Scy	struct wpa_supplicant *wpa_s, char *cmd)
3135351611Scy{
3136351611Scy	struct ether_header *eth;
3137351611Scy	u8 addr[ETH_ALEN];
3138351611Scy	u8 *buf;
3139351611Scy	char *pos;
3140351611Scy	size_t payload_len = 0, len;
3141351611Scy	int ret = -1;
3142351611Scy
3143351611Scy	if (hwaddr_aton(cmd, addr))
3144351611Scy		return -1;
3145351611Scy
3146351611Scy	pos = os_strstr(cmd, " payload=");
3147351611Scy	if (pos) {
3148351611Scy		pos = pos + 9;
3149351611Scy		payload_len = os_strlen(pos);
3150351611Scy		if (payload_len & 1)
3151351611Scy			return -1;
3152351611Scy
3153351611Scy		payload_len /= 2;
3154351611Scy	}
3155351611Scy
3156351611Scy	len = ETH_HLEN + payload_len;
3157351611Scy	buf = os_malloc(len);
3158351611Scy	if (!buf)
3159351611Scy		return -1;
3160351611Scy
3161351611Scy	eth = (struct ether_header *) buf;
3162351611Scy	os_memcpy(eth->ether_dhost, addr, ETH_ALEN);
3163351611Scy	os_memcpy(eth->ether_shost, wpa_s->own_addr, ETH_ALEN);
3164351611Scy	eth->ether_type = htons(ETH_P_802_3);
3165351611Scy
3166351611Scy	if (payload_len && hexstr2bin(pos, buf + ETH_HLEN, payload_len) < 0)
3167351611Scy		goto fail;
3168351611Scy
3169351611Scy	ret = wpa_drv_mesh_link_probe(wpa_s, addr, buf, len);
3170351611Scyfail:
3171351611Scy	os_free(buf);
3172351611Scy	return -ret;
3173351611Scy}
3174351611Scy
3175281806Srpaulo#endif /* CONFIG_MESH */
3176281806Srpaulo
3177281806Srpaulo
3178189251Ssamstatic int wpa_supplicant_ctrl_iface_select_network(
3179189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
3180189251Ssam{
3181189251Ssam	int id;
3182189251Ssam	struct wpa_ssid *ssid;
3183281806Srpaulo	char *pos;
3184189251Ssam
3185189251Ssam	/* cmd: "<network id>" or "any" */
3186281806Srpaulo	if (os_strncmp(cmd, "any", 3) == 0) {
3187189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
3188214734Srpaulo		ssid = NULL;
3189214734Srpaulo	} else {
3190214734Srpaulo		id = atoi(cmd);
3191214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id);
3192214734Srpaulo
3193214734Srpaulo		ssid = wpa_config_get_network(wpa_s->conf, id);
3194214734Srpaulo		if (ssid == NULL) {
3195214734Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
3196214734Srpaulo				   "network id=%d", id);
3197214734Srpaulo			return -1;
3198189251Ssam		}
3199252726Srpaulo		if (ssid->disabled == 2) {
3200252726Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
3201252726Srpaulo				   "SELECT_NETWORK with persistent P2P group");
3202252726Srpaulo			return -1;
3203252726Srpaulo		}
3204189251Ssam	}
3205189251Ssam
3206281806Srpaulo	pos = os_strstr(cmd, " freq=");
3207281806Srpaulo	if (pos) {
3208281806Srpaulo		int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
3209281806Srpaulo		if (freqs) {
3210346981Scy			os_free(wpa_s->select_network_scan_freqs);
3211346981Scy			wpa_s->select_network_scan_freqs = freqs;
3212281806Srpaulo		}
3213281806Srpaulo	}
3214281806Srpaulo
3215289549Srpaulo	wpa_s->scan_min_time.sec = 0;
3216289549Srpaulo	wpa_s->scan_min_time.usec = 0;
3217214734Srpaulo	wpa_supplicant_select_network(wpa_s, ssid);
3218189251Ssam
3219189251Ssam	return 0;
3220189251Ssam}
3221189251Ssam
3222189251Ssam
3223189251Ssamstatic int wpa_supplicant_ctrl_iface_enable_network(
3224189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
3225189251Ssam{
3226189251Ssam	int id;
3227189251Ssam	struct wpa_ssid *ssid;
3228189251Ssam
3229189251Ssam	/* cmd: "<network id>" or "all" */
3230189251Ssam	if (os_strcmp(cmd, "all") == 0) {
3231189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
3232214734Srpaulo		ssid = NULL;
3233214734Srpaulo	} else {
3234214734Srpaulo		id = atoi(cmd);
3235214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id);
3236214734Srpaulo
3237214734Srpaulo		ssid = wpa_config_get_network(wpa_s->conf, id);
3238214734Srpaulo		if (ssid == NULL) {
3239214734Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
3240214734Srpaulo				   "network id=%d", id);
3241214734Srpaulo			return -1;
3242189251Ssam		}
3243252726Srpaulo		if (ssid->disabled == 2) {
3244252726Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
3245252726Srpaulo				   "ENABLE_NETWORK with persistent P2P group");
3246252726Srpaulo			return -1;
3247252726Srpaulo		}
3248252726Srpaulo
3249252726Srpaulo		if (os_strstr(cmd, " no-connect")) {
3250252726Srpaulo			ssid->disabled = 0;
3251252726Srpaulo			return 0;
3252252726Srpaulo		}
3253189251Ssam	}
3254289549Srpaulo	wpa_s->scan_min_time.sec = 0;
3255289549Srpaulo	wpa_s->scan_min_time.usec = 0;
3256214734Srpaulo	wpa_supplicant_enable_network(wpa_s, ssid);
3257189251Ssam
3258189251Ssam	return 0;
3259189251Ssam}
3260189251Ssam
3261189251Ssam
3262189251Ssamstatic int wpa_supplicant_ctrl_iface_disable_network(
3263189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
3264189251Ssam{
3265189251Ssam	int id;
3266189251Ssam	struct wpa_ssid *ssid;
3267189251Ssam
3268189251Ssam	/* cmd: "<network id>" or "all" */
3269189251Ssam	if (os_strcmp(cmd, "all") == 0) {
3270189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
3271214734Srpaulo		ssid = NULL;
3272214734Srpaulo	} else {
3273214734Srpaulo		id = atoi(cmd);
3274214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id);
3275214734Srpaulo
3276214734Srpaulo		ssid = wpa_config_get_network(wpa_s->conf, id);
3277214734Srpaulo		if (ssid == NULL) {
3278214734Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
3279214734Srpaulo				   "network id=%d", id);
3280214734Srpaulo			return -1;
3281189251Ssam		}
3282252726Srpaulo		if (ssid->disabled == 2) {
3283252726Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
3284252726Srpaulo				   "DISABLE_NETWORK with persistent P2P "
3285252726Srpaulo				   "group");
3286252726Srpaulo			return -1;
3287252726Srpaulo		}
3288189251Ssam	}
3289214734Srpaulo	wpa_supplicant_disable_network(wpa_s, ssid);
3290189251Ssam
3291189251Ssam	return 0;
3292189251Ssam}
3293189251Ssam
3294189251Ssam
3295189251Ssamstatic int wpa_supplicant_ctrl_iface_add_network(
3296189251Ssam	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
3297189251Ssam{
3298189251Ssam	struct wpa_ssid *ssid;
3299189251Ssam	int ret;
3300189251Ssam
3301189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
3302189251Ssam
3303337817Scy	ssid = wpa_supplicant_add_network(wpa_s);
3304189251Ssam	if (ssid == NULL)
3305189251Ssam		return -1;
3306214734Srpaulo
3307189251Ssam	ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
3308281806Srpaulo	if (os_snprintf_error(buflen, ret))
3309189251Ssam		return -1;
3310189251Ssam	return ret;
3311189251Ssam}
3312189251Ssam
3313189251Ssam
3314189251Ssamstatic int wpa_supplicant_ctrl_iface_remove_network(
3315189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
3316189251Ssam{
3317189251Ssam	int id;
3318189251Ssam	struct wpa_ssid *ssid;
3319337817Scy	int result;
3320189251Ssam
3321189251Ssam	/* cmd: "<network id>" or "all" */
3322189251Ssam	if (os_strcmp(cmd, "all") == 0) {
3323189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
3324281806Srpaulo		if (wpa_s->sched_scanning)
3325281806Srpaulo			wpa_supplicant_cancel_sched_scan(wpa_s);
3326281806Srpaulo
3327252726Srpaulo		eapol_sm_invalidate_cached_session(wpa_s->eapol);
3328189251Ssam		if (wpa_s->current_ssid) {
3329252726Srpaulo#ifdef CONFIG_SME
3330252726Srpaulo			wpa_s->sme.prev_bssid_set = 0;
3331252726Srpaulo#endif /* CONFIG_SME */
3332252726Srpaulo			wpa_sm_set_config(wpa_s->wpa, NULL);
3333252726Srpaulo			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
3334289549Srpaulo			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
3335289549Srpaulo				wpa_s->own_disconnect_req = 1;
3336252726Srpaulo			wpa_supplicant_deauthenticate(
3337252726Srpaulo				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
3338189251Ssam		}
3339281806Srpaulo		ssid = wpa_s->conf->ssid;
3340281806Srpaulo		while (ssid) {
3341281806Srpaulo			struct wpa_ssid *remove_ssid = ssid;
3342281806Srpaulo			id = ssid->id;
3343281806Srpaulo			ssid = ssid->next;
3344281806Srpaulo			if (wpa_s->last_ssid == remove_ssid)
3345281806Srpaulo				wpa_s->last_ssid = NULL;
3346281806Srpaulo			wpas_notify_network_removed(wpa_s, remove_ssid);
3347281806Srpaulo			wpa_config_remove_network(wpa_s->conf, id);
3348281806Srpaulo		}
3349189251Ssam		return 0;
3350189251Ssam	}
3351189251Ssam
3352189251Ssam	id = atoi(cmd);
3353189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
3354189251Ssam
3355337817Scy	result = wpa_supplicant_remove_network(wpa_s, id);
3356337817Scy	if (result == -1) {
3357189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
3358189251Ssam			   "id=%d", id);
3359189251Ssam		return -1;
3360189251Ssam	}
3361337817Scy	if (result == -2) {
3362252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
3363252726Srpaulo			   "network id=%d", id);
3364252726Srpaulo		return -1;
3365252726Srpaulo	}
3366189251Ssam	return 0;
3367189251Ssam}
3368189251Ssam
3369189251Ssam
3370281806Srpaulostatic int wpa_supplicant_ctrl_iface_update_network(
3371281806Srpaulo	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
3372281806Srpaulo	char *name, char *value)
3373281806Srpaulo{
3374337817Scy	int ret;
3375337817Scy
3376337817Scy	ret = wpa_config_set(ssid, name, value, 0);
3377337817Scy	if (ret < 0) {
3378281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
3379281806Srpaulo			   "variable '%s'", name);
3380281806Srpaulo		return -1;
3381281806Srpaulo	}
3382337817Scy	if (ret == 1)
3383337817Scy		return 0; /* No change to the previously configured value */
3384281806Srpaulo
3385281806Srpaulo	if (os_strcmp(name, "bssid") != 0 &&
3386346981Scy	    os_strcmp(name, "bssid_hint") != 0 &&
3387337817Scy	    os_strcmp(name, "priority") != 0) {
3388281806Srpaulo		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
3389281806Srpaulo
3390337817Scy		if (wpa_s->current_ssid == ssid ||
3391337817Scy		    wpa_s->current_ssid == NULL) {
3392337817Scy			/*
3393337817Scy			 * Invalidate the EAP session cache if anything in the
3394337817Scy			 * current or previously used configuration changes.
3395337817Scy			 */
3396337817Scy			eapol_sm_invalidate_cached_session(wpa_s->eapol);
3397337817Scy		}
3398281806Srpaulo	}
3399281806Srpaulo
3400281806Srpaulo	if ((os_strcmp(name, "psk") == 0 &&
3401281806Srpaulo	     value[0] == '"' && ssid->ssid_len) ||
3402281806Srpaulo	    (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
3403281806Srpaulo		wpa_config_update_psk(ssid);
3404281806Srpaulo	else if (os_strcmp(name, "priority") == 0)
3405281806Srpaulo		wpa_config_update_prio_list(wpa_s->conf);
3406281806Srpaulo
3407281806Srpaulo	return 0;
3408281806Srpaulo}
3409281806Srpaulo
3410281806Srpaulo
3411189251Ssamstatic int wpa_supplicant_ctrl_iface_set_network(
3412189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
3413189251Ssam{
3414281806Srpaulo	int id, ret, prev_bssid_set, prev_disabled;
3415189251Ssam	struct wpa_ssid *ssid;
3416189251Ssam	char *name, *value;
3417281806Srpaulo	u8 prev_bssid[ETH_ALEN];
3418189251Ssam
3419189251Ssam	/* cmd: "<network id> <variable name> <value>" */
3420189251Ssam	name = os_strchr(cmd, ' ');
3421189251Ssam	if (name == NULL)
3422189251Ssam		return -1;
3423189251Ssam	*name++ = '\0';
3424189251Ssam
3425189251Ssam	value = os_strchr(name, ' ');
3426189251Ssam	if (value == NULL)
3427189251Ssam		return -1;
3428189251Ssam	*value++ = '\0';
3429189251Ssam
3430189251Ssam	id = atoi(cmd);
3431189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
3432189251Ssam		   id, name);
3433189251Ssam	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
3434189251Ssam			      (u8 *) value, os_strlen(value));
3435189251Ssam
3436189251Ssam	ssid = wpa_config_get_network(wpa_s->conf, id);
3437189251Ssam	if (ssid == NULL) {
3438189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
3439189251Ssam			   "id=%d", id);
3440189251Ssam		return -1;
3441189251Ssam	}
3442189251Ssam
3443281806Srpaulo	prev_bssid_set = ssid->bssid_set;
3444281806Srpaulo	prev_disabled = ssid->disabled;
3445281806Srpaulo	os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
3446281806Srpaulo	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
3447281806Srpaulo						       value);
3448281806Srpaulo	if (ret == 0 &&
3449281806Srpaulo	    (ssid->bssid_set != prev_bssid_set ||
3450281806Srpaulo	     os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
3451281806Srpaulo		wpas_notify_network_bssid_set_changed(wpa_s, ssid);
3452189251Ssam
3453281806Srpaulo	if (prev_disabled != ssid->disabled &&
3454281806Srpaulo	    (prev_disabled == 2 || ssid->disabled == 2))
3455281806Srpaulo		wpas_notify_network_type_changed(wpa_s, ssid);
3456252726Srpaulo
3457281806Srpaulo	return ret;
3458189251Ssam}
3459189251Ssam
3460189251Ssam
3461189251Ssamstatic int wpa_supplicant_ctrl_iface_get_network(
3462189251Ssam	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
3463189251Ssam{
3464189251Ssam	int id;
3465189251Ssam	size_t res;
3466189251Ssam	struct wpa_ssid *ssid;
3467189251Ssam	char *name, *value;
3468189251Ssam
3469189251Ssam	/* cmd: "<network id> <variable name>" */
3470189251Ssam	name = os_strchr(cmd, ' ');
3471189251Ssam	if (name == NULL || buflen == 0)
3472189251Ssam		return -1;
3473189251Ssam	*name++ = '\0';
3474189251Ssam
3475189251Ssam	id = atoi(cmd);
3476289549Srpaulo	wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
3477189251Ssam		   id, name);
3478189251Ssam
3479189251Ssam	ssid = wpa_config_get_network(wpa_s->conf, id);
3480189251Ssam	if (ssid == NULL) {
3481289549Srpaulo		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
3482189251Ssam			   "id=%d", id);
3483189251Ssam		return -1;
3484189251Ssam	}
3485189251Ssam
3486189251Ssam	value = wpa_config_get_no_key(ssid, name);
3487189251Ssam	if (value == NULL) {
3488289549Srpaulo		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
3489189251Ssam			   "variable '%s'", name);
3490189251Ssam		return -1;
3491189251Ssam	}
3492189251Ssam
3493189251Ssam	res = os_strlcpy(buf, value, buflen);
3494189251Ssam	if (res >= buflen) {
3495189251Ssam		os_free(value);
3496189251Ssam		return -1;
3497189251Ssam	}
3498189251Ssam
3499189251Ssam	os_free(value);
3500189251Ssam
3501189251Ssam	return res;
3502189251Ssam}
3503189251Ssam
3504189251Ssam
3505281806Srpaulostatic int wpa_supplicant_ctrl_iface_dup_network(
3506289549Srpaulo	struct wpa_supplicant *wpa_s, char *cmd,
3507289549Srpaulo	struct wpa_supplicant *dst_wpa_s)
3508281806Srpaulo{
3509281806Srpaulo	struct wpa_ssid *ssid_s, *ssid_d;
3510281806Srpaulo	char *name, *id, *value;
3511281806Srpaulo	int id_s, id_d, ret;
3512281806Srpaulo
3513281806Srpaulo	/* cmd: "<src network id> <dst network id> <variable name>" */
3514281806Srpaulo	id = os_strchr(cmd, ' ');
3515281806Srpaulo	if (id == NULL)
3516281806Srpaulo		return -1;
3517281806Srpaulo	*id++ = '\0';
3518281806Srpaulo
3519281806Srpaulo	name = os_strchr(id, ' ');
3520281806Srpaulo	if (name == NULL)
3521281806Srpaulo		return -1;
3522281806Srpaulo	*name++ = '\0';
3523281806Srpaulo
3524281806Srpaulo	id_s = atoi(cmd);
3525281806Srpaulo	id_d = atoi(id);
3526281806Srpaulo
3527289549Srpaulo	wpa_printf(MSG_DEBUG,
3528289549Srpaulo		   "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
3529289549Srpaulo		   wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
3530289549Srpaulo
3531281806Srpaulo	ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
3532281806Srpaulo	if (ssid_s == NULL) {
3533281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
3534281806Srpaulo			   "network id=%d", id_s);
3535281806Srpaulo		return -1;
3536281806Srpaulo	}
3537281806Srpaulo
3538289549Srpaulo	ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
3539281806Srpaulo	if (ssid_d == NULL) {
3540281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
3541281806Srpaulo			   "network id=%d", id_d);
3542281806Srpaulo		return -1;
3543281806Srpaulo	}
3544281806Srpaulo
3545281806Srpaulo	value = wpa_config_get(ssid_s, name);
3546281806Srpaulo	if (value == NULL) {
3547281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
3548281806Srpaulo			   "variable '%s'", name);
3549281806Srpaulo		return -1;
3550281806Srpaulo	}
3551281806Srpaulo
3552289549Srpaulo	ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
3553281806Srpaulo						       value);
3554281806Srpaulo
3555281806Srpaulo	os_free(value);
3556281806Srpaulo
3557281806Srpaulo	return ret;
3558281806Srpaulo}
3559281806Srpaulo
3560281806Srpaulo
3561252726Srpaulostatic int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
3562252726Srpaulo						char *buf, size_t buflen)
3563252726Srpaulo{
3564252726Srpaulo	char *pos, *end;
3565252726Srpaulo	struct wpa_cred *cred;
3566252726Srpaulo	int ret;
3567252726Srpaulo
3568252726Srpaulo	pos = buf;
3569252726Srpaulo	end = buf + buflen;
3570252726Srpaulo	ret = os_snprintf(pos, end - pos,
3571252726Srpaulo			  "cred id / realm / username / domain / imsi\n");
3572281806Srpaulo	if (os_snprintf_error(end - pos, ret))
3573252726Srpaulo		return pos - buf;
3574252726Srpaulo	pos += ret;
3575252726Srpaulo
3576252726Srpaulo	cred = wpa_s->conf->cred;
3577252726Srpaulo	while (cred) {
3578252726Srpaulo		ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
3579252726Srpaulo				  cred->id, cred->realm ? cred->realm : "",
3580252726Srpaulo				  cred->username ? cred->username : "",
3581281806Srpaulo				  cred->domain ? cred->domain[0] : "",
3582252726Srpaulo				  cred->imsi ? cred->imsi : "");
3583281806Srpaulo		if (os_snprintf_error(end - pos, ret))
3584252726Srpaulo			return pos - buf;
3585252726Srpaulo		pos += ret;
3586252726Srpaulo
3587252726Srpaulo		cred = cred->next;
3588252726Srpaulo	}
3589252726Srpaulo
3590252726Srpaulo	return pos - buf;
3591252726Srpaulo}
3592252726Srpaulo
3593252726Srpaulo
3594252726Srpaulostatic int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
3595252726Srpaulo					      char *buf, size_t buflen)
3596252726Srpaulo{
3597252726Srpaulo	struct wpa_cred *cred;
3598252726Srpaulo	int ret;
3599252726Srpaulo
3600252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
3601252726Srpaulo
3602252726Srpaulo	cred = wpa_config_add_cred(wpa_s->conf);
3603252726Srpaulo	if (cred == NULL)
3604252726Srpaulo		return -1;
3605252726Srpaulo
3606281806Srpaulo	wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
3607281806Srpaulo
3608252726Srpaulo	ret = os_snprintf(buf, buflen, "%d\n", cred->id);
3609281806Srpaulo	if (os_snprintf_error(buflen, ret))
3610252726Srpaulo		return -1;
3611252726Srpaulo	return ret;
3612252726Srpaulo}
3613252726Srpaulo
3614252726Srpaulo
3615252726Srpaulostatic int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
3616252726Srpaulo				 struct wpa_cred *cred)
3617252726Srpaulo{
3618252726Srpaulo	struct wpa_ssid *ssid;
3619252726Srpaulo	char str[20];
3620281806Srpaulo	int id;
3621252726Srpaulo
3622281806Srpaulo	if (cred == NULL) {
3623252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
3624252726Srpaulo		return -1;
3625252726Srpaulo	}
3626252726Srpaulo
3627281806Srpaulo	id = cred->id;
3628281806Srpaulo	if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
3629281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
3630281806Srpaulo		return -1;
3631281806Srpaulo	}
3632281806Srpaulo
3633281806Srpaulo	wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
3634281806Srpaulo
3635252726Srpaulo	/* Remove any network entry created based on the removed credential */
3636252726Srpaulo	ssid = wpa_s->conf->ssid;
3637252726Srpaulo	while (ssid) {
3638252726Srpaulo		if (ssid->parent_cred == cred) {
3639281806Srpaulo			int res;
3640281806Srpaulo
3641252726Srpaulo			wpa_printf(MSG_DEBUG, "Remove network id %d since it "
3642252726Srpaulo				   "used the removed credential", ssid->id);
3643281806Srpaulo			res = os_snprintf(str, sizeof(str), "%d", ssid->id);
3644281806Srpaulo			if (os_snprintf_error(sizeof(str), res))
3645281806Srpaulo				str[sizeof(str) - 1] = '\0';
3646252726Srpaulo			ssid = ssid->next;
3647252726Srpaulo			wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
3648252726Srpaulo		} else
3649252726Srpaulo			ssid = ssid->next;
3650252726Srpaulo	}
3651252726Srpaulo
3652252726Srpaulo	return 0;
3653252726Srpaulo}
3654252726Srpaulo
3655252726Srpaulo
3656252726Srpaulostatic int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
3657252726Srpaulo						 char *cmd)
3658252726Srpaulo{
3659252726Srpaulo	int id;
3660252726Srpaulo	struct wpa_cred *cred, *prev;
3661252726Srpaulo
3662281806Srpaulo	/* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
3663281806Srpaulo	 * "provisioning_sp=<FQDN> */
3664252726Srpaulo	if (os_strcmp(cmd, "all") == 0) {
3665252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
3666252726Srpaulo		cred = wpa_s->conf->cred;
3667252726Srpaulo		while (cred) {
3668252726Srpaulo			prev = cred;
3669252726Srpaulo			cred = cred->next;
3670252726Srpaulo			wpas_ctrl_remove_cred(wpa_s, prev);
3671252726Srpaulo		}
3672252726Srpaulo		return 0;
3673252726Srpaulo	}
3674252726Srpaulo
3675252726Srpaulo	if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
3676252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
3677252726Srpaulo			   cmd + 8);
3678252726Srpaulo		cred = wpa_s->conf->cred;
3679252726Srpaulo		while (cred) {
3680252726Srpaulo			prev = cred;
3681252726Srpaulo			cred = cred->next;
3682281806Srpaulo			if (prev->domain) {
3683281806Srpaulo				size_t i;
3684281806Srpaulo				for (i = 0; i < prev->num_domain; i++) {
3685281806Srpaulo					if (os_strcmp(prev->domain[i], cmd + 8)
3686281806Srpaulo					    != 0)
3687281806Srpaulo						continue;
3688281806Srpaulo					wpas_ctrl_remove_cred(wpa_s, prev);
3689281806Srpaulo					break;
3690281806Srpaulo				}
3691281806Srpaulo			}
3692281806Srpaulo		}
3693281806Srpaulo		return 0;
3694281806Srpaulo	}
3695281806Srpaulo
3696281806Srpaulo	if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
3697281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
3698281806Srpaulo			   cmd + 16);
3699281806Srpaulo		cred = wpa_s->conf->cred;
3700281806Srpaulo		while (cred) {
3701281806Srpaulo			prev = cred;
3702281806Srpaulo			cred = cred->next;
3703281806Srpaulo			if (prev->provisioning_sp &&
3704281806Srpaulo			    os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
3705252726Srpaulo				wpas_ctrl_remove_cred(wpa_s, prev);
3706252726Srpaulo		}
3707252726Srpaulo		return 0;
3708252726Srpaulo	}
3709252726Srpaulo
3710252726Srpaulo	id = atoi(cmd);
3711252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
3712252726Srpaulo
3713252726Srpaulo	cred = wpa_config_get_cred(wpa_s->conf, id);
3714252726Srpaulo	return wpas_ctrl_remove_cred(wpa_s, cred);
3715252726Srpaulo}
3716252726Srpaulo
3717252726Srpaulo
3718252726Srpaulostatic int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
3719252726Srpaulo					      char *cmd)
3720252726Srpaulo{
3721252726Srpaulo	int id;
3722252726Srpaulo	struct wpa_cred *cred;
3723252726Srpaulo	char *name, *value;
3724252726Srpaulo
3725252726Srpaulo	/* cmd: "<cred id> <variable name> <value>" */
3726252726Srpaulo	name = os_strchr(cmd, ' ');
3727252726Srpaulo	if (name == NULL)
3728252726Srpaulo		return -1;
3729252726Srpaulo	*name++ = '\0';
3730252726Srpaulo
3731252726Srpaulo	value = os_strchr(name, ' ');
3732252726Srpaulo	if (value == NULL)
3733252726Srpaulo		return -1;
3734252726Srpaulo	*value++ = '\0';
3735252726Srpaulo
3736252726Srpaulo	id = atoi(cmd);
3737252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
3738252726Srpaulo		   id, name);
3739252726Srpaulo	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
3740252726Srpaulo			      (u8 *) value, os_strlen(value));
3741252726Srpaulo
3742252726Srpaulo	cred = wpa_config_get_cred(wpa_s->conf, id);
3743252726Srpaulo	if (cred == NULL) {
3744252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
3745252726Srpaulo			   id);
3746252726Srpaulo		return -1;
3747252726Srpaulo	}
3748252726Srpaulo
3749252726Srpaulo	if (wpa_config_set_cred(cred, name, value, 0) < 0) {
3750252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
3751252726Srpaulo			   "variable '%s'", name);
3752252726Srpaulo		return -1;
3753252726Srpaulo	}
3754252726Srpaulo
3755281806Srpaulo	wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
3756281806Srpaulo
3757252726Srpaulo	return 0;
3758252726Srpaulo}
3759252726Srpaulo
3760252726Srpaulo
3761281806Srpaulostatic int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
3762281806Srpaulo					      char *cmd, char *buf,
3763281806Srpaulo					      size_t buflen)
3764281806Srpaulo{
3765281806Srpaulo	int id;
3766281806Srpaulo	size_t res;
3767281806Srpaulo	struct wpa_cred *cred;
3768281806Srpaulo	char *name, *value;
3769281806Srpaulo
3770281806Srpaulo	/* cmd: "<cred id> <variable name>" */
3771281806Srpaulo	name = os_strchr(cmd, ' ');
3772281806Srpaulo	if (name == NULL)
3773281806Srpaulo		return -1;
3774281806Srpaulo	*name++ = '\0';
3775281806Srpaulo
3776281806Srpaulo	id = atoi(cmd);
3777281806Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
3778281806Srpaulo		   id, name);
3779281806Srpaulo
3780281806Srpaulo	cred = wpa_config_get_cred(wpa_s->conf, id);
3781281806Srpaulo	if (cred == NULL) {
3782281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
3783281806Srpaulo			   id);
3784281806Srpaulo		return -1;
3785281806Srpaulo	}
3786281806Srpaulo
3787281806Srpaulo	value = wpa_config_get_cred_no_key(cred, name);
3788281806Srpaulo	if (value == NULL) {
3789281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
3790281806Srpaulo			   name);
3791281806Srpaulo		return -1;
3792281806Srpaulo	}
3793281806Srpaulo
3794281806Srpaulo	res = os_strlcpy(buf, value, buflen);
3795281806Srpaulo	if (res >= buflen) {
3796281806Srpaulo		os_free(value);
3797281806Srpaulo		return -1;
3798281806Srpaulo	}
3799281806Srpaulo
3800281806Srpaulo	os_free(value);
3801281806Srpaulo
3802281806Srpaulo	return res;
3803281806Srpaulo}
3804281806Srpaulo
3805281806Srpaulo
3806189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE
3807189251Ssamstatic int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
3808189251Ssam{
3809189251Ssam	int ret;
3810189251Ssam
3811189251Ssam	if (!wpa_s->conf->update_config) {
3812189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
3813189251Ssam			   "to update configuration (update_config=0)");
3814189251Ssam		return -1;
3815189251Ssam	}
3816189251Ssam
3817189251Ssam	ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
3818189251Ssam	if (ret) {
3819189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
3820189251Ssam			   "update configuration");
3821189251Ssam	} else {
3822189251Ssam		wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
3823189251Ssam			   " updated");
3824189251Ssam	}
3825189251Ssam
3826189251Ssam	return ret;
3827189251Ssam}
3828189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */
3829189251Ssam
3830189251Ssam
3831281806Srpaulostruct cipher_info {
3832281806Srpaulo	unsigned int capa;
3833281806Srpaulo	const char *name;
3834281806Srpaulo	int group_only;
3835281806Srpaulo};
3836281806Srpaulo
3837281806Srpaulostatic const struct cipher_info ciphers[] = {
3838281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
3839281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
3840281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
3841281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
3842281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
3843281806Srpaulo	{ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
3844281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
3845281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
3846281806Srpaulo};
3847281806Srpaulo
3848281806Srpaulostatic const struct cipher_info ciphers_group_mgmt[] = {
3849281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
3850281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
3851281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
3852281806Srpaulo	{ WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
3853281806Srpaulo};
3854281806Srpaulo
3855281806Srpaulo
3856189251Ssamstatic int ctrl_iface_get_capability_pairwise(int res, char *strict,
3857189251Ssam					      struct wpa_driver_capa *capa,
3858189251Ssam					      char *buf, size_t buflen)
3859189251Ssam{
3860281806Srpaulo	int ret;
3861189251Ssam	char *pos, *end;
3862189251Ssam	size_t len;
3863281806Srpaulo	unsigned int i;
3864189251Ssam
3865189251Ssam	pos = buf;
3866189251Ssam	end = pos + buflen;
3867189251Ssam
3868189251Ssam	if (res < 0) {
3869189251Ssam		if (strict)
3870189251Ssam			return 0;
3871189251Ssam		len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
3872189251Ssam		if (len >= buflen)
3873189251Ssam			return -1;
3874189251Ssam		return len;
3875189251Ssam	}
3876189251Ssam
3877281806Srpaulo	for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
3878281806Srpaulo		if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
3879281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s",
3880281806Srpaulo					  pos == buf ? "" : " ",
3881281806Srpaulo					  ciphers[i].name);
3882281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3883281806Srpaulo				return pos - buf;
3884281806Srpaulo			pos += ret;
3885281806Srpaulo		}
3886189251Ssam	}
3887189251Ssam
3888189251Ssam	return pos - buf;
3889189251Ssam}
3890189251Ssam
3891189251Ssam
3892189251Ssamstatic int ctrl_iface_get_capability_group(int res, char *strict,
3893189251Ssam					   struct wpa_driver_capa *capa,
3894189251Ssam					   char *buf, size_t buflen)
3895189251Ssam{
3896281806Srpaulo	int ret;
3897189251Ssam	char *pos, *end;
3898189251Ssam	size_t len;
3899281806Srpaulo	unsigned int i;
3900189251Ssam
3901189251Ssam	pos = buf;
3902189251Ssam	end = pos + buflen;
3903189251Ssam
3904189251Ssam	if (res < 0) {
3905189251Ssam		if (strict)
3906189251Ssam			return 0;
3907189251Ssam		len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
3908189251Ssam		if (len >= buflen)
3909189251Ssam			return -1;
3910189251Ssam		return len;
3911189251Ssam	}
3912189251Ssam
3913281806Srpaulo	for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
3914281806Srpaulo		if (capa->enc & ciphers[i].capa) {
3915281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s",
3916281806Srpaulo					  pos == buf ? "" : " ",
3917281806Srpaulo					  ciphers[i].name);
3918281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3919281806Srpaulo				return pos - buf;
3920281806Srpaulo			pos += ret;
3921281806Srpaulo		}
3922189251Ssam	}
3923189251Ssam
3924281806Srpaulo	return pos - buf;
3925281806Srpaulo}
3926252726Srpaulo
3927189251Ssam
3928281806Srpaulostatic int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
3929281806Srpaulo						struct wpa_driver_capa *capa,
3930281806Srpaulo						char *buf, size_t buflen)
3931281806Srpaulo{
3932281806Srpaulo	int ret;
3933281806Srpaulo	char *pos, *end;
3934281806Srpaulo	unsigned int i;
3935189251Ssam
3936281806Srpaulo	pos = buf;
3937281806Srpaulo	end = pos + buflen;
3938281806Srpaulo
3939281806Srpaulo	if (res < 0)
3940281806Srpaulo		return 0;
3941281806Srpaulo
3942281806Srpaulo	for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
3943281806Srpaulo		if (capa->enc & ciphers_group_mgmt[i].capa) {
3944281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s%s",
3945281806Srpaulo					  pos == buf ? "" : " ",
3946281806Srpaulo					  ciphers_group_mgmt[i].name);
3947281806Srpaulo			if (os_snprintf_error(end - pos, ret))
3948281806Srpaulo				return pos - buf;
3949281806Srpaulo			pos += ret;
3950281806Srpaulo		}
3951189251Ssam	}
3952189251Ssam
3953189251Ssam	return pos - buf;
3954189251Ssam}
3955189251Ssam
3956189251Ssam
3957189251Ssamstatic int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
3958189251Ssam					      struct wpa_driver_capa *capa,
3959189251Ssam					      char *buf, size_t buflen)
3960189251Ssam{
3961189251Ssam	int ret;
3962189251Ssam	char *pos, *end;
3963189251Ssam	size_t len;
3964189251Ssam
3965189251Ssam	pos = buf;
3966189251Ssam	end = pos + buflen;
3967189251Ssam
3968189251Ssam	if (res < 0) {
3969189251Ssam		if (strict)
3970189251Ssam			return 0;
3971189251Ssam		len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
3972189251Ssam				 "NONE", buflen);
3973189251Ssam		if (len >= buflen)
3974189251Ssam			return -1;
3975189251Ssam		return len;
3976189251Ssam	}
3977189251Ssam
3978189251Ssam	ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
3979281806Srpaulo	if (os_snprintf_error(end - pos, ret))
3980189251Ssam		return pos - buf;
3981189251Ssam	pos += ret;
3982189251Ssam
3983189251Ssam	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
3984189251Ssam			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
3985189251Ssam		ret = os_snprintf(pos, end - pos, " WPA-EAP");
3986281806Srpaulo		if (os_snprintf_error(end - pos, ret))
3987189251Ssam			return pos - buf;
3988189251Ssam		pos += ret;
3989189251Ssam	}
3990189251Ssam
3991189251Ssam	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
3992189251Ssam			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
3993189251Ssam		ret = os_snprintf(pos, end - pos, " WPA-PSK");
3994281806Srpaulo		if (os_snprintf_error(end - pos, ret))
3995189251Ssam			return pos - buf;
3996189251Ssam		pos += ret;
3997189251Ssam	}
3998189251Ssam
3999189251Ssam	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
4000189251Ssam		ret = os_snprintf(pos, end - pos, " WPA-NONE");
4001281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4002189251Ssam			return pos - buf;
4003189251Ssam		pos += ret;
4004189251Ssam	}
4005189251Ssam
4006281806Srpaulo#ifdef CONFIG_SUITEB
4007281806Srpaulo	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
4008281806Srpaulo		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
4009281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4010281806Srpaulo			return pos - buf;
4011281806Srpaulo		pos += ret;
4012281806Srpaulo	}
4013281806Srpaulo#endif /* CONFIG_SUITEB */
4014281806Srpaulo#ifdef CONFIG_SUITEB192
4015281806Srpaulo	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
4016281806Srpaulo		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
4017281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4018281806Srpaulo			return pos - buf;
4019281806Srpaulo		pos += ret;
4020281806Srpaulo	}
4021281806Srpaulo#endif /* CONFIG_SUITEB192 */
4022346981Scy#ifdef CONFIG_OWE
4023346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
4024346981Scy		ret = os_snprintf(pos, end - pos, " OWE");
4025346981Scy		if (os_snprintf_error(end - pos, ret))
4026346981Scy			return pos - buf;
4027346981Scy		pos += ret;
4028346981Scy	}
4029346981Scy#endif /* CONFIG_OWE */
4030346981Scy#ifdef CONFIG_DPP
4031346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
4032346981Scy		ret = os_snprintf(pos, end - pos, " DPP");
4033346981Scy		if (os_snprintf_error(end - pos, ret))
4034346981Scy			return pos - buf;
4035346981Scy		pos += ret;
4036346981Scy	}
4037346981Scy#endif /* CONFIG_DPP */
4038346981Scy#ifdef CONFIG_FILS
4039346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
4040346981Scy		ret = os_snprintf(pos, end - pos, " FILS-SHA256");
4041346981Scy		if (os_snprintf_error(end - pos, ret))
4042346981Scy			return pos - buf;
4043346981Scy		pos += ret;
4044346981Scy	}
4045346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
4046346981Scy		ret = os_snprintf(pos, end - pos, " FILS-SHA384");
4047346981Scy		if (os_snprintf_error(end - pos, ret))
4048346981Scy			return pos - buf;
4049346981Scy		pos += ret;
4050346981Scy	}
4051346981Scy#ifdef CONFIG_IEEE80211R
4052346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
4053346981Scy		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
4054346981Scy		if (os_snprintf_error(end - pos, ret))
4055346981Scy			return pos - buf;
4056346981Scy		pos += ret;
4057346981Scy	}
4058346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
4059346981Scy		ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
4060346981Scy		if (os_snprintf_error(end - pos, ret))
4061346981Scy			return pos - buf;
4062346981Scy		pos += ret;
4063346981Scy	}
4064346981Scy#endif /* CONFIG_IEEE80211R */
4065346981Scy#endif /* CONFIG_FILS */
4066346981Scy#ifdef CONFIG_IEEE80211R
4067346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
4068346981Scy		ret = os_snprintf(pos, end - pos, " FT-PSK");
4069346981Scy		if (os_snprintf_error(end - pos, ret))
4070346981Scy			return pos - buf;
4071346981Scy		pos += ret;
4072346981Scy	}
4073346981Scy#endif /* CONFIG_IEEE80211R */
4074346981Scy#ifdef CONFIG_SAE
4075346981Scy	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
4076346981Scy		ret = os_snprintf(pos, end - pos, " SAE");
4077346981Scy		if (os_snprintf_error(end - pos, ret))
4078346981Scy			return pos - buf;
4079346981Scy		pos += ret;
4080346981Scy	}
4081346981Scy#endif /* CONFIG_SAE */
4082281806Srpaulo
4083189251Ssam	return pos - buf;
4084189251Ssam}
4085189251Ssam
4086189251Ssam
4087189251Ssamstatic int ctrl_iface_get_capability_proto(int res, char *strict,
4088189251Ssam					   struct wpa_driver_capa *capa,
4089189251Ssam					   char *buf, size_t buflen)
4090189251Ssam{
4091281806Srpaulo	int ret;
4092189251Ssam	char *pos, *end;
4093189251Ssam	size_t len;
4094189251Ssam
4095189251Ssam	pos = buf;
4096189251Ssam	end = pos + buflen;
4097189251Ssam
4098189251Ssam	if (res < 0) {
4099189251Ssam		if (strict)
4100189251Ssam			return 0;
4101189251Ssam		len = os_strlcpy(buf, "RSN WPA", buflen);
4102189251Ssam		if (len >= buflen)
4103189251Ssam			return -1;
4104189251Ssam		return len;
4105189251Ssam	}
4106189251Ssam
4107189251Ssam	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
4108189251Ssam			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
4109281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sRSN",
4110281806Srpaulo				  pos == buf ? "" : " ");
4111281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4112189251Ssam			return pos - buf;
4113189251Ssam		pos += ret;
4114189251Ssam	}
4115189251Ssam
4116189251Ssam	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
4117189251Ssam			      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
4118281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sWPA",
4119281806Srpaulo				  pos == buf ? "" : " ");
4120281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4121189251Ssam			return pos - buf;
4122189251Ssam		pos += ret;
4123189251Ssam	}
4124189251Ssam
4125189251Ssam	return pos - buf;
4126189251Ssam}
4127189251Ssam
4128189251Ssam
4129281806Srpaulostatic int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
4130281806Srpaulo					      int res, char *strict,
4131189251Ssam					      struct wpa_driver_capa *capa,
4132189251Ssam					      char *buf, size_t buflen)
4133189251Ssam{
4134281806Srpaulo	int ret;
4135189251Ssam	char *pos, *end;
4136189251Ssam	size_t len;
4137189251Ssam
4138189251Ssam	pos = buf;
4139189251Ssam	end = pos + buflen;
4140189251Ssam
4141189251Ssam	if (res < 0) {
4142189251Ssam		if (strict)
4143189251Ssam			return 0;
4144189251Ssam		len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
4145189251Ssam		if (len >= buflen)
4146189251Ssam			return -1;
4147189251Ssam		return len;
4148189251Ssam	}
4149189251Ssam
4150189251Ssam	if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
4151281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sOPEN",
4152281806Srpaulo				  pos == buf ? "" : " ");
4153281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4154189251Ssam			return pos - buf;
4155189251Ssam		pos += ret;
4156189251Ssam	}
4157189251Ssam
4158189251Ssam	if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
4159189251Ssam		ret = os_snprintf(pos, end - pos, "%sSHARED",
4160281806Srpaulo				  pos == buf ? "" : " ");
4161281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4162189251Ssam			return pos - buf;
4163189251Ssam		pos += ret;
4164189251Ssam	}
4165189251Ssam
4166189251Ssam	if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
4167281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sLEAP",
4168281806Srpaulo				  pos == buf ? "" : " ");
4169281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4170189251Ssam			return pos - buf;
4171189251Ssam		pos += ret;
4172189251Ssam	}
4173189251Ssam
4174281806Srpaulo#ifdef CONFIG_SAE
4175281806Srpaulo	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
4176281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sSAE",
4177281806Srpaulo				  pos == buf ? "" : " ");
4178281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4179281806Srpaulo			return pos - buf;
4180281806Srpaulo		pos += ret;
4181281806Srpaulo	}
4182281806Srpaulo#endif /* CONFIG_SAE */
4183281806Srpaulo
4184346981Scy#ifdef CONFIG_FILS
4185346981Scy	if (wpa_is_fils_supported(wpa_s)) {
4186346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS",
4187346981Scy				  pos == buf ? "" : " ");
4188346981Scy		if (os_snprintf_error(end - pos, ret))
4189346981Scy			return pos - buf;
4190346981Scy		pos += ret;
4191346981Scy	}
4192346981Scy
4193346981Scy#ifdef CONFIG_FILS_SK_PFS
4194346981Scy	if (wpa_is_fils_sk_pfs_supported(wpa_s)) {
4195346981Scy		ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS",
4196346981Scy				  pos == buf ? "" : " ");
4197346981Scy		if (os_snprintf_error(end - pos, ret))
4198346981Scy			return pos - buf;
4199346981Scy		pos += ret;
4200346981Scy	}
4201346981Scy#endif /* CONFIG_FILS_SK_PFS */
4202346981Scy#endif /* CONFIG_FILS */
4203346981Scy
4204189251Ssam	return pos - buf;
4205189251Ssam}
4206189251Ssam
4207189251Ssam
4208281806Srpaulostatic int ctrl_iface_get_capability_modes(int res, char *strict,
4209281806Srpaulo					   struct wpa_driver_capa *capa,
4210281806Srpaulo					   char *buf, size_t buflen)
4211281806Srpaulo{
4212281806Srpaulo	int ret;
4213281806Srpaulo	char *pos, *end;
4214281806Srpaulo	size_t len;
4215281806Srpaulo
4216281806Srpaulo	pos = buf;
4217281806Srpaulo	end = pos + buflen;
4218281806Srpaulo
4219281806Srpaulo	if (res < 0) {
4220281806Srpaulo		if (strict)
4221281806Srpaulo			return 0;
4222281806Srpaulo		len = os_strlcpy(buf, "IBSS AP", buflen);
4223281806Srpaulo		if (len >= buflen)
4224281806Srpaulo			return -1;
4225281806Srpaulo		return len;
4226281806Srpaulo	}
4227281806Srpaulo
4228281806Srpaulo	if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
4229281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sIBSS",
4230281806Srpaulo				  pos == buf ? "" : " ");
4231281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4232281806Srpaulo			return pos - buf;
4233281806Srpaulo		pos += ret;
4234281806Srpaulo	}
4235281806Srpaulo
4236281806Srpaulo	if (capa->flags & WPA_DRIVER_FLAGS_AP) {
4237281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sAP",
4238281806Srpaulo				  pos == buf ? "" : " ");
4239281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4240281806Srpaulo			return pos - buf;
4241281806Srpaulo		pos += ret;
4242281806Srpaulo	}
4243281806Srpaulo
4244281806Srpaulo#ifdef CONFIG_MESH
4245281806Srpaulo	if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
4246281806Srpaulo		ret = os_snprintf(pos, end - pos, "%sMESH",
4247281806Srpaulo				  pos == buf ? "" : " ");
4248281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4249281806Srpaulo			return pos - buf;
4250281806Srpaulo		pos += ret;
4251281806Srpaulo	}
4252281806Srpaulo#endif /* CONFIG_MESH */
4253281806Srpaulo
4254281806Srpaulo	return pos - buf;
4255281806Srpaulo}
4256281806Srpaulo
4257281806Srpaulo
4258252726Srpaulostatic int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
4259252726Srpaulo					      char *buf, size_t buflen)
4260252726Srpaulo{
4261252726Srpaulo	struct hostapd_channel_data *chnl;
4262252726Srpaulo	int ret, i, j;
4263252726Srpaulo	char *pos, *end, *hmode;
4264252726Srpaulo
4265252726Srpaulo	pos = buf;
4266252726Srpaulo	end = pos + buflen;
4267252726Srpaulo
4268252726Srpaulo	for (j = 0; j < wpa_s->hw.num_modes; j++) {
4269252726Srpaulo		switch (wpa_s->hw.modes[j].mode) {
4270252726Srpaulo		case HOSTAPD_MODE_IEEE80211B:
4271252726Srpaulo			hmode = "B";
4272252726Srpaulo			break;
4273252726Srpaulo		case HOSTAPD_MODE_IEEE80211G:
4274252726Srpaulo			hmode = "G";
4275252726Srpaulo			break;
4276252726Srpaulo		case HOSTAPD_MODE_IEEE80211A:
4277252726Srpaulo			hmode = "A";
4278252726Srpaulo			break;
4279252726Srpaulo		case HOSTAPD_MODE_IEEE80211AD:
4280252726Srpaulo			hmode = "AD";
4281252726Srpaulo			break;
4282252726Srpaulo		default:
4283252726Srpaulo			continue;
4284252726Srpaulo		}
4285252726Srpaulo		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
4286281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4287252726Srpaulo			return pos - buf;
4288252726Srpaulo		pos += ret;
4289252726Srpaulo		chnl = wpa_s->hw.modes[j].channels;
4290252726Srpaulo		for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
4291252726Srpaulo			if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
4292252726Srpaulo				continue;
4293252726Srpaulo			ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
4294281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4295252726Srpaulo				return pos - buf;
4296252726Srpaulo			pos += ret;
4297252726Srpaulo		}
4298252726Srpaulo		ret = os_snprintf(pos, end - pos, "\n");
4299281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4300252726Srpaulo			return pos - buf;
4301252726Srpaulo		pos += ret;
4302252726Srpaulo	}
4303252726Srpaulo
4304252726Srpaulo	return pos - buf;
4305252726Srpaulo}
4306252726Srpaulo
4307252726Srpaulo
4308281806Srpaulostatic int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
4309281806Srpaulo					  char *buf, size_t buflen)
4310281806Srpaulo{
4311281806Srpaulo	struct hostapd_channel_data *chnl;
4312281806Srpaulo	int ret, i, j;
4313281806Srpaulo	char *pos, *end, *hmode;
4314281806Srpaulo
4315281806Srpaulo	pos = buf;
4316281806Srpaulo	end = pos + buflen;
4317281806Srpaulo
4318281806Srpaulo	for (j = 0; j < wpa_s->hw.num_modes; j++) {
4319281806Srpaulo		switch (wpa_s->hw.modes[j].mode) {
4320281806Srpaulo		case HOSTAPD_MODE_IEEE80211B:
4321281806Srpaulo			hmode = "B";
4322281806Srpaulo			break;
4323281806Srpaulo		case HOSTAPD_MODE_IEEE80211G:
4324281806Srpaulo			hmode = "G";
4325281806Srpaulo			break;
4326281806Srpaulo		case HOSTAPD_MODE_IEEE80211A:
4327281806Srpaulo			hmode = "A";
4328281806Srpaulo			break;
4329281806Srpaulo		case HOSTAPD_MODE_IEEE80211AD:
4330281806Srpaulo			hmode = "AD";
4331281806Srpaulo			break;
4332281806Srpaulo		default:
4333281806Srpaulo			continue;
4334281806Srpaulo		}
4335281806Srpaulo		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
4336281806Srpaulo				  hmode);
4337281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4338281806Srpaulo			return pos - buf;
4339281806Srpaulo		pos += ret;
4340281806Srpaulo		chnl = wpa_s->hw.modes[j].channels;
4341281806Srpaulo		for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
4342281806Srpaulo			if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
4343281806Srpaulo				continue;
4344281806Srpaulo			ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
4345281806Srpaulo					  chnl[i].chan, chnl[i].freq,
4346281806Srpaulo					  chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
4347281806Srpaulo					  " (NO_IR)" : "",
4348281806Srpaulo					  chnl[i].flag & HOSTAPD_CHAN_RADAR ?
4349281806Srpaulo					  " (DFS)" : "");
4350281806Srpaulo
4351281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4352281806Srpaulo				return pos - buf;
4353281806Srpaulo			pos += ret;
4354281806Srpaulo		}
4355281806Srpaulo		ret = os_snprintf(pos, end - pos, "\n");
4356281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4357281806Srpaulo			return pos - buf;
4358281806Srpaulo		pos += ret;
4359281806Srpaulo	}
4360281806Srpaulo
4361281806Srpaulo	return pos - buf;
4362281806Srpaulo}
4363281806Srpaulo
4364281806Srpaulo
4365189251Ssamstatic int wpa_supplicant_ctrl_iface_get_capability(
4366189251Ssam	struct wpa_supplicant *wpa_s, const char *_field, char *buf,
4367189251Ssam	size_t buflen)
4368189251Ssam{
4369189251Ssam	struct wpa_driver_capa capa;
4370189251Ssam	int res;
4371189251Ssam	char *strict;
4372189251Ssam	char field[30];
4373189251Ssam	size_t len;
4374189251Ssam
4375189251Ssam	/* Determine whether or not strict checking was requested */
4376189251Ssam	len = os_strlcpy(field, _field, sizeof(field));
4377189251Ssam	if (len >= sizeof(field))
4378189251Ssam		return -1;
4379189251Ssam	strict = os_strchr(field, ' ');
4380189251Ssam	if (strict != NULL) {
4381189251Ssam		*strict++ = '\0';
4382189251Ssam		if (os_strcmp(strict, "strict") != 0)
4383189251Ssam			return -1;
4384189251Ssam	}
4385189251Ssam
4386189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
4387189251Ssam		field, strict ? strict : "");
4388189251Ssam
4389189251Ssam	if (os_strcmp(field, "eap") == 0) {
4390189251Ssam		return eap_get_names(buf, buflen);
4391189251Ssam	}
4392189251Ssam
4393189251Ssam	res = wpa_drv_get_capa(wpa_s, &capa);
4394189251Ssam
4395189251Ssam	if (os_strcmp(field, "pairwise") == 0)
4396189251Ssam		return ctrl_iface_get_capability_pairwise(res, strict, &capa,
4397189251Ssam							  buf, buflen);
4398189251Ssam
4399189251Ssam	if (os_strcmp(field, "group") == 0)
4400189251Ssam		return ctrl_iface_get_capability_group(res, strict, &capa,
4401189251Ssam						       buf, buflen);
4402189251Ssam
4403281806Srpaulo	if (os_strcmp(field, "group_mgmt") == 0)
4404281806Srpaulo		return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
4405281806Srpaulo							    buf, buflen);
4406281806Srpaulo
4407189251Ssam	if (os_strcmp(field, "key_mgmt") == 0)
4408189251Ssam		return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
4409189251Ssam							  buf, buflen);
4410189251Ssam
4411189251Ssam	if (os_strcmp(field, "proto") == 0)
4412189251Ssam		return ctrl_iface_get_capability_proto(res, strict, &capa,
4413189251Ssam						       buf, buflen);
4414189251Ssam
4415189251Ssam	if (os_strcmp(field, "auth_alg") == 0)
4416281806Srpaulo		return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
4417281806Srpaulo							  &capa, buf, buflen);
4418189251Ssam
4419281806Srpaulo	if (os_strcmp(field, "modes") == 0)
4420281806Srpaulo		return ctrl_iface_get_capability_modes(res, strict, &capa,
4421281806Srpaulo						       buf, buflen);
4422281806Srpaulo
4423252726Srpaulo	if (os_strcmp(field, "channels") == 0)
4424252726Srpaulo		return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
4425252726Srpaulo
4426281806Srpaulo	if (os_strcmp(field, "freq") == 0)
4427281806Srpaulo		return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
4428281806Srpaulo
4429281806Srpaulo#ifdef CONFIG_TDLS
4430281806Srpaulo	if (os_strcmp(field, "tdls") == 0)
4431281806Srpaulo		return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
4432281806Srpaulo#endif /* CONFIG_TDLS */
4433281806Srpaulo
4434281806Srpaulo#ifdef CONFIG_ERP
4435281806Srpaulo	if (os_strcmp(field, "erp") == 0) {
4436281806Srpaulo		res = os_snprintf(buf, buflen, "ERP");
4437281806Srpaulo		if (os_snprintf_error(buflen, res))
4438281806Srpaulo			return -1;
4439281806Srpaulo		return res;
4440281806Srpaulo	}
4441281806Srpaulo#endif /* CONFIG_EPR */
4442281806Srpaulo
4443289549Srpaulo#ifdef CONFIG_FIPS
4444289549Srpaulo	if (os_strcmp(field, "fips") == 0) {
4445289549Srpaulo		res = os_snprintf(buf, buflen, "FIPS");
4446289549Srpaulo		if (os_snprintf_error(buflen, res))
4447289549Srpaulo			return -1;
4448289549Srpaulo		return res;
4449289549Srpaulo	}
4450289549Srpaulo#endif /* CONFIG_FIPS */
4451289549Srpaulo
4452337817Scy#ifdef CONFIG_ACS
4453337817Scy	if (os_strcmp(field, "acs") == 0) {
4454337817Scy		res = os_snprintf(buf, buflen, "ACS");
4455337817Scy		if (os_snprintf_error(buflen, res))
4456337817Scy			return -1;
4457337817Scy		return res;
4458337817Scy	}
4459337817Scy#endif /* CONFIG_ACS */
4460337817Scy
4461346981Scy#ifdef CONFIG_FILS
4462346981Scy	if (os_strcmp(field, "fils") == 0) {
4463346981Scy#ifdef CONFIG_FILS_SK_PFS
4464346981Scy		if (wpa_is_fils_supported(wpa_s) &&
4465346981Scy		    wpa_is_fils_sk_pfs_supported(wpa_s)) {
4466346981Scy			res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS");
4467346981Scy			if (os_snprintf_error(buflen, res))
4468346981Scy				return -1;
4469346981Scy			return res;
4470346981Scy		}
4471346981Scy#endif /* CONFIG_FILS_SK_PFS */
4472346981Scy
4473346981Scy		if (wpa_is_fils_supported(wpa_s)) {
4474346981Scy			res = os_snprintf(buf, buflen, "FILS");
4475346981Scy			if (os_snprintf_error(buflen, res))
4476346981Scy				return -1;
4477346981Scy			return res;
4478346981Scy		}
4479346981Scy	}
4480346981Scy#endif /* CONFIG_FILS */
4481346981Scy
4482346981Scy	if (os_strcmp(field, "multibss") == 0 && wpa_s->multi_bss_support) {
4483346981Scy		res = os_snprintf(buf, buflen, "MULTIBSS-STA");
4484346981Scy		if (os_snprintf_error(buflen, res))
4485346981Scy			return -1;
4486346981Scy		return res;
4487346981Scy	}
4488346981Scy
4489346981Scy#ifdef CONFIG_DPP
4490346981Scy	if (os_strcmp(field, "dpp") == 0) {
4491346981Scy#ifdef CONFIG_DPP2
4492346981Scy		res = os_snprintf(buf, buflen, "DPP=2");
4493346981Scy#else /* CONFIG_DPP2 */
4494346981Scy		res = os_snprintf(buf, buflen, "DPP=1");
4495346981Scy#endif /* CONFIG_DPP2 */
4496346981Scy		if (os_snprintf_error(buflen, res))
4497346981Scy			return -1;
4498346981Scy		return res;
4499346981Scy	}
4500346981Scy#endif /* CONFIG_DPP */
4501346981Scy
4502189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
4503189251Ssam		   field);
4504189251Ssam
4505189251Ssam	return -1;
4506189251Ssam}
4507189251Ssam
4508189251Ssam
4509252726Srpaulo#ifdef CONFIG_INTERWORKING
4510252726Srpaulostatic char * anqp_add_hex(char *pos, char *end, const char *title,
4511252726Srpaulo			   struct wpabuf *data)
4512252726Srpaulo{
4513252726Srpaulo	char *start = pos;
4514252726Srpaulo	size_t i;
4515252726Srpaulo	int ret;
4516252726Srpaulo	const u8 *d;
4517252726Srpaulo
4518252726Srpaulo	if (data == NULL)
4519252726Srpaulo		return start;
4520252726Srpaulo
4521252726Srpaulo	ret = os_snprintf(pos, end - pos, "%s=", title);
4522281806Srpaulo	if (os_snprintf_error(end - pos, ret))
4523252726Srpaulo		return start;
4524252726Srpaulo	pos += ret;
4525252726Srpaulo
4526252726Srpaulo	d = wpabuf_head_u8(data);
4527252726Srpaulo	for (i = 0; i < wpabuf_len(data); i++) {
4528252726Srpaulo		ret = os_snprintf(pos, end - pos, "%02x", *d++);
4529281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4530252726Srpaulo			return start;
4531252726Srpaulo		pos += ret;
4532252726Srpaulo	}
4533252726Srpaulo
4534252726Srpaulo	ret = os_snprintf(pos, end - pos, "\n");
4535281806Srpaulo	if (os_snprintf_error(end - pos, ret))
4536252726Srpaulo		return start;
4537252726Srpaulo	pos += ret;
4538252726Srpaulo
4539252726Srpaulo	return pos;
4540252726Srpaulo}
4541252726Srpaulo#endif /* CONFIG_INTERWORKING */
4542252726Srpaulo
4543252726Srpaulo
4544346981Scy#ifdef CONFIG_FILS
4545346981Scystatic int print_fils_indication(struct wpa_bss *bss, char *pos, char *end)
4546346981Scy{
4547346981Scy	char *start = pos;
4548346981Scy	const u8 *ie, *ie_end;
4549346981Scy	u16 info, realms;
4550346981Scy	int ret;
4551346981Scy
4552346981Scy	ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
4553346981Scy	if (!ie)
4554346981Scy		return 0;
4555346981Scy	ie_end = ie + 2 + ie[1];
4556346981Scy	ie += 2;
4557346981Scy	if (ie_end - ie < 2)
4558346981Scy		return -1;
4559346981Scy
4560346981Scy	info = WPA_GET_LE16(ie);
4561346981Scy	ie += 2;
4562346981Scy	ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info);
4563346981Scy	if (os_snprintf_error(end - pos, ret))
4564346981Scy		return 0;
4565346981Scy	pos += ret;
4566346981Scy
4567346981Scy	if (info & BIT(7)) {
4568346981Scy		/* Cache Identifier Included */
4569346981Scy		if (ie_end - ie < 2)
4570346981Scy			return -1;
4571346981Scy		ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n",
4572346981Scy				  ie[0], ie[1]);
4573346981Scy		if (os_snprintf_error(end - pos, ret))
4574346981Scy			return 0;
4575346981Scy		pos += ret;
4576346981Scy		ie += 2;
4577346981Scy	}
4578346981Scy
4579346981Scy	if (info & BIT(8)) {
4580346981Scy		/* HESSID Included */
4581346981Scy		if (ie_end - ie < ETH_ALEN)
4582346981Scy			return -1;
4583346981Scy		ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n",
4584346981Scy				  MAC2STR(ie));
4585346981Scy		if (os_snprintf_error(end - pos, ret))
4586346981Scy			return 0;
4587346981Scy		pos += ret;
4588346981Scy		ie += ETH_ALEN;
4589346981Scy	}
4590346981Scy
4591346981Scy	realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3;
4592346981Scy	if (realms) {
4593346981Scy		if (ie_end - ie < realms * 2)
4594346981Scy			return -1;
4595346981Scy		ret = os_snprintf(pos, end - pos, "fils_realms=");
4596346981Scy		if (os_snprintf_error(end - pos, ret))
4597346981Scy			return 0;
4598346981Scy		pos += ret;
4599346981Scy
4600346981Scy		ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2);
4601346981Scy		if (ret <= 0)
4602346981Scy			return 0;
4603346981Scy		pos += ret;
4604346981Scy		ie += realms * 2;
4605346981Scy		ret = os_snprintf(pos, end - pos, "\n");
4606346981Scy		if (os_snprintf_error(end - pos, ret))
4607346981Scy			return 0;
4608346981Scy		pos += ret;
4609346981Scy	}
4610346981Scy
4611346981Scy	return pos - start;
4612346981Scy}
4613346981Scy#endif /* CONFIG_FILS */
4614346981Scy
4615346981Scy
4616252726Srpaulostatic int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
4617252726Srpaulo			  unsigned long mask, char *buf, size_t buflen)
4618252726Srpaulo{
4619252726Srpaulo	size_t i;
4620252726Srpaulo	int ret;
4621252726Srpaulo	char *pos, *end;
4622346981Scy	const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
4623252726Srpaulo
4624252726Srpaulo	pos = buf;
4625252726Srpaulo	end = buf + buflen;
4626252726Srpaulo
4627252726Srpaulo	if (mask & WPA_BSS_MASK_ID) {
4628252726Srpaulo		ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
4629281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4630252726Srpaulo			return 0;
4631252726Srpaulo		pos += ret;
4632252726Srpaulo	}
4633252726Srpaulo
4634252726Srpaulo	if (mask & WPA_BSS_MASK_BSSID) {
4635252726Srpaulo		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
4636252726Srpaulo				  MAC2STR(bss->bssid));
4637281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4638252726Srpaulo			return 0;
4639252726Srpaulo		pos += ret;
4640252726Srpaulo	}
4641252726Srpaulo
4642252726Srpaulo	if (mask & WPA_BSS_MASK_FREQ) {
4643252726Srpaulo		ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
4644281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4645252726Srpaulo			return 0;
4646252726Srpaulo		pos += ret;
4647252726Srpaulo	}
4648252726Srpaulo
4649252726Srpaulo	if (mask & WPA_BSS_MASK_BEACON_INT) {
4650252726Srpaulo		ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
4651252726Srpaulo				  bss->beacon_int);
4652281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4653252726Srpaulo			return 0;
4654252726Srpaulo		pos += ret;
4655252726Srpaulo	}
4656252726Srpaulo
4657252726Srpaulo	if (mask & WPA_BSS_MASK_CAPABILITIES) {
4658252726Srpaulo		ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
4659252726Srpaulo				  bss->caps);
4660281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4661252726Srpaulo			return 0;
4662252726Srpaulo		pos += ret;
4663252726Srpaulo	}
4664252726Srpaulo
4665252726Srpaulo	if (mask & WPA_BSS_MASK_QUAL) {
4666252726Srpaulo		ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
4667281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4668252726Srpaulo			return 0;
4669252726Srpaulo		pos += ret;
4670252726Srpaulo	}
4671252726Srpaulo
4672252726Srpaulo	if (mask & WPA_BSS_MASK_NOISE) {
4673252726Srpaulo		ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
4674281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4675252726Srpaulo			return 0;
4676252726Srpaulo		pos += ret;
4677252726Srpaulo	}
4678252726Srpaulo
4679252726Srpaulo	if (mask & WPA_BSS_MASK_LEVEL) {
4680252726Srpaulo		ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
4681281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4682252726Srpaulo			return 0;
4683252726Srpaulo		pos += ret;
4684252726Srpaulo	}
4685252726Srpaulo
4686252726Srpaulo	if (mask & WPA_BSS_MASK_TSF) {
4687252726Srpaulo		ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
4688252726Srpaulo				  (unsigned long long) bss->tsf);
4689281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4690252726Srpaulo			return 0;
4691252726Srpaulo		pos += ret;
4692252726Srpaulo	}
4693252726Srpaulo
4694252726Srpaulo	if (mask & WPA_BSS_MASK_AGE) {
4695281806Srpaulo		struct os_reltime now;
4696252726Srpaulo
4697281806Srpaulo		os_get_reltime(&now);
4698252726Srpaulo		ret = os_snprintf(pos, end - pos, "age=%d\n",
4699252726Srpaulo				  (int) (now.sec - bss->last_update.sec));
4700281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4701252726Srpaulo			return 0;
4702252726Srpaulo		pos += ret;
4703252726Srpaulo	}
4704252726Srpaulo
4705252726Srpaulo	if (mask & WPA_BSS_MASK_IE) {
4706252726Srpaulo		ret = os_snprintf(pos, end - pos, "ie=");
4707281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4708252726Srpaulo			return 0;
4709252726Srpaulo		pos += ret;
4710252726Srpaulo
4711252726Srpaulo		ie = (const u8 *) (bss + 1);
4712252726Srpaulo		for (i = 0; i < bss->ie_len; i++) {
4713252726Srpaulo			ret = os_snprintf(pos, end - pos, "%02x", *ie++);
4714281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4715252726Srpaulo				return 0;
4716252726Srpaulo			pos += ret;
4717252726Srpaulo		}
4718252726Srpaulo
4719252726Srpaulo		ret = os_snprintf(pos, end - pos, "\n");
4720281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4721252726Srpaulo			return 0;
4722252726Srpaulo		pos += ret;
4723252726Srpaulo	}
4724252726Srpaulo
4725252726Srpaulo	if (mask & WPA_BSS_MASK_FLAGS) {
4726252726Srpaulo		ret = os_snprintf(pos, end - pos, "flags=");
4727281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4728252726Srpaulo			return 0;
4729252726Srpaulo		pos += ret;
4730252726Srpaulo
4731346981Scy		mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
4732346981Scy
4733252726Srpaulo		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
4734252726Srpaulo		if (ie)
4735252726Srpaulo			pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
4736252726Srpaulo						    2 + ie[1]);
4737252726Srpaulo		ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
4738252726Srpaulo		if (ie2)
4739346981Scy			pos = wpa_supplicant_ie_txt(pos, end,
4740346981Scy						    mesh ? "RSN" : "WPA2", ie2,
4741252726Srpaulo						    2 + ie2[1]);
4742289549Srpaulo		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
4743289549Srpaulo		if (osen_ie)
4744289549Srpaulo			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
4745289549Srpaulo						    osen_ie, 2 + osen_ie[1]);
4746346981Scy		owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
4747346981Scy		if (owe) {
4748346981Scy			ret = os_snprintf(
4749346981Scy				pos, end - pos,
4750346981Scy				ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]");
4751346981Scy			if (os_snprintf_error(end - pos, ret))
4752346981Scy				return 0;
4753346981Scy			pos += ret;
4754346981Scy		}
4755252726Srpaulo		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
4756289549Srpaulo		if (!ie && !ie2 && !osen_ie &&
4757289549Srpaulo		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
4758252726Srpaulo			ret = os_snprintf(pos, end - pos, "[WEP]");
4759281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4760252726Srpaulo				return 0;
4761252726Srpaulo			pos += ret;
4762252726Srpaulo		}
4763346981Scy
4764346981Scy		if (mesh) {
4765346981Scy			ret = os_snprintf(pos, end - pos, "[MESH]");
4766346981Scy			if (os_snprintf_error(end - pos, ret))
4767346981Scy				return 0;
4768346981Scy			pos += ret;
4769346981Scy		}
4770346981Scy
4771281806Srpaulo		if (bss_is_dmg(bss)) {
4772281806Srpaulo			const char *s;
4773281806Srpaulo			ret = os_snprintf(pos, end - pos, "[DMG]");
4774281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4775252726Srpaulo				return 0;
4776252726Srpaulo			pos += ret;
4777281806Srpaulo			switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
4778281806Srpaulo			case IEEE80211_CAP_DMG_IBSS:
4779281806Srpaulo				s = "[IBSS]";
4780281806Srpaulo				break;
4781281806Srpaulo			case IEEE80211_CAP_DMG_AP:
4782281806Srpaulo				s = "[ESS]";
4783281806Srpaulo				break;
4784281806Srpaulo			case IEEE80211_CAP_DMG_PBSS:
4785281806Srpaulo				s = "[PBSS]";
4786281806Srpaulo				break;
4787281806Srpaulo			default:
4788281806Srpaulo				s = "";
4789281806Srpaulo				break;
4790281806Srpaulo			}
4791281806Srpaulo			ret = os_snprintf(pos, end - pos, "%s", s);
4792281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4793252726Srpaulo				return 0;
4794252726Srpaulo			pos += ret;
4795281806Srpaulo		} else {
4796281806Srpaulo			if (bss->caps & IEEE80211_CAP_IBSS) {
4797281806Srpaulo				ret = os_snprintf(pos, end - pos, "[IBSS]");
4798281806Srpaulo				if (os_snprintf_error(end - pos, ret))
4799281806Srpaulo					return 0;
4800281806Srpaulo				pos += ret;
4801281806Srpaulo			}
4802281806Srpaulo			if (bss->caps & IEEE80211_CAP_ESS) {
4803281806Srpaulo				ret = os_snprintf(pos, end - pos, "[ESS]");
4804281806Srpaulo				if (os_snprintf_error(end - pos, ret))
4805281806Srpaulo					return 0;
4806281806Srpaulo				pos += ret;
4807281806Srpaulo			}
4808252726Srpaulo		}
4809281806Srpaulo		if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
4810281806Srpaulo		    wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
4811252726Srpaulo			ret = os_snprintf(pos, end - pos, "[P2P]");
4812281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4813252726Srpaulo				return 0;
4814252726Srpaulo			pos += ret;
4815252726Srpaulo		}
4816252726Srpaulo#ifdef CONFIG_HS20
4817252726Srpaulo		if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
4818252726Srpaulo			ret = os_snprintf(pos, end - pos, "[HS20]");
4819281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4820281806Srpaulo				return 0;
4821252726Srpaulo			pos += ret;
4822252726Srpaulo		}
4823252726Srpaulo#endif /* CONFIG_HS20 */
4824346981Scy#ifdef CONFIG_FILS
4825346981Scy		if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
4826346981Scy			ret = os_snprintf(pos, end - pos, "[FILS]");
4827346981Scy			if (os_snprintf_error(end - pos, ret))
4828346981Scy				return 0;
4829346981Scy			pos += ret;
4830346981Scy		}
4831346981Scy#endif /* CONFIG_FILS */
4832346981Scy#ifdef CONFIG_FST
4833346981Scy		if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
4834346981Scy			ret = os_snprintf(pos, end - pos, "[FST]");
4835346981Scy			if (os_snprintf_error(end - pos, ret))
4836346981Scy				return 0;
4837346981Scy			pos += ret;
4838346981Scy		}
4839346981Scy#endif /* CONFIG_FST */
4840346981Scy		if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) {
4841346981Scy			ret = os_snprintf(pos, end - pos, "[UTF-8]");
4842346981Scy			if (os_snprintf_error(end - pos, ret))
4843346981Scy				return 0;
4844346981Scy			pos += ret;
4845346981Scy		}
4846252726Srpaulo
4847252726Srpaulo		ret = os_snprintf(pos, end - pos, "\n");
4848281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4849252726Srpaulo			return 0;
4850252726Srpaulo		pos += ret;
4851252726Srpaulo	}
4852252726Srpaulo
4853252726Srpaulo	if (mask & WPA_BSS_MASK_SSID) {
4854252726Srpaulo		ret = os_snprintf(pos, end - pos, "ssid=%s\n",
4855252726Srpaulo				  wpa_ssid_txt(bss->ssid, bss->ssid_len));
4856281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4857252726Srpaulo			return 0;
4858252726Srpaulo		pos += ret;
4859252726Srpaulo	}
4860252726Srpaulo
4861252726Srpaulo#ifdef CONFIG_WPS
4862252726Srpaulo	if (mask & WPA_BSS_MASK_WPS_SCAN) {
4863252726Srpaulo		ie = (const u8 *) (bss + 1);
4864252726Srpaulo		ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
4865289549Srpaulo		if (ret >= end - pos)
4866252726Srpaulo			return 0;
4867289549Srpaulo		if (ret > 0)
4868289549Srpaulo			pos += ret;
4869252726Srpaulo	}
4870252726Srpaulo#endif /* CONFIG_WPS */
4871252726Srpaulo
4872252726Srpaulo#ifdef CONFIG_P2P
4873252726Srpaulo	if (mask & WPA_BSS_MASK_P2P_SCAN) {
4874252726Srpaulo		ie = (const u8 *) (bss + 1);
4875252726Srpaulo		ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
4876337817Scy		if (ret >= end - pos)
4877252726Srpaulo			return 0;
4878337817Scy		if (ret > 0)
4879337817Scy			pos += ret;
4880252726Srpaulo	}
4881252726Srpaulo#endif /* CONFIG_P2P */
4882252726Srpaulo
4883252726Srpaulo#ifdef CONFIG_WIFI_DISPLAY
4884252726Srpaulo	if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
4885252726Srpaulo		struct wpabuf *wfd;
4886252726Srpaulo		ie = (const u8 *) (bss + 1);
4887252726Srpaulo		wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
4888252726Srpaulo						  WFD_IE_VENDOR_TYPE);
4889252726Srpaulo		if (wfd) {
4890252726Srpaulo			ret = os_snprintf(pos, end - pos, "wfd_subelems=");
4891281806Srpaulo			if (os_snprintf_error(end - pos, ret)) {
4892281806Srpaulo				wpabuf_free(wfd);
4893281806Srpaulo				return 0;
4894281806Srpaulo			}
4895252726Srpaulo			pos += ret;
4896252726Srpaulo
4897252726Srpaulo			pos += wpa_snprintf_hex(pos, end - pos,
4898252726Srpaulo						wpabuf_head(wfd),
4899252726Srpaulo						wpabuf_len(wfd));
4900252726Srpaulo			wpabuf_free(wfd);
4901252726Srpaulo
4902252726Srpaulo			ret = os_snprintf(pos, end - pos, "\n");
4903281806Srpaulo			if (os_snprintf_error(end - pos, ret))
4904281806Srpaulo				return 0;
4905252726Srpaulo			pos += ret;
4906252726Srpaulo		}
4907252726Srpaulo	}
4908252726Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
4909252726Srpaulo
4910252726Srpaulo#ifdef CONFIG_INTERWORKING
4911252726Srpaulo	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
4912252726Srpaulo		struct wpa_bss_anqp *anqp = bss->anqp;
4913337817Scy		struct wpa_bss_anqp_elem *elem;
4914337817Scy
4915281806Srpaulo		pos = anqp_add_hex(pos, end, "anqp_capability_list",
4916281806Srpaulo				   anqp->capability_list);
4917252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_venue_name",
4918252726Srpaulo				   anqp->venue_name);
4919252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
4920252726Srpaulo				   anqp->network_auth_type);
4921252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
4922252726Srpaulo				   anqp->roaming_consortium);
4923252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
4924252726Srpaulo				   anqp->ip_addr_type_availability);
4925252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_nai_realm",
4926252726Srpaulo				   anqp->nai_realm);
4927252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
4928252726Srpaulo		pos = anqp_add_hex(pos, end, "anqp_domain_name",
4929252726Srpaulo				   anqp->domain_name);
4930346981Scy		pos = anqp_add_hex(pos, end, "anqp_fils_realm_info",
4931346981Scy				   anqp->fils_realm_info);
4932252726Srpaulo#ifdef CONFIG_HS20
4933281806Srpaulo		pos = anqp_add_hex(pos, end, "hs20_capability_list",
4934281806Srpaulo				   anqp->hs20_capability_list);
4935252726Srpaulo		pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
4936252726Srpaulo				   anqp->hs20_operator_friendly_name);
4937252726Srpaulo		pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
4938252726Srpaulo				   anqp->hs20_wan_metrics);
4939252726Srpaulo		pos = anqp_add_hex(pos, end, "hs20_connection_capability",
4940252726Srpaulo				   anqp->hs20_connection_capability);
4941281806Srpaulo		pos = anqp_add_hex(pos, end, "hs20_operating_class",
4942281806Srpaulo				   anqp->hs20_operating_class);
4943281806Srpaulo		pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
4944281806Srpaulo				   anqp->hs20_osu_providers_list);
4945346981Scy		pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata",
4946346981Scy				   anqp->hs20_operator_icon_metadata);
4947346981Scy		pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list",
4948346981Scy				   anqp->hs20_osu_providers_nai_list);
4949252726Srpaulo#endif /* CONFIG_HS20 */
4950337817Scy
4951337817Scy		dl_list_for_each(elem, &anqp->anqp_elems,
4952337817Scy				 struct wpa_bss_anqp_elem, list) {
4953337817Scy			char title[20];
4954337817Scy
4955337817Scy			os_snprintf(title, sizeof(title), "anqp[%u]",
4956337817Scy				    elem->infoid);
4957337817Scy			pos = anqp_add_hex(pos, end, title, elem->payload);
4958337817Scy		}
4959252726Srpaulo	}
4960252726Srpaulo#endif /* CONFIG_INTERWORKING */
4961252726Srpaulo
4962281806Srpaulo#ifdef CONFIG_MESH
4963281806Srpaulo	if (mask & WPA_BSS_MASK_MESH_SCAN) {
4964281806Srpaulo		ie = (const u8 *) (bss + 1);
4965281806Srpaulo		ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
4966337817Scy		if (ret >= end - pos)
4967281806Srpaulo			return 0;
4968337817Scy		if (ret > 0)
4969337817Scy			pos += ret;
4970281806Srpaulo	}
4971281806Srpaulo#endif /* CONFIG_MESH */
4972281806Srpaulo
4973281806Srpaulo	if (mask & WPA_BSS_MASK_SNR) {
4974281806Srpaulo		ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
4975281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4976281806Srpaulo			return 0;
4977281806Srpaulo		pos += ret;
4978281806Srpaulo	}
4979281806Srpaulo
4980281806Srpaulo	if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
4981281806Srpaulo		ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
4982281806Srpaulo				  bss->est_throughput);
4983281806Srpaulo		if (os_snprintf_error(end - pos, ret))
4984281806Srpaulo			return 0;
4985281806Srpaulo		pos += ret;
4986281806Srpaulo	}
4987281806Srpaulo
4988289549Srpaulo#ifdef CONFIG_FST
4989289549Srpaulo	if (mask & WPA_BSS_MASK_FST) {
4990289549Srpaulo		ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
4991289549Srpaulo		if (ret < 0 || ret >= end - pos)
4992289549Srpaulo			return 0;
4993289549Srpaulo		pos += ret;
4994289549Srpaulo	}
4995289549Srpaulo#endif /* CONFIG_FST */
4996289549Srpaulo
4997346981Scy	if (mask & WPA_BSS_MASK_UPDATE_IDX) {
4998346981Scy		ret = os_snprintf(pos, end - pos, "update_idx=%u\n",
4999346981Scy				  bss->last_update_idx);
5000346981Scy		if (os_snprintf_error(end - pos, ret))
5001346981Scy			return 0;
5002346981Scy		pos += ret;
5003346981Scy	}
5004346981Scy
5005346981Scy	if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) {
5006346981Scy		ret = os_snprintf(pos, end - pos, "beacon_ie=");
5007346981Scy		if (os_snprintf_error(end - pos, ret))
5008346981Scy			return 0;
5009346981Scy		pos += ret;
5010346981Scy
5011346981Scy		ie = (const u8 *) (bss + 1);
5012346981Scy		ie += bss->ie_len;
5013346981Scy		for (i = 0; i < bss->beacon_ie_len; i++) {
5014346981Scy			ret = os_snprintf(pos, end - pos, "%02x", *ie++);
5015346981Scy			if (os_snprintf_error(end - pos, ret))
5016346981Scy				return 0;
5017346981Scy			pos += ret;
5018346981Scy		}
5019346981Scy
5020346981Scy		ret = os_snprintf(pos, end - pos, "\n");
5021346981Scy		if (os_snprintf_error(end - pos, ret))
5022346981Scy			return 0;
5023346981Scy		pos += ret;
5024346981Scy	}
5025346981Scy
5026346981Scy#ifdef CONFIG_FILS
5027346981Scy	if (mask & WPA_BSS_MASK_FILS_INDICATION) {
5028346981Scy		ret = print_fils_indication(bss, pos, end);
5029346981Scy		if (ret < 0)
5030346981Scy			return 0;
5031346981Scy		pos += ret;
5032346981Scy	}
5033346981Scy#endif /* CONFIG_FILS */
5034346981Scy
5035281806Srpaulo	if (mask & WPA_BSS_MASK_DELIM) {
5036281806Srpaulo		ret = os_snprintf(pos, end - pos, "====\n");
5037281806Srpaulo		if (os_snprintf_error(end - pos, ret))
5038281806Srpaulo			return 0;
5039281806Srpaulo		pos += ret;
5040281806Srpaulo	}
5041281806Srpaulo
5042252726Srpaulo	return pos - buf;
5043252726Srpaulo}
5044252726Srpaulo
5045252726Srpaulo
5046189251Ssamstatic int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
5047189251Ssam					 const char *cmd, char *buf,
5048189251Ssam					 size_t buflen)
5049189251Ssam{
5050189251Ssam	u8 bssid[ETH_ALEN];
5051189251Ssam	size_t i;
5052214734Srpaulo	struct wpa_bss *bss;
5053252726Srpaulo	struct wpa_bss *bsslast = NULL;
5054252726Srpaulo	struct dl_list *next;
5055252726Srpaulo	int ret = 0;
5056252726Srpaulo	int len;
5057281806Srpaulo	char *ctmp, *end = buf + buflen;
5058252726Srpaulo	unsigned long mask = WPA_BSS_MASK_ALL;
5059189251Ssam
5060252726Srpaulo	if (os_strncmp(cmd, "RANGE=", 6) == 0) {
5061252726Srpaulo		if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
5062252726Srpaulo			bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
5063252726Srpaulo					    list_id);
5064252726Srpaulo			bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
5065252726Srpaulo					       list_id);
5066252726Srpaulo		} else { /* N1-N2 */
5067252726Srpaulo			unsigned int id1, id2;
5068252726Srpaulo
5069252726Srpaulo			if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
5070252726Srpaulo				wpa_printf(MSG_INFO, "Wrong BSS range "
5071252726Srpaulo					   "format");
5072252726Srpaulo				return 0;
5073252726Srpaulo			}
5074252726Srpaulo
5075281806Srpaulo			if (*(cmd + 6) == '-')
5076281806Srpaulo				id1 = 0;
5077281806Srpaulo			else
5078281806Srpaulo				id1 = atoi(cmd + 6);
5079281806Srpaulo			ctmp++;
5080281806Srpaulo			if (*ctmp >= '0' && *ctmp <= '9')
5081281806Srpaulo				id2 = atoi(ctmp);
5082281806Srpaulo			else
5083281806Srpaulo				id2 = (unsigned int) -1;
5084281806Srpaulo			bss = wpa_bss_get_id_range(wpa_s, id1, id2);
5085281806Srpaulo			if (id2 == (unsigned int) -1)
5086252726Srpaulo				bsslast = dl_list_last(&wpa_s->bss_id,
5087252726Srpaulo						       struct wpa_bss,
5088252726Srpaulo						       list_id);
5089252726Srpaulo			else {
5090252726Srpaulo				bsslast = wpa_bss_get_id(wpa_s, id2);
5091252726Srpaulo				if (bsslast == NULL && bss && id2 > id1) {
5092252726Srpaulo					struct wpa_bss *tmp = bss;
5093252726Srpaulo					for (;;) {
5094252726Srpaulo						next = tmp->list_id.next;
5095252726Srpaulo						if (next == &wpa_s->bss_id)
5096252726Srpaulo							break;
5097252726Srpaulo						tmp = dl_list_entry(
5098252726Srpaulo							next, struct wpa_bss,
5099252726Srpaulo							list_id);
5100252726Srpaulo						if (tmp->id > id2)
5101252726Srpaulo							break;
5102252726Srpaulo						bsslast = tmp;
5103252726Srpaulo					}
5104252726Srpaulo				}
5105252726Srpaulo			}
5106252726Srpaulo		}
5107281806Srpaulo	} else if (os_strncmp(cmd, "FIRST", 5) == 0)
5108252726Srpaulo		bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
5109281806Srpaulo	else if (os_strncmp(cmd, "LAST", 4) == 0)
5110281806Srpaulo		bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id);
5111214734Srpaulo	else if (os_strncmp(cmd, "ID-", 3) == 0) {
5112214734Srpaulo		i = atoi(cmd + 3);
5113214734Srpaulo		bss = wpa_bss_get_id(wpa_s, i);
5114214734Srpaulo	} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
5115214734Srpaulo		i = atoi(cmd + 5);
5116214734Srpaulo		bss = wpa_bss_get_id(wpa_s, i);
5117214734Srpaulo		if (bss) {
5118252726Srpaulo			next = bss->list_id.next;
5119214734Srpaulo			if (next == &wpa_s->bss_id)
5120214734Srpaulo				bss = NULL;
5121214734Srpaulo			else
5122214734Srpaulo				bss = dl_list_entry(next, struct wpa_bss,
5123214734Srpaulo						    list_id);
5124214734Srpaulo		}
5125346981Scy	} else if (os_strncmp(cmd, "CURRENT", 7) == 0) {
5126346981Scy		bss = wpa_s->current_bss;
5127252726Srpaulo#ifdef CONFIG_P2P
5128252726Srpaulo	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
5129252726Srpaulo		if (hwaddr_aton(cmd + 13, bssid) == 0)
5130252726Srpaulo			bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
5131252726Srpaulo		else
5132252726Srpaulo			bss = NULL;
5133252726Srpaulo#endif /* CONFIG_P2P */
5134214734Srpaulo	} else if (hwaddr_aton(cmd, bssid) == 0)
5135214734Srpaulo		bss = wpa_bss_get_bssid(wpa_s, bssid);
5136214734Srpaulo	else {
5137214734Srpaulo		struct wpa_bss *tmp;
5138214734Srpaulo		i = atoi(cmd);
5139214734Srpaulo		bss = NULL;
5140214734Srpaulo		dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
5141214734Srpaulo		{
5142346981Scy			if (i == 0) {
5143214734Srpaulo				bss = tmp;
5144189251Ssam				break;
5145214734Srpaulo			}
5146346981Scy			i--;
5147189251Ssam		}
5148214734Srpaulo	}
5149189251Ssam
5150252726Srpaulo	if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
5151252726Srpaulo		mask = strtoul(ctmp + 5, NULL, 0x10);
5152252726Srpaulo		if (mask == 0)
5153252726Srpaulo			mask = WPA_BSS_MASK_ALL;
5154252726Srpaulo	}
5155252726Srpaulo
5156214734Srpaulo	if (bss == NULL)
5157214734Srpaulo		return 0;
5158189251Ssam
5159252726Srpaulo	if (bsslast == NULL)
5160252726Srpaulo		bsslast = bss;
5161252726Srpaulo	do {
5162252726Srpaulo		len = print_bss_info(wpa_s, bss, mask, buf, buflen);
5163252726Srpaulo		ret += len;
5164252726Srpaulo		buf += len;
5165252726Srpaulo		buflen -= len;
5166281806Srpaulo		if (bss == bsslast) {
5167281806Srpaulo			if ((mask & WPA_BSS_MASK_DELIM) && len &&
5168281806Srpaulo			    (bss == dl_list_last(&wpa_s->bss_id,
5169281806Srpaulo						 struct wpa_bss, list_id))) {
5170281806Srpaulo				int res;
5171281806Srpaulo
5172281806Srpaulo				res = os_snprintf(buf - 5, end - buf + 5,
5173281806Srpaulo						  "####\n");
5174281806Srpaulo				if (os_snprintf_error(end - buf + 5, res)) {
5175281806Srpaulo					wpa_printf(MSG_DEBUG,
5176281806Srpaulo						   "Could not add end delim");
5177281806Srpaulo				}
5178281806Srpaulo			}
5179252726Srpaulo			break;
5180281806Srpaulo		}
5181252726Srpaulo		next = bss->list_id.next;
5182252726Srpaulo		if (next == &wpa_s->bss_id)
5183252726Srpaulo			break;
5184252726Srpaulo		bss = dl_list_entry(next, struct wpa_bss, list_id);
5185252726Srpaulo	} while (bss && len);
5186189251Ssam
5187252726Srpaulo	return ret;
5188252726Srpaulo}
5189189251Ssam
5190189251Ssam
5191252726Srpaulostatic int wpa_supplicant_ctrl_iface_ap_scan(
5192252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
5193252726Srpaulo{
5194252726Srpaulo	int ap_scan = atoi(cmd);
5195252726Srpaulo	return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
5196252726Srpaulo}
5197189251Ssam
5198189251Ssam
5199252726Srpaulostatic int wpa_supplicant_ctrl_iface_scan_interval(
5200252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
5201252726Srpaulo{
5202252726Srpaulo	int scan_int = atoi(cmd);
5203252726Srpaulo	return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
5204252726Srpaulo}
5205189251Ssam
5206189251Ssam
5207252726Srpaulostatic int wpa_supplicant_ctrl_iface_bss_expire_age(
5208252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
5209252726Srpaulo{
5210252726Srpaulo	int expire_age = atoi(cmd);
5211252726Srpaulo	return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
5212252726Srpaulo}
5213214734Srpaulo
5214252726Srpaulo
5215252726Srpaulostatic int wpa_supplicant_ctrl_iface_bss_expire_count(
5216252726Srpaulo	struct wpa_supplicant *wpa_s, char *cmd)
5217252726Srpaulo{
5218252726Srpaulo	int expire_count = atoi(cmd);
5219252726Srpaulo	return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
5220189251Ssam}
5221189251Ssam
5222189251Ssam
5223281806Srpaulostatic void wpa_supplicant_ctrl_iface_bss_flush(
5224189251Ssam	struct wpa_supplicant *wpa_s, char *cmd)
5225189251Ssam{
5226252726Srpaulo	int flush_age = atoi(cmd);
5227252726Srpaulo
5228252726Srpaulo	if (flush_age == 0)
5229252726Srpaulo		wpa_bss_flush(wpa_s);
5230252726Srpaulo	else
5231252726Srpaulo		wpa_bss_flush_by_age(wpa_s, flush_age);
5232214734Srpaulo}
5233189251Ssam
5234214734Srpaulo
5235281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
5236214734Srpaulostatic void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
5237214734Srpaulo{
5238214734Srpaulo	wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
5239214734Srpaulo	/* MLME-DELETEKEYS.request */
5240252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
5241252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
5242252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
5243252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
5244214734Srpaulo#ifdef CONFIG_IEEE80211W
5245252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
5246252726Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
5247214734Srpaulo#endif /* CONFIG_IEEE80211W */
5248214734Srpaulo
5249214734Srpaulo	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
5250214734Srpaulo			0);
5251214734Srpaulo	/* MLME-SETPROTECTION.request(None) */
5252214734Srpaulo	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
5253214734Srpaulo				   MLME_SETPROTECTION_PROTECT_TYPE_NONE,
5254214734Srpaulo				   MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
5255214734Srpaulo	wpa_sm_drop_sa(wpa_s->wpa);
5256214734Srpaulo}
5257281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
5258214734Srpaulo
5259214734Srpaulo
5260214734Srpaulostatic int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
5261214734Srpaulo					  char *addr)
5262214734Srpaulo{
5263252726Srpaulo#ifdef CONFIG_NO_SCAN_PROCESSING
5264252726Srpaulo	return -1;
5265252726Srpaulo#else /* CONFIG_NO_SCAN_PROCESSING */
5266214734Srpaulo	u8 bssid[ETH_ALEN];
5267214734Srpaulo	struct wpa_bss *bss;
5268214734Srpaulo	struct wpa_ssid *ssid = wpa_s->current_ssid;
5269214734Srpaulo
5270214734Srpaulo	if (hwaddr_aton(addr, bssid)) {
5271214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
5272214734Srpaulo			   "address '%s'", addr);
5273189251Ssam		return -1;
5274214734Srpaulo	}
5275214734Srpaulo
5276214734Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
5277214734Srpaulo
5278281806Srpaulo	if (!ssid) {
5279281806Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
5280281806Srpaulo			   "configuration known for the target AP");
5281281806Srpaulo		return -1;
5282281806Srpaulo	}
5283281806Srpaulo
5284281806Srpaulo	bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
5285214734Srpaulo	if (!bss) {
5286214734Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
5287214734Srpaulo			   "from BSS table");
5288214734Srpaulo		return -1;
5289214734Srpaulo	}
5290214734Srpaulo
5291214734Srpaulo	/*
5292214734Srpaulo	 * TODO: Find best network configuration block from configuration to
5293214734Srpaulo	 * allow roaming to other networks
5294214734Srpaulo	 */
5295214734Srpaulo
5296214734Srpaulo	wpa_s->reassociate = 1;
5297214734Srpaulo	wpa_supplicant_connect(wpa_s, bss, ssid);
5298214734Srpaulo
5299189251Ssam	return 0;
5300252726Srpaulo#endif /* CONFIG_NO_SCAN_PROCESSING */
5301189251Ssam}
5302189251Ssam
5303189251Ssam
5304252726Srpaulo#ifdef CONFIG_P2P
5305252726Srpaulostatic int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
5306252726Srpaulo{
5307252726Srpaulo	unsigned int timeout = atoi(cmd);
5308252726Srpaulo	enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
5309252726Srpaulo	u8 dev_id[ETH_ALEN], *_dev_id = NULL;
5310281806Srpaulo	u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
5311252726Srpaulo	char *pos;
5312252726Srpaulo	unsigned int search_delay;
5313281806Srpaulo	const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
5314281806Srpaulo	u8 seek_count = 0;
5315281806Srpaulo	int freq = 0;
5316252726Srpaulo
5317281806Srpaulo	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
5318281806Srpaulo		wpa_dbg(wpa_s, MSG_INFO,
5319281806Srpaulo			"Reject P2P_FIND since interface is disabled");
5320281806Srpaulo		return -1;
5321281806Srpaulo	}
5322252726Srpaulo	if (os_strstr(cmd, "type=social"))
5323252726Srpaulo		type = P2P_FIND_ONLY_SOCIAL;
5324252726Srpaulo	else if (os_strstr(cmd, "type=progressive"))
5325252726Srpaulo		type = P2P_FIND_PROGRESSIVE;
5326252726Srpaulo
5327252726Srpaulo	pos = os_strstr(cmd, "dev_id=");
5328252726Srpaulo	if (pos) {
5329252726Srpaulo		pos += 7;
5330252726Srpaulo		if (hwaddr_aton(pos, dev_id))
5331252726Srpaulo			return -1;
5332252726Srpaulo		_dev_id = dev_id;
5333252726Srpaulo	}
5334252726Srpaulo
5335281806Srpaulo	pos = os_strstr(cmd, "dev_type=");
5336281806Srpaulo	if (pos) {
5337281806Srpaulo		pos += 9;
5338281806Srpaulo		if (wps_dev_type_str2bin(pos, dev_type) < 0)
5339281806Srpaulo			return -1;
5340281806Srpaulo		_dev_type = dev_type;
5341281806Srpaulo	}
5342281806Srpaulo
5343252726Srpaulo	pos = os_strstr(cmd, "delay=");
5344252726Srpaulo	if (pos) {
5345252726Srpaulo		pos += 6;
5346252726Srpaulo		search_delay = atoi(pos);
5347252726Srpaulo	} else
5348252726Srpaulo		search_delay = wpas_p2p_search_delay(wpa_s);
5349252726Srpaulo
5350289549Srpaulo	pos = os_strstr(cmd, "freq=");
5351289549Srpaulo	if (pos) {
5352289549Srpaulo		pos += 5;
5353289549Srpaulo		freq = atoi(pos);
5354289549Srpaulo		if (freq <= 0)
5355289549Srpaulo			return -1;
5356289549Srpaulo	}
5357289549Srpaulo
5358281806Srpaulo	/* Must be searched for last, because it adds nul termination */
5359281806Srpaulo	pos = os_strstr(cmd, " seek=");
5360289549Srpaulo	if (pos)
5361289549Srpaulo		pos += 6;
5362281806Srpaulo	while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
5363281806Srpaulo		char *term;
5364281806Srpaulo
5365289549Srpaulo		_seek[seek_count++] = pos;
5366281806Srpaulo		seek = _seek;
5367289549Srpaulo		term = os_strchr(pos, ' ');
5368289549Srpaulo		if (!term)
5369289549Srpaulo			break;
5370289549Srpaulo		*term = '\0';
5371289549Srpaulo		pos = os_strstr(term + 1, "seek=");
5372289549Srpaulo		if (pos)
5373289549Srpaulo			pos += 5;
5374281806Srpaulo	}
5375281806Srpaulo	if (seek_count > P2P_MAX_QUERY_HASH) {
5376281806Srpaulo		seek[0] = NULL;
5377281806Srpaulo		seek_count = 1;
5378281806Srpaulo	}
5379281806Srpaulo
5380281806Srpaulo	return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
5381281806Srpaulo			     _dev_id, search_delay, seek_count, seek, freq);
5382252726Srpaulo}
5383252726Srpaulo
5384252726Srpaulo
5385289549Srpaulostatic int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
5386289549Srpaulo{
5387289549Srpaulo	const char *last = NULL;
5388289549Srpaulo	const char *token;
5389289549Srpaulo	long int token_len;
5390289549Srpaulo	unsigned int i;
5391289549Srpaulo
5392289549Srpaulo	/* Expected predefined CPT names delimited by ':' */
5393289549Srpaulo	for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
5394289549Srpaulo		if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
5395289549Srpaulo			wpa_printf(MSG_ERROR,
5396289549Srpaulo				   "P2PS: CPT name list is too long, expected up to %d names",
5397289549Srpaulo				   P2PS_FEATURE_CAPAB_CPT_MAX);
5398289549Srpaulo			cpt[0] = 0;
5399289549Srpaulo			return -1;
5400289549Srpaulo		}
5401289549Srpaulo
5402289549Srpaulo		token_len = last - token;
5403289549Srpaulo
5404289549Srpaulo		if (token_len  == 3 &&
5405289549Srpaulo		    os_memcmp(token, "UDP", token_len) == 0) {
5406289549Srpaulo			cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
5407289549Srpaulo		} else if (token_len == 3 &&
5408289549Srpaulo			   os_memcmp(token, "MAC", token_len) == 0) {
5409289549Srpaulo			cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
5410289549Srpaulo		} else {
5411289549Srpaulo			wpa_printf(MSG_ERROR,
5412289549Srpaulo				   "P2PS: Unsupported CPT name '%s'", token);
5413289549Srpaulo			cpt[0] = 0;
5414289549Srpaulo			return -1;
5415289549Srpaulo		}
5416289549Srpaulo
5417337817Scy		if (isblank((unsigned char) *last)) {
5418289549Srpaulo			i++;
5419289549Srpaulo			break;
5420289549Srpaulo		}
5421289549Srpaulo	}
5422289549Srpaulo	cpt[i] = 0;
5423289549Srpaulo	return 0;
5424289549Srpaulo}
5425289549Srpaulo
5426289549Srpaulo
5427281806Srpaulostatic struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
5428281806Srpaulo{
5429281806Srpaulo	struct p2ps_provision *p2ps_prov;
5430281806Srpaulo	char *pos;
5431281806Srpaulo	size_t info_len = 0;
5432281806Srpaulo	char *info = NULL;
5433281806Srpaulo	u8 role = P2PS_SETUP_NONE;
5434281806Srpaulo	long long unsigned val;
5435289549Srpaulo	int i;
5436281806Srpaulo
5437281806Srpaulo	pos = os_strstr(cmd, "info=");
5438281806Srpaulo	if (pos) {
5439281806Srpaulo		pos += 5;
5440281806Srpaulo		info_len = os_strlen(pos);
5441281806Srpaulo
5442281806Srpaulo		if (info_len) {
5443281806Srpaulo			info = os_malloc(info_len + 1);
5444281806Srpaulo			if (info) {
5445281806Srpaulo				info_len = utf8_unescape(pos, info_len,
5446281806Srpaulo							 info, info_len + 1);
5447281806Srpaulo			} else
5448281806Srpaulo				info_len = 0;
5449281806Srpaulo		}
5450281806Srpaulo	}
5451281806Srpaulo
5452281806Srpaulo	p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
5453281806Srpaulo	if (p2ps_prov == NULL) {
5454281806Srpaulo		os_free(info);
5455281806Srpaulo		return NULL;
5456281806Srpaulo	}
5457281806Srpaulo
5458281806Srpaulo	if (info) {
5459281806Srpaulo		os_memcpy(p2ps_prov->info, info, info_len);
5460281806Srpaulo		p2ps_prov->info[info_len] = '\0';
5461281806Srpaulo		os_free(info);
5462281806Srpaulo	}
5463281806Srpaulo
5464281806Srpaulo	pos = os_strstr(cmd, "status=");
5465281806Srpaulo	if (pos)
5466281806Srpaulo		p2ps_prov->status = atoi(pos + 7);
5467281806Srpaulo	else
5468281806Srpaulo		p2ps_prov->status = -1;
5469281806Srpaulo
5470281806Srpaulo	pos = os_strstr(cmd, "adv_id=");
5471281806Srpaulo	if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
5472281806Srpaulo		goto invalid_args;
5473281806Srpaulo	p2ps_prov->adv_id = val;
5474281806Srpaulo
5475281806Srpaulo	pos = os_strstr(cmd, "method=");
5476281806Srpaulo	if (pos)
5477281806Srpaulo		p2ps_prov->method = strtol(pos + 7, NULL, 16);
5478281806Srpaulo	else
5479281806Srpaulo		p2ps_prov->method = 0;
5480281806Srpaulo
5481281806Srpaulo	pos = os_strstr(cmd, "session=");
5482281806Srpaulo	if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
5483281806Srpaulo		goto invalid_args;
5484281806Srpaulo	p2ps_prov->session_id = val;
5485281806Srpaulo
5486281806Srpaulo	pos = os_strstr(cmd, "adv_mac=");
5487281806Srpaulo	if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
5488281806Srpaulo		goto invalid_args;
5489281806Srpaulo
5490281806Srpaulo	pos = os_strstr(cmd, "session_mac=");
5491281806Srpaulo	if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
5492281806Srpaulo		goto invalid_args;
5493281806Srpaulo
5494289549Srpaulo	pos = os_strstr(cmd, "cpt=");
5495289549Srpaulo	if (pos) {
5496289549Srpaulo		if (p2ps_ctrl_parse_cpt_priority(pos + 4,
5497289549Srpaulo						 p2ps_prov->cpt_priority))
5498289549Srpaulo			goto invalid_args;
5499289549Srpaulo	} else {
5500289549Srpaulo		p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
5501289549Srpaulo	}
5502289549Srpaulo
5503289549Srpaulo	for (i = 0; p2ps_prov->cpt_priority[i]; i++)
5504289549Srpaulo		p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
5505289549Srpaulo
5506281806Srpaulo	/* force conncap with tstCap (no sanity checks) */
5507281806Srpaulo	pos = os_strstr(cmd, "tstCap=");
5508281806Srpaulo	if (pos) {
5509281806Srpaulo		role = strtol(pos + 7, NULL, 16);
5510281806Srpaulo	} else {
5511281806Srpaulo		pos = os_strstr(cmd, "role=");
5512281806Srpaulo		if (pos) {
5513281806Srpaulo			role = strtol(pos + 5, NULL, 16);
5514281806Srpaulo			if (role != P2PS_SETUP_CLIENT &&
5515281806Srpaulo			    role != P2PS_SETUP_GROUP_OWNER)
5516281806Srpaulo				role = P2PS_SETUP_NONE;
5517281806Srpaulo		}
5518281806Srpaulo	}
5519281806Srpaulo	p2ps_prov->role = role;
5520281806Srpaulo
5521281806Srpaulo	return p2ps_prov;
5522281806Srpaulo
5523281806Srpauloinvalid_args:
5524281806Srpaulo	os_free(p2ps_prov);
5525281806Srpaulo	return NULL;
5526281806Srpaulo}
5527281806Srpaulo
5528281806Srpaulo
5529281806Srpaulostatic int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
5530281806Srpaulo{
5531281806Srpaulo	u8 addr[ETH_ALEN];
5532281806Srpaulo	struct p2ps_provision *p2ps_prov;
5533281806Srpaulo	char *pos;
5534281806Srpaulo
5535281806Srpaulo	/* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
5536281806Srpaulo
5537281806Srpaulo	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
5538281806Srpaulo
5539281806Srpaulo	if (hwaddr_aton(cmd, addr))
5540281806Srpaulo		return -1;
5541281806Srpaulo
5542281806Srpaulo	pos = cmd + 17;
5543281806Srpaulo	if (*pos != ' ')
5544281806Srpaulo		return -1;
5545281806Srpaulo
5546281806Srpaulo	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
5547281806Srpaulo	if (!p2ps_prov)
5548281806Srpaulo		return -1;
5549281806Srpaulo
5550281806Srpaulo	if (p2ps_prov->status < 0) {
5551281806Srpaulo		os_free(p2ps_prov);
5552281806Srpaulo		return -1;
5553281806Srpaulo	}
5554281806Srpaulo
5555281806Srpaulo	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
5556281806Srpaulo				  p2ps_prov);
5557281806Srpaulo}
5558281806Srpaulo
5559281806Srpaulo
5560281806Srpaulostatic int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
5561281806Srpaulo{
5562281806Srpaulo	u8 addr[ETH_ALEN];
5563281806Srpaulo	struct p2ps_provision *p2ps_prov;
5564281806Srpaulo	char *pos;
5565281806Srpaulo
5566281806Srpaulo	/* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
5567281806Srpaulo	 *        session=<ses_id> mac=<ses_mac> [info=<infodata>]
5568281806Srpaulo	 */
5569281806Srpaulo
5570281806Srpaulo	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
5571281806Srpaulo	if (hwaddr_aton(cmd, addr))
5572281806Srpaulo		return -1;
5573281806Srpaulo
5574281806Srpaulo	pos = cmd + 17;
5575281806Srpaulo	if (*pos != ' ')
5576281806Srpaulo		return -1;
5577281806Srpaulo
5578281806Srpaulo	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
5579281806Srpaulo	if (!p2ps_prov)
5580281806Srpaulo		return -1;
5581281806Srpaulo
5582289549Srpaulo	p2ps_prov->pd_seeker = 1;
5583289549Srpaulo
5584281806Srpaulo	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
5585281806Srpaulo				  p2ps_prov);
5586281806Srpaulo}
5587281806Srpaulo
5588281806Srpaulo
5589337817Scystatic int parse_freq(int chwidth, int freq2)
5590337817Scy{
5591337817Scy	if (freq2 < 0)
5592337817Scy		return -1;
5593337817Scy	if (freq2)
5594351611Scy		return CHANWIDTH_80P80MHZ;
5595337817Scy
5596337817Scy	switch (chwidth) {
5597337817Scy	case 0:
5598337817Scy	case 20:
5599337817Scy	case 40:
5600351611Scy		return CHANWIDTH_USE_HT;
5601337817Scy	case 80:
5602351611Scy		return CHANWIDTH_80MHZ;
5603337817Scy	case 160:
5604351611Scy		return CHANWIDTH_160MHZ;
5605337817Scy	default:
5606337817Scy		wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
5607337817Scy			   chwidth);
5608337817Scy		return -1;
5609337817Scy	}
5610337817Scy}
5611337817Scy
5612337817Scy
5613252726Srpaulostatic int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
5614252726Srpaulo			    char *buf, size_t buflen)
5615252726Srpaulo{
5616252726Srpaulo	u8 addr[ETH_ALEN];
5617252726Srpaulo	char *pos, *pos2;
5618252726Srpaulo	char *pin = NULL;
5619252726Srpaulo	enum p2p_wps_method wps_method;
5620252726Srpaulo	int new_pin;
5621252726Srpaulo	int ret;
5622252726Srpaulo	int persistent_group, persistent_id = -1;
5623252726Srpaulo	int join;
5624252726Srpaulo	int auth;
5625252726Srpaulo	int automatic;
5626252726Srpaulo	int go_intent = -1;
5627252726Srpaulo	int freq = 0;
5628252726Srpaulo	int pd;
5629337817Scy	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
5630337817Scy	u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL;
5631337817Scy	size_t group_ssid_len = 0;
5632346981Scy	int he;
5633252726Srpaulo
5634281806Srpaulo	if (!wpa_s->global->p2p_init_wpa_s)
5635281806Srpaulo		return -1;
5636281806Srpaulo	if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
5637281806Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
5638281806Srpaulo			wpa_s->global->p2p_init_wpa_s->ifname);
5639281806Srpaulo		wpa_s = wpa_s->global->p2p_init_wpa_s;
5640281806Srpaulo	}
5641281806Srpaulo
5642281806Srpaulo	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
5643252726Srpaulo	 * [persistent|persistent=<network id>]
5644252726Srpaulo	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
5645346981Scy	 * [ht40] [vht] [he] [auto] [ssid=<hexdump>] */
5646252726Srpaulo
5647252726Srpaulo	if (hwaddr_aton(cmd, addr))
5648252726Srpaulo		return -1;
5649252726Srpaulo
5650252726Srpaulo	pos = cmd + 17;
5651252726Srpaulo	if (*pos != ' ')
5652252726Srpaulo		return -1;
5653252726Srpaulo	pos++;
5654252726Srpaulo
5655252726Srpaulo	persistent_group = os_strstr(pos, " persistent") != NULL;
5656252726Srpaulo	pos2 = os_strstr(pos, " persistent=");
5657252726Srpaulo	if (pos2) {
5658252726Srpaulo		struct wpa_ssid *ssid;
5659252726Srpaulo		persistent_id = atoi(pos2 + 12);
5660252726Srpaulo		ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
5661252726Srpaulo		if (ssid == NULL || ssid->disabled != 2 ||
5662252726Srpaulo		    ssid->mode != WPAS_MODE_P2P_GO) {
5663252726Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
5664252726Srpaulo				   "SSID id=%d for persistent P2P group (GO)",
5665252726Srpaulo				   persistent_id);
5666252726Srpaulo			return -1;
5667252726Srpaulo		}
5668252726Srpaulo	}
5669252726Srpaulo	join = os_strstr(pos, " join") != NULL;
5670252726Srpaulo	auth = os_strstr(pos, " auth") != NULL;
5671252726Srpaulo	automatic = os_strstr(pos, " auto") != NULL;
5672252726Srpaulo	pd = os_strstr(pos, " provdisc") != NULL;
5673281806Srpaulo	vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
5674281806Srpaulo	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
5675281806Srpaulo		vht;
5676346981Scy	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
5677252726Srpaulo
5678252726Srpaulo	pos2 = os_strstr(pos, " go_intent=");
5679252726Srpaulo	if (pos2) {
5680252726Srpaulo		pos2 += 11;
5681252726Srpaulo		go_intent = atoi(pos2);
5682252726Srpaulo		if (go_intent < 0 || go_intent > 15)
5683252726Srpaulo			return -1;
5684252726Srpaulo	}
5685252726Srpaulo
5686252726Srpaulo	pos2 = os_strstr(pos, " freq=");
5687252726Srpaulo	if (pos2) {
5688252726Srpaulo		pos2 += 6;
5689252726Srpaulo		freq = atoi(pos2);
5690252726Srpaulo		if (freq <= 0)
5691252726Srpaulo			return -1;
5692252726Srpaulo	}
5693252726Srpaulo
5694337817Scy	pos2 = os_strstr(pos, " freq2=");
5695337817Scy	if (pos2)
5696337817Scy		freq2 = atoi(pos2 + 7);
5697337817Scy
5698337817Scy	pos2 = os_strstr(pos, " max_oper_chwidth=");
5699337817Scy	if (pos2)
5700337817Scy		chwidth = atoi(pos2 + 18);
5701337817Scy
5702337817Scy	max_oper_chwidth = parse_freq(chwidth, freq2);
5703337817Scy	if (max_oper_chwidth < 0)
5704337817Scy		return -1;
5705337817Scy
5706337817Scy	pos2 = os_strstr(pos, " ssid=");
5707337817Scy	if (pos2) {
5708337817Scy		char *end;
5709337817Scy
5710337817Scy		pos2 += 6;
5711337817Scy		end = os_strchr(pos2, ' ');
5712337817Scy		if (!end)
5713337817Scy			group_ssid_len = os_strlen(pos2) / 2;
5714337817Scy		else
5715337817Scy			group_ssid_len = (end - pos2) / 2;
5716337817Scy		if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN ||
5717337817Scy		    hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0)
5718337817Scy			return -1;
5719337817Scy		group_ssid = _group_ssid;
5720337817Scy	}
5721337817Scy
5722252726Srpaulo	if (os_strncmp(pos, "pin", 3) == 0) {
5723252726Srpaulo		/* Request random PIN (to be displayed) and enable the PIN */
5724252726Srpaulo		wps_method = WPS_PIN_DISPLAY;
5725252726Srpaulo	} else if (os_strncmp(pos, "pbc", 3) == 0) {
5726252726Srpaulo		wps_method = WPS_PBC;
5727337817Scy	} else if (os_strstr(pos, "p2ps") != NULL) {
5728337817Scy		wps_method = WPS_P2PS;
5729252726Srpaulo	} else {
5730252726Srpaulo		pin = pos;
5731252726Srpaulo		pos = os_strchr(pin, ' ');
5732252726Srpaulo		wps_method = WPS_PIN_KEYPAD;
5733252726Srpaulo		if (pos) {
5734252726Srpaulo			*pos++ = '\0';
5735252726Srpaulo			if (os_strncmp(pos, "display", 7) == 0)
5736252726Srpaulo				wps_method = WPS_PIN_DISPLAY;
5737252726Srpaulo		}
5738252726Srpaulo		if (!wps_pin_str_valid(pin)) {
5739252726Srpaulo			os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
5740252726Srpaulo			return 17;
5741252726Srpaulo		}
5742252726Srpaulo	}
5743252726Srpaulo
5744252726Srpaulo	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
5745252726Srpaulo				   persistent_group, automatic, join,
5746337817Scy				   auth, go_intent, freq, freq2, persistent_id,
5747346981Scy				   pd, ht40, vht, max_oper_chwidth, he,
5748337817Scy				   group_ssid, group_ssid_len);
5749252726Srpaulo	if (new_pin == -2) {
5750252726Srpaulo		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
5751252726Srpaulo		return 25;
5752252726Srpaulo	}
5753252726Srpaulo	if (new_pin == -3) {
5754252726Srpaulo		os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
5755252726Srpaulo		return 25;
5756252726Srpaulo	}
5757252726Srpaulo	if (new_pin < 0)
5758252726Srpaulo		return -1;
5759252726Srpaulo	if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
5760252726Srpaulo		ret = os_snprintf(buf, buflen, "%08d", new_pin);
5761281806Srpaulo		if (os_snprintf_error(buflen, ret))
5762252726Srpaulo			return -1;
5763252726Srpaulo		return ret;
5764252726Srpaulo	}
5765252726Srpaulo
5766252726Srpaulo	os_memcpy(buf, "OK\n", 3);
5767252726Srpaulo	return 3;
5768252726Srpaulo}
5769252726Srpaulo
5770252726Srpaulo
5771252726Srpaulostatic int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
5772252726Srpaulo{
5773252726Srpaulo	unsigned int timeout = atoi(cmd);
5774281806Srpaulo	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
5775281806Srpaulo		wpa_dbg(wpa_s, MSG_INFO,
5776281806Srpaulo			"Reject P2P_LISTEN since interface is disabled");
5777281806Srpaulo		return -1;
5778281806Srpaulo	}
5779252726Srpaulo	return wpas_p2p_listen(wpa_s, timeout);
5780252726Srpaulo}
5781252726Srpaulo
5782252726Srpaulo
5783252726Srpaulostatic int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
5784252726Srpaulo{
5785252726Srpaulo	u8 addr[ETH_ALEN];
5786252726Srpaulo	char *pos;
5787252726Srpaulo	enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
5788252726Srpaulo
5789252726Srpaulo	/* <addr> <config method> [join|auto] */
5790252726Srpaulo
5791252726Srpaulo	if (hwaddr_aton(cmd, addr))
5792252726Srpaulo		return -1;
5793252726Srpaulo
5794252726Srpaulo	pos = cmd + 17;
5795252726Srpaulo	if (*pos != ' ')
5796252726Srpaulo		return -1;
5797252726Srpaulo	pos++;
5798252726Srpaulo
5799252726Srpaulo	if (os_strstr(pos, " join") != NULL)
5800252726Srpaulo		use = WPAS_P2P_PD_FOR_JOIN;
5801252726Srpaulo	else if (os_strstr(pos, " auto") != NULL)
5802252726Srpaulo		use = WPAS_P2P_PD_AUTO;
5803252726Srpaulo
5804281806Srpaulo	return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
5805252726Srpaulo}
5806252726Srpaulo
5807252726Srpaulo
5808252726Srpaulostatic int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
5809252726Srpaulo			      size_t buflen)
5810252726Srpaulo{
5811252726Srpaulo	struct wpa_ssid *ssid = wpa_s->current_ssid;
5812252726Srpaulo
5813252726Srpaulo	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
5814252726Srpaulo	    ssid->passphrase == NULL)
5815252726Srpaulo		return -1;
5816252726Srpaulo
5817252726Srpaulo	os_strlcpy(buf, ssid->passphrase, buflen);
5818252726Srpaulo	return os_strlen(buf);
5819252726Srpaulo}
5820252726Srpaulo
5821252726Srpaulo
5822252726Srpaulostatic int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
5823252726Srpaulo				  char *buf, size_t buflen)
5824252726Srpaulo{
5825252726Srpaulo	u64 ref;
5826252726Srpaulo	int res;
5827252726Srpaulo	u8 dst_buf[ETH_ALEN], *dst;
5828252726Srpaulo	struct wpabuf *tlvs;
5829252726Srpaulo	char *pos;
5830252726Srpaulo	size_t len;
5831252726Srpaulo
5832252726Srpaulo	if (hwaddr_aton(cmd, dst_buf))
5833252726Srpaulo		return -1;
5834252726Srpaulo	dst = dst_buf;
5835252726Srpaulo	if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
5836252726Srpaulo	    dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
5837252726Srpaulo		dst = NULL;
5838252726Srpaulo	pos = cmd + 17;
5839252726Srpaulo	if (*pos != ' ')
5840252726Srpaulo		return -1;
5841252726Srpaulo	pos++;
5842252726Srpaulo
5843252726Srpaulo	if (os_strncmp(pos, "upnp ", 5) == 0) {
5844252726Srpaulo		u8 version;
5845252726Srpaulo		pos += 5;
5846252726Srpaulo		if (hexstr2bin(pos, &version, 1) < 0)
5847252726Srpaulo			return -1;
5848252726Srpaulo		pos += 2;
5849252726Srpaulo		if (*pos != ' ')
5850252726Srpaulo			return -1;
5851252726Srpaulo		pos++;
5852252726Srpaulo		ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
5853252726Srpaulo#ifdef CONFIG_WIFI_DISPLAY
5854252726Srpaulo	} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
5855252726Srpaulo		ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
5856252726Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
5857281806Srpaulo	} else if (os_strncmp(pos, "asp ", 4) == 0) {
5858281806Srpaulo		char *svc_str;
5859281806Srpaulo		char *svc_info = NULL;
5860281806Srpaulo		u32 id;
5861281806Srpaulo
5862281806Srpaulo		pos += 4;
5863281806Srpaulo		if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
5864281806Srpaulo			return -1;
5865281806Srpaulo
5866281806Srpaulo		pos = os_strchr(pos, ' ');
5867281806Srpaulo		if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
5868281806Srpaulo			return -1;
5869281806Srpaulo
5870281806Srpaulo		svc_str = pos + 1;
5871281806Srpaulo
5872281806Srpaulo		pos = os_strchr(svc_str, ' ');
5873281806Srpaulo
5874281806Srpaulo		if (pos)
5875281806Srpaulo			*pos++ = '\0';
5876281806Srpaulo
5877281806Srpaulo		/* All remaining data is the svc_info string */
5878281806Srpaulo		if (pos && pos[0] && pos[0] != ' ') {
5879281806Srpaulo			len = os_strlen(pos);
5880281806Srpaulo
5881281806Srpaulo			/* Unescape in place */
5882281806Srpaulo			len = utf8_unescape(pos, len, pos, len);
5883281806Srpaulo			if (len > 0xff)
5884281806Srpaulo				return -1;
5885281806Srpaulo
5886281806Srpaulo			svc_info = pos;
5887281806Srpaulo		}
5888281806Srpaulo
5889281806Srpaulo		ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
5890281806Srpaulo					      svc_str, svc_info);
5891252726Srpaulo	} else {
5892252726Srpaulo		len = os_strlen(pos);
5893252726Srpaulo		if (len & 1)
5894252726Srpaulo			return -1;
5895252726Srpaulo		len /= 2;
5896252726Srpaulo		tlvs = wpabuf_alloc(len);
5897252726Srpaulo		if (tlvs == NULL)
5898252726Srpaulo			return -1;
5899252726Srpaulo		if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
5900252726Srpaulo			wpabuf_free(tlvs);
5901252726Srpaulo			return -1;
5902252726Srpaulo		}
5903252726Srpaulo
5904252726Srpaulo		ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
5905252726Srpaulo		wpabuf_free(tlvs);
5906252726Srpaulo	}
5907252726Srpaulo	if (ref == 0)
5908252726Srpaulo		return -1;
5909252726Srpaulo	res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
5910281806Srpaulo	if (os_snprintf_error(buflen, res))
5911252726Srpaulo		return -1;
5912252726Srpaulo	return res;
5913252726Srpaulo}
5914252726Srpaulo
5915252726Srpaulo
5916252726Srpaulostatic int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
5917252726Srpaulo					 char *cmd)
5918252726Srpaulo{
5919252726Srpaulo	long long unsigned val;
5920252726Srpaulo	u64 req;
5921252726Srpaulo	if (sscanf(cmd, "%llx", &val) != 1)
5922252726Srpaulo		return -1;
5923252726Srpaulo	req = val;
5924252726Srpaulo	return wpas_p2p_sd_cancel_request(wpa_s, req);
5925252726Srpaulo}
5926252726Srpaulo
5927252726Srpaulo
5928252726Srpaulostatic int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
5929252726Srpaulo{
5930252726Srpaulo	int freq;
5931252726Srpaulo	u8 dst[ETH_ALEN];
5932252726Srpaulo	u8 dialog_token;
5933252726Srpaulo	struct wpabuf *resp_tlvs;
5934252726Srpaulo	char *pos, *pos2;
5935252726Srpaulo	size_t len;
5936252726Srpaulo
5937252726Srpaulo	pos = os_strchr(cmd, ' ');
5938252726Srpaulo	if (pos == NULL)
5939252726Srpaulo		return -1;
5940252726Srpaulo	*pos++ = '\0';
5941252726Srpaulo	freq = atoi(cmd);
5942252726Srpaulo	if (freq == 0)
5943252726Srpaulo		return -1;
5944252726Srpaulo
5945252726Srpaulo	if (hwaddr_aton(pos, dst))
5946252726Srpaulo		return -1;
5947252726Srpaulo	pos += 17;
5948252726Srpaulo	if (*pos != ' ')
5949252726Srpaulo		return -1;
5950252726Srpaulo	pos++;
5951252726Srpaulo
5952252726Srpaulo	pos2 = os_strchr(pos, ' ');
5953252726Srpaulo	if (pos2 == NULL)
5954252726Srpaulo		return -1;
5955252726Srpaulo	*pos2++ = '\0';
5956252726Srpaulo	dialog_token = atoi(pos);
5957252726Srpaulo
5958252726Srpaulo	len = os_strlen(pos2);
5959252726Srpaulo	if (len & 1)
5960252726Srpaulo		return -1;
5961252726Srpaulo	len /= 2;
5962252726Srpaulo	resp_tlvs = wpabuf_alloc(len);
5963252726Srpaulo	if (resp_tlvs == NULL)
5964252726Srpaulo		return -1;
5965252726Srpaulo	if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
5966252726Srpaulo		wpabuf_free(resp_tlvs);
5967252726Srpaulo		return -1;
5968252726Srpaulo	}
5969252726Srpaulo
5970252726Srpaulo	wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
5971252726Srpaulo	wpabuf_free(resp_tlvs);
5972252726Srpaulo	return 0;
5973252726Srpaulo}
5974252726Srpaulo
5975252726Srpaulo
5976252726Srpaulostatic int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
5977252726Srpaulo				       char *cmd)
5978252726Srpaulo{
5979252726Srpaulo	if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
5980252726Srpaulo		return -1;
5981252726Srpaulo	wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
5982252726Srpaulo	return 0;
5983252726Srpaulo}
5984252726Srpaulo
5985252726Srpaulo
5986252726Srpaulostatic int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
5987252726Srpaulo					char *cmd)
5988252726Srpaulo{
5989252726Srpaulo	char *pos;
5990252726Srpaulo	size_t len;
5991252726Srpaulo	struct wpabuf *query, *resp;
5992252726Srpaulo
5993252726Srpaulo	pos = os_strchr(cmd, ' ');
5994252726Srpaulo	if (pos == NULL)
5995252726Srpaulo		return -1;
5996252726Srpaulo	*pos++ = '\0';
5997252726Srpaulo
5998252726Srpaulo	len = os_strlen(cmd);
5999252726Srpaulo	if (len & 1)
6000252726Srpaulo		return -1;
6001252726Srpaulo	len /= 2;
6002252726Srpaulo	query = wpabuf_alloc(len);
6003252726Srpaulo	if (query == NULL)
6004252726Srpaulo		return -1;
6005252726Srpaulo	if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
6006252726Srpaulo		wpabuf_free(query);
6007252726Srpaulo		return -1;
6008252726Srpaulo	}
6009252726Srpaulo
6010252726Srpaulo	len = os_strlen(pos);
6011252726Srpaulo	if (len & 1) {
6012252726Srpaulo		wpabuf_free(query);
6013252726Srpaulo		return -1;
6014252726Srpaulo	}
6015252726Srpaulo	len /= 2;
6016252726Srpaulo	resp = wpabuf_alloc(len);
6017252726Srpaulo	if (resp == NULL) {
6018252726Srpaulo		wpabuf_free(query);
6019252726Srpaulo		return -1;
6020252726Srpaulo	}
6021252726Srpaulo	if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
6022252726Srpaulo		wpabuf_free(query);
6023252726Srpaulo		wpabuf_free(resp);
6024252726Srpaulo		return -1;
6025252726Srpaulo	}
6026252726Srpaulo
6027252726Srpaulo	if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
6028252726Srpaulo		wpabuf_free(query);
6029252726Srpaulo		wpabuf_free(resp);
6030252726Srpaulo		return -1;
6031252726Srpaulo	}
6032252726Srpaulo	return 0;
6033252726Srpaulo}
6034252726Srpaulo
6035252726Srpaulo
6036252726Srpaulostatic int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
6037252726Srpaulo{
6038252726Srpaulo	char *pos;
6039252726Srpaulo	u8 version;
6040252726Srpaulo
6041252726Srpaulo	pos = os_strchr(cmd, ' ');
6042252726Srpaulo	if (pos == NULL)
6043252726Srpaulo		return -1;
6044252726Srpaulo	*pos++ = '\0';
6045252726Srpaulo
6046252726Srpaulo	if (hexstr2bin(cmd, &version, 1) < 0)
6047252726Srpaulo		return -1;
6048252726Srpaulo
6049252726Srpaulo	return wpas_p2p_service_add_upnp(wpa_s, version, pos);
6050252726Srpaulo}
6051252726Srpaulo
6052252726Srpaulo
6053281806Srpaulostatic int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
6054281806Srpaulo				    u8 replace, char *cmd)
6055281806Srpaulo{
6056281806Srpaulo	char *pos;
6057281806Srpaulo	char *adv_str;
6058281806Srpaulo	u32 auto_accept, adv_id, svc_state, config_methods;
6059281806Srpaulo	char *svc_info = NULL;
6060289549Srpaulo	char *cpt_prio_str;
6061289549Srpaulo	u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
6062281806Srpaulo
6063281806Srpaulo	pos = os_strchr(cmd, ' ');
6064281806Srpaulo	if (pos == NULL)
6065281806Srpaulo		return -1;
6066281806Srpaulo	*pos++ = '\0';
6067281806Srpaulo
6068281806Srpaulo	/* Auto-Accept value is mandatory, and must be one of the
6069281806Srpaulo	 * single values (0, 1, 2, 4) */
6070281806Srpaulo	auto_accept = atoi(cmd);
6071281806Srpaulo	switch (auto_accept) {
6072281806Srpaulo	case P2PS_SETUP_NONE: /* No auto-accept */
6073281806Srpaulo	case P2PS_SETUP_NEW:
6074281806Srpaulo	case P2PS_SETUP_CLIENT:
6075281806Srpaulo	case P2PS_SETUP_GROUP_OWNER:
6076281806Srpaulo		break;
6077281806Srpaulo	default:
6078281806Srpaulo		return -1;
6079281806Srpaulo	}
6080281806Srpaulo
6081281806Srpaulo	/* Advertisement ID is mandatory */
6082281806Srpaulo	cmd = pos;
6083281806Srpaulo	pos = os_strchr(cmd, ' ');
6084281806Srpaulo	if (pos == NULL)
6085281806Srpaulo		return -1;
6086281806Srpaulo	*pos++ = '\0';
6087281806Srpaulo
6088281806Srpaulo	/* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
6089281806Srpaulo	if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
6090281806Srpaulo		return -1;
6091281806Srpaulo
6092281806Srpaulo	/* Only allow replacements if exist, and adds if not */
6093281806Srpaulo	if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
6094281806Srpaulo		if (!replace)
6095281806Srpaulo			return -1;
6096281806Srpaulo	} else {
6097281806Srpaulo		if (replace)
6098281806Srpaulo			return -1;
6099281806Srpaulo	}
6100281806Srpaulo
6101281806Srpaulo	/* svc_state between 0 - 0xff is mandatory */
6102281806Srpaulo	if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
6103281806Srpaulo		return -1;
6104281806Srpaulo
6105281806Srpaulo	pos = os_strchr(pos, ' ');
6106281806Srpaulo	if (pos == NULL)
6107281806Srpaulo		return -1;
6108281806Srpaulo
6109281806Srpaulo	/* config_methods is mandatory */
6110281806Srpaulo	pos++;
6111281806Srpaulo	if (sscanf(pos, "%x", &config_methods) != 1)
6112281806Srpaulo		return -1;
6113281806Srpaulo
6114281806Srpaulo	if (!(config_methods &
6115281806Srpaulo	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
6116281806Srpaulo		return -1;
6117281806Srpaulo
6118281806Srpaulo	pos = os_strchr(pos, ' ');
6119281806Srpaulo	if (pos == NULL)
6120281806Srpaulo		return -1;
6121281806Srpaulo
6122281806Srpaulo	pos++;
6123281806Srpaulo	adv_str = pos;
6124281806Srpaulo
6125281806Srpaulo	/* Advertisement string is mandatory */
6126281806Srpaulo	if (!pos[0] || pos[0] == ' ')
6127281806Srpaulo		return -1;
6128281806Srpaulo
6129281806Srpaulo	/* Terminate svc string */
6130281806Srpaulo	pos = os_strchr(pos, ' ');
6131281806Srpaulo	if (pos != NULL)
6132281806Srpaulo		*pos++ = '\0';
6133281806Srpaulo
6134289549Srpaulo	cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
6135289549Srpaulo	if (cpt_prio_str) {
6136289549Srpaulo		pos = os_strchr(pos, ' ');
6137289549Srpaulo		if (pos != NULL)
6138289549Srpaulo			*pos++ = '\0';
6139289549Srpaulo
6140289549Srpaulo		if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
6141289549Srpaulo			return -1;
6142289549Srpaulo	} else {
6143289549Srpaulo		cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
6144289549Srpaulo		cpt_prio[1] = 0;
6145289549Srpaulo	}
6146289549Srpaulo
6147281806Srpaulo	/* Service and Response Information are optional */
6148281806Srpaulo	if (pos && pos[0]) {
6149281806Srpaulo		size_t len;
6150281806Srpaulo
6151281806Srpaulo		/* Note the bare ' included, which cannot exist legally
6152281806Srpaulo		 * in unescaped string. */
6153281806Srpaulo		svc_info = os_strstr(pos, "svc_info='");
6154281806Srpaulo
6155281806Srpaulo		if (svc_info) {
6156281806Srpaulo			svc_info += 9;
6157281806Srpaulo			len = os_strlen(svc_info);
6158281806Srpaulo			utf8_unescape(svc_info, len, svc_info, len);
6159281806Srpaulo		}
6160281806Srpaulo	}
6161281806Srpaulo
6162281806Srpaulo	return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
6163281806Srpaulo					(u8) svc_state, (u16) config_methods,
6164289549Srpaulo					svc_info, cpt_prio);
6165281806Srpaulo}
6166281806Srpaulo
6167281806Srpaulo
6168252726Srpaulostatic int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
6169252726Srpaulo{
6170252726Srpaulo	char *pos;
6171252726Srpaulo
6172252726Srpaulo	pos = os_strchr(cmd, ' ');
6173252726Srpaulo	if (pos == NULL)
6174252726Srpaulo		return -1;
6175252726Srpaulo	*pos++ = '\0';
6176252726Srpaulo
6177252726Srpaulo	if (os_strcmp(cmd, "bonjour") == 0)
6178252726Srpaulo		return p2p_ctrl_service_add_bonjour(wpa_s, pos);
6179252726Srpaulo	if (os_strcmp(cmd, "upnp") == 0)
6180252726Srpaulo		return p2p_ctrl_service_add_upnp(wpa_s, pos);
6181281806Srpaulo	if (os_strcmp(cmd, "asp") == 0)
6182281806Srpaulo		return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
6183252726Srpaulo	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
6184252726Srpaulo	return -1;
6185252726Srpaulo}
6186252726Srpaulo
6187252726Srpaulo
6188252726Srpaulostatic int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
6189252726Srpaulo					char *cmd)
6190252726Srpaulo{
6191252726Srpaulo	size_t len;
6192252726Srpaulo	struct wpabuf *query;
6193252726Srpaulo	int ret;
6194252726Srpaulo
6195252726Srpaulo	len = os_strlen(cmd);
6196252726Srpaulo	if (len & 1)
6197252726Srpaulo		return -1;
6198252726Srpaulo	len /= 2;
6199252726Srpaulo	query = wpabuf_alloc(len);
6200252726Srpaulo	if (query == NULL)
6201252726Srpaulo		return -1;
6202252726Srpaulo	if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
6203252726Srpaulo		wpabuf_free(query);
6204252726Srpaulo		return -1;
6205252726Srpaulo	}
6206252726Srpaulo
6207252726Srpaulo	ret = wpas_p2p_service_del_bonjour(wpa_s, query);
6208252726Srpaulo	wpabuf_free(query);
6209252726Srpaulo	return ret;
6210252726Srpaulo}
6211252726Srpaulo
6212252726Srpaulo
6213252726Srpaulostatic int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
6214252726Srpaulo{
6215252726Srpaulo	char *pos;
6216252726Srpaulo	u8 version;
6217252726Srpaulo
6218252726Srpaulo	pos = os_strchr(cmd, ' ');
6219252726Srpaulo	if (pos == NULL)
6220252726Srpaulo		return -1;
6221252726Srpaulo	*pos++ = '\0';
6222252726Srpaulo
6223252726Srpaulo	if (hexstr2bin(cmd, &version, 1) < 0)
6224252726Srpaulo		return -1;
6225252726Srpaulo
6226252726Srpaulo	return wpas_p2p_service_del_upnp(wpa_s, version, pos);
6227252726Srpaulo}
6228252726Srpaulo
6229252726Srpaulo
6230281806Srpaulostatic int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
6231281806Srpaulo{
6232281806Srpaulo	u32 adv_id;
6233281806Srpaulo
6234289549Srpaulo	if (os_strcmp(cmd, "all") == 0) {
6235289549Srpaulo		wpas_p2p_service_flush_asp(wpa_s);
6236289549Srpaulo		return 0;
6237289549Srpaulo	}
6238289549Srpaulo
6239281806Srpaulo	if (sscanf(cmd, "%x", &adv_id) != 1)
6240281806Srpaulo		return -1;
6241281806Srpaulo
6242281806Srpaulo	return wpas_p2p_service_del_asp(wpa_s, adv_id);
6243281806Srpaulo}
6244281806Srpaulo
6245281806Srpaulo
6246252726Srpaulostatic int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
6247252726Srpaulo{
6248252726Srpaulo	char *pos;
6249252726Srpaulo
6250252726Srpaulo	pos = os_strchr(cmd, ' ');
6251252726Srpaulo	if (pos == NULL)
6252252726Srpaulo		return -1;
6253252726Srpaulo	*pos++ = '\0';
6254252726Srpaulo
6255252726Srpaulo	if (os_strcmp(cmd, "bonjour") == 0)
6256252726Srpaulo		return p2p_ctrl_service_del_bonjour(wpa_s, pos);
6257252726Srpaulo	if (os_strcmp(cmd, "upnp") == 0)
6258252726Srpaulo		return p2p_ctrl_service_del_upnp(wpa_s, pos);
6259281806Srpaulo	if (os_strcmp(cmd, "asp") == 0)
6260281806Srpaulo		return p2p_ctrl_service_del_asp(wpa_s, pos);
6261252726Srpaulo	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
6262252726Srpaulo	return -1;
6263252726Srpaulo}
6264252726Srpaulo
6265252726Srpaulo
6266281806Srpaulostatic int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
6267281806Srpaulo{
6268281806Srpaulo	char *pos;
6269281806Srpaulo
6270281806Srpaulo	pos = os_strchr(cmd, ' ');
6271281806Srpaulo	if (pos == NULL)
6272281806Srpaulo		return -1;
6273281806Srpaulo	*pos++ = '\0';
6274281806Srpaulo
6275281806Srpaulo	if (os_strcmp(cmd, "asp") == 0)
6276281806Srpaulo		return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
6277281806Srpaulo
6278281806Srpaulo	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
6279281806Srpaulo	return -1;
6280281806Srpaulo}
6281281806Srpaulo
6282281806Srpaulo
6283252726Srpaulostatic int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
6284252726Srpaulo{
6285252726Srpaulo	u8 addr[ETH_ALEN];
6286252726Srpaulo
6287252726Srpaulo	/* <addr> */
6288252726Srpaulo
6289252726Srpaulo	if (hwaddr_aton(cmd, addr))
6290252726Srpaulo		return -1;
6291252726Srpaulo
6292252726Srpaulo	return wpas_p2p_reject(wpa_s, addr);
6293252726Srpaulo}
6294252726Srpaulo
6295252726Srpaulo
6296252726Srpaulostatic int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
6297252726Srpaulo{
6298252726Srpaulo	char *pos;
6299252726Srpaulo	int id;
6300252726Srpaulo	struct wpa_ssid *ssid;
6301252726Srpaulo	u8 *_peer = NULL, peer[ETH_ALEN];
6302281806Srpaulo	int freq = 0, pref_freq = 0;
6303346981Scy	int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0;
6304252726Srpaulo
6305252726Srpaulo	id = atoi(cmd);
6306252726Srpaulo	pos = os_strstr(cmd, " peer=");
6307252726Srpaulo	if (pos) {
6308252726Srpaulo		pos += 6;
6309252726Srpaulo		if (hwaddr_aton(pos, peer))
6310252726Srpaulo			return -1;
6311252726Srpaulo		_peer = peer;
6312252726Srpaulo	}
6313252726Srpaulo	ssid = wpa_config_get_network(wpa_s->conf, id);
6314252726Srpaulo	if (ssid == NULL || ssid->disabled != 2) {
6315252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
6316252726Srpaulo			   "for persistent P2P group",
6317252726Srpaulo			   id);
6318252726Srpaulo		return -1;
6319252726Srpaulo	}
6320252726Srpaulo
6321252726Srpaulo	pos = os_strstr(cmd, " freq=");
6322252726Srpaulo	if (pos) {
6323252726Srpaulo		pos += 6;
6324252726Srpaulo		freq = atoi(pos);
6325252726Srpaulo		if (freq <= 0)
6326252726Srpaulo			return -1;
6327252726Srpaulo	}
6328252726Srpaulo
6329281806Srpaulo	pos = os_strstr(cmd, " pref=");
6330281806Srpaulo	if (pos) {
6331281806Srpaulo		pos += 6;
6332281806Srpaulo		pref_freq = atoi(pos);
6333281806Srpaulo		if (pref_freq <= 0)
6334281806Srpaulo			return -1;
6335281806Srpaulo	}
6336252726Srpaulo
6337281806Srpaulo	vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
6338281806Srpaulo	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
6339281806Srpaulo		vht;
6340346981Scy	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
6341281806Srpaulo
6342337817Scy	pos = os_strstr(cmd, "freq2=");
6343337817Scy	if (pos)
6344337817Scy		freq2 = atoi(pos + 6);
6345337817Scy
6346337817Scy	pos = os_strstr(cmd, " max_oper_chwidth=");
6347337817Scy	if (pos)
6348337817Scy		chwidth = atoi(pos + 18);
6349337817Scy
6350337817Scy	max_oper_chwidth = parse_freq(chwidth, freq2);
6351337817Scy	if (max_oper_chwidth < 0)
6352337817Scy		return -1;
6353337817Scy
6354337817Scy	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
6355346981Scy			       max_oper_chwidth, pref_freq, he);
6356252726Srpaulo}
6357252726Srpaulo
6358252726Srpaulo
6359252726Srpaulostatic int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
6360252726Srpaulo{
6361252726Srpaulo	char *pos;
6362252726Srpaulo	u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
6363252726Srpaulo
6364252726Srpaulo	pos = os_strstr(cmd, " peer=");
6365252726Srpaulo	if (!pos)
6366252726Srpaulo		return -1;
6367252726Srpaulo
6368252726Srpaulo	*pos = '\0';
6369252726Srpaulo	pos += 6;
6370252726Srpaulo	if (hwaddr_aton(pos, peer)) {
6371252726Srpaulo		wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
6372252726Srpaulo		return -1;
6373252726Srpaulo	}
6374252726Srpaulo
6375252726Srpaulo	pos = os_strstr(pos, " go_dev_addr=");
6376252726Srpaulo	if (pos) {
6377252726Srpaulo		pos += 13;
6378252726Srpaulo		if (hwaddr_aton(pos, go_dev_addr)) {
6379252726Srpaulo			wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
6380252726Srpaulo				   pos);
6381252726Srpaulo			return -1;
6382252726Srpaulo		}
6383252726Srpaulo		go_dev = go_dev_addr;
6384252726Srpaulo	}
6385252726Srpaulo
6386252726Srpaulo	return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
6387252726Srpaulo}
6388252726Srpaulo
6389252726Srpaulo
6390252726Srpaulostatic int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
6391252726Srpaulo{
6392252726Srpaulo	if (os_strncmp(cmd, "persistent=", 11) == 0)
6393252726Srpaulo		return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
6394252726Srpaulo	if (os_strncmp(cmd, "group=", 6) == 0)
6395252726Srpaulo		return p2p_ctrl_invite_group(wpa_s, cmd + 6);
6396252726Srpaulo
6397252726Srpaulo	return -1;
6398252726Srpaulo}
6399252726Srpaulo
6400252726Srpaulo
6401252726Srpaulostatic int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
6402337817Scy					 int id, int freq, int vht_center_freq2,
6403346981Scy					 int ht40, int vht, int vht_chwidth,
6404346981Scy					 int he)
6405252726Srpaulo{
6406252726Srpaulo	struct wpa_ssid *ssid;
6407252726Srpaulo
6408252726Srpaulo	ssid = wpa_config_get_network(wpa_s->conf, id);
6409252726Srpaulo	if (ssid == NULL || ssid->disabled != 2) {
6410252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
6411252726Srpaulo			   "for persistent P2P group",
6412252726Srpaulo			   id);
6413252726Srpaulo		return -1;
6414252726Srpaulo	}
6415252726Srpaulo
6416337817Scy	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
6417337817Scy					     vht_center_freq2, 0, ht40, vht,
6418346981Scy					     vht_chwidth, he, NULL, 0, 0);
6419252726Srpaulo}
6420252726Srpaulo
6421252726Srpaulo
6422252726Srpaulostatic int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
6423252726Srpaulo{
6424289549Srpaulo	int freq = 0, persistent = 0, group_id = -1;
6425289549Srpaulo	int vht = wpa_s->conf->p2p_go_vht;
6426289549Srpaulo	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
6427346981Scy	int he = wpa_s->conf->p2p_go_he;
6428337817Scy	int max_oper_chwidth, chwidth = 0, freq2 = 0;
6429289549Srpaulo	char *token, *context = NULL;
6430346981Scy#ifdef CONFIG_ACS
6431346981Scy	int acs = 0;
6432346981Scy#endif /* CONFIG_ACS */
6433252726Srpaulo
6434289549Srpaulo	while ((token = str_token(cmd, " ", &context))) {
6435346981Scy		if (sscanf(token, "freq2=%d", &freq2) == 1 ||
6436337817Scy		    sscanf(token, "persistent=%d", &group_id) == 1 ||
6437337817Scy		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
6438289549Srpaulo			continue;
6439346981Scy#ifdef CONFIG_ACS
6440346981Scy		} else if (os_strcmp(token, "freq=acs") == 0) {
6441346981Scy			acs = 1;
6442346981Scy#endif /* CONFIG_ACS */
6443346981Scy		} else if (sscanf(token, "freq=%d", &freq) == 1) {
6444346981Scy			continue;
6445289549Srpaulo		} else if (os_strcmp(token, "ht40") == 0) {
6446289549Srpaulo			ht40 = 1;
6447289549Srpaulo		} else if (os_strcmp(token, "vht") == 0) {
6448289549Srpaulo			vht = 1;
6449289549Srpaulo			ht40 = 1;
6450346981Scy		} else if (os_strcmp(token, "he") == 0) {
6451346981Scy			he = 1;
6452289549Srpaulo		} else if (os_strcmp(token, "persistent") == 0) {
6453289549Srpaulo			persistent = 1;
6454289549Srpaulo		} else {
6455289549Srpaulo			wpa_printf(MSG_DEBUG,
6456289549Srpaulo				   "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
6457289549Srpaulo				   token);
6458289549Srpaulo			return -1;
6459289549Srpaulo		}
6460289549Srpaulo	}
6461252726Srpaulo
6462346981Scy#ifdef CONFIG_ACS
6463346981Scy	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
6464346981Scy	    (acs || freq == 2 || freq == 5)) {
6465346981Scy		if (freq == 2 && wpa_s->best_24_freq <= 0) {
6466346981Scy			wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G;
6467346981Scy			wpa_s->p2p_go_do_acs = 1;
6468346981Scy			freq = 0;
6469346981Scy		} else if (freq == 5 && wpa_s->best_5_freq <= 0) {
6470346981Scy			wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211A;
6471346981Scy			wpa_s->p2p_go_do_acs = 1;
6472346981Scy			freq = 0;
6473346981Scy		} else {
6474346981Scy			wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY;
6475346981Scy			wpa_s->p2p_go_do_acs = 1;
6476346981Scy		}
6477346981Scy	} else {
6478346981Scy		wpa_s->p2p_go_do_acs = 0;
6479346981Scy	}
6480346981Scy#endif /* CONFIG_ACS */
6481346981Scy
6482337817Scy	max_oper_chwidth = parse_freq(chwidth, freq2);
6483337817Scy	if (max_oper_chwidth < 0)
6484337817Scy		return -1;
6485337817Scy
6486289549Srpaulo	if (group_id >= 0)
6487289549Srpaulo		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
6488337817Scy						     freq, freq2, ht40, vht,
6489346981Scy						     max_oper_chwidth, he);
6490252726Srpaulo
6491337817Scy	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
6492346981Scy				  max_oper_chwidth, he);
6493252726Srpaulo}
6494252726Srpaulo
6495252726Srpaulo
6496337817Scystatic int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd,
6497337817Scy				 char *buf, size_t buflen)
6498337817Scy{
6499337817Scy	u8 dev_addr[ETH_ALEN];
6500337817Scy	struct wpa_ssid *ssid;
6501337817Scy	int res;
6502337817Scy	const u8 *iaddr;
6503337817Scy
6504337817Scy	ssid = wpa_s->current_ssid;
6505337817Scy	if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO ||
6506337817Scy	    hwaddr_aton(cmd, dev_addr))
6507337817Scy		return -1;
6508337817Scy
6509337817Scy	iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr);
6510337817Scy	if (!iaddr)
6511337817Scy		return -1;
6512337817Scy	res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr));
6513337817Scy	if (os_snprintf_error(buflen, res))
6514337817Scy		return -1;
6515337817Scy	return res;
6516337817Scy}
6517337817Scy
6518337817Scy
6519346981Scystatic int wpas_find_p2p_dev_addr_bss(struct wpa_global *global,
6520346981Scy				      const u8 *p2p_dev_addr)
6521346981Scy{
6522346981Scy	struct wpa_supplicant *wpa_s;
6523346981Scy
6524346981Scy	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
6525346981Scy		if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr))
6526346981Scy			return 1;
6527346981Scy	}
6528346981Scy
6529346981Scy	return 0;
6530346981Scy}
6531346981Scy
6532346981Scy
6533252726Srpaulostatic int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
6534252726Srpaulo			 char *buf, size_t buflen)
6535252726Srpaulo{
6536346981Scy	u8 addr[ETH_ALEN], *addr_ptr, group_capab;
6537252726Srpaulo	int next, res;
6538252726Srpaulo	const struct p2p_peer_info *info;
6539252726Srpaulo	char *pos, *end;
6540252726Srpaulo	char devtype[WPS_DEV_TYPE_BUFSIZE];
6541252726Srpaulo	struct wpa_ssid *ssid;
6542252726Srpaulo	size_t i;
6543252726Srpaulo
6544252726Srpaulo	if (!wpa_s->global->p2p)
6545252726Srpaulo		return -1;
6546252726Srpaulo
6547252726Srpaulo	if (os_strcmp(cmd, "FIRST") == 0) {
6548252726Srpaulo		addr_ptr = NULL;
6549252726Srpaulo		next = 0;
6550252726Srpaulo	} else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
6551252726Srpaulo		if (hwaddr_aton(cmd + 5, addr) < 0)
6552252726Srpaulo			return -1;
6553252726Srpaulo		addr_ptr = addr;
6554252726Srpaulo		next = 1;
6555252726Srpaulo	} else {
6556252726Srpaulo		if (hwaddr_aton(cmd, addr) < 0)
6557252726Srpaulo			return -1;
6558252726Srpaulo		addr_ptr = addr;
6559252726Srpaulo		next = 0;
6560252726Srpaulo	}
6561252726Srpaulo
6562252726Srpaulo	info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
6563252726Srpaulo	if (info == NULL)
6564252726Srpaulo		return -1;
6565346981Scy	group_capab = info->group_capab;
6566252726Srpaulo
6567346981Scy	if (group_capab &&
6568346981Scy	    !wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) {
6569346981Scy		wpa_printf(MSG_DEBUG,
6570346981Scy			   "P2P: Could not find any BSS with p2p_dev_addr "
6571346981Scy			   MACSTR ", hence override group_capab from 0x%x to 0",
6572346981Scy			   MAC2STR(info->p2p_device_addr), group_capab);
6573346981Scy		group_capab = 0;
6574346981Scy	}
6575346981Scy
6576252726Srpaulo	pos = buf;
6577252726Srpaulo	end = buf + buflen;
6578252726Srpaulo
6579252726Srpaulo	res = os_snprintf(pos, end - pos, MACSTR "\n"
6580252726Srpaulo			  "pri_dev_type=%s\n"
6581252726Srpaulo			  "device_name=%s\n"
6582252726Srpaulo			  "manufacturer=%s\n"
6583252726Srpaulo			  "model_name=%s\n"
6584252726Srpaulo			  "model_number=%s\n"
6585252726Srpaulo			  "serial_number=%s\n"
6586252726Srpaulo			  "config_methods=0x%x\n"
6587252726Srpaulo			  "dev_capab=0x%x\n"
6588252726Srpaulo			  "group_capab=0x%x\n"
6589252726Srpaulo			  "level=%d\n",
6590252726Srpaulo			  MAC2STR(info->p2p_device_addr),
6591252726Srpaulo			  wps_dev_type_bin2str(info->pri_dev_type,
6592252726Srpaulo					       devtype, sizeof(devtype)),
6593252726Srpaulo			  info->device_name,
6594252726Srpaulo			  info->manufacturer,
6595252726Srpaulo			  info->model_name,
6596252726Srpaulo			  info->model_number,
6597252726Srpaulo			  info->serial_number,
6598252726Srpaulo			  info->config_methods,
6599252726Srpaulo			  info->dev_capab,
6600346981Scy			  group_capab,
6601252726Srpaulo			  info->level);
6602281806Srpaulo	if (os_snprintf_error(end - pos, res))
6603252726Srpaulo		return pos - buf;
6604252726Srpaulo	pos += res;
6605252726Srpaulo
6606252726Srpaulo	for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
6607252726Srpaulo	{
6608252726Srpaulo		const u8 *t;
6609252726Srpaulo		t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
6610252726Srpaulo		res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
6611252726Srpaulo				  wps_dev_type_bin2str(t, devtype,
6612252726Srpaulo						       sizeof(devtype)));
6613281806Srpaulo		if (os_snprintf_error(end - pos, res))
6614252726Srpaulo			return pos - buf;
6615252726Srpaulo		pos += res;
6616252726Srpaulo	}
6617252726Srpaulo
6618252726Srpaulo	ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
6619252726Srpaulo	if (ssid) {
6620252726Srpaulo		res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
6621281806Srpaulo		if (os_snprintf_error(end - pos, res))
6622252726Srpaulo			return pos - buf;
6623252726Srpaulo		pos += res;
6624252726Srpaulo	}
6625252726Srpaulo
6626252726Srpaulo	res = p2p_get_peer_info_txt(info, pos, end - pos);
6627252726Srpaulo	if (res < 0)
6628252726Srpaulo		return pos - buf;
6629252726Srpaulo	pos += res;
6630252726Srpaulo
6631281806Srpaulo	if (info->vendor_elems) {
6632281806Srpaulo		res = os_snprintf(pos, end - pos, "vendor_elems=");
6633281806Srpaulo		if (os_snprintf_error(end - pos, res))
6634281806Srpaulo			return pos - buf;
6635281806Srpaulo		pos += res;
6636281806Srpaulo
6637281806Srpaulo		pos += wpa_snprintf_hex(pos, end - pos,
6638281806Srpaulo					wpabuf_head(info->vendor_elems),
6639281806Srpaulo					wpabuf_len(info->vendor_elems));
6640281806Srpaulo
6641281806Srpaulo		res = os_snprintf(pos, end - pos, "\n");
6642281806Srpaulo		if (os_snprintf_error(end - pos, res))
6643281806Srpaulo			return pos - buf;
6644281806Srpaulo		pos += res;
6645281806Srpaulo	}
6646281806Srpaulo
6647252726Srpaulo	return pos - buf;
6648252726Srpaulo}
6649252726Srpaulo
6650252726Srpaulo
6651252726Srpaulostatic int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
6652252726Srpaulo				  const char *param)
6653252726Srpaulo{
6654281806Srpaulo	unsigned int i;
6655252726Srpaulo
6656252726Srpaulo	if (wpa_s->global->p2p == NULL)
6657252726Srpaulo		return -1;
6658252726Srpaulo
6659281806Srpaulo	if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
6660281806Srpaulo		return -1;
6661252726Srpaulo
6662281806Srpaulo	for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
6663281806Srpaulo		struct wpa_freq_range *freq;
6664281806Srpaulo		freq = &wpa_s->global->p2p_disallow_freq.range[i];
6665252726Srpaulo		wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
6666281806Srpaulo			   freq->min, freq->max);
6667252726Srpaulo	}
6668252726Srpaulo
6669289549Srpaulo	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
6670252726Srpaulo	return 0;
6671252726Srpaulo}
6672252726Srpaulo
6673252726Srpaulo
6674252726Srpaulostatic int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
6675252726Srpaulo{
6676252726Srpaulo	char *param;
6677252726Srpaulo
6678252726Srpaulo	if (wpa_s->global->p2p == NULL)
6679252726Srpaulo		return -1;
6680252726Srpaulo
6681252726Srpaulo	param = os_strchr(cmd, ' ');
6682252726Srpaulo	if (param == NULL)
6683252726Srpaulo		return -1;
6684252726Srpaulo	*param++ = '\0';
6685252726Srpaulo
6686252726Srpaulo	if (os_strcmp(cmd, "discoverability") == 0) {
6687252726Srpaulo		p2p_set_client_discoverability(wpa_s->global->p2p,
6688252726Srpaulo					       atoi(param));
6689252726Srpaulo		return 0;
6690252726Srpaulo	}
6691252726Srpaulo
6692252726Srpaulo	if (os_strcmp(cmd, "managed") == 0) {
6693252726Srpaulo		p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
6694252726Srpaulo		return 0;
6695252726Srpaulo	}
6696252726Srpaulo
6697252726Srpaulo	if (os_strcmp(cmd, "listen_channel") == 0) {
6698337817Scy		char *pos;
6699337817Scy		u8 channel, op_class;
6700337817Scy
6701337817Scy		channel = atoi(param);
6702337817Scy		pos = os_strchr(param, ' ');
6703337817Scy		op_class = pos ? atoi(pos) : 81;
6704337817Scy
6705337817Scy		return p2p_set_listen_channel(wpa_s->global->p2p, op_class,
6706337817Scy					      channel, 1);
6707252726Srpaulo	}
6708252726Srpaulo
6709252726Srpaulo	if (os_strcmp(cmd, "ssid_postfix") == 0) {
6710252726Srpaulo		return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
6711252726Srpaulo					    os_strlen(param));
6712252726Srpaulo	}
6713252726Srpaulo
6714252726Srpaulo	if (os_strcmp(cmd, "noa") == 0) {
6715252726Srpaulo		char *pos;
6716252726Srpaulo		int count, start, duration;
6717252726Srpaulo		/* GO NoA parameters: count,start_offset(ms),duration(ms) */
6718252726Srpaulo		count = atoi(param);
6719252726Srpaulo		pos = os_strchr(param, ',');
6720252726Srpaulo		if (pos == NULL)
6721252726Srpaulo			return -1;
6722252726Srpaulo		pos++;
6723252726Srpaulo		start = atoi(pos);
6724252726Srpaulo		pos = os_strchr(pos, ',');
6725252726Srpaulo		if (pos == NULL)
6726252726Srpaulo			return -1;
6727252726Srpaulo		pos++;
6728252726Srpaulo		duration = atoi(pos);
6729252726Srpaulo		if (count < 0 || count > 255 || start < 0 || duration < 0)
6730252726Srpaulo			return -1;
6731252726Srpaulo		if (count == 0 && duration > 0)
6732252726Srpaulo			return -1;
6733252726Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
6734252726Srpaulo			   "start=%d duration=%d", count, start, duration);
6735252726Srpaulo		return wpas_p2p_set_noa(wpa_s, count, start, duration);
6736252726Srpaulo	}
6737252726Srpaulo
6738252726Srpaulo	if (os_strcmp(cmd, "ps") == 0)
6739252726Srpaulo		return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
6740252726Srpaulo
6741252726Srpaulo	if (os_strcmp(cmd, "oppps") == 0)
6742252726Srpaulo		return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
6743252726Srpaulo
6744252726Srpaulo	if (os_strcmp(cmd, "ctwindow") == 0)
6745252726Srpaulo		return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
6746252726Srpaulo
6747252726Srpaulo	if (os_strcmp(cmd, "disabled") == 0) {
6748252726Srpaulo		wpa_s->global->p2p_disabled = atoi(param);
6749252726Srpaulo		wpa_printf(MSG_DEBUG, "P2P functionality %s",
6750252726Srpaulo			   wpa_s->global->p2p_disabled ?
6751252726Srpaulo			   "disabled" : "enabled");
6752252726Srpaulo		if (wpa_s->global->p2p_disabled) {
6753252726Srpaulo			wpas_p2p_stop_find(wpa_s);
6754252726Srpaulo			os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
6755252726Srpaulo			p2p_flush(wpa_s->global->p2p);
6756252726Srpaulo		}
6757252726Srpaulo		return 0;
6758252726Srpaulo	}
6759252726Srpaulo
6760252726Srpaulo	if (os_strcmp(cmd, "conc_pref") == 0) {
6761252726Srpaulo		if (os_strcmp(param, "sta") == 0)
6762252726Srpaulo			wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
6763252726Srpaulo		else if (os_strcmp(param, "p2p") == 0)
6764252726Srpaulo			wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
6765252726Srpaulo		else {
6766252726Srpaulo			wpa_printf(MSG_INFO, "Invalid conc_pref value");
6767252726Srpaulo			return -1;
6768252726Srpaulo		}
6769252726Srpaulo		wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
6770252726Srpaulo			   "%s", param);
6771252726Srpaulo		return 0;
6772252726Srpaulo	}
6773252726Srpaulo
6774252726Srpaulo	if (os_strcmp(cmd, "force_long_sd") == 0) {
6775252726Srpaulo		wpa_s->force_long_sd = atoi(param);
6776252726Srpaulo		return 0;
6777252726Srpaulo	}
6778252726Srpaulo
6779252726Srpaulo	if (os_strcmp(cmd, "peer_filter") == 0) {
6780252726Srpaulo		u8 addr[ETH_ALEN];
6781252726Srpaulo		if (hwaddr_aton(param, addr))
6782252726Srpaulo			return -1;
6783252726Srpaulo		p2p_set_peer_filter(wpa_s->global->p2p, addr);
6784252726Srpaulo		return 0;
6785252726Srpaulo	}
6786252726Srpaulo
6787252726Srpaulo	if (os_strcmp(cmd, "cross_connect") == 0)
6788252726Srpaulo		return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
6789252726Srpaulo
6790252726Srpaulo	if (os_strcmp(cmd, "go_apsd") == 0) {
6791252726Srpaulo		if (os_strcmp(param, "disable") == 0)
6792252726Srpaulo			wpa_s->set_ap_uapsd = 0;
6793252726Srpaulo		else {
6794252726Srpaulo			wpa_s->set_ap_uapsd = 1;
6795252726Srpaulo			wpa_s->ap_uapsd = atoi(param);
6796252726Srpaulo		}
6797252726Srpaulo		return 0;
6798252726Srpaulo	}
6799252726Srpaulo
6800252726Srpaulo	if (os_strcmp(cmd, "client_apsd") == 0) {
6801252726Srpaulo		if (os_strcmp(param, "disable") == 0)
6802252726Srpaulo			wpa_s->set_sta_uapsd = 0;
6803252726Srpaulo		else {
6804252726Srpaulo			int be, bk, vi, vo;
6805252726Srpaulo			char *pos;
6806252726Srpaulo			/* format: BE,BK,VI,VO;max SP Length */
6807252726Srpaulo			be = atoi(param);
6808252726Srpaulo			pos = os_strchr(param, ',');
6809252726Srpaulo			if (pos == NULL)
6810252726Srpaulo				return -1;
6811252726Srpaulo			pos++;
6812252726Srpaulo			bk = atoi(pos);
6813252726Srpaulo			pos = os_strchr(pos, ',');
6814252726Srpaulo			if (pos == NULL)
6815252726Srpaulo				return -1;
6816252726Srpaulo			pos++;
6817252726Srpaulo			vi = atoi(pos);
6818252726Srpaulo			pos = os_strchr(pos, ',');
6819252726Srpaulo			if (pos == NULL)
6820252726Srpaulo				return -1;
6821252726Srpaulo			pos++;
6822252726Srpaulo			vo = atoi(pos);
6823252726Srpaulo			/* ignore max SP Length for now */
6824252726Srpaulo
6825252726Srpaulo			wpa_s->set_sta_uapsd = 1;
6826252726Srpaulo			wpa_s->sta_uapsd = 0;
6827252726Srpaulo			if (be)
6828252726Srpaulo				wpa_s->sta_uapsd |= BIT(0);
6829252726Srpaulo			if (bk)
6830252726Srpaulo				wpa_s->sta_uapsd |= BIT(1);
6831252726Srpaulo			if (vi)
6832252726Srpaulo				wpa_s->sta_uapsd |= BIT(2);
6833252726Srpaulo			if (vo)
6834252726Srpaulo				wpa_s->sta_uapsd |= BIT(3);
6835252726Srpaulo		}
6836252726Srpaulo		return 0;
6837252726Srpaulo	}
6838252726Srpaulo
6839252726Srpaulo	if (os_strcmp(cmd, "disallow_freq") == 0)
6840252726Srpaulo		return p2p_ctrl_disallow_freq(wpa_s, param);
6841252726Srpaulo
6842252726Srpaulo	if (os_strcmp(cmd, "disc_int") == 0) {
6843252726Srpaulo		int min_disc_int, max_disc_int, max_disc_tu;
6844252726Srpaulo		char *pos;
6845252726Srpaulo
6846252726Srpaulo		pos = param;
6847252726Srpaulo
6848252726Srpaulo		min_disc_int = atoi(pos);
6849252726Srpaulo		pos = os_strchr(pos, ' ');
6850252726Srpaulo		if (pos == NULL)
6851252726Srpaulo			return -1;
6852252726Srpaulo		*pos++ = '\0';
6853252726Srpaulo
6854252726Srpaulo		max_disc_int = atoi(pos);
6855252726Srpaulo		pos = os_strchr(pos, ' ');
6856252726Srpaulo		if (pos == NULL)
6857252726Srpaulo			return -1;
6858252726Srpaulo		*pos++ = '\0';
6859252726Srpaulo
6860252726Srpaulo		max_disc_tu = atoi(pos);
6861252726Srpaulo
6862252726Srpaulo		return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
6863252726Srpaulo					max_disc_int, max_disc_tu);
6864252726Srpaulo	}
6865252726Srpaulo
6866281806Srpaulo	if (os_strcmp(cmd, "per_sta_psk") == 0) {
6867281806Srpaulo		wpa_s->global->p2p_per_sta_psk = !!atoi(param);
6868281806Srpaulo		return 0;
6869281806Srpaulo	}
6870281806Srpaulo
6871281806Srpaulo#ifdef CONFIG_WPS_NFC
6872281806Srpaulo	if (os_strcmp(cmd, "nfc_tag") == 0)
6873281806Srpaulo		return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
6874281806Srpaulo#endif /* CONFIG_WPS_NFC */
6875281806Srpaulo
6876281806Srpaulo	if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
6877281806Srpaulo		wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
6878281806Srpaulo		return 0;
6879281806Srpaulo	}
6880281806Srpaulo
6881346981Scy	if (os_strcmp(cmd, "override_pref_op_chan") == 0) {
6882346981Scy		int op_class, chan;
6883346981Scy
6884346981Scy		op_class = atoi(param);
6885346981Scy		param = os_strchr(param, ':');
6886346981Scy		if (!param)
6887346981Scy			return -1;
6888346981Scy		param++;
6889346981Scy		chan = atoi(param);
6890346981Scy		p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class,
6891346981Scy					      chan);
6892346981Scy		return 0;
6893346981Scy	}
6894346981Scy
6895252726Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
6896252726Srpaulo		   cmd);
6897252726Srpaulo
6898252726Srpaulo	return -1;
6899252726Srpaulo}
6900252726Srpaulo
6901252726Srpaulo
6902281806Srpaulostatic void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
6903281806Srpaulo{
6904281806Srpaulo	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
6905281806Srpaulo	wpa_s->force_long_sd = 0;
6906346981Scy
6907346981Scy#ifdef CONFIG_TESTING_OPTIONS
6908346981Scy	os_free(wpa_s->get_pref_freq_list_override);
6909346981Scy	wpa_s->get_pref_freq_list_override = NULL;
6910346981Scy#endif /* CONFIG_TESTING_OPTIONS */
6911346981Scy
6912281806Srpaulo	wpas_p2p_stop_find(wpa_s);
6913289549Srpaulo	wpa_s->parent->p2ps_method_config_any = 0;
6914281806Srpaulo	if (wpa_s->global->p2p)
6915281806Srpaulo		p2p_flush(wpa_s->global->p2p);
6916281806Srpaulo}
6917281806Srpaulo
6918281806Srpaulo
6919252726Srpaulostatic int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
6920252726Srpaulo{
6921252726Srpaulo	char *pos, *pos2;
6922252726Srpaulo	unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
6923252726Srpaulo
6924252726Srpaulo	if (cmd[0]) {
6925252726Srpaulo		pos = os_strchr(cmd, ' ');
6926252726Srpaulo		if (pos == NULL)
6927252726Srpaulo			return -1;
6928252726Srpaulo		*pos++ = '\0';
6929252726Srpaulo		dur1 = atoi(cmd);
6930252726Srpaulo
6931252726Srpaulo		pos2 = os_strchr(pos, ' ');
6932252726Srpaulo		if (pos2)
6933252726Srpaulo			*pos2++ = '\0';
6934252726Srpaulo		int1 = atoi(pos);
6935252726Srpaulo	} else
6936252726Srpaulo		pos2 = NULL;
6937252726Srpaulo
6938252726Srpaulo	if (pos2) {
6939252726Srpaulo		pos = os_strchr(pos2, ' ');
6940252726Srpaulo		if (pos == NULL)
6941252726Srpaulo			return -1;
6942252726Srpaulo		*pos++ = '\0';
6943252726Srpaulo		dur2 = atoi(pos2);
6944252726Srpaulo		int2 = atoi(pos);
6945252726Srpaulo	}
6946252726Srpaulo
6947252726Srpaulo	return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
6948252726Srpaulo}
6949252726Srpaulo
6950252726Srpaulo
6951252726Srpaulostatic int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
6952252726Srpaulo{
6953252726Srpaulo	char *pos;
6954252726Srpaulo	unsigned int period = 0, interval = 0;
6955252726Srpaulo
6956252726Srpaulo	if (cmd[0]) {
6957252726Srpaulo		pos = os_strchr(cmd, ' ');
6958252726Srpaulo		if (pos == NULL)
6959252726Srpaulo			return -1;
6960252726Srpaulo		*pos++ = '\0';
6961252726Srpaulo		period = atoi(cmd);
6962252726Srpaulo		interval = atoi(pos);
6963252726Srpaulo	}
6964252726Srpaulo
6965252726Srpaulo	return wpas_p2p_ext_listen(wpa_s, period, interval);
6966252726Srpaulo}
6967252726Srpaulo
6968281806Srpaulo
6969281806Srpaulostatic int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
6970281806Srpaulo{
6971281806Srpaulo	const char *pos;
6972281806Srpaulo	u8 peer[ETH_ALEN];
6973281806Srpaulo	int iface_addr = 0;
6974281806Srpaulo
6975281806Srpaulo	pos = cmd;
6976281806Srpaulo	if (os_strncmp(pos, "iface=", 6) == 0) {
6977281806Srpaulo		iface_addr = 1;
6978281806Srpaulo		pos += 6;
6979281806Srpaulo	}
6980281806Srpaulo	if (hwaddr_aton(pos, peer))
6981281806Srpaulo		return -1;
6982281806Srpaulo
6983281806Srpaulo	wpas_p2p_remove_client(wpa_s, peer, iface_addr);
6984281806Srpaulo	return 0;
6985281806Srpaulo}
6986281806Srpaulo
6987337817Scy
6988337817Scystatic int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd)
6989337817Scy{
6990337817Scy	int freq = 0, period = 0, interval = 0, count = 0;
6991337817Scy
6992337817Scy	if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4)
6993337817Scy	{
6994337817Scy		wpa_printf(MSG_DEBUG,
6995337817Scy			   "CTRL: Invalid P2P LO Start parameter: '%s'", cmd);
6996337817Scy		return -1;
6997337817Scy	}
6998337817Scy
6999337817Scy	return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
7000337817Scy}
7001337817Scy
7002252726Srpaulo#endif /* CONFIG_P2P */
7003252726Srpaulo
7004252726Srpaulo
7005281806Srpaulostatic int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
7006281806Srpaulo{
7007281806Srpaulo	struct wpa_freq_range_list ranges;
7008281806Srpaulo	int *freqs = NULL;
7009281806Srpaulo	struct hostapd_hw_modes *mode;
7010281806Srpaulo	u16 i;
7011281806Srpaulo
7012281806Srpaulo	if (wpa_s->hw.modes == NULL)
7013281806Srpaulo		return NULL;
7014281806Srpaulo
7015281806Srpaulo	os_memset(&ranges, 0, sizeof(ranges));
7016281806Srpaulo	if (freq_range_list_parse(&ranges, val) < 0)
7017281806Srpaulo		return NULL;
7018281806Srpaulo
7019281806Srpaulo	for (i = 0; i < wpa_s->hw.num_modes; i++) {
7020281806Srpaulo		int j;
7021281806Srpaulo
7022281806Srpaulo		mode = &wpa_s->hw.modes[i];
7023281806Srpaulo		for (j = 0; j < mode->num_channels; j++) {
7024281806Srpaulo			unsigned int freq;
7025281806Srpaulo
7026281806Srpaulo			if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
7027281806Srpaulo				continue;
7028281806Srpaulo
7029281806Srpaulo			freq = mode->channels[j].freq;
7030281806Srpaulo			if (!freq_range_list_includes(&ranges, freq))
7031281806Srpaulo				continue;
7032281806Srpaulo
7033281806Srpaulo			int_array_add_unique(&freqs, freq);
7034281806Srpaulo		}
7035281806Srpaulo	}
7036281806Srpaulo
7037281806Srpaulo	os_free(ranges.range);
7038281806Srpaulo	return freqs;
7039281806Srpaulo}
7040281806Srpaulo
7041281806Srpaulo
7042252726Srpaulo#ifdef CONFIG_INTERWORKING
7043281806Srpaulo
7044281806Srpaulostatic int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
7045252726Srpaulo{
7046281806Srpaulo	int auto_sel = 0;
7047281806Srpaulo	int *freqs = NULL;
7048281806Srpaulo
7049281806Srpaulo	if (param) {
7050281806Srpaulo		char *pos;
7051281806Srpaulo
7052281806Srpaulo		auto_sel = os_strstr(param, "auto") != NULL;
7053281806Srpaulo
7054281806Srpaulo		pos = os_strstr(param, "freq=");
7055281806Srpaulo		if (pos) {
7056281806Srpaulo			freqs = freq_range_to_channel_list(wpa_s, pos + 5);
7057281806Srpaulo			if (freqs == NULL)
7058281806Srpaulo				return -1;
7059281806Srpaulo		}
7060281806Srpaulo
7061281806Srpaulo	}
7062281806Srpaulo
7063281806Srpaulo	return interworking_select(wpa_s, auto_sel, freqs);
7064281806Srpaulo}
7065281806Srpaulo
7066281806Srpaulo
7067281806Srpaulostatic int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst,
7068281806Srpaulo				     int only_add)
7069281806Srpaulo{
7070252726Srpaulo	u8 bssid[ETH_ALEN];
7071252726Srpaulo	struct wpa_bss *bss;
7072252726Srpaulo
7073252726Srpaulo	if (hwaddr_aton(dst, bssid)) {
7074252726Srpaulo		wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
7075252726Srpaulo		return -1;
7076252726Srpaulo	}
7077252726Srpaulo
7078252726Srpaulo	bss = wpa_bss_get_bssid(wpa_s, bssid);
7079252726Srpaulo	if (bss == NULL) {
7080252726Srpaulo		wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
7081252726Srpaulo			   MAC2STR(bssid));
7082252726Srpaulo		return -1;
7083252726Srpaulo	}
7084252726Srpaulo
7085281806Srpaulo	if (bss->ssid_len == 0) {
7086281806Srpaulo		int found = 0;
7087281806Srpaulo
7088281806Srpaulo		wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
7089281806Srpaulo			   " does not have SSID information", MAC2STR(bssid));
7090281806Srpaulo
7091281806Srpaulo		dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
7092281806Srpaulo					 list) {
7093281806Srpaulo			if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
7094281806Srpaulo			    bss->ssid_len > 0) {
7095281806Srpaulo				found = 1;
7096281806Srpaulo				break;
7097281806Srpaulo			}
7098281806Srpaulo		}
7099281806Srpaulo
7100281806Srpaulo		if (!found)
7101281806Srpaulo			return -1;
7102281806Srpaulo		wpa_printf(MSG_DEBUG,
7103281806Srpaulo			   "Found another matching BSS entry with SSID");
7104281806Srpaulo	}
7105281806Srpaulo
7106281806Srpaulo	return interworking_connect(wpa_s, bss, only_add);
7107252726Srpaulo}
7108252726Srpaulo
7109252726Srpaulo
7110252726Srpaulostatic int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
7111252726Srpaulo{
7112252726Srpaulo	u8 dst_addr[ETH_ALEN];
7113252726Srpaulo	int used;
7114252726Srpaulo	char *pos;
7115252726Srpaulo#define MAX_ANQP_INFO_ID 100
7116252726Srpaulo	u16 id[MAX_ANQP_INFO_ID];
7117252726Srpaulo	size_t num_id = 0;
7118281806Srpaulo	u32 subtypes = 0;
7119346981Scy	u32 mbo_subtypes = 0;
7120252726Srpaulo
7121252726Srpaulo	used = hwaddr_aton2(dst, dst_addr);
7122252726Srpaulo	if (used < 0)
7123252726Srpaulo		return -1;
7124252726Srpaulo	pos = dst + used;
7125281806Srpaulo	if (*pos == ' ')
7126281806Srpaulo		pos++;
7127252726Srpaulo	while (num_id < MAX_ANQP_INFO_ID) {
7128281806Srpaulo		if (os_strncmp(pos, "hs20:", 5) == 0) {
7129281806Srpaulo#ifdef CONFIG_HS20
7130281806Srpaulo			int num = atoi(pos + 5);
7131281806Srpaulo			if (num <= 0 || num > 31)
7132281806Srpaulo				return -1;
7133281806Srpaulo			subtypes |= BIT(num);
7134281806Srpaulo#else /* CONFIG_HS20 */
7135281806Srpaulo			return -1;
7136281806Srpaulo#endif /* CONFIG_HS20 */
7137337817Scy		} else if (os_strncmp(pos, "mbo:", 4) == 0) {
7138337817Scy#ifdef CONFIG_MBO
7139337817Scy			int num = atoi(pos + 4);
7140346981Scy
7141346981Scy			if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE)
7142337817Scy				return -1;
7143346981Scy			mbo_subtypes |= BIT(num);
7144337817Scy#else /* CONFIG_MBO */
7145337817Scy			return -1;
7146337817Scy#endif /* CONFIG_MBO */
7147281806Srpaulo		} else {
7148281806Srpaulo			id[num_id] = atoi(pos);
7149281806Srpaulo			if (id[num_id])
7150281806Srpaulo				num_id++;
7151281806Srpaulo		}
7152252726Srpaulo		pos = os_strchr(pos + 1, ',');
7153252726Srpaulo		if (pos == NULL)
7154252726Srpaulo			break;
7155252726Srpaulo		pos++;
7156252726Srpaulo	}
7157252726Srpaulo
7158346981Scy	if (num_id == 0 && !subtypes && !mbo_subtypes)
7159252726Srpaulo		return -1;
7160252726Srpaulo
7161337817Scy	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
7162346981Scy			     mbo_subtypes);
7163252726Srpaulo}
7164252726Srpaulo
7165252726Srpaulo
7166252726Srpaulostatic int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
7167252726Srpaulo{
7168252726Srpaulo	u8 dst_addr[ETH_ALEN];
7169252726Srpaulo	struct wpabuf *advproto, *query = NULL;
7170252726Srpaulo	int used, ret = -1;
7171252726Srpaulo	char *pos, *end;
7172252726Srpaulo	size_t len;
7173252726Srpaulo
7174252726Srpaulo	used = hwaddr_aton2(cmd, dst_addr);
7175252726Srpaulo	if (used < 0)
7176252726Srpaulo		return -1;
7177252726Srpaulo
7178252726Srpaulo	pos = cmd + used;
7179252726Srpaulo	while (*pos == ' ')
7180252726Srpaulo		pos++;
7181252726Srpaulo
7182252726Srpaulo	/* Advertisement Protocol ID */
7183252726Srpaulo	end = os_strchr(pos, ' ');
7184252726Srpaulo	if (end)
7185252726Srpaulo		len = end - pos;
7186252726Srpaulo	else
7187252726Srpaulo		len = os_strlen(pos);
7188252726Srpaulo	if (len & 0x01)
7189252726Srpaulo		return -1;
7190252726Srpaulo	len /= 2;
7191252726Srpaulo	if (len == 0)
7192252726Srpaulo		return -1;
7193252726Srpaulo	advproto = wpabuf_alloc(len);
7194252726Srpaulo	if (advproto == NULL)
7195252726Srpaulo		return -1;
7196252726Srpaulo	if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
7197252726Srpaulo		goto fail;
7198252726Srpaulo
7199252726Srpaulo	if (end) {
7200252726Srpaulo		/* Optional Query Request */
7201252726Srpaulo		pos = end + 1;
7202252726Srpaulo		while (*pos == ' ')
7203252726Srpaulo			pos++;
7204252726Srpaulo
7205252726Srpaulo		len = os_strlen(pos);
7206252726Srpaulo		if (len) {
7207252726Srpaulo			if (len & 0x01)
7208252726Srpaulo				goto fail;
7209252726Srpaulo			len /= 2;
7210252726Srpaulo			if (len == 0)
7211252726Srpaulo				goto fail;
7212252726Srpaulo			query = wpabuf_alloc(len);
7213252726Srpaulo			if (query == NULL)
7214252726Srpaulo				goto fail;
7215252726Srpaulo			if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
7216252726Srpaulo				goto fail;
7217252726Srpaulo		}
7218252726Srpaulo	}
7219252726Srpaulo
7220252726Srpaulo	ret = gas_send_request(wpa_s, dst_addr, advproto, query);
7221252726Srpaulo
7222252726Srpaulofail:
7223252726Srpaulo	wpabuf_free(advproto);
7224252726Srpaulo	wpabuf_free(query);
7225252726Srpaulo
7226252726Srpaulo	return ret;
7227252726Srpaulo}
7228252726Srpaulo
7229252726Srpaulo
7230252726Srpaulostatic int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
7231252726Srpaulo			    size_t buflen)
7232252726Srpaulo{
7233252726Srpaulo	u8 addr[ETH_ALEN];
7234252726Srpaulo	int dialog_token;
7235252726Srpaulo	int used;
7236252726Srpaulo	char *pos;
7237252726Srpaulo	size_t resp_len, start, requested_len;
7238281806Srpaulo	struct wpabuf *resp;
7239281806Srpaulo	int ret;
7240252726Srpaulo
7241252726Srpaulo	used = hwaddr_aton2(cmd, addr);
7242252726Srpaulo	if (used < 0)
7243252726Srpaulo		return -1;
7244252726Srpaulo
7245252726Srpaulo	pos = cmd + used;
7246252726Srpaulo	while (*pos == ' ')
7247252726Srpaulo		pos++;
7248252726Srpaulo	dialog_token = atoi(pos);
7249252726Srpaulo
7250281806Srpaulo	if (wpa_s->last_gas_resp &&
7251281806Srpaulo	    os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
7252281806Srpaulo	    dialog_token == wpa_s->last_gas_dialog_token)
7253281806Srpaulo		resp = wpa_s->last_gas_resp;
7254281806Srpaulo	else if (wpa_s->prev_gas_resp &&
7255281806Srpaulo		 os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
7256281806Srpaulo		 dialog_token == wpa_s->prev_gas_dialog_token)
7257281806Srpaulo		resp = wpa_s->prev_gas_resp;
7258281806Srpaulo	else
7259252726Srpaulo		return -1;
7260252726Srpaulo
7261281806Srpaulo	resp_len = wpabuf_len(resp);
7262252726Srpaulo	start = 0;
7263252726Srpaulo	requested_len = resp_len;
7264252726Srpaulo
7265252726Srpaulo	pos = os_strchr(pos, ' ');
7266252726Srpaulo	if (pos) {
7267252726Srpaulo		start = atoi(pos);
7268252726Srpaulo		if (start > resp_len)
7269252726Srpaulo			return os_snprintf(buf, buflen, "FAIL-Invalid range");
7270252726Srpaulo		pos = os_strchr(pos, ',');
7271252726Srpaulo		if (pos == NULL)
7272252726Srpaulo			return -1;
7273252726Srpaulo		pos++;
7274252726Srpaulo		requested_len = atoi(pos);
7275252726Srpaulo		if (start + requested_len > resp_len)
7276252726Srpaulo			return os_snprintf(buf, buflen, "FAIL-Invalid range");
7277252726Srpaulo	}
7278252726Srpaulo
7279252726Srpaulo	if (requested_len * 2 + 1 > buflen)
7280252726Srpaulo		return os_snprintf(buf, buflen, "FAIL-Too long response");
7281252726Srpaulo
7282281806Srpaulo	ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
7283281806Srpaulo			       requested_len);
7284281806Srpaulo
7285281806Srpaulo	if (start + requested_len == resp_len) {
7286281806Srpaulo		/*
7287281806Srpaulo		 * Free memory by dropping the response after it has been
7288281806Srpaulo		 * fetched.
7289281806Srpaulo		 */
7290281806Srpaulo		if (resp == wpa_s->prev_gas_resp) {
7291281806Srpaulo			wpabuf_free(wpa_s->prev_gas_resp);
7292281806Srpaulo			wpa_s->prev_gas_resp = NULL;
7293281806Srpaulo		} else {
7294281806Srpaulo			wpabuf_free(wpa_s->last_gas_resp);
7295281806Srpaulo			wpa_s->last_gas_resp = NULL;
7296281806Srpaulo		}
7297281806Srpaulo	}
7298281806Srpaulo
7299281806Srpaulo	return ret;
7300252726Srpaulo}
7301252726Srpaulo#endif /* CONFIG_INTERWORKING */
7302252726Srpaulo
7303252726Srpaulo
7304252726Srpaulo#ifdef CONFIG_HS20
7305252726Srpaulo
7306252726Srpaulostatic int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
7307252726Srpaulo{
7308252726Srpaulo	u8 dst_addr[ETH_ALEN];
7309252726Srpaulo	int used;
7310252726Srpaulo	char *pos;
7311252726Srpaulo	u32 subtypes = 0;
7312252726Srpaulo
7313252726Srpaulo	used = hwaddr_aton2(dst, dst_addr);
7314252726Srpaulo	if (used < 0)
7315252726Srpaulo		return -1;
7316252726Srpaulo	pos = dst + used;
7317281806Srpaulo	if (*pos == ' ')
7318281806Srpaulo		pos++;
7319252726Srpaulo	for (;;) {
7320252726Srpaulo		int num = atoi(pos);
7321252726Srpaulo		if (num <= 0 || num > 31)
7322252726Srpaulo			return -1;
7323252726Srpaulo		subtypes |= BIT(num);
7324252726Srpaulo		pos = os_strchr(pos + 1, ',');
7325252726Srpaulo		if (pos == NULL)
7326252726Srpaulo			break;
7327252726Srpaulo		pos++;
7328252726Srpaulo	}
7329252726Srpaulo
7330252726Srpaulo	if (subtypes == 0)
7331252726Srpaulo		return -1;
7332252726Srpaulo
7333337817Scy	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0);
7334252726Srpaulo}
7335252726Srpaulo
7336252726Srpaulo
7337252726Srpaulostatic int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
7338252726Srpaulo				    const u8 *addr, const char *realm)
7339252726Srpaulo{
7340252726Srpaulo	u8 *buf;
7341252726Srpaulo	size_t rlen, len;
7342252726Srpaulo	int ret;
7343252726Srpaulo
7344252726Srpaulo	rlen = os_strlen(realm);
7345252726Srpaulo	len = 3 + rlen;
7346252726Srpaulo	buf = os_malloc(len);
7347252726Srpaulo	if (buf == NULL)
7348252726Srpaulo		return -1;
7349252726Srpaulo	buf[0] = 1; /* NAI Home Realm Count */
7350252726Srpaulo	buf[1] = 0; /* Formatted in accordance with RFC 4282 */
7351252726Srpaulo	buf[2] = rlen;
7352252726Srpaulo	os_memcpy(buf + 3, realm, rlen);
7353252726Srpaulo
7354252726Srpaulo	ret = hs20_anqp_send_req(wpa_s, addr,
7355252726Srpaulo				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
7356337817Scy				 buf, len, 0);
7357252726Srpaulo
7358252726Srpaulo	os_free(buf);
7359252726Srpaulo
7360252726Srpaulo	return ret;
7361252726Srpaulo}
7362252726Srpaulo
7363252726Srpaulo
7364252726Srpaulostatic int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
7365252726Srpaulo					char *dst)
7366252726Srpaulo{
7367252726Srpaulo	struct wpa_cred *cred = wpa_s->conf->cred;
7368252726Srpaulo	u8 dst_addr[ETH_ALEN];
7369252726Srpaulo	int used;
7370252726Srpaulo	u8 *buf;
7371252726Srpaulo	size_t len;
7372252726Srpaulo	int ret;
7373252726Srpaulo
7374252726Srpaulo	used = hwaddr_aton2(dst, dst_addr);
7375252726Srpaulo	if (used < 0)
7376252726Srpaulo		return -1;
7377252726Srpaulo
7378252726Srpaulo	while (dst[used] == ' ')
7379252726Srpaulo		used++;
7380252726Srpaulo	if (os_strncmp(dst + used, "realm=", 6) == 0)
7381252726Srpaulo		return hs20_nai_home_realm_list(wpa_s, dst_addr,
7382252726Srpaulo						dst + used + 6);
7383252726Srpaulo
7384252726Srpaulo	len = os_strlen(dst + used);
7385252726Srpaulo
7386252726Srpaulo	if (len == 0 && cred && cred->realm)
7387252726Srpaulo		return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
7388252726Srpaulo
7389281806Srpaulo	if (len & 1)
7390252726Srpaulo		return -1;
7391252726Srpaulo	len /= 2;
7392252726Srpaulo	buf = os_malloc(len);
7393252726Srpaulo	if (buf == NULL)
7394252726Srpaulo		return -1;
7395252726Srpaulo	if (hexstr2bin(dst + used, buf, len) < 0) {
7396252726Srpaulo		os_free(buf);
7397252726Srpaulo		return -1;
7398252726Srpaulo	}
7399252726Srpaulo
7400252726Srpaulo	ret = hs20_anqp_send_req(wpa_s, dst_addr,
7401252726Srpaulo				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
7402337817Scy				 buf, len, 0);
7403252726Srpaulo	os_free(buf);
7404252726Srpaulo
7405252726Srpaulo	return ret;
7406252726Srpaulo}
7407252726Srpaulo
7408252726Srpaulo
7409337817Scystatic int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply,
7410337817Scy			 int buflen)
7411281806Srpaulo{
7412281806Srpaulo	u8 dst_addr[ETH_ALEN];
7413281806Srpaulo	int used;
7414337817Scy	char *ctx = NULL, *icon, *poffset, *psize;
7415337817Scy
7416337817Scy	used = hwaddr_aton2(cmd, dst_addr);
7417337817Scy	if (used < 0)
7418337817Scy		return -1;
7419337817Scy	cmd += used;
7420337817Scy
7421337817Scy	icon = str_token(cmd, " ", &ctx);
7422337817Scy	poffset = str_token(cmd, " ", &ctx);
7423337817Scy	psize = str_token(cmd, " ", &ctx);
7424337817Scy	if (!icon || !poffset || !psize)
7425337817Scy		return -1;
7426337817Scy
7427337817Scy	wpa_s->fetch_osu_icon_in_progress = 0;
7428337817Scy	return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize),
7429337817Scy			     reply, buflen);
7430337817Scy}
7431337817Scy
7432337817Scy
7433337817Scystatic int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd)
7434337817Scy{
7435337817Scy	u8 dst_addr[ETH_ALEN];
7436337817Scy	int used;
7437281806Srpaulo	char *icon;
7438252726Srpaulo
7439337817Scy	if (!cmd[0])
7440337817Scy		return hs20_del_icon(wpa_s, NULL, NULL);
7441337817Scy
7442281806Srpaulo	used = hwaddr_aton2(cmd, dst_addr);
7443281806Srpaulo	if (used < 0)
7444281806Srpaulo		return -1;
7445281806Srpaulo
7446281806Srpaulo	while (cmd[used] == ' ')
7447281806Srpaulo		used++;
7448337817Scy	icon = cmd[used] ? &cmd[used] : NULL;
7449337817Scy
7450337817Scy	return hs20_del_icon(wpa_s, dst_addr, icon);
7451337817Scy}
7452337817Scy
7453337817Scy
7454337817Scystatic int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem)
7455337817Scy{
7456337817Scy	u8 dst_addr[ETH_ALEN];
7457337817Scy	int used;
7458337817Scy	char *icon;
7459337817Scy
7460337817Scy	used = hwaddr_aton2(cmd, dst_addr);
7461337817Scy	if (used < 0)
7462337817Scy		return -1;
7463337817Scy
7464337817Scy	while (cmd[used] == ' ')
7465337817Scy		used++;
7466281806Srpaulo	icon = &cmd[used];
7467281806Srpaulo
7468281806Srpaulo	wpa_s->fetch_osu_icon_in_progress = 0;
7469281806Srpaulo	return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
7470337817Scy				  (u8 *) icon, os_strlen(icon), inmem);
7471252726Srpaulo}
7472252726Srpaulo
7473281806Srpaulo#endif /* CONFIG_HS20 */
7474252726Srpaulo
7475281806Srpaulo
7476252726Srpaulo#ifdef CONFIG_AUTOSCAN
7477252726Srpaulo
7478252726Srpaulostatic int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
7479252726Srpaulo					      char *cmd)
7480252726Srpaulo{
7481252726Srpaulo	enum wpa_states state = wpa_s->wpa_state;
7482252726Srpaulo	char *new_params = NULL;
7483252726Srpaulo
7484252726Srpaulo	if (os_strlen(cmd) > 0) {
7485252726Srpaulo		new_params = os_strdup(cmd);
7486252726Srpaulo		if (new_params == NULL)
7487252726Srpaulo			return -1;
7488252726Srpaulo	}
7489252726Srpaulo
7490252726Srpaulo	os_free(wpa_s->conf->autoscan);
7491252726Srpaulo	wpa_s->conf->autoscan = new_params;
7492252726Srpaulo
7493252726Srpaulo	if (wpa_s->conf->autoscan == NULL)
7494252726Srpaulo		autoscan_deinit(wpa_s);
7495252726Srpaulo	else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
7496252726Srpaulo		autoscan_init(wpa_s, 1);
7497252726Srpaulo	else if (state == WPA_SCANNING)
7498252726Srpaulo		wpa_supplicant_reinit_autoscan(wpa_s);
7499346981Scy	else
7500346981Scy		wpa_printf(MSG_DEBUG, "No autoscan update in state %s",
7501346981Scy			   wpa_supplicant_state_txt(state));
7502252726Srpaulo
7503252726Srpaulo	return 0;
7504252726Srpaulo}
7505252726Srpaulo
7506252726Srpaulo#endif /* CONFIG_AUTOSCAN */
7507252726Srpaulo
7508252726Srpaulo
7509252726Srpaulo#ifdef CONFIG_WNM
7510252726Srpaulo
7511252726Srpaulostatic int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
7512252726Srpaulo{
7513252726Srpaulo	int enter;
7514252726Srpaulo	int intval = 0;
7515252726Srpaulo	char *pos;
7516252726Srpaulo	int ret;
7517252726Srpaulo	struct wpabuf *tfs_req = NULL;
7518252726Srpaulo
7519252726Srpaulo	if (os_strncmp(cmd, "enter", 5) == 0)
7520252726Srpaulo		enter = 1;
7521252726Srpaulo	else if (os_strncmp(cmd, "exit", 4) == 0)
7522252726Srpaulo		enter = 0;
7523252726Srpaulo	else
7524252726Srpaulo		return -1;
7525252726Srpaulo
7526252726Srpaulo	pos = os_strstr(cmd, " interval=");
7527252726Srpaulo	if (pos)
7528252726Srpaulo		intval = atoi(pos + 10);
7529252726Srpaulo
7530252726Srpaulo	pos = os_strstr(cmd, " tfs_req=");
7531252726Srpaulo	if (pos) {
7532252726Srpaulo		char *end;
7533252726Srpaulo		size_t len;
7534252726Srpaulo		pos += 9;
7535252726Srpaulo		end = os_strchr(pos, ' ');
7536252726Srpaulo		if (end)
7537252726Srpaulo			len = end - pos;
7538252726Srpaulo		else
7539252726Srpaulo			len = os_strlen(pos);
7540252726Srpaulo		if (len & 1)
7541252726Srpaulo			return -1;
7542252726Srpaulo		len /= 2;
7543252726Srpaulo		tfs_req = wpabuf_alloc(len);
7544252726Srpaulo		if (tfs_req == NULL)
7545252726Srpaulo			return -1;
7546252726Srpaulo		if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
7547252726Srpaulo			wpabuf_free(tfs_req);
7548252726Srpaulo			return -1;
7549252726Srpaulo		}
7550252726Srpaulo	}
7551252726Srpaulo
7552252726Srpaulo	ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
7553252726Srpaulo					   WNM_SLEEP_MODE_EXIT, intval,
7554252726Srpaulo					   tfs_req);
7555252726Srpaulo	wpabuf_free(tfs_req);
7556252726Srpaulo
7557252726Srpaulo	return ret;
7558252726Srpaulo}
7559252726Srpaulo
7560281806Srpaulo
7561281806Srpaulostatic int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
7562281806Srpaulo{
7563337817Scy	int query_reason, list = 0;
7564346981Scy	char *btm_candidates = NULL;
7565281806Srpaulo
7566281806Srpaulo	query_reason = atoi(cmd);
7567281806Srpaulo
7568337817Scy	cmd = os_strchr(cmd, ' ');
7569337817Scy	if (cmd) {
7570346981Scy		if (os_strncmp(cmd, " list", 5) == 0)
7571337817Scy			list = 1;
7572346981Scy		else
7573346981Scy			btm_candidates = cmd;
7574337817Scy	}
7575281806Srpaulo
7576337817Scy	wpa_printf(MSG_DEBUG,
7577337817Scy		   "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
7578337817Scy		   query_reason, list ? " candidate list" : "");
7579337817Scy
7580346981Scy	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason,
7581346981Scy						  btm_candidates,
7582346981Scy						  list);
7583281806Srpaulo}
7584281806Srpaulo
7585346981Scy
7586346981Scystatic int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s,
7587346981Scy					     char *cmd)
7588346981Scy{
7589346981Scy	struct wpabuf *elems;
7590346981Scy	int ret;
7591346981Scy
7592346981Scy	elems = wpabuf_parse_bin(cmd);
7593346981Scy	if (!elems)
7594346981Scy		return -1;
7595346981Scy
7596346981Scy	ret = wnm_send_coloc_intf_report(wpa_s, 0, elems);
7597346981Scy	wpabuf_free(elems);
7598346981Scy	return ret;
7599346981Scy}
7600346981Scy
7601252726Srpaulo#endif /* CONFIG_WNM */
7602252726Srpaulo
7603252726Srpaulo
7604252726Srpaulostatic int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
7605252726Srpaulo				      size_t buflen)
7606252726Srpaulo{
7607252726Srpaulo	struct wpa_signal_info si;
7608252726Srpaulo	int ret;
7609281806Srpaulo	char *pos, *end;
7610252726Srpaulo
7611252726Srpaulo	ret = wpa_drv_signal_poll(wpa_s, &si);
7612252726Srpaulo	if (ret)
7613252726Srpaulo		return -1;
7614252726Srpaulo
7615281806Srpaulo	pos = buf;
7616281806Srpaulo	end = buf + buflen;
7617281806Srpaulo
7618281806Srpaulo	ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
7619252726Srpaulo			  "NOISE=%d\nFREQUENCY=%u\n",
7620252726Srpaulo			  si.current_signal, si.current_txrate / 1000,
7621252726Srpaulo			  si.current_noise, si.frequency);
7622281806Srpaulo	if (os_snprintf_error(end - pos, ret))
7623252726Srpaulo		return -1;
7624281806Srpaulo	pos += ret;
7625281806Srpaulo
7626281806Srpaulo	if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
7627281806Srpaulo		ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
7628281806Srpaulo				  channel_width_to_string(si.chanwidth));
7629281806Srpaulo		if (os_snprintf_error(end - pos, ret))
7630281806Srpaulo			return -1;
7631281806Srpaulo		pos += ret;
7632281806Srpaulo	}
7633281806Srpaulo
7634346981Scy	if (si.center_frq1 > 0) {
7635346981Scy		ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
7636346981Scy				  si.center_frq1);
7637281806Srpaulo		if (os_snprintf_error(end - pos, ret))
7638281806Srpaulo			return -1;
7639281806Srpaulo		pos += ret;
7640281806Srpaulo	}
7641281806Srpaulo
7642346981Scy	if (si.center_frq2 > 0) {
7643346981Scy		ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
7644346981Scy				  si.center_frq2);
7645346981Scy		if (os_snprintf_error(end - pos, ret))
7646346981Scy			return -1;
7647346981Scy		pos += ret;
7648346981Scy	}
7649346981Scy
7650281806Srpaulo	if (si.avg_signal) {
7651281806Srpaulo		ret = os_snprintf(pos, end - pos,
7652281806Srpaulo				  "AVG_RSSI=%d\n", si.avg_signal);
7653281806Srpaulo		if (os_snprintf_error(end - pos, ret))
7654281806Srpaulo			return -1;
7655281806Srpaulo		pos += ret;
7656281806Srpaulo	}
7657281806Srpaulo
7658289549Srpaulo	if (si.avg_beacon_signal) {
7659289549Srpaulo		ret = os_snprintf(pos, end - pos,
7660289549Srpaulo				  "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
7661289549Srpaulo		if (os_snprintf_error(end - pos, ret))
7662289549Srpaulo			return -1;
7663289549Srpaulo		pos += ret;
7664289549Srpaulo	}
7665289549Srpaulo
7666281806Srpaulo	return pos - buf;
7667252726Srpaulo}
7668252726Srpaulo
7669252726Srpaulo
7670337817Scystatic int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s,
7671337817Scy					  const char *cmd)
7672337817Scy{
7673337817Scy	const char *pos;
7674337817Scy	int threshold = 0;
7675337817Scy	int hysteresis = 0;
7676337817Scy
7677337817Scy	if (wpa_s->bgscan && wpa_s->bgscan_priv) {
7678337817Scy		wpa_printf(MSG_DEBUG,
7679337817Scy			   "Reject SIGNAL_MONITOR command - bgscan is active");
7680337817Scy		return -1;
7681337817Scy	}
7682337817Scy	pos = os_strstr(cmd, "THRESHOLD=");
7683337817Scy	if (pos)
7684337817Scy		threshold = atoi(pos + 10);
7685337817Scy	pos = os_strstr(cmd, "HYSTERESIS=");
7686337817Scy	if (pos)
7687337817Scy		hysteresis = atoi(pos + 11);
7688337817Scy	return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis);
7689337817Scy}
7690337817Scy
7691337817Scy
7692346981Scy#ifdef CONFIG_TESTING_OPTIONS
7693346981Scyint wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
7694346981Scy						enum wpa_driver_if_type if_type,
7695346981Scy						unsigned int *num,
7696346981Scy						unsigned int *freq_list)
7697346981Scy{
7698346981Scy	char *pos = wpa_s->get_pref_freq_list_override;
7699346981Scy	char *end;
7700346981Scy	unsigned int count = 0;
7701346981Scy
7702346981Scy	/* Override string format:
7703346981Scy	 *  <if_type1>:<freq1>,<freq2>,... <if_type2>:... */
7704346981Scy
7705346981Scy	while (pos) {
7706346981Scy		if (atoi(pos) == (int) if_type)
7707346981Scy			break;
7708346981Scy		pos = os_strchr(pos, ' ');
7709346981Scy		if (pos)
7710346981Scy			pos++;
7711346981Scy	}
7712346981Scy	if (!pos)
7713346981Scy		return -1;
7714346981Scy	pos = os_strchr(pos, ':');
7715346981Scy	if (!pos)
7716346981Scy		return -1;
7717346981Scy	pos++;
7718346981Scy	end = os_strchr(pos, ' ');
7719346981Scy	while (pos && (!end || pos < end) && count < *num) {
7720346981Scy		freq_list[count++] = atoi(pos);
7721346981Scy		pos = os_strchr(pos, ',');
7722346981Scy		if (pos)
7723346981Scy			pos++;
7724346981Scy	}
7725346981Scy
7726346981Scy	*num = count;
7727346981Scy	return 0;
7728346981Scy}
7729346981Scy#endif /* CONFIG_TESTING_OPTIONS */
7730346981Scy
7731346981Scy
7732289549Srpaulostatic int wpas_ctrl_iface_get_pref_freq_list(
7733289549Srpaulo	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
7734289549Srpaulo{
7735289549Srpaulo	unsigned int freq_list[100], num = 100, i;
7736289549Srpaulo	int ret;
7737289549Srpaulo	enum wpa_driver_if_type iface_type;
7738289549Srpaulo	char *pos, *end;
7739289549Srpaulo
7740289549Srpaulo	pos = buf;
7741289549Srpaulo	end = buf + buflen;
7742289549Srpaulo
7743289549Srpaulo	/* buf: "<interface_type>" */
7744289549Srpaulo	if (os_strcmp(cmd, "STATION") == 0)
7745289549Srpaulo		iface_type = WPA_IF_STATION;
7746289549Srpaulo	else if (os_strcmp(cmd, "AP") == 0)
7747289549Srpaulo		iface_type = WPA_IF_AP_BSS;
7748289549Srpaulo	else if (os_strcmp(cmd, "P2P_GO") == 0)
7749289549Srpaulo		iface_type = WPA_IF_P2P_GO;
7750289549Srpaulo	else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
7751289549Srpaulo		iface_type = WPA_IF_P2P_CLIENT;
7752289549Srpaulo	else if (os_strcmp(cmd, "IBSS") == 0)
7753289549Srpaulo		iface_type = WPA_IF_IBSS;
7754289549Srpaulo	else if (os_strcmp(cmd, "TDLS") == 0)
7755289549Srpaulo		iface_type = WPA_IF_TDLS;
7756289549Srpaulo	else
7757289549Srpaulo		return -1;
7758289549Srpaulo
7759289549Srpaulo	wpa_printf(MSG_DEBUG,
7760289549Srpaulo		   "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
7761346981Scy		   iface_type, cmd);
7762289549Srpaulo
7763289549Srpaulo	ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
7764289549Srpaulo	if (ret)
7765289549Srpaulo		return -1;
7766289549Srpaulo
7767289549Srpaulo	for (i = 0; i < num; i++) {
7768289549Srpaulo		ret = os_snprintf(pos, end - pos, "%s%u",
7769289549Srpaulo				  i > 0 ? "," : "", freq_list[i]);
7770289549Srpaulo		if (os_snprintf_error(end - pos, ret))
7771289549Srpaulo			return -1;
7772289549Srpaulo		pos += ret;
7773289549Srpaulo	}
7774289549Srpaulo
7775289549Srpaulo	return pos - buf;
7776289549Srpaulo}
7777289549Srpaulo
7778289549Srpaulo
7779337817Scystatic int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s,
7780337817Scy					char *buf, size_t buflen)
7781337817Scy{
7782337817Scy	int ret, i;
7783337817Scy	char *pos, *end;
7784337817Scy
7785337817Scy	ret = os_snprintf(buf, buflen, "%016llX:\n",
7786337817Scy			  (long long unsigned) wpa_s->drv_flags);
7787337817Scy	if (os_snprintf_error(buflen, ret))
7788337817Scy		return -1;
7789337817Scy
7790337817Scy	pos = buf + ret;
7791337817Scy	end = buf + buflen;
7792337817Scy
7793337817Scy	for (i = 0; i < 64; i++) {
7794337817Scy		if (wpa_s->drv_flags & (1LLU << i)) {
7795337817Scy			ret = os_snprintf(pos, end - pos, "%s\n",
7796337817Scy					  driver_flag_to_string(1LLU << i));
7797337817Scy			if (os_snprintf_error(end - pos, ret))
7798337817Scy				return -1;
7799337817Scy			pos += ret;
7800337817Scy		}
7801337817Scy	}
7802337817Scy
7803337817Scy	return pos - buf;
7804337817Scy}
7805337817Scy
7806337817Scy
7807252726Srpaulostatic int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
7808252726Srpaulo				      size_t buflen)
7809252726Srpaulo{
7810252726Srpaulo	struct hostap_sta_driver_data sta;
7811252726Srpaulo	int ret;
7812252726Srpaulo
7813252726Srpaulo	ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
7814252726Srpaulo	if (ret)
7815252726Srpaulo		return -1;
7816252726Srpaulo
7817252726Srpaulo	ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
7818252726Srpaulo			  sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
7819281806Srpaulo	if (os_snprintf_error(buflen, ret))
7820252726Srpaulo		return -1;
7821252726Srpaulo	return ret;
7822252726Srpaulo}
7823252726Srpaulo
7824252726Srpaulo
7825281806Srpaulo#ifdef ANDROID
7826281806Srpaulostatic int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
7827281806Srpaulo				     char *buf, size_t buflen)
7828281806Srpaulo{
7829281806Srpaulo	int ret;
7830281806Srpaulo
7831281806Srpaulo	ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
7832281806Srpaulo	if (ret == 0) {
7833281806Srpaulo		if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
7834281806Srpaulo			struct p2p_data *p2p = wpa_s->global->p2p;
7835281806Srpaulo			if (p2p) {
7836281806Srpaulo				char country[3];
7837281806Srpaulo				country[0] = cmd[8];
7838281806Srpaulo				country[1] = cmd[9];
7839281806Srpaulo				country[2] = 0x04;
7840281806Srpaulo				p2p_set_country(p2p, country);
7841281806Srpaulo			}
7842281806Srpaulo		}
7843281806Srpaulo		ret = os_snprintf(buf, buflen, "%s\n", "OK");
7844281806Srpaulo		if (os_snprintf_error(buflen, ret))
7845281806Srpaulo			ret = -1;
7846281806Srpaulo	}
7847281806Srpaulo	return ret;
7848281806Srpaulo}
7849281806Srpaulo#endif /* ANDROID */
7850281806Srpaulo
7851281806Srpaulo
7852281806Srpaulostatic int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
7853281806Srpaulo				     char *buf, size_t buflen)
7854281806Srpaulo{
7855281806Srpaulo	int ret;
7856281806Srpaulo	char *pos;
7857281806Srpaulo	u8 *data = NULL;
7858281806Srpaulo	unsigned int vendor_id, subcmd;
7859281806Srpaulo	struct wpabuf *reply;
7860281806Srpaulo	size_t data_len = 0;
7861281806Srpaulo
7862281806Srpaulo	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
7863281806Srpaulo	vendor_id = strtoul(cmd, &pos, 16);
7864337817Scy	if (!isblank((unsigned char) *pos))
7865281806Srpaulo		return -EINVAL;
7866281806Srpaulo
7867281806Srpaulo	subcmd = strtoul(pos, &pos, 10);
7868281806Srpaulo
7869281806Srpaulo	if (*pos != '\0') {
7870337817Scy		if (!isblank((unsigned char) *pos++))
7871281806Srpaulo			return -EINVAL;
7872281806Srpaulo		data_len = os_strlen(pos);
7873281806Srpaulo	}
7874281806Srpaulo
7875281806Srpaulo	if (data_len) {
7876281806Srpaulo		data_len /= 2;
7877281806Srpaulo		data = os_malloc(data_len);
7878281806Srpaulo		if (!data)
7879281806Srpaulo			return -1;
7880281806Srpaulo
7881281806Srpaulo		if (hexstr2bin(pos, data, data_len)) {
7882281806Srpaulo			wpa_printf(MSG_DEBUG,
7883281806Srpaulo				   "Vendor command: wrong parameter format");
7884281806Srpaulo			os_free(data);
7885281806Srpaulo			return -EINVAL;
7886281806Srpaulo		}
7887281806Srpaulo	}
7888281806Srpaulo
7889281806Srpaulo	reply = wpabuf_alloc((buflen - 1) / 2);
7890281806Srpaulo	if (!reply) {
7891281806Srpaulo		os_free(data);
7892281806Srpaulo		return -1;
7893281806Srpaulo	}
7894281806Srpaulo
7895281806Srpaulo	ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
7896281806Srpaulo				 reply);
7897281806Srpaulo
7898281806Srpaulo	if (ret == 0)
7899281806Srpaulo		ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
7900281806Srpaulo				       wpabuf_len(reply));
7901281806Srpaulo
7902281806Srpaulo	wpabuf_free(reply);
7903281806Srpaulo	os_free(data);
7904281806Srpaulo
7905281806Srpaulo	return ret;
7906281806Srpaulo}
7907281806Srpaulo
7908281806Srpaulo
7909281806Srpaulostatic void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
7910281806Srpaulo{
7911281806Srpaulo#ifdef CONFIG_P2P
7912281806Srpaulo	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
7913281806Srpaulo		wpa_s->global->p2p_init_wpa_s : wpa_s;
7914281806Srpaulo#endif /* CONFIG_P2P */
7915281806Srpaulo
7916281806Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
7917281806Srpaulo
7918346981Scy	if (wpas_abort_ongoing_scan(wpa_s) == 0)
7919346981Scy		wpa_s->ignore_post_flush_scan_res = 1;
7920337817Scy
7921337817Scy	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
7922337817Scy		/*
7923337817Scy		 * Avoid possible auto connect re-connection on getting
7924337817Scy		 * disconnected due to state flush.
7925337817Scy		 */
7926337817Scy		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
7927337817Scy	}
7928337817Scy
7929281806Srpaulo#ifdef CONFIG_P2P
7930337817Scy	wpas_p2p_group_remove(p2p_wpa_s, "*");
7931281806Srpaulo	wpas_p2p_cancel(p2p_wpa_s);
7932281806Srpaulo	p2p_ctrl_flush(p2p_wpa_s);
7933281806Srpaulo	wpas_p2p_service_flush(p2p_wpa_s);
7934281806Srpaulo	p2p_wpa_s->global->p2p_disabled = 0;
7935281806Srpaulo	p2p_wpa_s->global->p2p_per_sta_psk = 0;
7936281806Srpaulo	p2p_wpa_s->conf->num_sec_device_types = 0;
7937281806Srpaulo	p2p_wpa_s->p2p_disable_ip_addr_req = 0;
7938281806Srpaulo	os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
7939281806Srpaulo	p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
7940289549Srpaulo	p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
7941281806Srpaulo	p2p_wpa_s->global->pending_p2ps_group = 0;
7942337817Scy	p2p_wpa_s->global->pending_p2ps_group_freq = 0;
7943281806Srpaulo#endif /* CONFIG_P2P */
7944281806Srpaulo
7945281806Srpaulo#ifdef CONFIG_WPS_TESTING
7946281806Srpaulo	wps_version_number = 0x20;
7947281806Srpaulo	wps_testing_dummy_cred = 0;
7948281806Srpaulo	wps_corrupt_pkhash = 0;
7949337817Scy	wps_force_auth_types_in_use = 0;
7950337817Scy	wps_force_encr_types_in_use = 0;
7951281806Srpaulo#endif /* CONFIG_WPS_TESTING */
7952281806Srpaulo#ifdef CONFIG_WPS
7953281806Srpaulo	wpa_s->wps_fragment_size = 0;
7954281806Srpaulo	wpas_wps_cancel(wpa_s);
7955281806Srpaulo	wps_registrar_flush(wpa_s->wps->registrar);
7956281806Srpaulo#endif /* CONFIG_WPS */
7957281806Srpaulo	wpa_s->after_wps = 0;
7958281806Srpaulo	wpa_s->known_wps_freq = 0;
7959281806Srpaulo
7960346981Scy#ifdef CONFIG_DPP
7961346981Scy	wpas_dpp_deinit(wpa_s);
7962346981Scy	wpa_s->dpp_init_max_tries = 0;
7963346981Scy	wpa_s->dpp_init_retry_time = 0;
7964346981Scy	wpa_s->dpp_resp_wait_time = 0;
7965346981Scy	wpa_s->dpp_resp_max_tries = 0;
7966346981Scy	wpa_s->dpp_resp_retry_time = 0;
7967346981Scy#ifdef CONFIG_TESTING_OPTIONS
7968346981Scy	os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN);
7969346981Scy	os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN);
7970346981Scy	dpp_pkex_ephemeral_key_override_len = 0;
7971346981Scy	dpp_protocol_key_override_len = 0;
7972346981Scy	dpp_nonce_override_len = 0;
7973346981Scy#endif /* CONFIG_TESTING_OPTIONS */
7974346981Scy#endif /* CONFIG_DPP */
7975346981Scy
7976281806Srpaulo#ifdef CONFIG_TDLS
7977281806Srpaulo#ifdef CONFIG_TDLS_TESTING
7978281806Srpaulo	tdls_testing = 0;
7979281806Srpaulo#endif /* CONFIG_TDLS_TESTING */
7980281806Srpaulo	wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
7981281806Srpaulo	wpa_tdls_enable(wpa_s->wpa, 1);
7982281806Srpaulo#endif /* CONFIG_TDLS */
7983281806Srpaulo
7984281806Srpaulo	eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
7985281806Srpaulo	wpa_supplicant_stop_countermeasures(wpa_s, NULL);
7986281806Srpaulo
7987281806Srpaulo	wpa_s->no_keep_alive = 0;
7988281806Srpaulo	wpa_s->own_disconnect_req = 0;
7989281806Srpaulo
7990281806Srpaulo	os_free(wpa_s->disallow_aps_bssid);
7991281806Srpaulo	wpa_s->disallow_aps_bssid = NULL;
7992281806Srpaulo	wpa_s->disallow_aps_bssid_count = 0;
7993281806Srpaulo	os_free(wpa_s->disallow_aps_ssid);
7994281806Srpaulo	wpa_s->disallow_aps_ssid = NULL;
7995281806Srpaulo	wpa_s->disallow_aps_ssid_count = 0;
7996281806Srpaulo
7997281806Srpaulo	wpa_s->set_sta_uapsd = 0;
7998281806Srpaulo	wpa_s->sta_uapsd = 0;
7999281806Srpaulo
8000281806Srpaulo	wpa_drv_radio_disable(wpa_s, 0);
8001281806Srpaulo	wpa_blacklist_clear(wpa_s);
8002281806Srpaulo	wpa_s->extra_blacklist_count = 0;
8003281806Srpaulo	wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
8004281806Srpaulo	wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
8005281806Srpaulo	wpa_config_flush_blobs(wpa_s->conf);
8006281806Srpaulo	wpa_s->conf->auto_interworking = 0;
8007281806Srpaulo	wpa_s->conf->okc = 0;
8008281806Srpaulo
8009281806Srpaulo	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
8010281806Srpaulo	rsn_preauth_deinit(wpa_s->wpa);
8011281806Srpaulo
8012281806Srpaulo	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
8013281806Srpaulo	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
8014281806Srpaulo	wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
8015281806Srpaulo	eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
8016281806Srpaulo
8017281806Srpaulo	radio_remove_works(wpa_s, NULL, 1);
8018281806Srpaulo	wpa_s->ext_work_in_progress = 0;
8019281806Srpaulo
8020281806Srpaulo	wpa_s->next_ssid = NULL;
8021281806Srpaulo
8022281806Srpaulo#ifdef CONFIG_INTERWORKING
8023337817Scy#ifdef CONFIG_HS20
8024281806Srpaulo	hs20_cancel_fetch_osu(wpa_s);
8025337817Scy	hs20_del_icon(wpa_s, NULL, NULL);
8026337817Scy#endif /* CONFIG_HS20 */
8027281806Srpaulo#endif /* CONFIG_INTERWORKING */
8028281806Srpaulo
8029281806Srpaulo	wpa_s->ext_mgmt_frame_handling = 0;
8030281806Srpaulo	wpa_s->ext_eapol_frame_io = 0;
8031281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
8032281806Srpaulo	wpa_s->extra_roc_dur = 0;
8033281806Srpaulo	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
8034337817Scy	wpa_s->p2p_go_csa_on_inv = 0;
8035337817Scy	wpa_s->ignore_auth_resp = 0;
8036337817Scy	wpa_s->ignore_assoc_disallow = 0;
8037346981Scy	wpa_s->testing_resend_assoc = 0;
8038337817Scy	wpa_s->reject_btm_req_reason = 0;
8039337817Scy	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
8040346981Scy	os_free(wpa_s->get_pref_freq_list_override);
8041346981Scy	wpa_s->get_pref_freq_list_override = NULL;
8042346981Scy	wpabuf_free(wpa_s->sae_commit_override);
8043346981Scy	wpa_s->sae_commit_override = NULL;
8044346981Scy#ifdef CONFIG_DPP
8045346981Scy	os_free(wpa_s->dpp_config_obj_override);
8046346981Scy	wpa_s->dpp_config_obj_override = NULL;
8047346981Scy	os_free(wpa_s->dpp_discovery_override);
8048346981Scy	wpa_s->dpp_discovery_override = NULL;
8049346981Scy	os_free(wpa_s->dpp_groups_override);
8050346981Scy	wpa_s->dpp_groups_override = NULL;
8051346981Scy	dpp_test = DPP_TEST_DISABLED;
8052346981Scy#endif /* CONFIG_DPP */
8053281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
8054281806Srpaulo
8055281806Srpaulo	wpa_s->disconnected = 0;
8056281806Srpaulo	os_free(wpa_s->next_scan_freqs);
8057281806Srpaulo	wpa_s->next_scan_freqs = NULL;
8058346981Scy	os_free(wpa_s->select_network_scan_freqs);
8059346981Scy	wpa_s->select_network_scan_freqs = NULL;
8060281806Srpaulo
8061281806Srpaulo	wpa_bss_flush(wpa_s);
8062281806Srpaulo	if (!dl_list_empty(&wpa_s->bss)) {
8063281806Srpaulo		wpa_printf(MSG_DEBUG,
8064281806Srpaulo			   "BSS table not empty after flush: %u entries, current_bss=%p bssid="
8065281806Srpaulo			   MACSTR " pending_bssid=" MACSTR,
8066281806Srpaulo			   dl_list_len(&wpa_s->bss), wpa_s->current_bss,
8067281806Srpaulo			   MAC2STR(wpa_s->bssid),
8068281806Srpaulo			   MAC2STR(wpa_s->pending_bssid));
8069281806Srpaulo	}
8070289549Srpaulo
8071289549Srpaulo	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
8072324697Sgordon	wpa_s->wnmsleep_used = 0;
8073337817Scy
8074337817Scy#ifdef CONFIG_SME
8075337817Scy	wpa_s->sme.last_unprot_disconnect.sec = 0;
8076337817Scy#endif /* CONFIG_SME */
8077346981Scy
8078346981Scy	wpabuf_free(wpa_s->ric_ies);
8079346981Scy	wpa_s->ric_ies = NULL;
8080346981Scy
8081346981Scy	wpa_supplicant_update_channel_list(wpa_s, NULL);
8082346981Scy
8083346981Scy	free_bss_tmp_disallowed(wpa_s);
8084281806Srpaulo}
8085281806Srpaulo
8086281806Srpaulo
8087281806Srpaulostatic int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
8088281806Srpaulo				     char *buf, size_t buflen)
8089281806Srpaulo{
8090281806Srpaulo	struct wpa_radio_work *work;
8091281806Srpaulo	char *pos, *end;
8092281806Srpaulo	struct os_reltime now, diff;
8093281806Srpaulo
8094281806Srpaulo	pos = buf;
8095281806Srpaulo	end = buf + buflen;
8096281806Srpaulo
8097281806Srpaulo	os_get_reltime(&now);
8098281806Srpaulo
8099281806Srpaulo	dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
8100281806Srpaulo	{
8101281806Srpaulo		int ret;
8102281806Srpaulo
8103281806Srpaulo		os_reltime_sub(&now, &work->time, &diff);
8104281806Srpaulo		ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
8105281806Srpaulo				  work->type, work->wpa_s->ifname, work->freq,
8106281806Srpaulo				  work->started, diff.sec, diff.usec);
8107281806Srpaulo		if (os_snprintf_error(end - pos, ret))
8108281806Srpaulo			break;
8109281806Srpaulo		pos += ret;
8110281806Srpaulo	}
8111281806Srpaulo
8112281806Srpaulo	return pos - buf;
8113281806Srpaulo}
8114281806Srpaulo
8115281806Srpaulo
8116281806Srpaulostatic void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
8117281806Srpaulo{
8118281806Srpaulo	struct wpa_radio_work *work = eloop_ctx;
8119281806Srpaulo	struct wpa_external_work *ework = work->ctx;
8120281806Srpaulo
8121281806Srpaulo	wpa_dbg(work->wpa_s, MSG_DEBUG,
8122281806Srpaulo		"Timing out external radio work %u (%s)",
8123281806Srpaulo		ework->id, work->type);
8124281806Srpaulo	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
8125281806Srpaulo	work->wpa_s->ext_work_in_progress = 0;
8126281806Srpaulo	radio_work_done(work);
8127281806Srpaulo	os_free(ework);
8128281806Srpaulo}
8129281806Srpaulo
8130281806Srpaulo
8131281806Srpaulostatic void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
8132281806Srpaulo{
8133281806Srpaulo	struct wpa_external_work *ework = work->ctx;
8134281806Srpaulo
8135281806Srpaulo	if (deinit) {
8136281806Srpaulo		if (work->started)
8137281806Srpaulo			eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
8138281806Srpaulo					     work, NULL);
8139281806Srpaulo
8140337817Scy		/*
8141337817Scy		 * work->type points to a buffer in ework, so need to replace
8142337817Scy		 * that here with a fixed string to avoid use of freed memory
8143337817Scy		 * in debug prints.
8144337817Scy		 */
8145337817Scy		work->type = "freed-ext-work";
8146337817Scy		work->ctx = NULL;
8147281806Srpaulo		os_free(ework);
8148281806Srpaulo		return;
8149281806Srpaulo	}
8150281806Srpaulo
8151281806Srpaulo	wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
8152281806Srpaulo		ework->id, ework->type);
8153281806Srpaulo	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
8154281806Srpaulo	work->wpa_s->ext_work_in_progress = 1;
8155281806Srpaulo	if (!ework->timeout)
8156281806Srpaulo		ework->timeout = 10;
8157281806Srpaulo	eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
8158281806Srpaulo			       work, NULL);
8159281806Srpaulo}
8160281806Srpaulo
8161281806Srpaulo
8162281806Srpaulostatic int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
8163281806Srpaulo				    char *buf, size_t buflen)
8164281806Srpaulo{
8165281806Srpaulo	struct wpa_external_work *ework;
8166281806Srpaulo	char *pos, *pos2;
8167281806Srpaulo	size_t type_len;
8168281806Srpaulo	int ret;
8169281806Srpaulo	unsigned int freq = 0;
8170281806Srpaulo
8171281806Srpaulo	/* format: <name> [freq=<MHz>] [timeout=<seconds>] */
8172281806Srpaulo
8173281806Srpaulo	ework = os_zalloc(sizeof(*ework));
8174281806Srpaulo	if (ework == NULL)
8175281806Srpaulo		return -1;
8176281806Srpaulo
8177281806Srpaulo	pos = os_strchr(cmd, ' ');
8178281806Srpaulo	if (pos) {
8179281806Srpaulo		type_len = pos - cmd;
8180281806Srpaulo		pos++;
8181281806Srpaulo
8182281806Srpaulo		pos2 = os_strstr(pos, "freq=");
8183281806Srpaulo		if (pos2)
8184281806Srpaulo			freq = atoi(pos2 + 5);
8185281806Srpaulo
8186281806Srpaulo		pos2 = os_strstr(pos, "timeout=");
8187281806Srpaulo		if (pos2)
8188281806Srpaulo			ework->timeout = atoi(pos2 + 8);
8189281806Srpaulo	} else {
8190281806Srpaulo		type_len = os_strlen(cmd);
8191281806Srpaulo	}
8192281806Srpaulo	if (4 + type_len >= sizeof(ework->type))
8193281806Srpaulo		type_len = sizeof(ework->type) - 4 - 1;
8194281806Srpaulo	os_strlcpy(ework->type, "ext:", sizeof(ework->type));
8195281806Srpaulo	os_memcpy(ework->type + 4, cmd, type_len);
8196281806Srpaulo	ework->type[4 + type_len] = '\0';
8197281806Srpaulo
8198281806Srpaulo	wpa_s->ext_work_id++;
8199281806Srpaulo	if (wpa_s->ext_work_id == 0)
8200281806Srpaulo		wpa_s->ext_work_id++;
8201281806Srpaulo	ework->id = wpa_s->ext_work_id;
8202281806Srpaulo
8203281806Srpaulo	if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
8204281806Srpaulo			   ework) < 0) {
8205281806Srpaulo		os_free(ework);
8206281806Srpaulo		return -1;
8207281806Srpaulo	}
8208281806Srpaulo
8209281806Srpaulo	ret = os_snprintf(buf, buflen, "%u", ework->id);
8210281806Srpaulo	if (os_snprintf_error(buflen, ret))
8211281806Srpaulo		return -1;
8212281806Srpaulo	return ret;
8213281806Srpaulo}
8214281806Srpaulo
8215281806Srpaulo
8216281806Srpaulostatic int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
8217281806Srpaulo{
8218281806Srpaulo	struct wpa_radio_work *work;
8219281806Srpaulo	unsigned int id = atoi(cmd);
8220281806Srpaulo
8221281806Srpaulo	dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
8222281806Srpaulo	{
8223281806Srpaulo		struct wpa_external_work *ework;
8224281806Srpaulo
8225281806Srpaulo		if (os_strncmp(work->type, "ext:", 4) != 0)
8226281806Srpaulo			continue;
8227281806Srpaulo		ework = work->ctx;
8228281806Srpaulo		if (id && ework->id != id)
8229281806Srpaulo			continue;
8230281806Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG,
8231281806Srpaulo			"Completed external radio work %u (%s)",
8232281806Srpaulo			ework->id, ework->type);
8233281806Srpaulo		eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
8234281806Srpaulo		wpa_s->ext_work_in_progress = 0;
8235281806Srpaulo		radio_work_done(work);
8236281806Srpaulo		os_free(ework);
8237281806Srpaulo		return 3; /* "OK\n" */
8238281806Srpaulo	}
8239281806Srpaulo
8240281806Srpaulo	return -1;
8241281806Srpaulo}
8242281806Srpaulo
8243281806Srpaulo
8244281806Srpaulostatic int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
8245281806Srpaulo				char *buf, size_t buflen)
8246281806Srpaulo{
8247281806Srpaulo	if (os_strcmp(cmd, "show") == 0)
8248281806Srpaulo		return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
8249281806Srpaulo	if (os_strncmp(cmd, "add ", 4) == 0)
8250281806Srpaulo		return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
8251281806Srpaulo	if (os_strncmp(cmd, "done ", 5) == 0)
8252281806Srpaulo		return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
8253281806Srpaulo	return -1;
8254281806Srpaulo}
8255281806Srpaulo
8256281806Srpaulo
8257281806Srpaulovoid wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
8258281806Srpaulo{
8259281806Srpaulo	struct wpa_radio_work *work, *tmp;
8260281806Srpaulo
8261281806Srpaulo	if (!wpa_s || !wpa_s->radio)
8262281806Srpaulo		return;
8263281806Srpaulo
8264281806Srpaulo	dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
8265281806Srpaulo			      struct wpa_radio_work, list) {
8266281806Srpaulo		struct wpa_external_work *ework;
8267281806Srpaulo
8268281806Srpaulo		if (os_strncmp(work->type, "ext:", 4) != 0)
8269281806Srpaulo			continue;
8270281806Srpaulo		ework = work->ctx;
8271281806Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG,
8272281806Srpaulo			"Flushing%s external radio work %u (%s)",
8273281806Srpaulo			work->started ? " started" : "", ework->id,
8274281806Srpaulo			ework->type);
8275281806Srpaulo		if (work->started)
8276281806Srpaulo			eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
8277281806Srpaulo					     work, NULL);
8278281806Srpaulo		radio_work_done(work);
8279281806Srpaulo		os_free(ework);
8280281806Srpaulo	}
8281281806Srpaulo}
8282281806Srpaulo
8283281806Srpaulo
8284281806Srpaulostatic void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
8285281806Srpaulo{
8286281806Srpaulo	struct wpa_supplicant *wpa_s = eloop_ctx;
8287281806Srpaulo	eapol_sm_notify_ctrl_response(wpa_s->eapol);
8288281806Srpaulo}
8289281806Srpaulo
8290281806Srpaulo
8291281806Srpaulostatic int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
8292281806Srpaulo			      unsigned int *scan_id_count, int scan_id[])
8293281806Srpaulo{
8294281806Srpaulo	const char *pos = value;
8295281806Srpaulo
8296281806Srpaulo	while (pos) {
8297281806Srpaulo		if (*pos == ' ' || *pos == '\0')
8298281806Srpaulo			break;
8299281806Srpaulo		if (*scan_id_count == MAX_SCAN_ID)
8300281806Srpaulo			return -1;
8301281806Srpaulo		scan_id[(*scan_id_count)++] = atoi(pos);
8302281806Srpaulo		pos = os_strchr(pos, ',');
8303281806Srpaulo		if (pos)
8304281806Srpaulo			pos++;
8305281806Srpaulo	}
8306281806Srpaulo
8307281806Srpaulo	return 0;
8308281806Srpaulo}
8309281806Srpaulo
8310281806Srpaulo
8311281806Srpaulostatic void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
8312281806Srpaulo			   char *reply, int reply_size, int *reply_len)
8313281806Srpaulo{
8314281806Srpaulo	char *pos;
8315281806Srpaulo	unsigned int manual_scan_passive = 0;
8316281806Srpaulo	unsigned int manual_scan_use_id = 0;
8317281806Srpaulo	unsigned int manual_scan_only_new = 0;
8318281806Srpaulo	unsigned int scan_only = 0;
8319281806Srpaulo	unsigned int scan_id_count = 0;
8320281806Srpaulo	int scan_id[MAX_SCAN_ID];
8321281806Srpaulo	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
8322281806Srpaulo				 struct wpa_scan_results *scan_res);
8323281806Srpaulo	int *manual_scan_freqs = NULL;
8324289549Srpaulo	struct wpa_ssid_value *ssid = NULL, *ns;
8325289549Srpaulo	unsigned int ssid_count = 0;
8326281806Srpaulo
8327281806Srpaulo	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
8328281806Srpaulo		*reply_len = -1;
8329281806Srpaulo		return;
8330281806Srpaulo	}
8331281806Srpaulo
8332281806Srpaulo	if (radio_work_pending(wpa_s, "scan")) {
8333281806Srpaulo		wpa_printf(MSG_DEBUG,
8334281806Srpaulo			   "Pending scan scheduled - reject new request");
8335281806Srpaulo		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
8336281806Srpaulo		return;
8337281806Srpaulo	}
8338281806Srpaulo
8339289549Srpaulo#ifdef CONFIG_INTERWORKING
8340289549Srpaulo	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
8341289549Srpaulo		wpa_printf(MSG_DEBUG,
8342289549Srpaulo			   "Interworking select in progress - reject new scan");
8343289549Srpaulo		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
8344289549Srpaulo		return;
8345289549Srpaulo	}
8346289549Srpaulo#endif /* CONFIG_INTERWORKING */
8347289549Srpaulo
8348281806Srpaulo	if (params) {
8349281806Srpaulo		if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
8350281806Srpaulo			scan_only = 1;
8351281806Srpaulo
8352281806Srpaulo		pos = os_strstr(params, "freq=");
8353281806Srpaulo		if (pos) {
8354281806Srpaulo			manual_scan_freqs = freq_range_to_channel_list(wpa_s,
8355281806Srpaulo								       pos + 5);
8356281806Srpaulo			if (manual_scan_freqs == NULL) {
8357281806Srpaulo				*reply_len = -1;
8358281806Srpaulo				goto done;
8359281806Srpaulo			}
8360281806Srpaulo		}
8361281806Srpaulo
8362281806Srpaulo		pos = os_strstr(params, "passive=");
8363281806Srpaulo		if (pos)
8364281806Srpaulo			manual_scan_passive = !!atoi(pos + 8);
8365281806Srpaulo
8366281806Srpaulo		pos = os_strstr(params, "use_id=");
8367281806Srpaulo		if (pos)
8368281806Srpaulo			manual_scan_use_id = atoi(pos + 7);
8369281806Srpaulo
8370281806Srpaulo		pos = os_strstr(params, "only_new=1");
8371281806Srpaulo		if (pos)
8372281806Srpaulo			manual_scan_only_new = 1;
8373281806Srpaulo
8374281806Srpaulo		pos = os_strstr(params, "scan_id=");
8375281806Srpaulo		if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
8376281806Srpaulo					      scan_id) < 0) {
8377281806Srpaulo			*reply_len = -1;
8378281806Srpaulo			goto done;
8379281806Srpaulo		}
8380289549Srpaulo
8381346981Scy		pos = os_strstr(params, "bssid=");
8382346981Scy		if (pos) {
8383346981Scy			u8 bssid[ETH_ALEN];
8384346981Scy
8385346981Scy			pos += 6;
8386346981Scy			if (hwaddr_aton(pos, bssid)) {
8387346981Scy				wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos);
8388346981Scy				*reply_len = -1;
8389346981Scy				goto done;
8390346981Scy			}
8391346981Scy			os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN);
8392346981Scy		}
8393346981Scy
8394289549Srpaulo		pos = params;
8395289549Srpaulo		while (pos && *pos != '\0') {
8396289549Srpaulo			if (os_strncmp(pos, "ssid ", 5) == 0) {
8397289549Srpaulo				char *end;
8398289549Srpaulo
8399289549Srpaulo				pos += 5;
8400289549Srpaulo				end = pos;
8401289549Srpaulo				while (*end) {
8402289549Srpaulo					if (*end == '\0' || *end == ' ')
8403289549Srpaulo						break;
8404289549Srpaulo					end++;
8405289549Srpaulo				}
8406289549Srpaulo
8407289549Srpaulo				ns = os_realloc_array(
8408289549Srpaulo					ssid, ssid_count + 1,
8409289549Srpaulo					sizeof(struct wpa_ssid_value));
8410289549Srpaulo				if (ns == NULL) {
8411289549Srpaulo					*reply_len = -1;
8412289549Srpaulo					goto done;
8413289549Srpaulo				}
8414289549Srpaulo				ssid = ns;
8415289549Srpaulo
8416289549Srpaulo				if ((end - pos) & 0x01 ||
8417289549Srpaulo				    end - pos > 2 * SSID_MAX_LEN ||
8418289549Srpaulo				    hexstr2bin(pos, ssid[ssid_count].ssid,
8419289549Srpaulo					       (end - pos) / 2) < 0) {
8420289549Srpaulo					wpa_printf(MSG_DEBUG,
8421289549Srpaulo						   "Invalid SSID value '%s'",
8422289549Srpaulo						   pos);
8423289549Srpaulo					*reply_len = -1;
8424289549Srpaulo					goto done;
8425289549Srpaulo				}
8426289549Srpaulo				ssid[ssid_count].ssid_len = (end - pos) / 2;
8427289549Srpaulo				wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
8428289549Srpaulo						  ssid[ssid_count].ssid,
8429289549Srpaulo						  ssid[ssid_count].ssid_len);
8430289549Srpaulo				ssid_count++;
8431289549Srpaulo				pos = end;
8432289549Srpaulo			}
8433289549Srpaulo
8434289549Srpaulo			pos = os_strchr(pos, ' ');
8435289549Srpaulo			if (pos)
8436289549Srpaulo				pos++;
8437289549Srpaulo		}
8438281806Srpaulo	}
8439281806Srpaulo
8440289549Srpaulo	wpa_s->num_ssids_from_scan_req = ssid_count;
8441289549Srpaulo	os_free(wpa_s->ssids_from_scan_req);
8442289549Srpaulo	if (ssid_count) {
8443289549Srpaulo		wpa_s->ssids_from_scan_req = ssid;
8444289549Srpaulo		ssid = NULL;
8445289549Srpaulo	} else {
8446289549Srpaulo		wpa_s->ssids_from_scan_req = NULL;
8447289549Srpaulo	}
8448289549Srpaulo
8449281806Srpaulo	if (scan_only)
8450281806Srpaulo		scan_res_handler = scan_only_handler;
8451281806Srpaulo	else if (wpa_s->scan_res_handler == scan_only_handler)
8452281806Srpaulo		scan_res_handler = NULL;
8453281806Srpaulo	else
8454281806Srpaulo		scan_res_handler = wpa_s->scan_res_handler;
8455281806Srpaulo
8456281806Srpaulo	if (!wpa_s->sched_scanning && !wpa_s->scanning &&
8457281806Srpaulo	    ((wpa_s->wpa_state <= WPA_SCANNING) ||
8458281806Srpaulo	     (wpa_s->wpa_state == WPA_COMPLETED))) {
8459281806Srpaulo		wpa_s->manual_scan_passive = manual_scan_passive;
8460281806Srpaulo		wpa_s->manual_scan_use_id = manual_scan_use_id;
8461281806Srpaulo		wpa_s->manual_scan_only_new = manual_scan_only_new;
8462281806Srpaulo		wpa_s->scan_id_count = scan_id_count;
8463281806Srpaulo		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
8464281806Srpaulo		wpa_s->scan_res_handler = scan_res_handler;
8465281806Srpaulo		os_free(wpa_s->manual_scan_freqs);
8466281806Srpaulo		wpa_s->manual_scan_freqs = manual_scan_freqs;
8467281806Srpaulo		manual_scan_freqs = NULL;
8468281806Srpaulo
8469281806Srpaulo		wpa_s->normal_scans = 0;
8470281806Srpaulo		wpa_s->scan_req = MANUAL_SCAN_REQ;
8471281806Srpaulo		wpa_s->after_wps = 0;
8472281806Srpaulo		wpa_s->known_wps_freq = 0;
8473281806Srpaulo		wpa_supplicant_req_scan(wpa_s, 0, 0);
8474281806Srpaulo		if (wpa_s->manual_scan_use_id) {
8475281806Srpaulo			wpa_s->manual_scan_id++;
8476281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
8477281806Srpaulo				wpa_s->manual_scan_id);
8478281806Srpaulo			*reply_len = os_snprintf(reply, reply_size, "%u\n",
8479281806Srpaulo						 wpa_s->manual_scan_id);
8480281806Srpaulo		}
8481281806Srpaulo	} else if (wpa_s->sched_scanning) {
8482281806Srpaulo		wpa_s->manual_scan_passive = manual_scan_passive;
8483281806Srpaulo		wpa_s->manual_scan_use_id = manual_scan_use_id;
8484281806Srpaulo		wpa_s->manual_scan_only_new = manual_scan_only_new;
8485281806Srpaulo		wpa_s->scan_id_count = scan_id_count;
8486281806Srpaulo		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
8487281806Srpaulo		wpa_s->scan_res_handler = scan_res_handler;
8488281806Srpaulo		os_free(wpa_s->manual_scan_freqs);
8489281806Srpaulo		wpa_s->manual_scan_freqs = manual_scan_freqs;
8490281806Srpaulo		manual_scan_freqs = NULL;
8491281806Srpaulo
8492281806Srpaulo		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
8493281806Srpaulo		wpa_supplicant_cancel_sched_scan(wpa_s);
8494281806Srpaulo		wpa_s->scan_req = MANUAL_SCAN_REQ;
8495281806Srpaulo		wpa_supplicant_req_scan(wpa_s, 0, 0);
8496281806Srpaulo		if (wpa_s->manual_scan_use_id) {
8497281806Srpaulo			wpa_s->manual_scan_id++;
8498281806Srpaulo			*reply_len = os_snprintf(reply, reply_size, "%u\n",
8499281806Srpaulo						 wpa_s->manual_scan_id);
8500281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
8501281806Srpaulo				wpa_s->manual_scan_id);
8502281806Srpaulo		}
8503281806Srpaulo	} else {
8504281806Srpaulo		wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
8505281806Srpaulo		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
8506281806Srpaulo	}
8507281806Srpaulo
8508281806Srpaulodone:
8509281806Srpaulo	os_free(manual_scan_freqs);
8510289549Srpaulo	os_free(ssid);
8511281806Srpaulo}
8512281806Srpaulo
8513281806Srpaulo
8514281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
8515281806Srpaulo
8516281806Srpaulostatic void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s,
8517281806Srpaulo				       unsigned int freq, const u8 *dst,
8518281806Srpaulo				       const u8 *src, const u8 *bssid,
8519281806Srpaulo				       const u8 *data, size_t data_len,
8520281806Srpaulo				       enum offchannel_send_action_result
8521281806Srpaulo				       result)
8522281806Srpaulo{
8523281806Srpaulo	wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
8524281806Srpaulo		" src=" MACSTR " bssid=" MACSTR " result=%s",
8525281806Srpaulo		freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
8526281806Srpaulo		result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
8527281806Srpaulo		"SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
8528281806Srpaulo			     "NO_ACK" : "FAILED"));
8529281806Srpaulo}
8530281806Srpaulo
8531281806Srpaulo
8532281806Srpaulostatic int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
8533281806Srpaulo{
8534281806Srpaulo	char *pos, *param;
8535281806Srpaulo	size_t len;
8536281806Srpaulo	u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
8537281806Srpaulo	int res, used;
8538281806Srpaulo	int freq = 0, no_cck = 0, wait_time = 0;
8539281806Srpaulo
8540281806Srpaulo	/* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
8541281806Srpaulo	 *    <action=Action frame payload> */
8542281806Srpaulo
8543281806Srpaulo	wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
8544281806Srpaulo
8545281806Srpaulo	pos = cmd;
8546281806Srpaulo	used = hwaddr_aton2(pos, da);
8547281806Srpaulo	if (used < 0)
8548281806Srpaulo		return -1;
8549281806Srpaulo	pos += used;
8550281806Srpaulo	while (*pos == ' ')
8551281806Srpaulo		pos++;
8552281806Srpaulo	used = hwaddr_aton2(pos, bssid);
8553281806Srpaulo	if (used < 0)
8554281806Srpaulo		return -1;
8555281806Srpaulo	pos += used;
8556281806Srpaulo
8557281806Srpaulo	param = os_strstr(pos, " freq=");
8558281806Srpaulo	if (param) {
8559281806Srpaulo		param += 6;
8560281806Srpaulo		freq = atoi(param);
8561281806Srpaulo	}
8562281806Srpaulo
8563281806Srpaulo	param = os_strstr(pos, " no_cck=");
8564281806Srpaulo	if (param) {
8565281806Srpaulo		param += 8;
8566281806Srpaulo		no_cck = atoi(param);
8567281806Srpaulo	}
8568281806Srpaulo
8569281806Srpaulo	param = os_strstr(pos, " wait_time=");
8570281806Srpaulo	if (param) {
8571281806Srpaulo		param += 11;
8572281806Srpaulo		wait_time = atoi(param);
8573281806Srpaulo	}
8574281806Srpaulo
8575281806Srpaulo	param = os_strstr(pos, " action=");
8576281806Srpaulo	if (param == NULL)
8577281806Srpaulo		return -1;
8578281806Srpaulo	param += 8;
8579281806Srpaulo
8580281806Srpaulo	len = os_strlen(param);
8581281806Srpaulo	if (len & 1)
8582281806Srpaulo		return -1;
8583281806Srpaulo	len /= 2;
8584281806Srpaulo
8585281806Srpaulo	buf = os_malloc(len);
8586281806Srpaulo	if (buf == NULL)
8587281806Srpaulo		return -1;
8588281806Srpaulo
8589281806Srpaulo	if (hexstr2bin(param, buf, len) < 0) {
8590281806Srpaulo		os_free(buf);
8591281806Srpaulo		return -1;
8592281806Srpaulo	}
8593281806Srpaulo
8594281806Srpaulo	res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
8595281806Srpaulo				     buf, len, wait_time,
8596281806Srpaulo				     wpas_ctrl_iface_mgmt_tx_cb, no_cck);
8597281806Srpaulo	os_free(buf);
8598281806Srpaulo	return res;
8599281806Srpaulo}
8600281806Srpaulo
8601281806Srpaulo
8602281806Srpaulostatic void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
8603281806Srpaulo{
8604281806Srpaulo	wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
8605281806Srpaulo	offchannel_send_action_done(wpa_s);
8606281806Srpaulo}
8607281806Srpaulo
8608281806Srpaulo
8609337817Scystatic int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s,
8610337817Scy					   char *cmd)
8611337817Scy{
8612337817Scy	char *pos, *param;
8613337817Scy	size_t len;
8614337817Scy	u8 *buf;
8615337817Scy	int freq = 0, datarate = 0, ssi_signal = 0;
8616337817Scy	union wpa_event_data event;
8617337817Scy
8618337817Scy	if (!wpa_s->ext_mgmt_frame_handling)
8619337817Scy		return -1;
8620337817Scy
8621337817Scy	/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
8622337817Scy
8623337817Scy	wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
8624337817Scy
8625337817Scy	pos = cmd;
8626337817Scy	param = os_strstr(pos, "freq=");
8627337817Scy	if (param) {
8628337817Scy		param += 5;
8629337817Scy		freq = atoi(param);
8630337817Scy	}
8631337817Scy
8632337817Scy	param = os_strstr(pos, " datarate=");
8633337817Scy	if (param) {
8634337817Scy		param += 10;
8635337817Scy		datarate = atoi(param);
8636337817Scy	}
8637337817Scy
8638337817Scy	param = os_strstr(pos, " ssi_signal=");
8639337817Scy	if (param) {
8640337817Scy		param += 12;
8641337817Scy		ssi_signal = atoi(param);
8642337817Scy	}
8643337817Scy
8644337817Scy	param = os_strstr(pos, " frame=");
8645337817Scy	if (param == NULL)
8646337817Scy		return -1;
8647337817Scy	param += 7;
8648337817Scy
8649337817Scy	len = os_strlen(param);
8650337817Scy	if (len & 1)
8651337817Scy		return -1;
8652337817Scy	len /= 2;
8653337817Scy
8654337817Scy	buf = os_malloc(len);
8655337817Scy	if (buf == NULL)
8656337817Scy		return -1;
8657337817Scy
8658337817Scy	if (hexstr2bin(param, buf, len) < 0) {
8659337817Scy		os_free(buf);
8660337817Scy		return -1;
8661337817Scy	}
8662337817Scy
8663337817Scy	os_memset(&event, 0, sizeof(event));
8664337817Scy	event.rx_mgmt.freq = freq;
8665337817Scy	event.rx_mgmt.frame = buf;
8666337817Scy	event.rx_mgmt.frame_len = len;
8667337817Scy	event.rx_mgmt.ssi_signal = ssi_signal;
8668337817Scy	event.rx_mgmt.datarate = datarate;
8669337817Scy	wpa_s->ext_mgmt_frame_handling = 0;
8670337817Scy	wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event);
8671337817Scy	wpa_s->ext_mgmt_frame_handling = 1;
8672337817Scy
8673337817Scy	os_free(buf);
8674337817Scy
8675337817Scy	return 0;
8676337817Scy}
8677337817Scy
8678337817Scy
8679346981Scystatic int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s,
8680346981Scy					   char *param)
8681346981Scy{
8682346981Scy	struct wpa_scan_res *res;
8683346981Scy	struct os_reltime now;
8684346981Scy	char *pos, *end;
8685346981Scy	int ret = -1;
8686346981Scy
8687346981Scy	if (!param)
8688346981Scy		return -1;
8689346981Scy
8690346981Scy	if (os_strcmp(param, "START") == 0) {
8691346981Scy		wpa_bss_update_start(wpa_s);
8692346981Scy		return 0;
8693346981Scy	}
8694346981Scy
8695346981Scy	if (os_strcmp(param, "END") == 0) {
8696346981Scy		wpa_bss_update_end(wpa_s, NULL, 1);
8697346981Scy		return 0;
8698346981Scy	}
8699346981Scy
8700346981Scy	if (os_strncmp(param, "BSS ", 4) != 0)
8701346981Scy		return -1;
8702346981Scy	param += 3;
8703346981Scy
8704346981Scy	res = os_zalloc(sizeof(*res) + os_strlen(param) / 2);
8705346981Scy	if (!res)
8706346981Scy		return -1;
8707346981Scy
8708346981Scy	pos = os_strstr(param, " flags=");
8709346981Scy	if (pos)
8710346981Scy		res->flags = strtol(pos + 7, NULL, 16);
8711346981Scy
8712346981Scy	pos = os_strstr(param, " bssid=");
8713346981Scy	if (pos && hwaddr_aton(pos + 7, res->bssid))
8714346981Scy		goto fail;
8715346981Scy
8716346981Scy	pos = os_strstr(param, " freq=");
8717346981Scy	if (pos)
8718346981Scy		res->freq = atoi(pos + 6);
8719346981Scy
8720346981Scy	pos = os_strstr(param, " beacon_int=");
8721346981Scy	if (pos)
8722346981Scy		res->beacon_int = atoi(pos + 12);
8723346981Scy
8724346981Scy	pos = os_strstr(param, " caps=");
8725346981Scy	if (pos)
8726346981Scy		res->caps = strtol(pos + 6, NULL, 16);
8727346981Scy
8728346981Scy	pos = os_strstr(param, " qual=");
8729346981Scy	if (pos)
8730346981Scy		res->qual = atoi(pos + 6);
8731346981Scy
8732346981Scy	pos = os_strstr(param, " noise=");
8733346981Scy	if (pos)
8734346981Scy		res->noise = atoi(pos + 7);
8735346981Scy
8736346981Scy	pos = os_strstr(param, " level=");
8737346981Scy	if (pos)
8738346981Scy		res->level = atoi(pos + 7);
8739346981Scy
8740346981Scy	pos = os_strstr(param, " tsf=");
8741346981Scy	if (pos)
8742346981Scy		res->tsf = strtoll(pos + 5, NULL, 16);
8743346981Scy
8744346981Scy	pos = os_strstr(param, " age=");
8745346981Scy	if (pos)
8746346981Scy		res->age = atoi(pos + 5);
8747346981Scy
8748346981Scy	pos = os_strstr(param, " est_throughput=");
8749346981Scy	if (pos)
8750346981Scy		res->est_throughput = atoi(pos + 16);
8751346981Scy
8752346981Scy	pos = os_strstr(param, " snr=");
8753346981Scy	if (pos)
8754346981Scy		res->snr = atoi(pos + 5);
8755346981Scy
8756346981Scy	pos = os_strstr(param, " parent_tsf=");
8757346981Scy	if (pos)
8758346981Scy		res->parent_tsf = strtoll(pos + 7, NULL, 16);
8759346981Scy
8760346981Scy	pos = os_strstr(param, " tsf_bssid=");
8761346981Scy	if (pos && hwaddr_aton(pos + 11, res->tsf_bssid))
8762346981Scy		goto fail;
8763346981Scy
8764346981Scy	pos = os_strstr(param, " ie=");
8765346981Scy	if (pos) {
8766346981Scy		pos += 4;
8767346981Scy		end = os_strchr(pos, ' ');
8768346981Scy		if (!end)
8769346981Scy			end = pos + os_strlen(pos);
8770346981Scy		res->ie_len = (end - pos) / 2;
8771346981Scy		if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len))
8772346981Scy			goto fail;
8773346981Scy	}
8774346981Scy
8775346981Scy	pos = os_strstr(param, " beacon_ie=");
8776346981Scy	if (pos) {
8777346981Scy		pos += 11;
8778346981Scy		end = os_strchr(pos, ' ');
8779346981Scy		if (!end)
8780346981Scy			end = pos + os_strlen(pos);
8781346981Scy		res->beacon_ie_len = (end - pos) / 2;
8782346981Scy		if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len,
8783346981Scy			       res->beacon_ie_len))
8784346981Scy			goto fail;
8785346981Scy	}
8786346981Scy
8787346981Scy	os_get_reltime(&now);
8788346981Scy	wpa_bss_update_scan_res(wpa_s, res, &now);
8789346981Scy	ret = 0;
8790346981Scyfail:
8791346981Scy	os_free(res);
8792346981Scy
8793346981Scy	return ret;
8794346981Scy}
8795346981Scy
8796346981Scy
8797281806Srpaulostatic int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
8798281806Srpaulo{
8799281806Srpaulo	char *pos, *param;
8800281806Srpaulo	union wpa_event_data event;
8801281806Srpaulo	enum wpa_event_type ev;
8802281806Srpaulo
8803281806Srpaulo	/* <event name> [parameters..] */
8804281806Srpaulo
8805281806Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
8806281806Srpaulo
8807281806Srpaulo	pos = cmd;
8808281806Srpaulo	param = os_strchr(pos, ' ');
8809281806Srpaulo	if (param)
8810281806Srpaulo		*param++ = '\0';
8811281806Srpaulo
8812281806Srpaulo	os_memset(&event, 0, sizeof(event));
8813281806Srpaulo
8814281806Srpaulo	if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
8815281806Srpaulo		ev = EVENT_INTERFACE_ENABLED;
8816281806Srpaulo	} else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
8817281806Srpaulo		ev = EVENT_INTERFACE_DISABLED;
8818281806Srpaulo	} else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
8819281806Srpaulo		ev = EVENT_AVOID_FREQUENCIES;
8820281806Srpaulo		if (param == NULL)
8821281806Srpaulo			param = "";
8822281806Srpaulo		if (freq_range_list_parse(&event.freq_range, param) < 0)
8823281806Srpaulo			return -1;
8824281806Srpaulo		wpa_supplicant_event(wpa_s, ev, &event);
8825281806Srpaulo		os_free(event.freq_range.range);
8826281806Srpaulo		return 0;
8827346981Scy	} else if (os_strcmp(cmd, "SCAN_RES") == 0) {
8828346981Scy		return wpas_ctrl_iface_driver_scan_res(wpa_s, param);
8829281806Srpaulo	} else {
8830281806Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
8831281806Srpaulo			cmd);
8832281806Srpaulo		return -1;
8833281806Srpaulo	}
8834281806Srpaulo
8835281806Srpaulo	wpa_supplicant_event(wpa_s, ev, &event);
8836281806Srpaulo
8837281806Srpaulo	return 0;
8838281806Srpaulo}
8839281806Srpaulo
8840281806Srpaulo
8841281806Srpaulostatic int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
8842281806Srpaulo{
8843281806Srpaulo	char *pos;
8844281806Srpaulo	u8 src[ETH_ALEN], *buf;
8845281806Srpaulo	int used;
8846281806Srpaulo	size_t len;
8847281806Srpaulo
8848281806Srpaulo	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
8849281806Srpaulo
8850281806Srpaulo	pos = cmd;
8851281806Srpaulo	used = hwaddr_aton2(pos, src);
8852281806Srpaulo	if (used < 0)
8853281806Srpaulo		return -1;
8854281806Srpaulo	pos += used;
8855281806Srpaulo	while (*pos == ' ')
8856281806Srpaulo		pos++;
8857281806Srpaulo
8858281806Srpaulo	len = os_strlen(pos);
8859281806Srpaulo	if (len & 1)
8860281806Srpaulo		return -1;
8861281806Srpaulo	len /= 2;
8862281806Srpaulo
8863281806Srpaulo	buf = os_malloc(len);
8864281806Srpaulo	if (buf == NULL)
8865281806Srpaulo		return -1;
8866281806Srpaulo
8867281806Srpaulo	if (hexstr2bin(pos, buf, len) < 0) {
8868281806Srpaulo		os_free(buf);
8869281806Srpaulo		return -1;
8870281806Srpaulo	}
8871281806Srpaulo
8872281806Srpaulo	wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
8873281806Srpaulo	os_free(buf);
8874281806Srpaulo
8875281806Srpaulo	return 0;
8876281806Srpaulo}
8877281806Srpaulo
8878281806Srpaulo
8879281806Srpaulostatic u16 ipv4_hdr_checksum(const void *buf, size_t len)
8880281806Srpaulo{
8881281806Srpaulo	size_t i;
8882281806Srpaulo	u32 sum = 0;
8883281806Srpaulo	const u16 *pos = buf;
8884281806Srpaulo
8885281806Srpaulo	for (i = 0; i < len / 2; i++)
8886281806Srpaulo		sum += *pos++;
8887281806Srpaulo
8888281806Srpaulo	while (sum >> 16)
8889281806Srpaulo		sum = (sum & 0xffff) + (sum >> 16);
8890281806Srpaulo
8891281806Srpaulo	return sum ^ 0xffff;
8892281806Srpaulo}
8893281806Srpaulo
8894281806Srpaulo
8895281806Srpaulo#define HWSIM_PACKETLEN 1500
8896281806Srpaulo#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
8897281806Srpaulo
8898337817Scystatic void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
8899337817Scy			      size_t len)
8900281806Srpaulo{
8901281806Srpaulo	struct wpa_supplicant *wpa_s = ctx;
8902281806Srpaulo	const struct ether_header *eth;
8903289549Srpaulo	struct iphdr ip;
8904281806Srpaulo	const u8 *pos;
8905281806Srpaulo	unsigned int i;
8906346981Scy	char extra[30];
8907281806Srpaulo
8908346981Scy	if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) {
8909346981Scy		wpa_printf(MSG_DEBUG,
8910346981Scy			   "test data: RX - ignore unexpected length %d",
8911346981Scy			   (int) len);
8912281806Srpaulo		return;
8913346981Scy	}
8914281806Srpaulo
8915281806Srpaulo	eth = (const struct ether_header *) buf;
8916289549Srpaulo	os_memcpy(&ip, eth + 1, sizeof(ip));
8917289549Srpaulo	pos = &buf[sizeof(*eth) + sizeof(ip)];
8918281806Srpaulo
8919289549Srpaulo	if (ip.ihl != 5 || ip.version != 4 ||
8920346981Scy	    ntohs(ip.tot_len) > HWSIM_IP_LEN) {
8921346981Scy		wpa_printf(MSG_DEBUG,
8922346981Scy			   "test data: RX - ignore unexpect IP header");
8923281806Srpaulo		return;
8924346981Scy	}
8925281806Srpaulo
8926346981Scy	for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) {
8927346981Scy		if (*pos != (u8) i) {
8928346981Scy			wpa_printf(MSG_DEBUG,
8929346981Scy				   "test data: RX - ignore mismatching payload");
8930281806Srpaulo			return;
8931346981Scy		}
8932281806Srpaulo		pos++;
8933281806Srpaulo	}
8934346981Scy	extra[0] = '\0';
8935346981Scy	if (ntohs(ip.tot_len) != HWSIM_IP_LEN)
8936346981Scy		os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len));
8937346981Scy	wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
8938346981Scy		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
8939281806Srpaulo}
8940281806Srpaulo
8941281806Srpaulo
8942281806Srpaulostatic int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
8943281806Srpaulo					    char *cmd)
8944281806Srpaulo{
8945281806Srpaulo	int enabled = atoi(cmd);
8946337817Scy	char *pos;
8947337817Scy	const char *ifname;
8948281806Srpaulo
8949281806Srpaulo	if (!enabled) {
8950281806Srpaulo		if (wpa_s->l2_test) {
8951281806Srpaulo			l2_packet_deinit(wpa_s->l2_test);
8952281806Srpaulo			wpa_s->l2_test = NULL;
8953281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
8954281806Srpaulo		}
8955281806Srpaulo		return 0;
8956281806Srpaulo	}
8957281806Srpaulo
8958281806Srpaulo	if (wpa_s->l2_test)
8959281806Srpaulo		return 0;
8960281806Srpaulo
8961337817Scy	pos = os_strstr(cmd, " ifname=");
8962337817Scy	if (pos)
8963337817Scy		ifname = pos + 8;
8964337817Scy	else
8965337817Scy		ifname = wpa_s->ifname;
8966337817Scy
8967337817Scy	wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr,
8968281806Srpaulo					ETHERTYPE_IP, wpas_data_test_rx,
8969281806Srpaulo					wpa_s, 1);
8970281806Srpaulo	if (wpa_s->l2_test == NULL)
8971281806Srpaulo		return -1;
8972281806Srpaulo
8973281806Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
8974281806Srpaulo
8975281806Srpaulo	return 0;
8976281806Srpaulo}
8977281806Srpaulo
8978281806Srpaulo
8979281806Srpaulostatic int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
8980281806Srpaulo{
8981281806Srpaulo	u8 dst[ETH_ALEN], src[ETH_ALEN];
8982346981Scy	char *pos, *pos2;
8983281806Srpaulo	int used;
8984281806Srpaulo	long int val;
8985281806Srpaulo	u8 tos;
8986289549Srpaulo	u8 buf[2 + HWSIM_PACKETLEN];
8987281806Srpaulo	struct ether_header *eth;
8988281806Srpaulo	struct iphdr *ip;
8989281806Srpaulo	u8 *dpos;
8990281806Srpaulo	unsigned int i;
8991346981Scy	size_t send_len = HWSIM_IP_LEN;
8992281806Srpaulo
8993281806Srpaulo	if (wpa_s->l2_test == NULL)
8994281806Srpaulo		return -1;
8995281806Srpaulo
8996346981Scy	/* format: <dst> <src> <tos> [len=<length>] */
8997281806Srpaulo
8998281806Srpaulo	pos = cmd;
8999281806Srpaulo	used = hwaddr_aton2(pos, dst);
9000281806Srpaulo	if (used < 0)
9001281806Srpaulo		return -1;
9002281806Srpaulo	pos += used;
9003281806Srpaulo	while (*pos == ' ')
9004281806Srpaulo		pos++;
9005281806Srpaulo	used = hwaddr_aton2(pos, src);
9006281806Srpaulo	if (used < 0)
9007281806Srpaulo		return -1;
9008281806Srpaulo	pos += used;
9009281806Srpaulo
9010346981Scy	val = strtol(pos, &pos2, 0);
9011281806Srpaulo	if (val < 0 || val > 0xff)
9012281806Srpaulo		return -1;
9013281806Srpaulo	tos = val;
9014281806Srpaulo
9015346981Scy	pos = os_strstr(pos2, " len=");
9016346981Scy	if (pos) {
9017346981Scy		i = atoi(pos + 5);
9018346981Scy		if (i < sizeof(*ip) || i > HWSIM_IP_LEN)
9019346981Scy			return -1;
9020346981Scy		send_len = i;
9021346981Scy	}
9022346981Scy
9023289549Srpaulo	eth = (struct ether_header *) &buf[2];
9024281806Srpaulo	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
9025281806Srpaulo	os_memcpy(eth->ether_shost, src, ETH_ALEN);
9026281806Srpaulo	eth->ether_type = htons(ETHERTYPE_IP);
9027281806Srpaulo	ip = (struct iphdr *) (eth + 1);
9028281806Srpaulo	os_memset(ip, 0, sizeof(*ip));
9029281806Srpaulo	ip->ihl = 5;
9030281806Srpaulo	ip->version = 4;
9031281806Srpaulo	ip->ttl = 64;
9032281806Srpaulo	ip->tos = tos;
9033346981Scy	ip->tot_len = htons(send_len);
9034281806Srpaulo	ip->protocol = 1;
9035289549Srpaulo	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
9036289549Srpaulo	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
9037281806Srpaulo	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
9038281806Srpaulo	dpos = (u8 *) (ip + 1);
9039346981Scy	for (i = 0; i < send_len - sizeof(*ip); i++)
9040281806Srpaulo		*dpos++ = i;
9041281806Srpaulo
9042289549Srpaulo	if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
9043346981Scy			   sizeof(struct ether_header) + send_len) < 0)
9044281806Srpaulo		return -1;
9045281806Srpaulo
9046281806Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
9047281806Srpaulo		" tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
9048281806Srpaulo
9049281806Srpaulo	return 0;
9050281806Srpaulo}
9051281806Srpaulo
9052281806Srpaulo
9053281806Srpaulostatic int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
9054281806Srpaulo					   char *cmd)
9055281806Srpaulo{
9056281806Srpaulo	u8 *buf;
9057281806Srpaulo	struct ether_header *eth;
9058281806Srpaulo	struct l2_packet_data *l2 = NULL;
9059281806Srpaulo	size_t len;
9060281806Srpaulo	u16 ethertype;
9061281806Srpaulo	int res = -1;
9062281806Srpaulo
9063281806Srpaulo	len = os_strlen(cmd);
9064281806Srpaulo	if (len & 1 || len < ETH_HLEN * 2)
9065281806Srpaulo		return -1;
9066281806Srpaulo	len /= 2;
9067281806Srpaulo
9068281806Srpaulo	buf = os_malloc(len);
9069281806Srpaulo	if (buf == NULL)
9070281806Srpaulo		return -1;
9071281806Srpaulo
9072281806Srpaulo	if (hexstr2bin(cmd, buf, len) < 0)
9073281806Srpaulo		goto done;
9074281806Srpaulo
9075281806Srpaulo	eth = (struct ether_header *) buf;
9076281806Srpaulo	ethertype = ntohs(eth->ether_type);
9077281806Srpaulo
9078281806Srpaulo	l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
9079281806Srpaulo			    wpas_data_test_rx, wpa_s, 1);
9080281806Srpaulo	if (l2 == NULL)
9081281806Srpaulo		goto done;
9082281806Srpaulo
9083281806Srpaulo	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
9084281806Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
9085281806Srpaulodone:
9086281806Srpaulo	if (l2)
9087281806Srpaulo		l2_packet_deinit(l2);
9088281806Srpaulo	os_free(buf);
9089281806Srpaulo
9090281806Srpaulo	return res < 0 ? -1 : 0;
9091281806Srpaulo}
9092281806Srpaulo
9093281806Srpaulo
9094281806Srpaulostatic int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
9095281806Srpaulo{
9096281806Srpaulo#ifdef WPA_TRACE_BFD
9097281806Srpaulo	char *pos;
9098281806Srpaulo
9099281806Srpaulo	wpa_trace_fail_after = atoi(cmd);
9100281806Srpaulo	pos = os_strchr(cmd, ':');
9101281806Srpaulo	if (pos) {
9102281806Srpaulo		pos++;
9103281806Srpaulo		os_strlcpy(wpa_trace_fail_func, pos,
9104281806Srpaulo			   sizeof(wpa_trace_fail_func));
9105281806Srpaulo	} else {
9106281806Srpaulo		wpa_trace_fail_after = 0;
9107281806Srpaulo	}
9108281806Srpaulo	return 0;
9109281806Srpaulo#else /* WPA_TRACE_BFD */
9110281806Srpaulo	return -1;
9111281806Srpaulo#endif /* WPA_TRACE_BFD */
9112281806Srpaulo}
9113281806Srpaulo
9114281806Srpaulo
9115281806Srpaulostatic int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
9116281806Srpaulo				    char *buf, size_t buflen)
9117281806Srpaulo{
9118281806Srpaulo#ifdef WPA_TRACE_BFD
9119281806Srpaulo	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
9120281806Srpaulo			   wpa_trace_fail_func);
9121281806Srpaulo#else /* WPA_TRACE_BFD */
9122281806Srpaulo	return -1;
9123281806Srpaulo#endif /* WPA_TRACE_BFD */
9124281806Srpaulo}
9125281806Srpaulo
9126289549Srpaulo
9127289549Srpaulostatic int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
9128289549Srpaulo{
9129289549Srpaulo#ifdef WPA_TRACE_BFD
9130289549Srpaulo	char *pos;
9131289549Srpaulo
9132289549Srpaulo	wpa_trace_test_fail_after = atoi(cmd);
9133289549Srpaulo	pos = os_strchr(cmd, ':');
9134289549Srpaulo	if (pos) {
9135289549Srpaulo		pos++;
9136289549Srpaulo		os_strlcpy(wpa_trace_test_fail_func, pos,
9137289549Srpaulo			   sizeof(wpa_trace_test_fail_func));
9138289549Srpaulo	} else {
9139289549Srpaulo		wpa_trace_test_fail_after = 0;
9140289549Srpaulo	}
9141289549Srpaulo	return 0;
9142289549Srpaulo#else /* WPA_TRACE_BFD */
9143289549Srpaulo	return -1;
9144289549Srpaulo#endif /* WPA_TRACE_BFD */
9145289549Srpaulo}
9146289549Srpaulo
9147289549Srpaulo
9148289549Srpaulostatic int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
9149289549Srpaulo				    char *buf, size_t buflen)
9150289549Srpaulo{
9151289549Srpaulo#ifdef WPA_TRACE_BFD
9152289549Srpaulo	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
9153289549Srpaulo			   wpa_trace_test_fail_func);
9154289549Srpaulo#else /* WPA_TRACE_BFD */
9155289549Srpaulo	return -1;
9156289549Srpaulo#endif /* WPA_TRACE_BFD */
9157289549Srpaulo}
9158289549Srpaulo
9159281806Srpaulo
9160337817Scystatic void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
9161281806Srpaulo{
9162337817Scy	struct wpa_supplicant *wpa_s = eloop_ctx;
9163337817Scy	int i, count = (intptr_t) timeout_ctx;
9164281806Srpaulo
9165337817Scy	wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages",
9166337817Scy		   count);
9167337817Scy	for (i = 0; i < count; i++) {
9168337817Scy		wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d",
9169337817Scy			     i + 1, count);
9170337817Scy	}
9171337817Scy}
9172281806Srpaulo
9173281806Srpaulo
9174337817Scystatic int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd)
9175337817Scy{
9176337817Scy	int count;
9177281806Srpaulo
9178337817Scy	count = atoi(cmd);
9179337817Scy	if (count <= 0)
9180337817Scy		return -1;
9181337817Scy
9182337817Scy	return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s,
9183337817Scy				      (void *) (intptr_t) count);
9184281806Srpaulo}
9185281806Srpaulo
9186281806Srpaulo
9187337817Scystatic int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
9188337817Scy				   const char *cmd)
9189281806Srpaulo{
9190337817Scy	struct wpabuf *buf;
9191337817Scy	size_t len;
9192337817Scy
9193337817Scy	len = os_strlen(cmd);
9194337817Scy	if (len & 1)
9195337817Scy		return -1;
9196337817Scy	len /= 2;
9197337817Scy
9198337817Scy	if (len == 0) {
9199337817Scy		buf = NULL;
9200337817Scy	} else {
9201337817Scy		buf = wpabuf_alloc(len);
9202337817Scy		if (buf == NULL)
9203337817Scy			return -1;
9204337817Scy
9205337817Scy		if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
9206337817Scy			wpabuf_free(buf);
9207337817Scy			return -1;
9208337817Scy		}
9209281806Srpaulo	}
9210337817Scy
9211337817Scy	wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf);
9212337817Scy	return 0;
9213281806Srpaulo}
9214281806Srpaulo
9215346981Scy
9216346981Scystatic int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
9217346981Scy{
9218346981Scy	u8 zero[WPA_TK_MAX_LEN];
9219346981Scy
9220346981Scy	if (wpa_s->last_tk_alg == WPA_ALG_NONE)
9221346981Scy		return -1;
9222346981Scy
9223346981Scy	wpa_printf(MSG_INFO, "TESTING: Reset PN");
9224346981Scy	os_memset(zero, 0, sizeof(zero));
9225346981Scy
9226346981Scy	/* First, use a zero key to avoid any possible duplicate key avoidance
9227346981Scy	 * in the driver. */
9228346981Scy	if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
9229346981Scy			    wpa_s->last_tk_key_idx, 1, zero, 6,
9230346981Scy			    zero, wpa_s->last_tk_len) < 0)
9231346981Scy		return -1;
9232346981Scy
9233346981Scy	/* Set the previously configured key to reset its TSC/RSC */
9234346981Scy	return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
9235346981Scy			       wpa_s->last_tk_key_idx, 1, zero, 6,
9236346981Scy			       wpa_s->last_tk, wpa_s->last_tk_len);
9237346981Scy}
9238346981Scy
9239346981Scy
9240346981Scystatic int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd)
9241346981Scy{
9242346981Scy	const char *pos = cmd;
9243346981Scy	int error, pairwise;
9244346981Scy
9245346981Scy	error = atoi(pos);
9246346981Scy	pos = os_strchr(pos, ' ');
9247346981Scy	if (!pos)
9248346981Scy		return -1;
9249346981Scy	pairwise = atoi(pos);
9250346981Scy	wpa_sm_key_request(wpa_s->wpa, error, pairwise);
9251346981Scy	return 0;
9252346981Scy}
9253346981Scy
9254346981Scy
9255346981Scystatic int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s)
9256346981Scy{
9257346981Scy#ifdef CONFIG_SME
9258346981Scy	struct wpa_driver_associate_params params;
9259346981Scy	int ret;
9260346981Scy
9261346981Scy	os_memset(&params, 0, sizeof(params));
9262346981Scy	params.bssid = wpa_s->bssid;
9263346981Scy	params.ssid = wpa_s->sme.ssid;
9264346981Scy	params.ssid_len = wpa_s->sme.ssid_len;
9265346981Scy	params.freq.freq = wpa_s->sme.freq;
9266346981Scy	if (wpa_s->last_assoc_req_wpa_ie) {
9267346981Scy		params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie);
9268346981Scy		params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie);
9269346981Scy	}
9270346981Scy	params.pairwise_suite = wpa_s->pairwise_cipher;
9271346981Scy	params.group_suite = wpa_s->group_cipher;
9272346981Scy	params.mgmt_group_suite = wpa_s->mgmt_group_cipher;
9273346981Scy	params.key_mgmt_suite = wpa_s->key_mgmt;
9274346981Scy	params.wpa_proto = wpa_s->wpa_proto;
9275346981Scy	params.mgmt_frame_protection = wpa_s->sme.mfp;
9276346981Scy	params.rrm_used = wpa_s->rrm.rrm_used;
9277346981Scy	if (wpa_s->sme.prev_bssid_set)
9278346981Scy		params.prev_bssid = wpa_s->sme.prev_bssid;
9279346981Scy	wpa_printf(MSG_INFO, "TESTING: Resend association request");
9280346981Scy	ret = wpa_drv_associate(wpa_s, &params);
9281346981Scy	wpa_s->testing_resend_assoc = 1;
9282346981Scy	return ret;
9283346981Scy#else /* CONFIG_SME */
9284346981Scy	return -1;
9285346981Scy#endif /* CONFIG_SME */
9286346981Scy}
9287346981Scy
9288337817Scy#endif /* CONFIG_TESTING_OPTIONS */
9289281806Srpaulo
9290337817Scy
9291281806Srpaulostatic int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
9292281806Srpaulo{
9293281806Srpaulo	char *pos = cmd;
9294281806Srpaulo	int frame;
9295281806Srpaulo	size_t len;
9296281806Srpaulo	struct wpabuf *buf;
9297281806Srpaulo	struct ieee802_11_elems elems;
9298281806Srpaulo
9299281806Srpaulo	frame = atoi(pos);
9300281806Srpaulo	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
9301281806Srpaulo		return -1;
9302337817Scy	wpa_s = wpas_vendor_elem(wpa_s, frame);
9303281806Srpaulo
9304281806Srpaulo	pos = os_strchr(pos, ' ');
9305281806Srpaulo	if (pos == NULL)
9306281806Srpaulo		return -1;
9307281806Srpaulo	pos++;
9308281806Srpaulo
9309281806Srpaulo	len = os_strlen(pos);
9310281806Srpaulo	if (len == 0)
9311281806Srpaulo		return 0;
9312281806Srpaulo	if (len & 1)
9313281806Srpaulo		return -1;
9314281806Srpaulo	len /= 2;
9315281806Srpaulo
9316281806Srpaulo	buf = wpabuf_alloc(len);
9317281806Srpaulo	if (buf == NULL)
9318281806Srpaulo		return -1;
9319281806Srpaulo
9320281806Srpaulo	if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
9321281806Srpaulo		wpabuf_free(buf);
9322281806Srpaulo		return -1;
9323281806Srpaulo	}
9324281806Srpaulo
9325281806Srpaulo	if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
9326281806Srpaulo	    ParseFailed) {
9327281806Srpaulo		wpabuf_free(buf);
9328281806Srpaulo		return -1;
9329281806Srpaulo	}
9330281806Srpaulo
9331281806Srpaulo	if (wpa_s->vendor_elem[frame] == NULL) {
9332281806Srpaulo		wpa_s->vendor_elem[frame] = buf;
9333337817Scy		wpas_vendor_elem_update(wpa_s);
9334281806Srpaulo		return 0;
9335281806Srpaulo	}
9336281806Srpaulo
9337281806Srpaulo	if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
9338281806Srpaulo		wpabuf_free(buf);
9339281806Srpaulo		return -1;
9340281806Srpaulo	}
9341281806Srpaulo
9342281806Srpaulo	wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
9343281806Srpaulo	wpabuf_free(buf);
9344337817Scy	wpas_vendor_elem_update(wpa_s);
9345281806Srpaulo
9346281806Srpaulo	return 0;
9347281806Srpaulo}
9348281806Srpaulo
9349281806Srpaulo
9350281806Srpaulostatic int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
9351281806Srpaulo				     char *buf, size_t buflen)
9352281806Srpaulo{
9353281806Srpaulo	int frame = atoi(cmd);
9354281806Srpaulo
9355281806Srpaulo	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
9356281806Srpaulo		return -1;
9357337817Scy	wpa_s = wpas_vendor_elem(wpa_s, frame);
9358281806Srpaulo
9359281806Srpaulo	if (wpa_s->vendor_elem[frame] == NULL)
9360281806Srpaulo		return 0;
9361281806Srpaulo
9362281806Srpaulo	return wpa_snprintf_hex(buf, buflen,
9363281806Srpaulo				wpabuf_head_u8(wpa_s->vendor_elem[frame]),
9364281806Srpaulo				wpabuf_len(wpa_s->vendor_elem[frame]));
9365281806Srpaulo}
9366281806Srpaulo
9367281806Srpaulo
9368281806Srpaulostatic int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
9369281806Srpaulo{
9370281806Srpaulo	char *pos = cmd;
9371281806Srpaulo	int frame;
9372281806Srpaulo	size_t len;
9373281806Srpaulo	u8 *buf;
9374281806Srpaulo	struct ieee802_11_elems elems;
9375337817Scy	int res;
9376281806Srpaulo
9377281806Srpaulo	frame = atoi(pos);
9378281806Srpaulo	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
9379281806Srpaulo		return -1;
9380337817Scy	wpa_s = wpas_vendor_elem(wpa_s, frame);
9381281806Srpaulo
9382281806Srpaulo	pos = os_strchr(pos, ' ');
9383281806Srpaulo	if (pos == NULL)
9384281806Srpaulo		return -1;
9385281806Srpaulo	pos++;
9386281806Srpaulo
9387281806Srpaulo	if (*pos == '*') {
9388281806Srpaulo		wpabuf_free(wpa_s->vendor_elem[frame]);
9389281806Srpaulo		wpa_s->vendor_elem[frame] = NULL;
9390337817Scy		wpas_vendor_elem_update(wpa_s);
9391281806Srpaulo		return 0;
9392281806Srpaulo	}
9393281806Srpaulo
9394281806Srpaulo	if (wpa_s->vendor_elem[frame] == NULL)
9395281806Srpaulo		return -1;
9396281806Srpaulo
9397281806Srpaulo	len = os_strlen(pos);
9398281806Srpaulo	if (len == 0)
9399281806Srpaulo		return 0;
9400281806Srpaulo	if (len & 1)
9401281806Srpaulo		return -1;
9402281806Srpaulo	len /= 2;
9403281806Srpaulo
9404281806Srpaulo	buf = os_malloc(len);
9405281806Srpaulo	if (buf == NULL)
9406281806Srpaulo		return -1;
9407281806Srpaulo
9408281806Srpaulo	if (hexstr2bin(pos, buf, len) < 0) {
9409281806Srpaulo		os_free(buf);
9410281806Srpaulo		return -1;
9411281806Srpaulo	}
9412281806Srpaulo
9413281806Srpaulo	if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
9414281806Srpaulo		os_free(buf);
9415281806Srpaulo		return -1;
9416281806Srpaulo	}
9417281806Srpaulo
9418337817Scy	res = wpas_vendor_elem_remove(wpa_s, frame, buf, len);
9419281806Srpaulo	os_free(buf);
9420337817Scy	return res;
9421281806Srpaulo}
9422281806Srpaulo
9423281806Srpaulo
9424281806Srpaulostatic void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
9425281806Srpaulo{
9426281806Srpaulo	struct wpa_supplicant *wpa_s = ctx;
9427337817Scy	size_t len;
9428337817Scy	const u8 *data;
9429281806Srpaulo
9430337817Scy	/*
9431337817Scy	 * Neighbor Report element (IEEE P802.11-REVmc/D5.0)
9432337817Scy	 * BSSID[6]
9433337817Scy	 * BSSID Information[4]
9434337817Scy	 * Operating Class[1]
9435337817Scy	 * Channel Number[1]
9436337817Scy	 * PHY Type[1]
9437337817Scy	 * Optional Subelements[variable]
9438337817Scy	 */
9439337817Scy#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
9440337817Scy
9441337817Scy	if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) {
9442281806Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
9443337817Scy		goto out;
9444281806Srpaulo	}
9445337817Scy
9446337817Scy	data = wpabuf_head_u8(neighbor_rep);
9447337817Scy	len = wpabuf_len(neighbor_rep);
9448337817Scy
9449337817Scy	while (len >= 2 + NR_IE_MIN_LEN) {
9450337817Scy		const u8 *nr;
9451337817Scy		char lci[256 * 2 + 1];
9452337817Scy		char civic[256 * 2 + 1];
9453337817Scy		u8 nr_len = data[1];
9454337817Scy		const u8 *pos = data, *end;
9455337817Scy
9456337817Scy		if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
9457337817Scy		    nr_len < NR_IE_MIN_LEN) {
9458337817Scy			wpa_printf(MSG_DEBUG,
9459337817Scy				   "CTRL: Invalid Neighbor Report element: id=%u len=%u",
9460337817Scy				   data[0], nr_len);
9461337817Scy			goto out;
9462337817Scy		}
9463337817Scy
9464337817Scy		if (2U + nr_len > len) {
9465337817Scy			wpa_printf(MSG_DEBUG,
9466337817Scy				   "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
9467337817Scy				   data[0], len, nr_len);
9468337817Scy			goto out;
9469337817Scy		}
9470337817Scy		pos += 2;
9471337817Scy		end = pos + nr_len;
9472337817Scy
9473337817Scy		nr = pos;
9474337817Scy		pos += NR_IE_MIN_LEN;
9475337817Scy
9476337817Scy		lci[0] = '\0';
9477337817Scy		civic[0] = '\0';
9478337817Scy		while (end - pos > 2) {
9479337817Scy			u8 s_id, s_len;
9480337817Scy
9481337817Scy			s_id = *pos++;
9482337817Scy			s_len = *pos++;
9483337817Scy			if (s_len > end - pos)
9484337817Scy				goto out;
9485337817Scy			if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) {
9486337817Scy				/* Measurement Token[1] */
9487337817Scy				/* Measurement Report Mode[1] */
9488337817Scy				/* Measurement Type[1] */
9489337817Scy				/* Measurement Report[variable] */
9490337817Scy				switch (pos[2]) {
9491337817Scy				case MEASURE_TYPE_LCI:
9492337817Scy					if (lci[0])
9493337817Scy						break;
9494337817Scy					wpa_snprintf_hex(lci, sizeof(lci),
9495337817Scy							 pos, s_len);
9496337817Scy					break;
9497337817Scy				case MEASURE_TYPE_LOCATION_CIVIC:
9498337817Scy					if (civic[0])
9499337817Scy						break;
9500337817Scy					wpa_snprintf_hex(civic, sizeof(civic),
9501337817Scy							 pos, s_len);
9502337817Scy					break;
9503337817Scy				}
9504337817Scy			}
9505337817Scy
9506337817Scy			pos += s_len;
9507337817Scy		}
9508337817Scy
9509337817Scy		wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
9510337817Scy			"bssid=" MACSTR
9511337817Scy			" info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
9512337817Scy			MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
9513337817Scy			nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
9514337817Scy			nr[ETH_ALEN + 6],
9515337817Scy			lci[0] ? " lci=" : "", lci,
9516337817Scy			civic[0] ? " civic=" : "", civic);
9517337817Scy
9518337817Scy		data = end;
9519337817Scy		len -= 2 + nr_len;
9520337817Scy	}
9521337817Scy
9522337817Scyout:
9523337817Scy	wpabuf_free(neighbor_rep);
9524281806Srpaulo}
9525281806Srpaulo
9526281806Srpaulo
9527337817Scystatic int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s,
9528337817Scy					     char *cmd)
9529281806Srpaulo{
9530337817Scy	struct wpa_ssid_value ssid, *ssid_p = NULL;
9531337817Scy	int ret, lci = 0, civic = 0;
9532337817Scy	char *ssid_s;
9533281806Srpaulo
9534337817Scy	ssid_s = os_strstr(cmd, "ssid=");
9535337817Scy	if (ssid_s) {
9536337817Scy		if (ssid_parse(ssid_s + 5, &ssid)) {
9537337817Scy			wpa_printf(MSG_ERROR,
9538337817Scy				   "CTRL: Send Neighbor Report: bad SSID");
9539281806Srpaulo			return -1;
9540337817Scy		}
9541337817Scy
9542281806Srpaulo		ssid_p = &ssid;
9543337817Scy
9544337817Scy		/*
9545337817Scy		 * Move cmd after the SSID text that may include "lci" or
9546337817Scy		 * "civic".
9547337817Scy		 */
9548337817Scy		cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' ');
9549337817Scy		if (cmd)
9550337817Scy			cmd++;
9551337817Scy
9552281806Srpaulo	}
9553281806Srpaulo
9554337817Scy	if (cmd && os_strstr(cmd, "lci"))
9555337817Scy		lci = 1;
9556337817Scy
9557337817Scy	if (cmd && os_strstr(cmd, "civic"))
9558337817Scy		civic = 1;
9559337817Scy
9560337817Scy	ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic,
9561281806Srpaulo						 wpas_ctrl_neighbor_rep_cb,
9562281806Srpaulo						 wpa_s);
9563281806Srpaulo
9564281806Srpaulo	return ret;
9565281806Srpaulo}
9566281806Srpaulo
9567281806Srpaulo
9568281806Srpaulostatic int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
9569281806Srpaulo{
9570281806Srpaulo	eapol_sm_erp_flush(wpa_s->eapol);
9571281806Srpaulo	return 0;
9572281806Srpaulo}
9573281806Srpaulo
9574281806Srpaulo
9575281806Srpaulostatic int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
9576281806Srpaulo					 char *cmd)
9577281806Srpaulo{
9578281806Srpaulo	char *token, *context = NULL;
9579281806Srpaulo	unsigned int enable = ~0, type = 0;
9580281806Srpaulo	u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
9581281806Srpaulo	u8 *addr = NULL, *mask = NULL;
9582281806Srpaulo
9583281806Srpaulo	while ((token = str_token(cmd, " ", &context))) {
9584281806Srpaulo		if (os_strcasecmp(token, "scan") == 0) {
9585281806Srpaulo			type |= MAC_ADDR_RAND_SCAN;
9586281806Srpaulo		} else if (os_strcasecmp(token, "sched") == 0) {
9587281806Srpaulo			type |= MAC_ADDR_RAND_SCHED_SCAN;
9588281806Srpaulo		} else if (os_strcasecmp(token, "pno") == 0) {
9589281806Srpaulo			type |= MAC_ADDR_RAND_PNO;
9590281806Srpaulo		} else if (os_strcasecmp(token, "all") == 0) {
9591281806Srpaulo			type = wpa_s->mac_addr_rand_supported;
9592281806Srpaulo		} else if (os_strncasecmp(token, "enable=", 7) == 0) {
9593281806Srpaulo			enable = atoi(token + 7);
9594281806Srpaulo		} else if (os_strncasecmp(token, "addr=", 5) == 0) {
9595281806Srpaulo			addr = _addr;
9596281806Srpaulo			if (hwaddr_aton(token + 5, addr)) {
9597281806Srpaulo				wpa_printf(MSG_INFO,
9598281806Srpaulo					   "CTRL: Invalid MAC address: %s",
9599281806Srpaulo					   token);
9600281806Srpaulo				return -1;
9601281806Srpaulo			}
9602281806Srpaulo		} else if (os_strncasecmp(token, "mask=", 5) == 0) {
9603281806Srpaulo			mask = _mask;
9604281806Srpaulo			if (hwaddr_aton(token + 5, mask)) {
9605281806Srpaulo				wpa_printf(MSG_INFO,
9606281806Srpaulo					   "CTRL: Invalid MAC address mask: %s",
9607281806Srpaulo					   token);
9608281806Srpaulo				return -1;
9609281806Srpaulo			}
9610281806Srpaulo		} else {
9611281806Srpaulo			wpa_printf(MSG_INFO,
9612281806Srpaulo				   "CTRL: Invalid MAC_RAND_SCAN parameter: %s",
9613281806Srpaulo				   token);
9614281806Srpaulo			return -1;
9615281806Srpaulo		}
9616281806Srpaulo	}
9617281806Srpaulo
9618281806Srpaulo	if (!type) {
9619281806Srpaulo		wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
9620281806Srpaulo		return -1;
9621281806Srpaulo	}
9622281806Srpaulo
9623281806Srpaulo	if (enable > 1) {
9624281806Srpaulo		wpa_printf(MSG_INFO,
9625281806Srpaulo			   "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
9626281806Srpaulo		return -1;
9627281806Srpaulo	}
9628281806Srpaulo
9629351611Scy	if (!enable)
9630351611Scy		return wpas_disable_mac_addr_randomization(wpa_s, type);
9631281806Srpaulo
9632351611Scy	return wpas_enable_mac_addr_randomization(wpa_s, type, addr, mask);
9633281806Srpaulo}
9634281806Srpaulo
9635281806Srpaulo
9636337817Scystatic int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s,
9637337817Scy				 char *buf, size_t buflen)
9638337817Scy{
9639337817Scy	size_t reply_len;
9640337817Scy
9641337817Scy	reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen);
9642337817Scy#ifdef CONFIG_AP
9643337817Scy	reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len],
9644337817Scy					      buflen - reply_len);
9645337817Scy#endif /* CONFIG_AP */
9646337817Scy	return reply_len;
9647337817Scy}
9648337817Scy
9649337817Scy
9650337817Scystatic void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
9651337817Scy{
9652337817Scy	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
9653337817Scy#ifdef CONFIG_AP
9654337817Scy	wpas_ap_pmksa_cache_flush(wpa_s);
9655337817Scy#endif /* CONFIG_AP */
9656337817Scy}
9657337817Scy
9658337817Scy
9659346981Scy#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
9660346981Scy
9661346981Scystatic int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s,
9662346981Scy				     const char *cmd, char *buf, size_t buflen)
9663346981Scy{
9664346981Scy	struct rsn_pmksa_cache_entry *entry;
9665346981Scy	struct wpa_ssid *ssid;
9666346981Scy	char *pos, *pos2, *end;
9667346981Scy	int ret;
9668346981Scy	struct os_reltime now;
9669346981Scy
9670346981Scy	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
9671346981Scy	if (!ssid)
9672346981Scy		return -1;
9673346981Scy
9674346981Scy	pos = buf;
9675346981Scy	end = buf + buflen;
9676346981Scy
9677346981Scy	os_get_reltime(&now);
9678346981Scy
9679346981Scy	/*
9680346981Scy	 * Entry format:
9681346981Scy	 * <BSSID> <PMKID> <PMK> <reauth_time in seconds>
9682346981Scy	 * <expiration in seconds> <akmp> <opportunistic>
9683346981Scy	 * [FILS Cache Identifier]
9684346981Scy	 */
9685346981Scy
9686346981Scy	for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
9687346981Scy	     entry = entry->next) {
9688346981Scy		if (entry->network_ctx != ssid)
9689346981Scy			continue;
9690346981Scy
9691346981Scy		pos2 = pos;
9692346981Scy		ret = os_snprintf(pos2, end - pos2, MACSTR " ",
9693346981Scy				  MAC2STR(entry->aa));
9694346981Scy		if (os_snprintf_error(end - pos2, ret))
9695346981Scy			break;
9696346981Scy		pos2 += ret;
9697346981Scy
9698346981Scy		pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid,
9699346981Scy					 PMKID_LEN);
9700346981Scy
9701346981Scy		ret = os_snprintf(pos2, end - pos2, " ");
9702346981Scy		if (os_snprintf_error(end - pos2, ret))
9703346981Scy			break;
9704346981Scy		pos2 += ret;
9705346981Scy
9706346981Scy		pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk,
9707346981Scy					 entry->pmk_len);
9708346981Scy
9709346981Scy		ret = os_snprintf(pos2, end - pos2, " %d %d %d %d",
9710346981Scy				  (int) (entry->reauth_time - now.sec),
9711346981Scy				  (int) (entry->expiration - now.sec),
9712346981Scy				  entry->akmp,
9713346981Scy				  entry->opportunistic);
9714346981Scy		if (os_snprintf_error(end - pos2, ret))
9715346981Scy			break;
9716346981Scy		pos2 += ret;
9717346981Scy
9718346981Scy		if (entry->fils_cache_id_set) {
9719346981Scy			ret = os_snprintf(pos2, end - pos2, " %02x%02x",
9720346981Scy					  entry->fils_cache_id[0],
9721346981Scy					  entry->fils_cache_id[1]);
9722346981Scy			if (os_snprintf_error(end - pos2, ret))
9723346981Scy				break;
9724346981Scy			pos2 += ret;
9725346981Scy		}
9726346981Scy
9727346981Scy		ret = os_snprintf(pos2, end - pos2, "\n");
9728346981Scy		if (os_snprintf_error(end - pos2, ret))
9729346981Scy			break;
9730346981Scy		pos2 += ret;
9731346981Scy
9732346981Scy		pos = pos2;
9733346981Scy	}
9734346981Scy
9735346981Scy	return pos - buf;
9736346981Scy}
9737346981Scy
9738346981Scy
9739346981Scystatic int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
9740346981Scy				     char *cmd)
9741346981Scy{
9742346981Scy	struct rsn_pmksa_cache_entry *entry;
9743346981Scy	struct wpa_ssid *ssid;
9744346981Scy	char *pos, *pos2;
9745346981Scy	int ret = -1;
9746346981Scy	struct os_reltime now;
9747346981Scy	int reauth_time = 0, expiration = 0, i;
9748346981Scy
9749346981Scy	/*
9750346981Scy	 * Entry format:
9751346981Scy	 * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
9752346981Scy	 * <expiration in seconds> <akmp> <opportunistic>
9753346981Scy	 * [FILS Cache Identifier]
9754346981Scy	 */
9755346981Scy
9756346981Scy	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
9757346981Scy	if (!ssid)
9758346981Scy		return -1;
9759346981Scy
9760346981Scy	pos = os_strchr(cmd, ' ');
9761346981Scy	if (!pos)
9762346981Scy		return -1;
9763346981Scy	pos++;
9764346981Scy
9765346981Scy	entry = os_zalloc(sizeof(*entry));
9766346981Scy	if (!entry)
9767346981Scy		return -1;
9768346981Scy
9769346981Scy	if (hwaddr_aton(pos, entry->aa))
9770346981Scy		goto fail;
9771346981Scy
9772346981Scy	pos = os_strchr(pos, ' ');
9773346981Scy	if (!pos)
9774346981Scy		goto fail;
9775346981Scy	pos++;
9776346981Scy
9777346981Scy	if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0)
9778346981Scy		goto fail;
9779346981Scy
9780346981Scy	pos = os_strchr(pos, ' ');
9781346981Scy	if (!pos)
9782346981Scy		goto fail;
9783346981Scy	pos++;
9784346981Scy
9785346981Scy	pos2 = os_strchr(pos, ' ');
9786346981Scy	if (!pos2)
9787346981Scy		goto fail;
9788346981Scy	entry->pmk_len = (pos2 - pos) / 2;
9789346981Scy	if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX ||
9790346981Scy	    hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0)
9791346981Scy		goto fail;
9792346981Scy
9793346981Scy	pos = os_strchr(pos, ' ');
9794346981Scy	if (!pos)
9795346981Scy		goto fail;
9796346981Scy	pos++;
9797346981Scy
9798346981Scy	if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
9799346981Scy		   &entry->akmp, &entry->opportunistic) != 4)
9800346981Scy		goto fail;
9801346981Scy	for (i = 0; i < 4; i++) {
9802346981Scy		pos = os_strchr(pos, ' ');
9803346981Scy		if (!pos) {
9804346981Scy			if (i < 3)
9805346981Scy				goto fail;
9806346981Scy			break;
9807346981Scy		}
9808346981Scy		pos++;
9809346981Scy	}
9810346981Scy	if (pos) {
9811346981Scy		if (hexstr2bin(pos, entry->fils_cache_id,
9812346981Scy			       FILS_CACHE_ID_LEN) < 0)
9813346981Scy			goto fail;
9814346981Scy		entry->fils_cache_id_set = 1;
9815346981Scy	}
9816346981Scy	os_get_reltime(&now);
9817346981Scy	entry->expiration = now.sec + expiration;
9818346981Scy	entry->reauth_time = now.sec + reauth_time;
9819346981Scy
9820346981Scy	entry->network_ctx = ssid;
9821346981Scy
9822346981Scy	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
9823346981Scy	entry = NULL;
9824346981Scy	ret = 0;
9825346981Scyfail:
9826346981Scy	os_free(entry);
9827346981Scy	return ret;
9828346981Scy}
9829346981Scy
9830346981Scy
9831346981Scy#ifdef CONFIG_MESH
9832346981Scy
9833346981Scystatic int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s,
9834346981Scy					  const char *cmd, char *buf,
9835346981Scy					  size_t buflen)
9836346981Scy{
9837346981Scy	u8 spa[ETH_ALEN];
9838346981Scy
9839346981Scy	if (!wpa_s->ifmsh)
9840346981Scy		return -1;
9841346981Scy
9842346981Scy	if (os_strcasecmp(cmd, "any") == 0)
9843346981Scy		return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen);
9844346981Scy
9845346981Scy	if (hwaddr_aton(cmd, spa))
9846346981Scy		return -1;
9847346981Scy
9848346981Scy	return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen);
9849346981Scy}
9850346981Scy
9851346981Scy
9852346981Scystatic int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s,
9853346981Scy					  char *cmd)
9854346981Scy{
9855346981Scy	/*
9856346981Scy	 * We do not check mesh interface existance because PMKSA should be
9857346981Scy	 * stored before wpa_s->ifmsh creation to suppress commit message
9858346981Scy	 * creation.
9859346981Scy	 */
9860346981Scy	return wpas_ap_pmksa_cache_add_external(wpa_s, cmd);
9861346981Scy}
9862346981Scy
9863346981Scy#endif /* CONFIG_MESH */
9864346981Scy#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
9865346981Scy
9866346981Scy
9867346981Scy#ifdef CONFIG_FILS
9868346981Scystatic int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s,
9869346981Scy					    const char *cmd)
9870346981Scy{
9871346981Scy	struct fils_hlp_req *req;
9872346981Scy	const char *pos;
9873346981Scy
9874346981Scy	/* format: <dst> <packet starting from ethertype> */
9875346981Scy
9876346981Scy	req = os_zalloc(sizeof(*req));
9877346981Scy	if (!req)
9878346981Scy		return -1;
9879346981Scy
9880346981Scy	if (hwaddr_aton(cmd, req->dst))
9881346981Scy		goto fail;
9882346981Scy
9883346981Scy	pos = os_strchr(cmd, ' ');
9884346981Scy	if (!pos)
9885346981Scy		goto fail;
9886346981Scy	pos++;
9887346981Scy	req->pkt = wpabuf_parse_bin(pos);
9888346981Scy	if (!req->pkt)
9889346981Scy		goto fail;
9890346981Scy
9891346981Scy	dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list);
9892346981Scy	return 0;
9893346981Scyfail:
9894346981Scy	wpabuf_free(req->pkt);
9895346981Scy	os_free(req);
9896346981Scy	return -1;
9897346981Scy}
9898346981Scy#endif /* CONFIG_FILS */
9899346981Scy
9900346981Scy
9901289549Srpaulostatic int wpas_ctrl_cmd_debug_level(const char *cmd)
9902289549Srpaulo{
9903289549Srpaulo	if (os_strcmp(cmd, "PING") == 0 ||
9904289549Srpaulo	    os_strncmp(cmd, "BSS ", 4) == 0 ||
9905289549Srpaulo	    os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
9906289549Srpaulo	    os_strncmp(cmd, "STATUS", 6) == 0 ||
9907289549Srpaulo	    os_strncmp(cmd, "STA ", 4) == 0 ||
9908289549Srpaulo	    os_strncmp(cmd, "STA-", 4) == 0)
9909289549Srpaulo		return MSG_EXCESSIVE;
9910289549Srpaulo	return MSG_DEBUG;
9911289549Srpaulo}
9912289549Srpaulo
9913289549Srpaulo
9914189251Ssamchar * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
9915189251Ssam					 char *buf, size_t *resp_len)
9916189251Ssam{
9917189251Ssam	char *reply;
9918252726Srpaulo	const int reply_size = 4096;
9919189251Ssam	int reply_len;
9920189251Ssam
9921189251Ssam	if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
9922346981Scy	    os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
9923346981Scy	    os_strncmp(buf, "PMKSA_ADD ", 10) == 0 ||
9924346981Scy	    os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
9925281806Srpaulo		if (wpa_debug_show_keys)
9926281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG,
9927281806Srpaulo				"Control interface command '%s'", buf);
9928281806Srpaulo		else
9929281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG,
9930281806Srpaulo				"Control interface command '%s [REMOVED]'",
9931281806Srpaulo				os_strncmp(buf, WPA_CTRL_RSP,
9932281806Srpaulo					   os_strlen(WPA_CTRL_RSP)) == 0 ?
9933346981Scy				WPA_CTRL_RSP :
9934346981Scy				(os_strncmp(buf, "SET_NETWORK ", 12) == 0 ?
9935346981Scy				 "SET_NETWORK" : "key-add"));
9936281806Srpaulo	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
9937281806Srpaulo		   os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
9938189251Ssam		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
9939189251Ssam				      (const u8 *) buf, os_strlen(buf));
9940189251Ssam	} else {
9941289549Srpaulo		int level = wpas_ctrl_cmd_debug_level(buf);
9942252726Srpaulo		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
9943189251Ssam	}
9944189251Ssam
9945189251Ssam	reply = os_malloc(reply_size);
9946189251Ssam	if (reply == NULL) {
9947189251Ssam		*resp_len = 1;
9948189251Ssam		return NULL;
9949189251Ssam	}
9950189251Ssam
9951189251Ssam	os_memcpy(reply, "OK\n", 3);
9952189251Ssam	reply_len = 3;
9953189251Ssam
9954189251Ssam	if (os_strcmp(buf, "PING") == 0) {
9955189251Ssam		os_memcpy(reply, "PONG\n", 5);
9956189251Ssam		reply_len = 5;
9957252726Srpaulo	} else if (os_strcmp(buf, "IFNAME") == 0) {
9958252726Srpaulo		reply_len = os_strlen(wpa_s->ifname);
9959252726Srpaulo		os_memcpy(reply, wpa_s->ifname, reply_len);
9960252726Srpaulo	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
9961252726Srpaulo		if (wpa_debug_reopen_file() < 0)
9962252726Srpaulo			reply_len = -1;
9963252726Srpaulo	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
9964252726Srpaulo		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
9965189251Ssam	} else if (os_strcmp(buf, "MIB") == 0) {
9966189251Ssam		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
9967189251Ssam		if (reply_len >= 0) {
9968281806Srpaulo			reply_len += eapol_sm_get_mib(wpa_s->eapol,
9969281806Srpaulo						      reply + reply_len,
9970281806Srpaulo						      reply_size - reply_len);
9971346981Scy#ifdef CONFIG_MACSEC
9972346981Scy			reply_len += ieee802_1x_kay_get_mib(
9973346981Scy				wpa_s->kay, reply + reply_len,
9974346981Scy				reply_size - reply_len);
9975346981Scy#endif /* CONFIG_MACSEC */
9976189251Ssam		}
9977189251Ssam	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
9978189251Ssam		reply_len = wpa_supplicant_ctrl_iface_status(
9979189251Ssam			wpa_s, buf + 6, reply, reply_size);
9980189251Ssam	} else if (os_strcmp(buf, "PMKSA") == 0) {
9981337817Scy		reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
9982281806Srpaulo	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
9983337817Scy		wpas_ctrl_iface_pmksa_flush(wpa_s);
9984346981Scy#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
9985346981Scy	} else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) {
9986346981Scy		reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10,
9987346981Scy						      reply, reply_size);
9988346981Scy	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
9989346981Scy		if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0)
9990346981Scy			reply_len = -1;
9991346981Scy#ifdef CONFIG_MESH
9992346981Scy	} else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) {
9993346981Scy		reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15,
9994346981Scy							   reply, reply_size);
9995346981Scy	} else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
9996346981Scy		if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0)
9997346981Scy			reply_len = -1;
9998346981Scy#endif /* CONFIG_MESH */
9999346981Scy#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
10000189251Ssam	} else if (os_strncmp(buf, "SET ", 4) == 0) {
10001189251Ssam		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
10002189251Ssam			reply_len = -1;
10003281806Srpaulo	} else if (os_strncmp(buf, "DUMP", 4) == 0) {
10004281806Srpaulo		reply_len = wpa_config_dump_values(wpa_s->conf,
10005281806Srpaulo						   reply, reply_size);
10006252726Srpaulo	} else if (os_strncmp(buf, "GET ", 4) == 0) {
10007252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
10008252726Srpaulo							  reply, reply_size);
10009189251Ssam	} else if (os_strcmp(buf, "LOGON") == 0) {
10010189251Ssam		eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
10011189251Ssam	} else if (os_strcmp(buf, "LOGOFF") == 0) {
10012189251Ssam		eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
10013189251Ssam	} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
10014252726Srpaulo		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
10015252726Srpaulo			reply_len = -1;
10016252726Srpaulo		else
10017252726Srpaulo			wpas_request_connection(wpa_s);
10018281806Srpaulo	} else if (os_strcmp(buf, "REATTACH") == 0) {
10019281806Srpaulo		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
10020281806Srpaulo		    !wpa_s->current_ssid)
10021281806Srpaulo			reply_len = -1;
10022281806Srpaulo		else {
10023281806Srpaulo			wpa_s->reattach = 1;
10024281806Srpaulo			wpas_request_connection(wpa_s);
10025281806Srpaulo		}
10026189251Ssam	} else if (os_strcmp(buf, "RECONNECT") == 0) {
10027252726Srpaulo		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
10028252726Srpaulo			reply_len = -1;
10029252726Srpaulo		else if (wpa_s->disconnected)
10030252726Srpaulo			wpas_request_connection(wpa_s);
10031189251Ssam#ifdef IEEE8021X_EAPOL
10032189251Ssam	} else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
10033189251Ssam		if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
10034189251Ssam			reply_len = -1;
10035189251Ssam#endif /* IEEE8021X_EAPOL */
10036189251Ssam#ifdef CONFIG_IEEE80211R
10037189251Ssam	} else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
10038189251Ssam		if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
10039189251Ssam			reply_len = -1;
10040189251Ssam#endif /* CONFIG_IEEE80211R */
10041189251Ssam#ifdef CONFIG_WPS
10042189251Ssam	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
10043252726Srpaulo		int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
10044252726Srpaulo		if (res == -2) {
10045252726Srpaulo			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
10046252726Srpaulo			reply_len = 17;
10047252726Srpaulo		} else if (res)
10048189251Ssam			reply_len = -1;
10049189251Ssam	} else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
10050252726Srpaulo		int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
10051252726Srpaulo		if (res == -2) {
10052252726Srpaulo			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
10053252726Srpaulo			reply_len = 17;
10054252726Srpaulo		} else if (res)
10055189251Ssam			reply_len = -1;
10056189251Ssam	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
10057189251Ssam		reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
10058189251Ssam							      reply,
10059189251Ssam							      reply_size);
10060252726Srpaulo	} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
10061252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
10062252726Srpaulo			wpa_s, buf + 14, reply, reply_size);
10063252726Srpaulo	} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
10064252726Srpaulo		if (wpas_wps_cancel(wpa_s))
10065214734Srpaulo			reply_len = -1;
10066252726Srpaulo#ifdef CONFIG_WPS_NFC
10067252726Srpaulo	} else if (os_strcmp(buf, "WPS_NFC") == 0) {
10068252726Srpaulo		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
10069252726Srpaulo			reply_len = -1;
10070252726Srpaulo	} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
10071252726Srpaulo		if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
10072252726Srpaulo			reply_len = -1;
10073281806Srpaulo	} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
10074281806Srpaulo		reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
10075281806Srpaulo			wpa_s, buf + 21, reply, reply_size);
10076252726Srpaulo	} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
10077252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
10078252726Srpaulo			wpa_s, buf + 14, reply, reply_size);
10079252726Srpaulo	} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
10080252726Srpaulo		if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
10081252726Srpaulo							       buf + 17))
10082252726Srpaulo			reply_len = -1;
10083252726Srpaulo	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
10084252726Srpaulo		reply_len = wpas_ctrl_nfc_get_handover_req(
10085252726Srpaulo			wpa_s, buf + 21, reply, reply_size);
10086252726Srpaulo	} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
10087252726Srpaulo		reply_len = wpas_ctrl_nfc_get_handover_sel(
10088252726Srpaulo			wpa_s, buf + 21, reply, reply_size);
10089281806Srpaulo	} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
10090281806Srpaulo		if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
10091252726Srpaulo			reply_len = -1;
10092252726Srpaulo#endif /* CONFIG_WPS_NFC */
10093189251Ssam	} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
10094189251Ssam		if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
10095189251Ssam			reply_len = -1;
10096252726Srpaulo#ifdef CONFIG_AP
10097252726Srpaulo	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
10098252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
10099252726Srpaulo			wpa_s, buf + 11, reply, reply_size);
10100252726Srpaulo#endif /* CONFIG_AP */
10101214734Srpaulo#ifdef CONFIG_WPS_ER
10102214734Srpaulo	} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
10103252726Srpaulo		if (wpas_wps_er_start(wpa_s, NULL))
10104214734Srpaulo			reply_len = -1;
10105252726Srpaulo	} else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
10106252726Srpaulo		if (wpas_wps_er_start(wpa_s, buf + 13))
10107252726Srpaulo			reply_len = -1;
10108214734Srpaulo	} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
10109281806Srpaulo		wpas_wps_er_stop(wpa_s);
10110214734Srpaulo	} else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
10111214734Srpaulo		if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
10112214734Srpaulo			reply_len = -1;
10113214734Srpaulo	} else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
10114252726Srpaulo		int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
10115252726Srpaulo		if (ret == -2) {
10116252726Srpaulo			os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
10117252726Srpaulo			reply_len = 17;
10118252726Srpaulo		} else if (ret == -3) {
10119252726Srpaulo			os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
10120252726Srpaulo			reply_len = 18;
10121252726Srpaulo		} else if (ret == -4) {
10122252726Srpaulo			os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
10123252726Srpaulo			reply_len = 20;
10124252726Srpaulo		} else if (ret)
10125214734Srpaulo			reply_len = -1;
10126214734Srpaulo	} else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
10127214734Srpaulo		if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
10128214734Srpaulo			reply_len = -1;
10129252726Srpaulo	} else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
10130252726Srpaulo		if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
10131252726Srpaulo								buf + 18))
10132252726Srpaulo			reply_len = -1;
10133252726Srpaulo	} else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
10134252726Srpaulo		if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
10135252726Srpaulo			reply_len = -1;
10136252726Srpaulo#ifdef CONFIG_WPS_NFC
10137252726Srpaulo	} else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
10138252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
10139252726Srpaulo			wpa_s, buf + 24, reply, reply_size);
10140252726Srpaulo#endif /* CONFIG_WPS_NFC */
10141214734Srpaulo#endif /* CONFIG_WPS_ER */
10142189251Ssam#endif /* CONFIG_WPS */
10143214734Srpaulo#ifdef CONFIG_IBSS_RSN
10144214734Srpaulo	} else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
10145214734Srpaulo		if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
10146214734Srpaulo			reply_len = -1;
10147214734Srpaulo#endif /* CONFIG_IBSS_RSN */
10148281806Srpaulo#ifdef CONFIG_MESH
10149281806Srpaulo	} else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
10150281806Srpaulo		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
10151281806Srpaulo			wpa_s, buf + 19, reply, reply_size);
10152281806Srpaulo	} else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
10153281806Srpaulo		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
10154281806Srpaulo			wpa_s, "", reply, reply_size);
10155281806Srpaulo	} else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
10156281806Srpaulo		if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
10157281806Srpaulo			reply_len = -1;
10158281806Srpaulo	} else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
10159281806Srpaulo		if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
10160281806Srpaulo								buf + 18))
10161281806Srpaulo			reply_len = -1;
10162337817Scy	} else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) {
10163337817Scy		if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17))
10164337817Scy			reply_len = -1;
10165337817Scy	} else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) {
10166337817Scy		if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14))
10167337817Scy			reply_len = -1;
10168351611Scy	} else if (os_strncmp(buf, "MESH_LINK_PROBE ", 16) == 0) {
10169351611Scy		if (wpa_supplicant_ctrl_iface_mesh_link_probe(wpa_s, buf + 16))
10170351611Scy			reply_len = -1;
10171281806Srpaulo#endif /* CONFIG_MESH */
10172252726Srpaulo#ifdef CONFIG_P2P
10173252726Srpaulo	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
10174281806Srpaulo		if (p2p_ctrl_find(wpa_s, buf + 8))
10175252726Srpaulo			reply_len = -1;
10176252726Srpaulo	} else if (os_strcmp(buf, "P2P_FIND") == 0) {
10177252726Srpaulo		if (p2p_ctrl_find(wpa_s, ""))
10178252726Srpaulo			reply_len = -1;
10179252726Srpaulo	} else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
10180252726Srpaulo		wpas_p2p_stop_find(wpa_s);
10181281806Srpaulo	} else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
10182281806Srpaulo		if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
10183281806Srpaulo			reply_len = -1;
10184281806Srpaulo	} else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
10185281806Srpaulo		if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
10186281806Srpaulo			reply_len = -1;
10187252726Srpaulo	} else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
10188252726Srpaulo		reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
10189252726Srpaulo					     reply_size);
10190252726Srpaulo	} else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
10191252726Srpaulo		if (p2p_ctrl_listen(wpa_s, buf + 11))
10192252726Srpaulo			reply_len = -1;
10193252726Srpaulo	} else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
10194252726Srpaulo		if (p2p_ctrl_listen(wpa_s, ""))
10195252726Srpaulo			reply_len = -1;
10196252726Srpaulo	} else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
10197252726Srpaulo		if (wpas_p2p_group_remove(wpa_s, buf + 17))
10198252726Srpaulo			reply_len = -1;
10199252726Srpaulo	} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
10200289549Srpaulo		if (p2p_ctrl_group_add(wpa_s, ""))
10201252726Srpaulo			reply_len = -1;
10202252726Srpaulo	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
10203252726Srpaulo		if (p2p_ctrl_group_add(wpa_s, buf + 14))
10204252726Srpaulo			reply_len = -1;
10205337817Scy	} else if (os_strncmp(buf, "P2P_GROUP_MEMBER ", 17) == 0) {
10206337817Scy		reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply,
10207337817Scy						  reply_size);
10208252726Srpaulo	} else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
10209252726Srpaulo		if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
10210252726Srpaulo			reply_len = -1;
10211252726Srpaulo	} else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
10212252726Srpaulo		reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
10213252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
10214252726Srpaulo		reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
10215252726Srpaulo						   reply_size);
10216252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
10217252726Srpaulo		if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
10218252726Srpaulo			reply_len = -1;
10219252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
10220252726Srpaulo		if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
10221252726Srpaulo			reply_len = -1;
10222252726Srpaulo	} else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
10223252726Srpaulo		wpas_p2p_sd_service_update(wpa_s);
10224252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
10225252726Srpaulo		if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
10226252726Srpaulo			reply_len = -1;
10227252726Srpaulo	} else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
10228252726Srpaulo		wpas_p2p_service_flush(wpa_s);
10229252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
10230252726Srpaulo		if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
10231252726Srpaulo			reply_len = -1;
10232252726Srpaulo	} else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
10233252726Srpaulo		if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
10234252726Srpaulo			reply_len = -1;
10235281806Srpaulo	} else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
10236281806Srpaulo		if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
10237281806Srpaulo			reply_len = -1;
10238252726Srpaulo	} else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
10239252726Srpaulo		if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
10240252726Srpaulo			reply_len = -1;
10241252726Srpaulo	} else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
10242252726Srpaulo		if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
10243252726Srpaulo			reply_len = -1;
10244252726Srpaulo	} else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
10245252726Srpaulo		reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
10246252726Srpaulo					      reply_size);
10247252726Srpaulo	} else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
10248252726Srpaulo		if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
10249252726Srpaulo			reply_len = -1;
10250252726Srpaulo	} else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
10251281806Srpaulo		p2p_ctrl_flush(wpa_s);
10252252726Srpaulo	} else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
10253252726Srpaulo		if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
10254252726Srpaulo			reply_len = -1;
10255252726Srpaulo	} else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
10256252726Srpaulo		if (wpas_p2p_cancel(wpa_s))
10257252726Srpaulo			reply_len = -1;
10258252726Srpaulo	} else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
10259252726Srpaulo		if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
10260252726Srpaulo			reply_len = -1;
10261252726Srpaulo	} else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
10262252726Srpaulo		if (p2p_ctrl_presence_req(wpa_s, "") < 0)
10263252726Srpaulo			reply_len = -1;
10264252726Srpaulo	} else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
10265252726Srpaulo		if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
10266252726Srpaulo			reply_len = -1;
10267252726Srpaulo	} else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
10268252726Srpaulo		if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
10269252726Srpaulo			reply_len = -1;
10270281806Srpaulo	} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
10271281806Srpaulo		if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
10272281806Srpaulo			reply_len = -1;
10273337817Scy	} else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) {
10274337817Scy		if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13))
10275337817Scy			reply_len = -1;
10276337817Scy	} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
10277337817Scy		if (wpas_p2p_lo_stop(wpa_s))
10278337817Scy			reply_len = -1;
10279252726Srpaulo#endif /* CONFIG_P2P */
10280252726Srpaulo#ifdef CONFIG_WIFI_DISPLAY
10281252726Srpaulo	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
10282252726Srpaulo		if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
10283252726Srpaulo			reply_len = -1;
10284252726Srpaulo	} else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
10285252726Srpaulo		reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
10286252726Srpaulo						     reply, reply_size);
10287252726Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
10288252726Srpaulo#ifdef CONFIG_INTERWORKING
10289252726Srpaulo	} else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
10290252726Srpaulo		if (interworking_fetch_anqp(wpa_s) < 0)
10291252726Srpaulo			reply_len = -1;
10292252726Srpaulo	} else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
10293252726Srpaulo		interworking_stop_fetch_anqp(wpa_s);
10294281806Srpaulo	} else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
10295281806Srpaulo		if (ctrl_interworking_select(wpa_s, NULL) < 0)
10296252726Srpaulo			reply_len = -1;
10297281806Srpaulo	} else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
10298281806Srpaulo		if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
10299281806Srpaulo			reply_len = -1;
10300252726Srpaulo	} else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
10301281806Srpaulo		if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0)
10302252726Srpaulo			reply_len = -1;
10303281806Srpaulo	} else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
10304281806Srpaulo		int id;
10305281806Srpaulo
10306281806Srpaulo		id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
10307281806Srpaulo		if (id < 0)
10308281806Srpaulo			reply_len = -1;
10309281806Srpaulo		else {
10310281806Srpaulo			reply_len = os_snprintf(reply, reply_size, "%d\n", id);
10311281806Srpaulo			if (os_snprintf_error(reply_size, reply_len))
10312281806Srpaulo				reply_len = -1;
10313281806Srpaulo		}
10314252726Srpaulo	} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
10315252726Srpaulo		if (get_anqp(wpa_s, buf + 9) < 0)
10316252726Srpaulo			reply_len = -1;
10317252726Srpaulo	} else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
10318252726Srpaulo		if (gas_request(wpa_s, buf + 12) < 0)
10319252726Srpaulo			reply_len = -1;
10320252726Srpaulo	} else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
10321252726Srpaulo		reply_len = gas_response_get(wpa_s, buf + 17, reply,
10322252726Srpaulo					     reply_size);
10323252726Srpaulo#endif /* CONFIG_INTERWORKING */
10324252726Srpaulo#ifdef CONFIG_HS20
10325252726Srpaulo	} else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
10326252726Srpaulo		if (get_hs20_anqp(wpa_s, buf + 14) < 0)
10327252726Srpaulo			reply_len = -1;
10328252726Srpaulo	} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
10329252726Srpaulo		if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
10330252726Srpaulo			reply_len = -1;
10331281806Srpaulo	} else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
10332337817Scy		if (hs20_icon_request(wpa_s, buf + 18, 0) < 0)
10333281806Srpaulo			reply_len = -1;
10334337817Scy	} else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) {
10335337817Scy		if (hs20_icon_request(wpa_s, buf + 14, 1) < 0)
10336337817Scy			reply_len = -1;
10337337817Scy	} else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) {
10338337817Scy		reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size);
10339337817Scy	} else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) {
10340337817Scy		if (del_hs20_icon(wpa_s, buf + 14) < 0)
10341337817Scy			reply_len = -1;
10342281806Srpaulo	} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
10343337817Scy		if (hs20_fetch_osu(wpa_s, 0) < 0)
10344281806Srpaulo			reply_len = -1;
10345337817Scy	} else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) {
10346337817Scy		if (hs20_fetch_osu(wpa_s, 1) < 0)
10347337817Scy			reply_len = -1;
10348281806Srpaulo	} else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
10349281806Srpaulo		hs20_cancel_fetch_osu(wpa_s);
10350252726Srpaulo#endif /* CONFIG_HS20 */
10351189251Ssam	} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
10352189251Ssam	{
10353189251Ssam		if (wpa_supplicant_ctrl_iface_ctrl_rsp(
10354189251Ssam			    wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
10355189251Ssam			reply_len = -1;
10356281806Srpaulo		else {
10357281806Srpaulo			/*
10358281806Srpaulo			 * Notify response from timeout to allow the control
10359281806Srpaulo			 * interface response to be sent first.
10360281806Srpaulo			 */
10361281806Srpaulo			eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
10362281806Srpaulo					       wpa_s, NULL);
10363281806Srpaulo		}
10364189251Ssam	} else if (os_strcmp(buf, "RECONFIGURE") == 0) {
10365189251Ssam		if (wpa_supplicant_reload_configuration(wpa_s))
10366189251Ssam			reply_len = -1;
10367189251Ssam	} else if (os_strcmp(buf, "TERMINATE") == 0) {
10368214734Srpaulo		wpa_supplicant_terminate_proc(wpa_s->global);
10369189251Ssam	} else if (os_strncmp(buf, "BSSID ", 6) == 0) {
10370189251Ssam		if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
10371189251Ssam			reply_len = -1;
10372252726Srpaulo	} else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
10373252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_blacklist(
10374252726Srpaulo			wpa_s, buf + 9, reply, reply_size);
10375252726Srpaulo	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
10376252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_log_level(
10377252726Srpaulo			wpa_s, buf + 9, reply, reply_size);
10378281806Srpaulo	} else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) {
10379281806Srpaulo		reply_len = wpa_supplicant_ctrl_iface_list_networks(
10380281806Srpaulo			wpa_s, buf + 14, reply, reply_size);
10381189251Ssam	} else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
10382189251Ssam		reply_len = wpa_supplicant_ctrl_iface_list_networks(
10383281806Srpaulo			wpa_s, NULL, reply, reply_size);
10384189251Ssam	} else if (os_strcmp(buf, "DISCONNECT") == 0) {
10385337817Scy		wpas_request_disconnection(wpa_s);
10386189251Ssam	} else if (os_strcmp(buf, "SCAN") == 0) {
10387281806Srpaulo		wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
10388281806Srpaulo	} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
10389281806Srpaulo		wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
10390189251Ssam	} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
10391189251Ssam		reply_len = wpa_supplicant_ctrl_iface_scan_results(
10392189251Ssam			wpa_s, reply, reply_size);
10393337817Scy	} else if (os_strcmp(buf, "ABORT_SCAN") == 0) {
10394337817Scy		if (wpas_abort_ongoing_scan(wpa_s) < 0)
10395337817Scy			reply_len = -1;
10396189251Ssam	} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
10397189251Ssam		if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
10398189251Ssam			reply_len = -1;
10399189251Ssam	} else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
10400189251Ssam		if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
10401189251Ssam			reply_len = -1;
10402189251Ssam	} else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
10403189251Ssam		if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
10404189251Ssam			reply_len = -1;
10405189251Ssam	} else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
10406189251Ssam		reply_len = wpa_supplicant_ctrl_iface_add_network(
10407189251Ssam			wpa_s, reply, reply_size);
10408189251Ssam	} else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
10409189251Ssam		if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
10410189251Ssam			reply_len = -1;
10411189251Ssam	} else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
10412189251Ssam		if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
10413189251Ssam			reply_len = -1;
10414189251Ssam	} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
10415189251Ssam		reply_len = wpa_supplicant_ctrl_iface_get_network(
10416189251Ssam			wpa_s, buf + 12, reply, reply_size);
10417281806Srpaulo	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
10418289549Srpaulo		if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
10419289549Srpaulo							  wpa_s))
10420281806Srpaulo			reply_len = -1;
10421252726Srpaulo	} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
10422252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_list_creds(
10423252726Srpaulo			wpa_s, reply, reply_size);
10424252726Srpaulo	} else if (os_strcmp(buf, "ADD_CRED") == 0) {
10425252726Srpaulo		reply_len = wpa_supplicant_ctrl_iface_add_cred(
10426252726Srpaulo			wpa_s, reply, reply_size);
10427252726Srpaulo	} else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
10428252726Srpaulo		if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
10429252726Srpaulo			reply_len = -1;
10430252726Srpaulo	} else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
10431252726Srpaulo		if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
10432252726Srpaulo			reply_len = -1;
10433281806Srpaulo	} else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
10434281806Srpaulo		reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
10435281806Srpaulo							       reply,
10436281806Srpaulo							       reply_size);
10437189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE
10438189251Ssam	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
10439189251Ssam		if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
10440189251Ssam			reply_len = -1;
10441189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */
10442189251Ssam	} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
10443189251Ssam		reply_len = wpa_supplicant_ctrl_iface_get_capability(
10444189251Ssam			wpa_s, buf + 15, reply, reply_size);
10445189251Ssam	} else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
10446189251Ssam		if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
10447189251Ssam			reply_len = -1;
10448252726Srpaulo	} else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
10449252726Srpaulo		if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
10450252726Srpaulo			reply_len = -1;
10451189251Ssam	} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
10452189251Ssam		reply_len = wpa_supplicant_global_iface_list(
10453189251Ssam			wpa_s->global, reply, reply_size);
10454337817Scy	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
10455189251Ssam		reply_len = wpa_supplicant_global_iface_interfaces(
10456337817Scy			wpa_s->global, buf + 10, reply, reply_size);
10457189251Ssam	} else if (os_strncmp(buf, "BSS ", 4) == 0) {
10458189251Ssam		reply_len = wpa_supplicant_ctrl_iface_bss(
10459189251Ssam			wpa_s, buf + 4, reply, reply_size);
10460214734Srpaulo#ifdef CONFIG_AP
10461214734Srpaulo	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
10462214734Srpaulo		reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
10463214734Srpaulo	} else if (os_strncmp(buf, "STA ", 4) == 0) {
10464214734Srpaulo		reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
10465214734Srpaulo					      reply_size);
10466214734Srpaulo	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
10467214734Srpaulo		reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
10468214734Srpaulo						   reply_size);
10469252726Srpaulo	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
10470252726Srpaulo		if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
10471252726Srpaulo			reply_len = -1;
10472252726Srpaulo	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
10473252726Srpaulo		if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
10474252726Srpaulo			reply_len = -1;
10475281806Srpaulo	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
10476281806Srpaulo		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
10477281806Srpaulo			reply_len = -1;
10478281806Srpaulo	} else if (os_strcmp(buf, "STOP_AP") == 0) {
10479281806Srpaulo		if (wpas_ap_stop_ap(wpa_s))
10480281806Srpaulo			reply_len = -1;
10481214734Srpaulo#endif /* CONFIG_AP */
10482214734Srpaulo	} else if (os_strcmp(buf, "SUSPEND") == 0) {
10483214734Srpaulo		wpas_notify_suspend(wpa_s->global);
10484214734Srpaulo	} else if (os_strcmp(buf, "RESUME") == 0) {
10485214734Srpaulo		wpas_notify_resume(wpa_s->global);
10486281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
10487214734Srpaulo	} else if (os_strcmp(buf, "DROP_SA") == 0) {
10488214734Srpaulo		wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
10489281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
10490214734Srpaulo	} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
10491214734Srpaulo		if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
10492214734Srpaulo			reply_len = -1;
10493252726Srpaulo	} else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
10494281806Srpaulo		wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
10495252726Srpaulo	} else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
10496252726Srpaulo		if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
10497252726Srpaulo			reply_len = -1;
10498252726Srpaulo	} else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
10499252726Srpaulo		if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
10500252726Srpaulo							       buf + 17))
10501252726Srpaulo			reply_len = -1;
10502252726Srpaulo	} else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
10503281806Srpaulo		wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
10504252726Srpaulo#ifdef CONFIG_TDLS
10505252726Srpaulo	} else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
10506252726Srpaulo		if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
10507252726Srpaulo			reply_len = -1;
10508252726Srpaulo	} else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
10509252726Srpaulo		if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
10510252726Srpaulo			reply_len = -1;
10511252726Srpaulo	} else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
10512252726Srpaulo		if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
10513252726Srpaulo			reply_len = -1;
10514281806Srpaulo	} else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
10515281806Srpaulo		if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
10516281806Srpaulo							       buf + 17))
10517281806Srpaulo			reply_len = -1;
10518281806Srpaulo	} else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
10519281806Srpaulo		if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
10520281806Srpaulo								      buf + 24))
10521281806Srpaulo			reply_len = -1;
10522289549Srpaulo	} else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
10523289549Srpaulo		reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
10524289549Srpaulo			wpa_s, buf + 17, reply, reply_size);
10525252726Srpaulo#endif /* CONFIG_TDLS */
10526281806Srpaulo	} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
10527281806Srpaulo		reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
10528281806Srpaulo	} else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
10529281806Srpaulo		if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
10530281806Srpaulo			reply_len = -1;
10531281806Srpaulo	} else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
10532281806Srpaulo		if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
10533281806Srpaulo			reply_len = -1;
10534252726Srpaulo	} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
10535252726Srpaulo		reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
10536252726Srpaulo						       reply_size);
10537337817Scy	} else if (os_strncmp(buf, "SIGNAL_MONITOR", 14) == 0) {
10538337817Scy		if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14))
10539337817Scy			reply_len = -1;
10540252726Srpaulo	} else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
10541252726Srpaulo		reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
10542252726Srpaulo						       reply_size);
10543252726Srpaulo#ifdef CONFIG_AUTOSCAN
10544252726Srpaulo	} else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
10545252726Srpaulo		if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
10546252726Srpaulo			reply_len = -1;
10547252726Srpaulo#endif /* CONFIG_AUTOSCAN */
10548337817Scy	} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
10549337817Scy		reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply,
10550337817Scy							 reply_size);
10551281806Srpaulo#ifdef ANDROID
10552281806Srpaulo	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
10553281806Srpaulo		reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
10554281806Srpaulo						      reply_size);
10555281806Srpaulo#endif /* ANDROID */
10556281806Srpaulo	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
10557281806Srpaulo		reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
10558281806Srpaulo						      reply_size);
10559252726Srpaulo	} else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
10560252726Srpaulo		pmksa_cache_clear_current(wpa_s->wpa);
10561252726Srpaulo		eapol_sm_request_reauth(wpa_s->eapol);
10562252726Srpaulo#ifdef CONFIG_WNM
10563252726Srpaulo	} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
10564252726Srpaulo		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
10565252726Srpaulo			reply_len = -1;
10566281806Srpaulo	} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
10567281806Srpaulo		if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
10568281806Srpaulo				reply_len = -1;
10569346981Scy	} else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) {
10570346981Scy		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
10571346981Scy			reply_len = -1;
10572252726Srpaulo#endif /* CONFIG_WNM */
10573281806Srpaulo	} else if (os_strcmp(buf, "FLUSH") == 0) {
10574281806Srpaulo		wpa_supplicant_ctrl_iface_flush(wpa_s);
10575281806Srpaulo	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
10576281806Srpaulo		reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
10577281806Srpaulo						 reply_size);
10578281806Srpaulo#ifdef CONFIG_TESTING_OPTIONS
10579281806Srpaulo	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
10580281806Srpaulo		if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
10581281806Srpaulo			reply_len = -1;
10582281806Srpaulo	} else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
10583281806Srpaulo		wpas_ctrl_iface_mgmt_tx_done(wpa_s);
10584337817Scy	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
10585337817Scy		if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0)
10586337817Scy			reply_len = -1;
10587281806Srpaulo	} else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
10588281806Srpaulo		if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
10589281806Srpaulo			reply_len = -1;
10590281806Srpaulo	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
10591281806Srpaulo		if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
10592281806Srpaulo			reply_len = -1;
10593281806Srpaulo	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
10594281806Srpaulo		if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
10595281806Srpaulo			reply_len = -1;
10596281806Srpaulo	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
10597281806Srpaulo		if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
10598281806Srpaulo			reply_len = -1;
10599281806Srpaulo	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
10600281806Srpaulo		if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
10601281806Srpaulo			reply_len = -1;
10602281806Srpaulo	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
10603281806Srpaulo		if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
10604281806Srpaulo			reply_len = -1;
10605281806Srpaulo	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
10606281806Srpaulo		reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
10607289549Srpaulo	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
10608289549Srpaulo		if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
10609289549Srpaulo			reply_len = -1;
10610289549Srpaulo	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
10611289549Srpaulo		reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
10612337817Scy	} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
10613337817Scy		if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
10614337817Scy			reply_len = -1;
10615337817Scy	} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
10616337817Scy		if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
10617337817Scy			reply_len = -1;
10618346981Scy	} else if (os_strcmp(buf, "RESET_PN") == 0) {
10619346981Scy		if (wpas_ctrl_reset_pn(wpa_s) < 0)
10620346981Scy			reply_len = -1;
10621346981Scy	} else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) {
10622346981Scy		if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0)
10623346981Scy			reply_len = -1;
10624346981Scy	} else if (os_strcmp(buf, "RESEND_ASSOC") == 0) {
10625346981Scy		if (wpas_ctrl_resend_assoc(wpa_s) < 0)
10626346981Scy			reply_len = -1;
10627346981Scy#ifdef CONFIG_IEEE80211W
10628346981Scy	} else if (os_strcmp(buf, "UNPROT_DEAUTH") == 0) {
10629346981Scy		sme_event_unprot_disconnect(
10630346981Scy			wpa_s, wpa_s->bssid, NULL,
10631346981Scy			WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
10632346981Scy#endif /* CONFIG_IEEE80211W */
10633281806Srpaulo#endif /* CONFIG_TESTING_OPTIONS */
10634281806Srpaulo	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
10635281806Srpaulo		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
10636281806Srpaulo			reply_len = -1;
10637281806Srpaulo	} else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
10638281806Srpaulo		reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
10639281806Srpaulo						      reply_size);
10640281806Srpaulo	} else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
10641281806Srpaulo		if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
10642281806Srpaulo			reply_len = -1;
10643281806Srpaulo	} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
10644337817Scy		if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20))
10645281806Srpaulo			reply_len = -1;
10646281806Srpaulo	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
10647281806Srpaulo		wpas_ctrl_iface_erp_flush(wpa_s);
10648281806Srpaulo	} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
10649281806Srpaulo		if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
10650281806Srpaulo			reply_len = -1;
10651289549Srpaulo	} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
10652289549Srpaulo		reply_len = wpas_ctrl_iface_get_pref_freq_list(
10653289549Srpaulo			wpa_s, buf + 19, reply, reply_size);
10654346981Scy#ifdef CONFIG_FILS
10655346981Scy	} else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) {
10656346981Scy		if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17))
10657346981Scy			reply_len = -1;
10658346981Scy	} else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
10659346981Scy		wpas_flush_fils_hlp_req(wpa_s);
10660346981Scy#endif /* CONFIG_FILS */
10661346981Scy#ifdef CONFIG_DPP
10662346981Scy	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
10663346981Scy		int res;
10664346981Scy
10665346981Scy		res = wpas_dpp_qr_code(wpa_s, buf + 12);
10666346981Scy		if (res < 0) {
10667346981Scy			reply_len = -1;
10668346981Scy		} else {
10669346981Scy			reply_len = os_snprintf(reply, reply_size, "%d", res);
10670346981Scy			if (os_snprintf_error(reply_size, reply_len))
10671346981Scy				reply_len = -1;
10672346981Scy		}
10673346981Scy	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
10674346981Scy		int res;
10675346981Scy
10676346981Scy		res = dpp_bootstrap_gen(wpa_s->dpp, buf + 18);
10677346981Scy		if (res < 0) {
10678346981Scy			reply_len = -1;
10679346981Scy		} else {
10680346981Scy			reply_len = os_snprintf(reply, reply_size, "%d", res);
10681346981Scy			if (os_snprintf_error(reply_size, reply_len))
10682346981Scy				reply_len = -1;
10683346981Scy		}
10684346981Scy	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
10685346981Scy		if (dpp_bootstrap_remove(wpa_s->dpp, buf + 21) < 0)
10686346981Scy			reply_len = -1;
10687346981Scy	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) {
10688346981Scy		const char *uri;
10689346981Scy
10690346981Scy		uri = dpp_bootstrap_get_uri(wpa_s->dpp, atoi(buf + 22));
10691346981Scy		if (!uri) {
10692346981Scy			reply_len = -1;
10693346981Scy		} else {
10694346981Scy			reply_len = os_snprintf(reply, reply_size, "%s", uri);
10695346981Scy			if (os_snprintf_error(reply_size, reply_len))
10696346981Scy				reply_len = -1;
10697346981Scy		}
10698346981Scy	} else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) {
10699346981Scy		reply_len = dpp_bootstrap_info(wpa_s->dpp, atoi(buf + 19),
10700346981Scy					       reply, reply_size);
10701346981Scy	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
10702346981Scy		if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
10703346981Scy			reply_len = -1;
10704346981Scy	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
10705346981Scy		if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
10706346981Scy			reply_len = -1;
10707346981Scy	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
10708346981Scy		wpas_dpp_stop(wpa_s);
10709346981Scy		wpas_dpp_listen_stop(wpa_s);
10710346981Scy	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
10711346981Scy		int res;
10712346981Scy
10713346981Scy		res = dpp_configurator_add(wpa_s->dpp, buf + 20);
10714346981Scy		if (res < 0) {
10715346981Scy			reply_len = -1;
10716346981Scy		} else {
10717346981Scy			reply_len = os_snprintf(reply, reply_size, "%d", res);
10718346981Scy			if (os_snprintf_error(reply_size, reply_len))
10719346981Scy				reply_len = -1;
10720346981Scy		}
10721346981Scy	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
10722346981Scy		if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0)
10723346981Scy			reply_len = -1;
10724346981Scy	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
10725346981Scy		if (wpas_dpp_configurator_sign(wpa_s, buf + 21) < 0)
10726346981Scy			reply_len = -1;
10727346981Scy	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
10728346981Scy		reply_len = dpp_configurator_get_key_id(wpa_s->dpp,
10729346981Scy							atoi(buf + 25),
10730346981Scy							reply, reply_size);
10731346981Scy	} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
10732346981Scy		int res;
10733346981Scy
10734346981Scy		res = wpas_dpp_pkex_add(wpa_s, buf + 12);
10735346981Scy		if (res < 0) {
10736346981Scy			reply_len = -1;
10737346981Scy		} else {
10738346981Scy			reply_len = os_snprintf(reply, reply_size, "%d", res);
10739346981Scy			if (os_snprintf_error(reply_size, reply_len))
10740346981Scy				reply_len = -1;
10741346981Scy		}
10742346981Scy	} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
10743346981Scy		if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
10744346981Scy			reply_len = -1;
10745351611Scy#ifdef CONFIG_DPP2
10746351611Scy	} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
10747351611Scy		if (wpas_dpp_controller_start(wpa_s, buf + 20) < 0)
10748351611Scy			reply_len = -1;
10749351611Scy	} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
10750351611Scy		if (wpas_dpp_controller_start(wpa_s, NULL) < 0)
10751351611Scy			reply_len = -1;
10752351611Scy	} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
10753351611Scy		dpp_controller_stop(wpa_s->dpp);
10754351611Scy#endif /* CONFIG_DPP2 */
10755346981Scy#endif /* CONFIG_DPP */
10756189251Ssam	} else {
10757189251Ssam		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
10758189251Ssam		reply_len = 16;
10759189251Ssam	}
10760189251Ssam
10761189251Ssam	if (reply_len < 0) {
10762189251Ssam		os_memcpy(reply, "FAIL\n", 5);
10763189251Ssam		reply_len = 5;
10764189251Ssam	}
10765189251Ssam
10766189251Ssam	*resp_len = reply_len;
10767189251Ssam	return reply;
10768189251Ssam}
10769189251Ssam
10770189251Ssam
10771189251Ssamstatic int wpa_supplicant_global_iface_add(struct wpa_global *global,
10772189251Ssam					   char *cmd)
10773189251Ssam{
10774189251Ssam	struct wpa_interface iface;
10775289549Srpaulo	char *pos, *extra;
10776289549Srpaulo	struct wpa_supplicant *wpa_s;
10777289549Srpaulo	unsigned int create_iface = 0;
10778289549Srpaulo	u8 mac_addr[ETH_ALEN];
10779337817Scy	enum wpa_driver_if_type type = WPA_IF_STATION;
10780189251Ssam
10781189251Ssam	/*
10782189251Ssam	 * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
10783337817Scy	 * TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]]
10784189251Ssam	 */
10785189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
10786189251Ssam
10787189251Ssam	os_memset(&iface, 0, sizeof(iface));
10788189251Ssam
10789189251Ssam	do {
10790189251Ssam		iface.ifname = pos = cmd;
10791189251Ssam		pos = os_strchr(pos, '\t');
10792189251Ssam		if (pos)
10793189251Ssam			*pos++ = '\0';
10794189251Ssam		if (iface.ifname[0] == '\0')
10795189251Ssam			return -1;
10796189251Ssam		if (pos == NULL)
10797189251Ssam			break;
10798189251Ssam
10799189251Ssam		iface.confname = pos;
10800189251Ssam		pos = os_strchr(pos, '\t');
10801189251Ssam		if (pos)
10802189251Ssam			*pos++ = '\0';
10803189251Ssam		if (iface.confname[0] == '\0')
10804189251Ssam			iface.confname = NULL;
10805189251Ssam		if (pos == NULL)
10806189251Ssam			break;
10807189251Ssam
10808189251Ssam		iface.driver = pos;
10809189251Ssam		pos = os_strchr(pos, '\t');
10810189251Ssam		if (pos)
10811189251Ssam			*pos++ = '\0';
10812189251Ssam		if (iface.driver[0] == '\0')
10813189251Ssam			iface.driver = NULL;
10814189251Ssam		if (pos == NULL)
10815189251Ssam			break;
10816189251Ssam
10817189251Ssam		iface.ctrl_interface = pos;
10818189251Ssam		pos = os_strchr(pos, '\t');
10819189251Ssam		if (pos)
10820189251Ssam			*pos++ = '\0';
10821189251Ssam		if (iface.ctrl_interface[0] == '\0')
10822189251Ssam			iface.ctrl_interface = NULL;
10823189251Ssam		if (pos == NULL)
10824189251Ssam			break;
10825189251Ssam
10826189251Ssam		iface.driver_param = pos;
10827189251Ssam		pos = os_strchr(pos, '\t');
10828189251Ssam		if (pos)
10829189251Ssam			*pos++ = '\0';
10830189251Ssam		if (iface.driver_param[0] == '\0')
10831189251Ssam			iface.driver_param = NULL;
10832189251Ssam		if (pos == NULL)
10833189251Ssam			break;
10834189251Ssam
10835189251Ssam		iface.bridge_ifname = pos;
10836189251Ssam		pos = os_strchr(pos, '\t');
10837189251Ssam		if (pos)
10838189251Ssam			*pos++ = '\0';
10839189251Ssam		if (iface.bridge_ifname[0] == '\0')
10840189251Ssam			iface.bridge_ifname = NULL;
10841189251Ssam		if (pos == NULL)
10842189251Ssam			break;
10843289549Srpaulo
10844289549Srpaulo		extra = pos;
10845289549Srpaulo		pos = os_strchr(pos, '\t');
10846289549Srpaulo		if (pos)
10847289549Srpaulo			*pos++ = '\0';
10848289549Srpaulo		if (!extra[0])
10849289549Srpaulo			break;
10850289549Srpaulo
10851337817Scy		if (os_strcmp(extra, "create") == 0) {
10852289549Srpaulo			create_iface = 1;
10853337817Scy			if (!pos)
10854337817Scy				break;
10855337817Scy
10856337817Scy			if (os_strcmp(pos, "sta") == 0) {
10857337817Scy				type = WPA_IF_STATION;
10858337817Scy			} else if (os_strcmp(pos, "ap") == 0) {
10859337817Scy				type = WPA_IF_AP_BSS;
10860337817Scy			} else {
10861337817Scy				wpa_printf(MSG_DEBUG,
10862337817Scy					   "INTERFACE_ADD unsupported interface type: '%s'",
10863337817Scy					   pos);
10864337817Scy				return -1;
10865337817Scy			}
10866337817Scy		} else {
10867289549Srpaulo			wpa_printf(MSG_DEBUG,
10868289549Srpaulo				   "INTERFACE_ADD unsupported extra parameter: '%s'",
10869289549Srpaulo				   extra);
10870289549Srpaulo			return -1;
10871289549Srpaulo		}
10872189251Ssam	} while (0);
10873189251Ssam
10874289549Srpaulo	if (create_iface) {
10875289549Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
10876289549Srpaulo			   iface.ifname);
10877289549Srpaulo		if (!global->ifaces)
10878289549Srpaulo			return -1;
10879337817Scy		if (wpa_drv_if_add(global->ifaces, type, iface.ifname,
10880289549Srpaulo				   NULL, NULL, NULL, mac_addr, NULL) < 0) {
10881289549Srpaulo			wpa_printf(MSG_ERROR,
10882289549Srpaulo				   "CTRL_IFACE interface creation failed");
10883289549Srpaulo			return -1;
10884289549Srpaulo		}
10885289549Srpaulo
10886289549Srpaulo		wpa_printf(MSG_DEBUG,
10887289549Srpaulo			   "CTRL_IFACE interface '%s' created with MAC addr: "
10888289549Srpaulo			   MACSTR, iface.ifname, MAC2STR(mac_addr));
10889289549Srpaulo	}
10890289549Srpaulo
10891189251Ssam	if (wpa_supplicant_get_iface(global, iface.ifname))
10892289549Srpaulo		goto fail;
10893189251Ssam
10894289549Srpaulo	wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
10895289549Srpaulo	if (!wpa_s)
10896289549Srpaulo		goto fail;
10897289549Srpaulo	wpa_s->added_vif = create_iface;
10898289549Srpaulo	return 0;
10899289549Srpaulo
10900289549Srpaulofail:
10901289549Srpaulo	if (create_iface)
10902289549Srpaulo		wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
10903289549Srpaulo	return -1;
10904189251Ssam}
10905189251Ssam
10906189251Ssam
10907189251Ssamstatic int wpa_supplicant_global_iface_remove(struct wpa_global *global,
10908189251Ssam					      char *cmd)
10909189251Ssam{
10910189251Ssam	struct wpa_supplicant *wpa_s;
10911289549Srpaulo	int ret;
10912289549Srpaulo	unsigned int delete_iface;
10913189251Ssam
10914189251Ssam	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
10915189251Ssam
10916189251Ssam	wpa_s = wpa_supplicant_get_iface(global, cmd);
10917189251Ssam	if (wpa_s == NULL)
10918189251Ssam		return -1;
10919289549Srpaulo	delete_iface = wpa_s->added_vif;
10920289549Srpaulo	ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
10921289549Srpaulo	if (!ret && delete_iface) {
10922289549Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
10923289549Srpaulo			   cmd);
10924289549Srpaulo		ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
10925289549Srpaulo	}
10926289549Srpaulo	return ret;
10927189251Ssam}
10928189251Ssam
10929189251Ssam
10930189251Ssamstatic void wpa_free_iface_info(struct wpa_interface_info *iface)
10931189251Ssam{
10932189251Ssam	struct wpa_interface_info *prev;
10933189251Ssam
10934189251Ssam	while (iface) {
10935189251Ssam		prev = iface;
10936189251Ssam		iface = iface->next;
10937189251Ssam
10938189251Ssam		os_free(prev->ifname);
10939189251Ssam		os_free(prev->desc);
10940189251Ssam		os_free(prev);
10941189251Ssam	}
10942189251Ssam}
10943189251Ssam
10944189251Ssam
10945189251Ssamstatic int wpa_supplicant_global_iface_list(struct wpa_global *global,
10946189251Ssam					    char *buf, int len)
10947189251Ssam{
10948189251Ssam	int i, res;
10949189251Ssam	struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
10950189251Ssam	char *pos, *end;
10951189251Ssam
10952214734Srpaulo	for (i = 0; wpa_drivers[i]; i++) {
10953289549Srpaulo		const struct wpa_driver_ops *drv = wpa_drivers[i];
10954189251Ssam		if (drv->get_interfaces == NULL)
10955189251Ssam			continue;
10956214734Srpaulo		tmp = drv->get_interfaces(global->drv_priv[i]);
10957189251Ssam		if (tmp == NULL)
10958189251Ssam			continue;
10959189251Ssam
10960189251Ssam		if (last == NULL)
10961189251Ssam			iface = last = tmp;
10962189251Ssam		else
10963189251Ssam			last->next = tmp;
10964189251Ssam		while (last->next)
10965189251Ssam			last = last->next;
10966189251Ssam	}
10967189251Ssam
10968189251Ssam	pos = buf;
10969189251Ssam	end = buf + len;
10970189251Ssam	for (tmp = iface; tmp; tmp = tmp->next) {
10971189251Ssam		res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
10972189251Ssam				  tmp->drv_name, tmp->ifname,
10973189251Ssam				  tmp->desc ? tmp->desc : "");
10974281806Srpaulo		if (os_snprintf_error(end - pos, res)) {
10975189251Ssam			*pos = '\0';
10976189251Ssam			break;
10977189251Ssam		}
10978189251Ssam		pos += res;
10979189251Ssam	}
10980189251Ssam
10981189251Ssam	wpa_free_iface_info(iface);
10982189251Ssam
10983189251Ssam	return pos - buf;
10984189251Ssam}
10985189251Ssam
10986189251Ssam
10987189251Ssamstatic int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
10988337817Scy						  const char *input,
10989189251Ssam						  char *buf, int len)
10990189251Ssam{
10991189251Ssam	int res;
10992189251Ssam	char *pos, *end;
10993189251Ssam	struct wpa_supplicant *wpa_s;
10994337817Scy	int show_ctrl = 0;
10995189251Ssam
10996337817Scy	if (input)
10997337817Scy		show_ctrl = !!os_strstr(input, "ctrl");
10998337817Scy
10999189251Ssam	wpa_s = global->ifaces;
11000189251Ssam	pos = buf;
11001189251Ssam	end = buf + len;
11002189251Ssam
11003189251Ssam	while (wpa_s) {
11004337817Scy		if (show_ctrl)
11005337817Scy			res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n",
11006337817Scy					  wpa_s->ifname,
11007337817Scy					  wpa_s->conf->ctrl_interface ?
11008337817Scy					  wpa_s->conf->ctrl_interface : "N/A");
11009337817Scy		else
11010337817Scy			res = os_snprintf(pos, end - pos, "%s\n",
11011337817Scy					  wpa_s->ifname);
11012337817Scy
11013281806Srpaulo		if (os_snprintf_error(end - pos, res)) {
11014189251Ssam			*pos = '\0';
11015189251Ssam			break;
11016189251Ssam		}
11017189251Ssam		pos += res;
11018189251Ssam		wpa_s = wpa_s->next;
11019189251Ssam	}
11020189251Ssam	return pos - buf;
11021189251Ssam}
11022189251Ssam
11023189251Ssam
11024281806Srpaulostatic char * wpas_global_ctrl_iface_ifname(struct wpa_global *global,
11025281806Srpaulo					    const char *ifname,
11026281806Srpaulo					    char *cmd, size_t *resp_len)
11027281806Srpaulo{
11028281806Srpaulo	struct wpa_supplicant *wpa_s;
11029281806Srpaulo
11030281806Srpaulo	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
11031281806Srpaulo		if (os_strcmp(ifname, wpa_s->ifname) == 0)
11032281806Srpaulo			break;
11033281806Srpaulo	}
11034281806Srpaulo
11035281806Srpaulo	if (wpa_s == NULL) {
11036281806Srpaulo		char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n");
11037281806Srpaulo		if (resp)
11038281806Srpaulo			*resp_len = os_strlen(resp);
11039281806Srpaulo		else
11040281806Srpaulo			*resp_len = 1;
11041281806Srpaulo		return resp;
11042281806Srpaulo	}
11043281806Srpaulo
11044281806Srpaulo	return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len);
11045281806Srpaulo}
11046281806Srpaulo
11047281806Srpaulo
11048281806Srpaulostatic char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
11049281806Srpaulo					       char *buf, size_t *resp_len)
11050281806Srpaulo{
11051281806Srpaulo#ifdef CONFIG_P2P
11052281806Srpaulo	static const char * cmd[] = {
11053281806Srpaulo		"LIST_NETWORKS",
11054281806Srpaulo		"P2P_FIND",
11055281806Srpaulo		"P2P_STOP_FIND",
11056281806Srpaulo		"P2P_LISTEN",
11057281806Srpaulo		"P2P_GROUP_ADD",
11058281806Srpaulo		"P2P_GET_PASSPHRASE",
11059281806Srpaulo		"P2P_SERVICE_UPDATE",
11060281806Srpaulo		"P2P_SERVICE_FLUSH",
11061281806Srpaulo		"P2P_FLUSH",
11062281806Srpaulo		"P2P_CANCEL",
11063281806Srpaulo		"P2P_PRESENCE_REQ",
11064281806Srpaulo		"P2P_EXT_LISTEN",
11065346981Scy#ifdef CONFIG_AP
11066346981Scy		"STA-FIRST",
11067346981Scy#endif /* CONFIG_AP */
11068281806Srpaulo		NULL
11069281806Srpaulo	};
11070281806Srpaulo	static const char * prefix[] = {
11071281806Srpaulo#ifdef ANDROID
11072281806Srpaulo		"DRIVER ",
11073281806Srpaulo#endif /* ANDROID */
11074346981Scy		"GET_CAPABILITY ",
11075281806Srpaulo		"GET_NETWORK ",
11076281806Srpaulo		"REMOVE_NETWORK ",
11077281806Srpaulo		"P2P_FIND ",
11078281806Srpaulo		"P2P_CONNECT ",
11079281806Srpaulo		"P2P_LISTEN ",
11080281806Srpaulo		"P2P_GROUP_REMOVE ",
11081281806Srpaulo		"P2P_GROUP_ADD ",
11082337817Scy		"P2P_GROUP_MEMBER ",
11083281806Srpaulo		"P2P_PROV_DISC ",
11084281806Srpaulo		"P2P_SERV_DISC_REQ ",
11085281806Srpaulo		"P2P_SERV_DISC_CANCEL_REQ ",
11086281806Srpaulo		"P2P_SERV_DISC_RESP ",
11087281806Srpaulo		"P2P_SERV_DISC_EXTERNAL ",
11088281806Srpaulo		"P2P_SERVICE_ADD ",
11089281806Srpaulo		"P2P_SERVICE_DEL ",
11090281806Srpaulo		"P2P_SERVICE_REP ",
11091281806Srpaulo		"P2P_REJECT ",
11092281806Srpaulo		"P2P_INVITE ",
11093281806Srpaulo		"P2P_PEER ",
11094281806Srpaulo		"P2P_SET ",
11095281806Srpaulo		"P2P_UNAUTHORIZE ",
11096281806Srpaulo		"P2P_PRESENCE_REQ ",
11097281806Srpaulo		"P2P_EXT_LISTEN ",
11098281806Srpaulo		"P2P_REMOVE_CLIENT ",
11099281806Srpaulo		"WPS_NFC_TOKEN ",
11100281806Srpaulo		"WPS_NFC_TAG_READ ",
11101281806Srpaulo		"NFC_GET_HANDOVER_SEL ",
11102281806Srpaulo		"NFC_GET_HANDOVER_REQ ",
11103281806Srpaulo		"NFC_REPORT_HANDOVER ",
11104281806Srpaulo		"P2P_ASP_PROVISION ",
11105281806Srpaulo		"P2P_ASP_PROVISION_RESP ",
11106346981Scy#ifdef CONFIG_AP
11107346981Scy		"STA ",
11108346981Scy		"STA-NEXT ",
11109346981Scy#endif /* CONFIG_AP */
11110281806Srpaulo		NULL
11111281806Srpaulo	};
11112281806Srpaulo	int found = 0;
11113281806Srpaulo	int i;
11114281806Srpaulo
11115281806Srpaulo	if (global->p2p_init_wpa_s == NULL)
11116281806Srpaulo		return NULL;
11117281806Srpaulo
11118281806Srpaulo	for (i = 0; !found && cmd[i]; i++) {
11119281806Srpaulo		if (os_strcmp(buf, cmd[i]) == 0)
11120281806Srpaulo			found = 1;
11121281806Srpaulo	}
11122281806Srpaulo
11123281806Srpaulo	for (i = 0; !found && prefix[i]; i++) {
11124281806Srpaulo		if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0)
11125281806Srpaulo			found = 1;
11126281806Srpaulo	}
11127281806Srpaulo
11128281806Srpaulo	if (found)
11129281806Srpaulo		return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
11130281806Srpaulo							 buf, resp_len);
11131281806Srpaulo#endif /* CONFIG_P2P */
11132281806Srpaulo	return NULL;
11133281806Srpaulo}
11134281806Srpaulo
11135281806Srpaulo
11136281806Srpaulostatic char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global,
11137281806Srpaulo					       char *buf, size_t *resp_len)
11138281806Srpaulo{
11139281806Srpaulo#ifdef CONFIG_WIFI_DISPLAY
11140281806Srpaulo	if (global->p2p_init_wpa_s == NULL)
11141281806Srpaulo		return NULL;
11142281806Srpaulo	if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 ||
11143281806Srpaulo	    os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0)
11144281806Srpaulo		return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
11145281806Srpaulo							 buf, resp_len);
11146281806Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
11147281806Srpaulo	return NULL;
11148281806Srpaulo}
11149281806Srpaulo
11150281806Srpaulo
11151281806Srpaulostatic char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
11152281806Srpaulo					   char *buf, size_t *resp_len)
11153281806Srpaulo{
11154281806Srpaulo	char *ret;
11155281806Srpaulo
11156281806Srpaulo	ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len);
11157281806Srpaulo	if (ret)
11158281806Srpaulo		return ret;
11159281806Srpaulo
11160281806Srpaulo	ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len);
11161281806Srpaulo	if (ret)
11162281806Srpaulo		return ret;
11163281806Srpaulo
11164281806Srpaulo	return NULL;
11165281806Srpaulo}
11166281806Srpaulo
11167281806Srpaulo
11168281806Srpaulostatic int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
11169281806Srpaulo{
11170281806Srpaulo	char *value;
11171281806Srpaulo
11172281806Srpaulo	value = os_strchr(cmd, ' ');
11173281806Srpaulo	if (value == NULL)
11174281806Srpaulo		return -1;
11175281806Srpaulo	*value++ = '\0';
11176281806Srpaulo
11177281806Srpaulo	wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value);
11178281806Srpaulo
11179281806Srpaulo#ifdef CONFIG_WIFI_DISPLAY
11180281806Srpaulo	if (os_strcasecmp(cmd, "wifi_display") == 0) {
11181281806Srpaulo		wifi_display_enable(global, !!atoi(value));
11182281806Srpaulo		return 0;
11183281806Srpaulo	}
11184281806Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
11185281806Srpaulo
11186281806Srpaulo	/* Restore cmd to its original value to allow redirection */
11187281806Srpaulo	value[-1] = ' ';
11188281806Srpaulo
11189281806Srpaulo	return -1;
11190281806Srpaulo}
11191281806Srpaulo
11192281806Srpaulo
11193289549Srpaulostatic int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
11194289549Srpaulo					      char *cmd)
11195289549Srpaulo{
11196289549Srpaulo	struct wpa_supplicant *wpa_s[2]; /* src, dst */
11197289549Srpaulo	char *p;
11198289549Srpaulo	unsigned int i;
11199289549Srpaulo
11200289549Srpaulo	/* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
11201289549Srpaulo	 * <variable name> */
11202289549Srpaulo
11203289549Srpaulo	for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
11204289549Srpaulo		p = os_strchr(cmd, ' ');
11205289549Srpaulo		if (p == NULL)
11206289549Srpaulo			return -1;
11207289549Srpaulo		*p = '\0';
11208289549Srpaulo
11209289549Srpaulo		wpa_s[i] = global->ifaces;
11210289549Srpaulo		for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
11211289549Srpaulo			if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
11212289549Srpaulo				break;
11213289549Srpaulo		}
11214289549Srpaulo
11215289549Srpaulo		if (!wpa_s[i]) {
11216289549Srpaulo			wpa_printf(MSG_DEBUG,
11217289549Srpaulo				   "CTRL_IFACE: Could not find iface=%s", cmd);
11218289549Srpaulo			return -1;
11219289549Srpaulo		}
11220289549Srpaulo
11221289549Srpaulo		cmd = p + 1;
11222289549Srpaulo	}
11223289549Srpaulo
11224289549Srpaulo	return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
11225289549Srpaulo}
11226289549Srpaulo
11227289549Srpaulo
11228281806Srpaulo#ifndef CONFIG_NO_CONFIG_WRITE
11229281806Srpaulostatic int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
11230281806Srpaulo{
11231281806Srpaulo	int ret = 0, saved = 0;
11232281806Srpaulo	struct wpa_supplicant *wpa_s;
11233281806Srpaulo
11234281806Srpaulo	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
11235281806Srpaulo		if (!wpa_s->conf->update_config) {
11236281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)");
11237281806Srpaulo			continue;
11238281806Srpaulo		}
11239281806Srpaulo
11240281806Srpaulo		if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
11241281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration");
11242281806Srpaulo			ret = 1;
11243281806Srpaulo		} else {
11244281806Srpaulo			wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
11245281806Srpaulo			saved++;
11246281806Srpaulo		}
11247281806Srpaulo	}
11248281806Srpaulo
11249281806Srpaulo	if (!saved && !ret) {
11250281806Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG,
11251281806Srpaulo			"CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
11252281806Srpaulo		ret = 1;
11253281806Srpaulo	}
11254281806Srpaulo
11255281806Srpaulo	return ret;
11256281806Srpaulo}
11257281806Srpaulo#endif /* CONFIG_NO_CONFIG_WRITE */
11258281806Srpaulo
11259281806Srpaulo
11260281806Srpaulostatic int wpas_global_ctrl_iface_status(struct wpa_global *global,
11261281806Srpaulo					 char *buf, size_t buflen)
11262281806Srpaulo{
11263281806Srpaulo	char *pos, *end;
11264281806Srpaulo	int ret;
11265281806Srpaulo	struct wpa_supplicant *wpa_s;
11266281806Srpaulo
11267281806Srpaulo	pos = buf;
11268281806Srpaulo	end = buf + buflen;
11269281806Srpaulo
11270281806Srpaulo#ifdef CONFIG_P2P
11271281806Srpaulo	if (global->p2p && !global->p2p_disabled) {
11272281806Srpaulo		ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
11273281806Srpaulo				  "\n"
11274281806Srpaulo				  "p2p_state=%s\n",
11275281806Srpaulo				  MAC2STR(global->p2p_dev_addr),
11276281806Srpaulo				  p2p_get_state_txt(global->p2p));
11277281806Srpaulo		if (os_snprintf_error(end - pos, ret))
11278281806Srpaulo			return pos - buf;
11279281806Srpaulo		pos += ret;
11280281806Srpaulo	} else if (global->p2p) {
11281281806Srpaulo		ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
11282281806Srpaulo		if (os_snprintf_error(end - pos, ret))
11283281806Srpaulo			return pos - buf;
11284281806Srpaulo		pos += ret;
11285281806Srpaulo	}
11286281806Srpaulo#endif /* CONFIG_P2P */
11287281806Srpaulo
11288281806Srpaulo#ifdef CONFIG_WIFI_DISPLAY
11289281806Srpaulo	ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
11290281806Srpaulo			  !!global->wifi_display);
11291281806Srpaulo	if (os_snprintf_error(end - pos, ret))
11292281806Srpaulo		return pos - buf;
11293281806Srpaulo	pos += ret;
11294281806Srpaulo#endif /* CONFIG_WIFI_DISPLAY */
11295281806Srpaulo
11296281806Srpaulo	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
11297281806Srpaulo		ret = os_snprintf(pos, end - pos, "ifname=%s\n"
11298281806Srpaulo				  "address=" MACSTR "\n",
11299281806Srpaulo				  wpa_s->ifname, MAC2STR(wpa_s->own_addr));
11300281806Srpaulo		if (os_snprintf_error(end - pos, ret))
11301281806Srpaulo			return pos - buf;
11302281806Srpaulo		pos += ret;
11303281806Srpaulo	}
11304281806Srpaulo
11305281806Srpaulo	return pos - buf;
11306281806Srpaulo}
11307281806Srpaulo
11308281806Srpaulo
11309289549Srpaulo#ifdef CONFIG_FST
11310289549Srpaulo
11311289549Srpaulostatic int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
11312289549Srpaulo					     char *cmd, char *buf,
11313289549Srpaulo					     size_t reply_size)
11314289549Srpaulo{
11315289549Srpaulo	char ifname[IFNAMSIZ + 1];
11316289549Srpaulo	struct fst_iface_cfg cfg;
11317289549Srpaulo	struct wpa_supplicant *wpa_s;
11318289549Srpaulo	struct fst_wpa_obj iface_obj;
11319289549Srpaulo
11320289549Srpaulo	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
11321289549Srpaulo		wpa_s = wpa_supplicant_get_iface(global, ifname);
11322289549Srpaulo		if (wpa_s) {
11323289549Srpaulo			if (wpa_s->fst) {
11324289549Srpaulo				wpa_printf(MSG_INFO, "FST: Already attached");
11325289549Srpaulo				return -1;
11326289549Srpaulo			}
11327289549Srpaulo			fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
11328289549Srpaulo			wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
11329289549Srpaulo						&iface_obj, &cfg);
11330289549Srpaulo			if (wpa_s->fst)
11331289549Srpaulo				return os_snprintf(buf, reply_size, "OK\n");
11332289549Srpaulo		}
11333289549Srpaulo	}
11334289549Srpaulo
11335289549Srpaulo	return -1;
11336289549Srpaulo}
11337289549Srpaulo
11338289549Srpaulo
11339289549Srpaulostatic int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
11340289549Srpaulo					     char *cmd, char *buf,
11341289549Srpaulo					     size_t reply_size)
11342289549Srpaulo{
11343289549Srpaulo	char ifname[IFNAMSIZ + 1];
11344289549Srpaulo	struct wpa_supplicant *wpa_s;
11345289549Srpaulo
11346289549Srpaulo	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
11347289549Srpaulo		wpa_s = wpa_supplicant_get_iface(global, ifname);
11348289549Srpaulo		if (wpa_s) {
11349289549Srpaulo			if (!fst_iface_detach(ifname)) {
11350289549Srpaulo				wpa_s->fst = NULL;
11351289549Srpaulo				return os_snprintf(buf, reply_size, "OK\n");
11352289549Srpaulo			}
11353289549Srpaulo		}
11354289549Srpaulo	}
11355289549Srpaulo
11356289549Srpaulo	return -1;
11357289549Srpaulo}
11358289549Srpaulo
11359289549Srpaulo#endif /* CONFIG_FST */
11360289549Srpaulo
11361289549Srpaulo
11362189251Ssamchar * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
11363189251Ssam						char *buf, size_t *resp_len)
11364189251Ssam{
11365189251Ssam	char *reply;
11366189251Ssam	const int reply_size = 2048;
11367189251Ssam	int reply_len;
11368252726Srpaulo	int level = MSG_DEBUG;
11369189251Ssam
11370281806Srpaulo	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
11371281806Srpaulo		char *pos = os_strchr(buf + 7, ' ');
11372281806Srpaulo		if (pos) {
11373281806Srpaulo			*pos++ = '\0';
11374281806Srpaulo			return wpas_global_ctrl_iface_ifname(global,
11375281806Srpaulo							     buf + 7, pos,
11376281806Srpaulo							     resp_len);
11377281806Srpaulo		}
11378281806Srpaulo	}
11379281806Srpaulo
11380281806Srpaulo	reply = wpas_global_ctrl_iface_redir(global, buf, resp_len);
11381281806Srpaulo	if (reply)
11382281806Srpaulo		return reply;
11383281806Srpaulo
11384252726Srpaulo	if (os_strcmp(buf, "PING") == 0)
11385252726Srpaulo		level = MSG_EXCESSIVE;
11386252726Srpaulo	wpa_hexdump_ascii(level, "RX global ctrl_iface",
11387189251Ssam			  (const u8 *) buf, os_strlen(buf));
11388189251Ssam
11389189251Ssam	reply = os_malloc(reply_size);
11390189251Ssam	if (reply == NULL) {
11391189251Ssam		*resp_len = 1;
11392189251Ssam		return NULL;
11393189251Ssam	}
11394189251Ssam
11395189251Ssam	os_memcpy(reply, "OK\n", 3);
11396189251Ssam	reply_len = 3;
11397189251Ssam
11398189251Ssam	if (os_strcmp(buf, "PING") == 0) {
11399189251Ssam		os_memcpy(reply, "PONG\n", 5);
11400189251Ssam		reply_len = 5;
11401189251Ssam	} else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
11402189251Ssam		if (wpa_supplicant_global_iface_add(global, buf + 14))
11403189251Ssam			reply_len = -1;
11404189251Ssam	} else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
11405189251Ssam		if (wpa_supplicant_global_iface_remove(global, buf + 17))
11406189251Ssam			reply_len = -1;
11407189251Ssam	} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
11408189251Ssam		reply_len = wpa_supplicant_global_iface_list(
11409189251Ssam			global, reply, reply_size);
11410337817Scy	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
11411189251Ssam		reply_len = wpa_supplicant_global_iface_interfaces(
11412337817Scy			global, buf + 10, reply, reply_size);
11413289549Srpaulo#ifdef CONFIG_FST
11414289549Srpaulo	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
11415289549Srpaulo		reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
11416289549Srpaulo							      reply,
11417289549Srpaulo							      reply_size);
11418289549Srpaulo	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
11419289549Srpaulo		reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
11420289549Srpaulo							      reply,
11421289549Srpaulo							      reply_size);
11422289549Srpaulo	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
11423289549Srpaulo		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
11424289549Srpaulo#endif /* CONFIG_FST */
11425189251Ssam	} else if (os_strcmp(buf, "TERMINATE") == 0) {
11426214734Srpaulo		wpa_supplicant_terminate_proc(global);
11427214734Srpaulo	} else if (os_strcmp(buf, "SUSPEND") == 0) {
11428214734Srpaulo		wpas_notify_suspend(global);
11429214734Srpaulo	} else if (os_strcmp(buf, "RESUME") == 0) {
11430214734Srpaulo		wpas_notify_resume(global);
11431281806Srpaulo	} else if (os_strncmp(buf, "SET ", 4) == 0) {
11432281806Srpaulo		if (wpas_global_ctrl_iface_set(global, buf + 4)) {
11433281806Srpaulo#ifdef CONFIG_P2P
11434281806Srpaulo			if (global->p2p_init_wpa_s) {
11435281806Srpaulo				os_free(reply);
11436281806Srpaulo				/* Check if P2P redirection would work for this
11437281806Srpaulo				 * command. */
11438281806Srpaulo				return wpa_supplicant_ctrl_iface_process(
11439281806Srpaulo					global->p2p_init_wpa_s,
11440281806Srpaulo					buf, resp_len);
11441281806Srpaulo			}
11442281806Srpaulo#endif /* CONFIG_P2P */
11443281806Srpaulo			reply_len = -1;
11444281806Srpaulo		}
11445289549Srpaulo	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
11446289549Srpaulo		if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
11447289549Srpaulo			reply_len = -1;
11448281806Srpaulo#ifndef CONFIG_NO_CONFIG_WRITE
11449281806Srpaulo	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
11450281806Srpaulo		if (wpas_global_ctrl_iface_save_config(global))
11451281806Srpaulo			reply_len = -1;
11452281806Srpaulo#endif /* CONFIG_NO_CONFIG_WRITE */
11453281806Srpaulo	} else if (os_strcmp(buf, "STATUS") == 0) {
11454281806Srpaulo		reply_len = wpas_global_ctrl_iface_status(global, reply,
11455281806Srpaulo							  reply_size);
11456281806Srpaulo#ifdef CONFIG_MODULE_TESTS
11457281806Srpaulo	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
11458281806Srpaulo		if (wpas_module_tests() < 0)
11459281806Srpaulo			reply_len = -1;
11460281806Srpaulo#endif /* CONFIG_MODULE_TESTS */
11461281806Srpaulo	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
11462281806Srpaulo		if (wpa_debug_reopen_file() < 0)
11463281806Srpaulo			reply_len = -1;
11464189251Ssam	} else {
11465189251Ssam		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
11466189251Ssam		reply_len = 16;
11467189251Ssam	}
11468189251Ssam
11469189251Ssam	if (reply_len < 0) {
11470189251Ssam		os_memcpy(reply, "FAIL\n", 5);
11471189251Ssam		reply_len = 5;
11472189251Ssam	}
11473189251Ssam
11474189251Ssam	*resp_len = reply_len;
11475189251Ssam	return reply;
11476189251Ssam}
11477