1*a1157835SDaniel Fojt /*
2*a1157835SDaniel Fojt  * FST module - Control Interface implementation
3*a1157835SDaniel Fojt  * Copyright (c) 2014, Qualcomm Atheros, Inc.
4*a1157835SDaniel Fojt  *
5*a1157835SDaniel Fojt  * This software may be distributed under the terms of the BSD license.
6*a1157835SDaniel Fojt  * See README for more details.
7*a1157835SDaniel Fojt  */
8*a1157835SDaniel Fojt 
9*a1157835SDaniel Fojt #include "utils/includes.h"
10*a1157835SDaniel Fojt #include "utils/common.h"
11*a1157835SDaniel Fojt #include "common/defs.h"
12*a1157835SDaniel Fojt #include "list.h"
13*a1157835SDaniel Fojt #include "fst/fst.h"
14*a1157835SDaniel Fojt #include "fst/fst_internal.h"
15*a1157835SDaniel Fojt #include "fst_ctrl_defs.h"
16*a1157835SDaniel Fojt #include "fst_ctrl_iface.h"
17*a1157835SDaniel Fojt 
18*a1157835SDaniel Fojt 
get_fst_group_by_id(const char * id)19*a1157835SDaniel Fojt static struct fst_group * get_fst_group_by_id(const char *id)
20*a1157835SDaniel Fojt {
21*a1157835SDaniel Fojt 	struct fst_group *g;
22*a1157835SDaniel Fojt 
23*a1157835SDaniel Fojt 	foreach_fst_group(g) {
24*a1157835SDaniel Fojt 		const char *group_id = fst_group_get_id(g);
25*a1157835SDaniel Fojt 
26*a1157835SDaniel Fojt 		if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
27*a1157835SDaniel Fojt 			return g;
28*a1157835SDaniel Fojt 	}
29*a1157835SDaniel Fojt 
30*a1157835SDaniel Fojt 	return NULL;
31*a1157835SDaniel Fojt }
32*a1157835SDaniel Fojt 
33*a1157835SDaniel Fojt 
34*a1157835SDaniel Fojt /* notifications */
format_session_state_extra(const union fst_event_extra * extra,char * buffer,size_t size)35*a1157835SDaniel Fojt static Boolean format_session_state_extra(const union fst_event_extra *extra,
36*a1157835SDaniel Fojt 					  char *buffer, size_t size)
37*a1157835SDaniel Fojt {
38*a1157835SDaniel Fojt 	int len;
39*a1157835SDaniel Fojt 	char reject_str[32] = FST_CTRL_PVAL_NONE;
40*a1157835SDaniel Fojt 	const char *initiator = FST_CTRL_PVAL_NONE;
41*a1157835SDaniel Fojt 	const struct fst_event_extra_session_state *ss;
42*a1157835SDaniel Fojt 
43*a1157835SDaniel Fojt 	ss = &extra->session_state;
44*a1157835SDaniel Fojt 	if (ss->new_state != FST_SESSION_STATE_INITIAL)
45*a1157835SDaniel Fojt 		return TRUE;
46*a1157835SDaniel Fojt 
47*a1157835SDaniel Fojt 	switch (ss->extra.to_initial.reason) {
48*a1157835SDaniel Fojt 	case REASON_REJECT:
49*a1157835SDaniel Fojt 		if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
50*a1157835SDaniel Fojt 			os_snprintf(reject_str, sizeof(reject_str), "%u",
51*a1157835SDaniel Fojt 				    ss->extra.to_initial.reject_code);
52*a1157835SDaniel Fojt 		/* fall through */
53*a1157835SDaniel Fojt 	case REASON_TEARDOWN:
54*a1157835SDaniel Fojt 	case REASON_SWITCH:
55*a1157835SDaniel Fojt 		switch (ss->extra.to_initial.initiator) {
56*a1157835SDaniel Fojt 		case FST_INITIATOR_LOCAL:
57*a1157835SDaniel Fojt 			initiator = FST_CS_PVAL_INITIATOR_LOCAL;
58*a1157835SDaniel Fojt 			break;
59*a1157835SDaniel Fojt 		case FST_INITIATOR_REMOTE:
60*a1157835SDaniel Fojt 			initiator = FST_CS_PVAL_INITIATOR_REMOTE;
61*a1157835SDaniel Fojt 			break;
62*a1157835SDaniel Fojt 		default:
63*a1157835SDaniel Fojt 			break;
64*a1157835SDaniel Fojt 		}
65*a1157835SDaniel Fojt 		break;
66*a1157835SDaniel Fojt 	default:
67*a1157835SDaniel Fojt 		break;
68*a1157835SDaniel Fojt 	}
69*a1157835SDaniel Fojt 
70*a1157835SDaniel Fojt 	len = os_snprintf(buffer, size,
71*a1157835SDaniel Fojt 			  FST_CES_PNAME_REASON "=%s "
72*a1157835SDaniel Fojt 			  FST_CES_PNAME_REJECT_CODE "=%s "
73*a1157835SDaniel Fojt 			  FST_CES_PNAME_INITIATOR "=%s",
74*a1157835SDaniel Fojt 			  fst_reason_name(ss->extra.to_initial.reason),
75*a1157835SDaniel Fojt 			  reject_str, initiator);
76*a1157835SDaniel Fojt 
77*a1157835SDaniel Fojt 	return !os_snprintf_error(size, len);
78*a1157835SDaniel Fojt }
79*a1157835SDaniel Fojt 
80*a1157835SDaniel Fojt 
fst_ctrl_iface_notify(struct fst_iface * f,u32 session_id,enum fst_event_type event_type,const union fst_event_extra * extra)81*a1157835SDaniel Fojt static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
82*a1157835SDaniel Fojt 				  enum fst_event_type event_type,
83*a1157835SDaniel Fojt 				  const union fst_event_extra *extra)
84*a1157835SDaniel Fojt {
85*a1157835SDaniel Fojt 	struct fst_group *g;
86*a1157835SDaniel Fojt 	char extra_str[128] = "";
87*a1157835SDaniel Fojt 	const struct fst_event_extra_session_state *ss;
88*a1157835SDaniel Fojt 	const struct fst_event_extra_iface_state *is;
89*a1157835SDaniel Fojt 	const struct fst_event_extra_peer_state *ps;
90*a1157835SDaniel Fojt 
91*a1157835SDaniel Fojt 	/*
92*a1157835SDaniel Fojt 	 * FST can use any of interface objects as it only sends messages
93*a1157835SDaniel Fojt 	 * on global Control Interface, so we just pick the 1st one.
94*a1157835SDaniel Fojt 	 */
95*a1157835SDaniel Fojt 
96*a1157835SDaniel Fojt 	if (!f) {
97*a1157835SDaniel Fojt 		foreach_fst_group(g) {
98*a1157835SDaniel Fojt 			f = fst_group_first_iface(g);
99*a1157835SDaniel Fojt 			if (f)
100*a1157835SDaniel Fojt 				break;
101*a1157835SDaniel Fojt 		}
102*a1157835SDaniel Fojt 		if (!f)
103*a1157835SDaniel Fojt 			return;
104*a1157835SDaniel Fojt 	}
105*a1157835SDaniel Fojt 
106*a1157835SDaniel Fojt 	WPA_ASSERT(f->iface_obj.ctx);
107*a1157835SDaniel Fojt 
108*a1157835SDaniel Fojt 	switch (event_type) {
109*a1157835SDaniel Fojt 	case EVENT_FST_IFACE_STATE_CHANGED:
110*a1157835SDaniel Fojt 		if (!extra)
111*a1157835SDaniel Fojt 			return;
112*a1157835SDaniel Fojt 		is = &extra->iface_state;
113*a1157835SDaniel Fojt 		wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
114*a1157835SDaniel Fojt 				    FST_CTRL_EVENT_IFACE " %s "
115*a1157835SDaniel Fojt 				    FST_CEI_PNAME_IFNAME "=%s "
116*a1157835SDaniel Fojt 				    FST_CEI_PNAME_GROUP "=%s",
117*a1157835SDaniel Fojt 				    is->attached ? FST_CEI_PNAME_ATTACHED :
118*a1157835SDaniel Fojt 				    FST_CEI_PNAME_DETACHED,
119*a1157835SDaniel Fojt 				    is->ifname, is->group_id);
120*a1157835SDaniel Fojt 		break;
121*a1157835SDaniel Fojt 	case EVENT_PEER_STATE_CHANGED:
122*a1157835SDaniel Fojt 		if (!extra)
123*a1157835SDaniel Fojt 			return;
124*a1157835SDaniel Fojt 		ps = &extra->peer_state;
125*a1157835SDaniel Fojt 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
126*a1157835SDaniel Fojt 				    FST_CTRL_EVENT_PEER " %s "
127*a1157835SDaniel Fojt 				    FST_CEP_PNAME_IFNAME "=%s "
128*a1157835SDaniel Fojt 				    FST_CEP_PNAME_ADDR "=" MACSTR,
129*a1157835SDaniel Fojt 				    ps->connected ? FST_CEP_PNAME_CONNECTED :
130*a1157835SDaniel Fojt 				    FST_CEP_PNAME_DISCONNECTED,
131*a1157835SDaniel Fojt 				    ps->ifname, MAC2STR(ps->addr));
132*a1157835SDaniel Fojt 		break;
133*a1157835SDaniel Fojt 	case EVENT_FST_SESSION_STATE_CHANGED:
134*a1157835SDaniel Fojt 		if (!extra)
135*a1157835SDaniel Fojt 			return;
136*a1157835SDaniel Fojt 		if (!format_session_state_extra(extra, extra_str,
137*a1157835SDaniel Fojt 						sizeof(extra_str))) {
138*a1157835SDaniel Fojt 			fst_printf(MSG_ERROR,
139*a1157835SDaniel Fojt 				   "CTRL: Cannot format STATE_CHANGE extra");
140*a1157835SDaniel Fojt 			extra_str[0] = 0;
141*a1157835SDaniel Fojt 		}
142*a1157835SDaniel Fojt 		ss = &extra->session_state;
143*a1157835SDaniel Fojt 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
144*a1157835SDaniel Fojt 				    FST_CTRL_EVENT_SESSION " "
145*a1157835SDaniel Fojt 				    FST_CES_PNAME_SESSION_ID "=%u "
146*a1157835SDaniel Fojt 				    FST_CES_PNAME_EVT_TYPE "=%s "
147*a1157835SDaniel Fojt 				    FST_CES_PNAME_OLD_STATE "=%s "
148*a1157835SDaniel Fojt 				    FST_CES_PNAME_NEW_STATE "=%s %s",
149*a1157835SDaniel Fojt 				    session_id,
150*a1157835SDaniel Fojt 				    fst_session_event_type_name(event_type),
151*a1157835SDaniel Fojt 				    fst_session_state_name(ss->old_state),
152*a1157835SDaniel Fojt 				    fst_session_state_name(ss->new_state),
153*a1157835SDaniel Fojt 				    extra_str);
154*a1157835SDaniel Fojt 		break;
155*a1157835SDaniel Fojt 	case EVENT_FST_ESTABLISHED:
156*a1157835SDaniel Fojt 	case EVENT_FST_SETUP:
157*a1157835SDaniel Fojt 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
158*a1157835SDaniel Fojt 				    FST_CTRL_EVENT_SESSION " "
159*a1157835SDaniel Fojt 				    FST_CES_PNAME_SESSION_ID "=%u "
160*a1157835SDaniel Fojt 				    FST_CES_PNAME_EVT_TYPE "=%s",
161*a1157835SDaniel Fojt 				    session_id,
162*a1157835SDaniel Fojt 				    fst_session_event_type_name(event_type));
163*a1157835SDaniel Fojt 		break;
164*a1157835SDaniel Fojt 	}
165*a1157835SDaniel Fojt }
166*a1157835SDaniel Fojt 
167*a1157835SDaniel Fojt 
168*a1157835SDaniel Fojt /* command processors */
169*a1157835SDaniel Fojt 
170*a1157835SDaniel Fojt /* fst session_get */
session_get(const char * session_id,char * buf,size_t buflen)171*a1157835SDaniel Fojt static int session_get(const char *session_id, char *buf, size_t buflen)
172*a1157835SDaniel Fojt {
173*a1157835SDaniel Fojt 	struct fst_session *s;
174*a1157835SDaniel Fojt 	struct fst_iface *new_iface, *old_iface;
175*a1157835SDaniel Fojt 	const u8 *old_peer_addr, *new_peer_addr;
176*a1157835SDaniel Fojt 	u32 id;
177*a1157835SDaniel Fojt 
178*a1157835SDaniel Fojt 	id = strtoul(session_id, NULL, 0);
179*a1157835SDaniel Fojt 
180*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
181*a1157835SDaniel Fojt 	if (!s) {
182*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
183*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
184*a1157835SDaniel Fojt 	}
185*a1157835SDaniel Fojt 
186*a1157835SDaniel Fojt 	old_peer_addr = fst_session_get_peer_addr(s, TRUE);
187*a1157835SDaniel Fojt 	new_peer_addr = fst_session_get_peer_addr(s, FALSE);
188*a1157835SDaniel Fojt 	new_iface = fst_session_get_iface(s, FALSE);
189*a1157835SDaniel Fojt 	old_iface = fst_session_get_iface(s, TRUE);
190*a1157835SDaniel Fojt 
191*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen,
192*a1157835SDaniel Fojt 			   FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
193*a1157835SDaniel Fojt 			   FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
194*a1157835SDaniel Fojt 			   FST_CSG_PNAME_NEW_IFNAME "=%s\n"
195*a1157835SDaniel Fojt 			   FST_CSG_PNAME_OLD_IFNAME "=%s\n"
196*a1157835SDaniel Fojt 			   FST_CSG_PNAME_LLT "=%u\n"
197*a1157835SDaniel Fojt 			   FST_CSG_PNAME_STATE "=%s\n",
198*a1157835SDaniel Fojt 			   MAC2STR(old_peer_addr),
199*a1157835SDaniel Fojt 			   MAC2STR(new_peer_addr),
200*a1157835SDaniel Fojt 			   new_iface ? fst_iface_get_name(new_iface) :
201*a1157835SDaniel Fojt 			   FST_CTRL_PVAL_NONE,
202*a1157835SDaniel Fojt 			   old_iface ? fst_iface_get_name(old_iface) :
203*a1157835SDaniel Fojt 			   FST_CTRL_PVAL_NONE,
204*a1157835SDaniel Fojt 			   fst_session_get_llt(s),
205*a1157835SDaniel Fojt 			   fst_session_state_name(fst_session_get_state(s)));
206*a1157835SDaniel Fojt }
207*a1157835SDaniel Fojt 
208*a1157835SDaniel Fojt 
209*a1157835SDaniel Fojt /* fst session_set */
session_set(const char * session_id,char * buf,size_t buflen)210*a1157835SDaniel Fojt static int session_set(const char *session_id, char *buf, size_t buflen)
211*a1157835SDaniel Fojt {
212*a1157835SDaniel Fojt 	struct fst_session *s;
213*a1157835SDaniel Fojt 	char *p, *q;
214*a1157835SDaniel Fojt 	u32 id;
215*a1157835SDaniel Fojt 	int ret;
216*a1157835SDaniel Fojt 
217*a1157835SDaniel Fojt 	id = strtoul(session_id, &p, 0);
218*a1157835SDaniel Fojt 
219*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
220*a1157835SDaniel Fojt 	if (!s) {
221*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
222*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
223*a1157835SDaniel Fojt 	}
224*a1157835SDaniel Fojt 
225*a1157835SDaniel Fojt 	if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
226*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
227*a1157835SDaniel Fojt 	p++;
228*a1157835SDaniel Fojt 
229*a1157835SDaniel Fojt 	if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
230*a1157835SDaniel Fojt 		ret = fst_session_set_str_ifname(s, q + 1, TRUE);
231*a1157835SDaniel Fojt 	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
232*a1157835SDaniel Fojt 		ret = fst_session_set_str_ifname(s, q + 1, FALSE);
233*a1157835SDaniel Fojt 	} else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
234*a1157835SDaniel Fojt 		ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
235*a1157835SDaniel Fojt 	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
236*a1157835SDaniel Fojt 		ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
237*a1157835SDaniel Fojt 	} else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
238*a1157835SDaniel Fojt 		ret = fst_session_set_str_llt(s, q + 1);
239*a1157835SDaniel Fojt 	} else {
240*a1157835SDaniel Fojt 		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
241*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
242*a1157835SDaniel Fojt 	}
243*a1157835SDaniel Fojt 
244*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
245*a1157835SDaniel Fojt }
246*a1157835SDaniel Fojt 
247*a1157835SDaniel Fojt 
248*a1157835SDaniel Fojt /* fst session_add/remove */
session_add(const char * group_id,char * buf,size_t buflen)249*a1157835SDaniel Fojt static int session_add(const char *group_id, char *buf, size_t buflen)
250*a1157835SDaniel Fojt {
251*a1157835SDaniel Fojt 	struct fst_group *g;
252*a1157835SDaniel Fojt 	struct fst_session *s;
253*a1157835SDaniel Fojt 
254*a1157835SDaniel Fojt 	g = get_fst_group_by_id(group_id);
255*a1157835SDaniel Fojt 	if (!g) {
256*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
257*a1157835SDaniel Fojt 			   group_id);
258*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
259*a1157835SDaniel Fojt 	}
260*a1157835SDaniel Fojt 
261*a1157835SDaniel Fojt 	s = fst_session_create(g);
262*a1157835SDaniel Fojt 	if (!s) {
263*a1157835SDaniel Fojt 		fst_printf(MSG_ERROR,
264*a1157835SDaniel Fojt 			   "CTRL: Cannot create session for group '%s'",
265*a1157835SDaniel Fojt 			   group_id);
266*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
267*a1157835SDaniel Fojt 	}
268*a1157835SDaniel Fojt 
269*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
270*a1157835SDaniel Fojt }
271*a1157835SDaniel Fojt 
272*a1157835SDaniel Fojt 
session_remove(const char * session_id,char * buf,size_t buflen)273*a1157835SDaniel Fojt static int session_remove(const char *session_id, char *buf, size_t buflen)
274*a1157835SDaniel Fojt {
275*a1157835SDaniel Fojt 	struct fst_session *s;
276*a1157835SDaniel Fojt 	struct fst_group *g;
277*a1157835SDaniel Fojt 	u32 id;
278*a1157835SDaniel Fojt 
279*a1157835SDaniel Fojt 	id = strtoul(session_id, NULL, 0);
280*a1157835SDaniel Fojt 
281*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
282*a1157835SDaniel Fojt 	if (!s) {
283*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
284*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
285*a1157835SDaniel Fojt 	}
286*a1157835SDaniel Fojt 
287*a1157835SDaniel Fojt 	g = fst_session_get_group(s);
288*a1157835SDaniel Fojt 	fst_session_reset(s);
289*a1157835SDaniel Fojt 	fst_session_delete(s);
290*a1157835SDaniel Fojt 	fst_group_delete_if_empty(g);
291*a1157835SDaniel Fojt 
292*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "OK\n");
293*a1157835SDaniel Fojt }
294*a1157835SDaniel Fojt 
295*a1157835SDaniel Fojt 
296*a1157835SDaniel Fojt /* fst session_initiate */
session_initiate(const char * session_id,char * buf,size_t buflen)297*a1157835SDaniel Fojt static int session_initiate(const char *session_id, char *buf, size_t buflen)
298*a1157835SDaniel Fojt {
299*a1157835SDaniel Fojt 	struct fst_session *s;
300*a1157835SDaniel Fojt 	u32 id;
301*a1157835SDaniel Fojt 
302*a1157835SDaniel Fojt 	id = strtoul(session_id, NULL, 0);
303*a1157835SDaniel Fojt 
304*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
305*a1157835SDaniel Fojt 	if (!s) {
306*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
307*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
308*a1157835SDaniel Fojt 	}
309*a1157835SDaniel Fojt 
310*a1157835SDaniel Fojt 	if (fst_session_initiate_setup(s)) {
311*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
312*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
313*a1157835SDaniel Fojt 	}
314*a1157835SDaniel Fojt 
315*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "OK\n");
316*a1157835SDaniel Fojt }
317*a1157835SDaniel Fojt 
318*a1157835SDaniel Fojt 
319*a1157835SDaniel Fojt /* fst session_respond */
session_respond(const char * session_id,char * buf,size_t buflen)320*a1157835SDaniel Fojt static int session_respond(const char *session_id, char *buf, size_t buflen)
321*a1157835SDaniel Fojt {
322*a1157835SDaniel Fojt 	struct fst_session *s;
323*a1157835SDaniel Fojt 	char *p;
324*a1157835SDaniel Fojt 	u32 id;
325*a1157835SDaniel Fojt 	u8 status_code;
326*a1157835SDaniel Fojt 
327*a1157835SDaniel Fojt 	id = strtoul(session_id, &p, 0);
328*a1157835SDaniel Fojt 
329*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
330*a1157835SDaniel Fojt 	if (!s) {
331*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
332*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
333*a1157835SDaniel Fojt 	}
334*a1157835SDaniel Fojt 
335*a1157835SDaniel Fojt 	if (*p != ' ')
336*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
337*a1157835SDaniel Fojt 	p++;
338*a1157835SDaniel Fojt 
339*a1157835SDaniel Fojt 	if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
340*a1157835SDaniel Fojt 		status_code = WLAN_STATUS_SUCCESS;
341*a1157835SDaniel Fojt 	} else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
342*a1157835SDaniel Fojt 		status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
343*a1157835SDaniel Fojt 	} else {
344*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING,
345*a1157835SDaniel Fojt 			   "CTRL: session %u: unknown response status: %s",
346*a1157835SDaniel Fojt 			   id, p);
347*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
348*a1157835SDaniel Fojt 	}
349*a1157835SDaniel Fojt 
350*a1157835SDaniel Fojt 	if (fst_session_respond(s, status_code)) {
351*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
352*a1157835SDaniel Fojt 			   id);
353*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
354*a1157835SDaniel Fojt 	}
355*a1157835SDaniel Fojt 
356*a1157835SDaniel Fojt 	fst_printf(MSG_INFO, "CTRL: session %u responded", id);
357*a1157835SDaniel Fojt 
358*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "OK\n");
359*a1157835SDaniel Fojt }
360*a1157835SDaniel Fojt 
361*a1157835SDaniel Fojt 
362*a1157835SDaniel Fojt /* fst session_transfer */
session_transfer(const char * session_id,char * buf,size_t buflen)363*a1157835SDaniel Fojt static int session_transfer(const char *session_id, char *buf, size_t buflen)
364*a1157835SDaniel Fojt {
365*a1157835SDaniel Fojt 	struct fst_session *s;
366*a1157835SDaniel Fojt 	u32 id;
367*a1157835SDaniel Fojt 
368*a1157835SDaniel Fojt 	id = strtoul(session_id, NULL, 0);
369*a1157835SDaniel Fojt 
370*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
371*a1157835SDaniel Fojt 	if (!s) {
372*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
373*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
374*a1157835SDaniel Fojt 	}
375*a1157835SDaniel Fojt 
376*a1157835SDaniel Fojt 	if (fst_session_initiate_switch(s)) {
377*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING,
378*a1157835SDaniel Fojt 			   "CTRL: Cannot initiate ST for session %u", id);
379*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
380*a1157835SDaniel Fojt 	}
381*a1157835SDaniel Fojt 
382*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "OK\n");
383*a1157835SDaniel Fojt }
384*a1157835SDaniel Fojt 
385*a1157835SDaniel Fojt 
386*a1157835SDaniel Fojt /* fst session_teardown */
session_teardown(const char * session_id,char * buf,size_t buflen)387*a1157835SDaniel Fojt static int session_teardown(const char *session_id, char *buf, size_t buflen)
388*a1157835SDaniel Fojt {
389*a1157835SDaniel Fojt 	struct fst_session *s;
390*a1157835SDaniel Fojt 	u32 id;
391*a1157835SDaniel Fojt 
392*a1157835SDaniel Fojt 	id = strtoul(session_id, NULL, 0);
393*a1157835SDaniel Fojt 
394*a1157835SDaniel Fojt 	s = fst_session_get_by_id(id);
395*a1157835SDaniel Fojt 	if (!s) {
396*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
397*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
398*a1157835SDaniel Fojt 	}
399*a1157835SDaniel Fojt 
400*a1157835SDaniel Fojt 	if (fst_session_tear_down_setup(s)) {
401*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
402*a1157835SDaniel Fojt 			   id);
403*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
404*a1157835SDaniel Fojt 	}
405*a1157835SDaniel Fojt 
406*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "OK\n");
407*a1157835SDaniel Fojt }
408*a1157835SDaniel Fojt 
409*a1157835SDaniel Fojt 
410*a1157835SDaniel Fojt #ifdef CONFIG_FST_TEST
411*a1157835SDaniel Fojt /* fst test_request */
test_request(const char * request,char * buf,size_t buflen)412*a1157835SDaniel Fojt static int test_request(const char *request, char *buf, size_t buflen)
413*a1157835SDaniel Fojt {
414*a1157835SDaniel Fojt 	const char *p = request;
415*a1157835SDaniel Fojt 	int ret;
416*a1157835SDaniel Fojt 
417*a1157835SDaniel Fojt 	if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
418*a1157835SDaniel Fojt 			    os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
419*a1157835SDaniel Fojt 		ret = fst_test_req_send_fst_request(
420*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
421*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
422*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
423*a1157835SDaniel Fojt 		ret = fst_test_req_send_fst_response(
424*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
425*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
426*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
427*a1157835SDaniel Fojt 		ret = fst_test_req_send_ack_request(
428*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
429*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
430*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
431*a1157835SDaniel Fojt 		ret = fst_test_req_send_ack_response(
432*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
433*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
434*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
435*a1157835SDaniel Fojt 		ret = fst_test_req_send_tear_down(
436*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
437*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
438*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_GET_FSTS_ID))) {
439*a1157835SDaniel Fojt 		u32 fsts_id = fst_test_req_get_fsts_id(
440*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_GET_FSTS_ID));
441*a1157835SDaniel Fojt 		if (fsts_id != FST_FSTS_ID_NOT_FOUND)
442*a1157835SDaniel Fojt 			return os_snprintf(buf, buflen, "%u\n", fsts_id);
443*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
444*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
445*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
446*a1157835SDaniel Fojt 		return fst_test_req_get_local_mbies(
447*a1157835SDaniel Fojt 			p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
448*a1157835SDaniel Fojt 	} else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
449*a1157835SDaniel Fojt 				   os_strlen(FST_CTR_IS_SUPPORTED))) {
450*a1157835SDaniel Fojt 		ret = 0;
451*a1157835SDaniel Fojt 	} else {
452*a1157835SDaniel Fojt 		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
453*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
454*a1157835SDaniel Fojt 	}
455*a1157835SDaniel Fojt 
456*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
457*a1157835SDaniel Fojt }
458*a1157835SDaniel Fojt #endif /* CONFIG_FST_TEST */
459*a1157835SDaniel Fojt 
460*a1157835SDaniel Fojt 
461*a1157835SDaniel Fojt /* fst list_sessions */
462*a1157835SDaniel Fojt struct list_sessions_cb_ctx {
463*a1157835SDaniel Fojt 	char *buf;
464*a1157835SDaniel Fojt 	size_t buflen;
465*a1157835SDaniel Fojt 	size_t reply_len;
466*a1157835SDaniel Fojt };
467*a1157835SDaniel Fojt 
468*a1157835SDaniel Fojt 
list_session_enum_cb(struct fst_group * g,struct fst_session * s,void * ctx)469*a1157835SDaniel Fojt static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
470*a1157835SDaniel Fojt 				 void *ctx)
471*a1157835SDaniel Fojt {
472*a1157835SDaniel Fojt 	struct list_sessions_cb_ctx *c = ctx;
473*a1157835SDaniel Fojt 	int ret;
474*a1157835SDaniel Fojt 
475*a1157835SDaniel Fojt 	ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
476*a1157835SDaniel Fojt 
477*a1157835SDaniel Fojt 	c->buf += ret;
478*a1157835SDaniel Fojt 	c->buflen -= ret;
479*a1157835SDaniel Fojt 	c->reply_len += ret;
480*a1157835SDaniel Fojt }
481*a1157835SDaniel Fojt 
482*a1157835SDaniel Fojt 
list_sessions(const char * group_id,char * buf,size_t buflen)483*a1157835SDaniel Fojt static int list_sessions(const char *group_id, char *buf, size_t buflen)
484*a1157835SDaniel Fojt {
485*a1157835SDaniel Fojt 	struct list_sessions_cb_ctx ctx;
486*a1157835SDaniel Fojt 	struct fst_group *g;
487*a1157835SDaniel Fojt 
488*a1157835SDaniel Fojt 	g = get_fst_group_by_id(group_id);
489*a1157835SDaniel Fojt 	if (!g) {
490*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
491*a1157835SDaniel Fojt 			   group_id);
492*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
493*a1157835SDaniel Fojt 	}
494*a1157835SDaniel Fojt 
495*a1157835SDaniel Fojt 	ctx.buf = buf;
496*a1157835SDaniel Fojt 	ctx.buflen = buflen;
497*a1157835SDaniel Fojt 	ctx.reply_len = 0;
498*a1157835SDaniel Fojt 
499*a1157835SDaniel Fojt 	fst_session_enum(g, list_session_enum_cb, &ctx);
500*a1157835SDaniel Fojt 
501*a1157835SDaniel Fojt 	ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
502*a1157835SDaniel Fojt 
503*a1157835SDaniel Fojt 	return ctx.reply_len;
504*a1157835SDaniel Fojt }
505*a1157835SDaniel Fojt 
506*a1157835SDaniel Fojt 
507*a1157835SDaniel Fojt /* fst iface_peers */
iface_peers(const char * group_id,char * buf,size_t buflen)508*a1157835SDaniel Fojt static int iface_peers(const char *group_id, char *buf, size_t buflen)
509*a1157835SDaniel Fojt {
510*a1157835SDaniel Fojt 	const char *ifname;
511*a1157835SDaniel Fojt 	struct fst_group *g;
512*a1157835SDaniel Fojt 	struct fst_iface *f;
513*a1157835SDaniel Fojt 	struct fst_get_peer_ctx *ctx;
514*a1157835SDaniel Fojt 	const u8 *addr;
515*a1157835SDaniel Fojt 	unsigned found = 0;
516*a1157835SDaniel Fojt 	int ret = 0;
517*a1157835SDaniel Fojt 
518*a1157835SDaniel Fojt 	g = get_fst_group_by_id(group_id);
519*a1157835SDaniel Fojt 	if (!g) {
520*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
521*a1157835SDaniel Fojt 			   group_id);
522*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
523*a1157835SDaniel Fojt 	}
524*a1157835SDaniel Fojt 
525*a1157835SDaniel Fojt 	ifname = os_strchr(group_id, ' ');
526*a1157835SDaniel Fojt 	if (!ifname)
527*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
528*a1157835SDaniel Fojt 	ifname++;
529*a1157835SDaniel Fojt 
530*a1157835SDaniel Fojt 	foreach_fst_group_iface(g, f) {
531*a1157835SDaniel Fojt 		const char *in = fst_iface_get_name(f);
532*a1157835SDaniel Fojt 
533*a1157835SDaniel Fojt 		if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
534*a1157835SDaniel Fojt 			found = 1;
535*a1157835SDaniel Fojt 			break;
536*a1157835SDaniel Fojt 		}
537*a1157835SDaniel Fojt 	}
538*a1157835SDaniel Fojt 
539*a1157835SDaniel Fojt 	if (!found)
540*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
541*a1157835SDaniel Fojt 
542*a1157835SDaniel Fojt 	addr = fst_iface_get_peer_first(f, &ctx, FALSE);
543*a1157835SDaniel Fojt 	for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
544*a1157835SDaniel Fojt 		int res;
545*a1157835SDaniel Fojt 
546*a1157835SDaniel Fojt 		res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
547*a1157835SDaniel Fojt 				  MAC2STR(addr));
548*a1157835SDaniel Fojt 		if (os_snprintf_error(buflen - ret, res))
549*a1157835SDaniel Fojt 			break;
550*a1157835SDaniel Fojt 		ret += res;
551*a1157835SDaniel Fojt 	}
552*a1157835SDaniel Fojt 
553*a1157835SDaniel Fojt 	return ret;
554*a1157835SDaniel Fojt }
555*a1157835SDaniel Fojt 
556*a1157835SDaniel Fojt 
get_peer_mbies(const char * params,char * buf,size_t buflen)557*a1157835SDaniel Fojt static int get_peer_mbies(const char *params, char *buf, size_t buflen)
558*a1157835SDaniel Fojt {
559*a1157835SDaniel Fojt 	char *endp;
560*a1157835SDaniel Fojt 	char ifname[FST_MAX_INTERFACE_SIZE];
561*a1157835SDaniel Fojt 	u8 peer_addr[ETH_ALEN];
562*a1157835SDaniel Fojt 	struct fst_group *g;
563*a1157835SDaniel Fojt 	struct fst_iface *iface = NULL;
564*a1157835SDaniel Fojt 	const struct wpabuf *mbies;
565*a1157835SDaniel Fojt 
566*a1157835SDaniel Fojt 	if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
567*a1157835SDaniel Fojt 	    !*ifname)
568*a1157835SDaniel Fojt 		goto problem;
569*a1157835SDaniel Fojt 
570*a1157835SDaniel Fojt 	while (isspace(*endp))
571*a1157835SDaniel Fojt 		endp++;
572*a1157835SDaniel Fojt 	if (fst_read_peer_addr(endp, peer_addr))
573*a1157835SDaniel Fojt 		goto problem;
574*a1157835SDaniel Fojt 
575*a1157835SDaniel Fojt 	foreach_fst_group(g) {
576*a1157835SDaniel Fojt 		iface = fst_group_get_iface_by_name(g, ifname);
577*a1157835SDaniel Fojt 		if (iface)
578*a1157835SDaniel Fojt 			break;
579*a1157835SDaniel Fojt 	}
580*a1157835SDaniel Fojt 	if (!iface)
581*a1157835SDaniel Fojt 		goto problem;
582*a1157835SDaniel Fojt 
583*a1157835SDaniel Fojt 	mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
584*a1157835SDaniel Fojt 	if (!mbies)
585*a1157835SDaniel Fojt 		goto problem;
586*a1157835SDaniel Fojt 
587*a1157835SDaniel Fojt 	return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
588*a1157835SDaniel Fojt 				wpabuf_len(mbies));
589*a1157835SDaniel Fojt 
590*a1157835SDaniel Fojt problem:
591*a1157835SDaniel Fojt 	return os_snprintf(buf, buflen, "FAIL\n");
592*a1157835SDaniel Fojt }
593*a1157835SDaniel Fojt 
594*a1157835SDaniel Fojt 
595*a1157835SDaniel Fojt /* fst list_ifaces */
list_ifaces(const char * group_id,char * buf,size_t buflen)596*a1157835SDaniel Fojt static int list_ifaces(const char *group_id, char *buf, size_t buflen)
597*a1157835SDaniel Fojt {
598*a1157835SDaniel Fojt 	struct fst_group *g;
599*a1157835SDaniel Fojt 	struct fst_iface *f;
600*a1157835SDaniel Fojt 	int ret = 0;
601*a1157835SDaniel Fojt 
602*a1157835SDaniel Fojt 	g = get_fst_group_by_id(group_id);
603*a1157835SDaniel Fojt 	if (!g) {
604*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
605*a1157835SDaniel Fojt 			   group_id);
606*a1157835SDaniel Fojt 		return os_snprintf(buf, buflen, "FAIL\n");
607*a1157835SDaniel Fojt 	}
608*a1157835SDaniel Fojt 
609*a1157835SDaniel Fojt 	foreach_fst_group_iface(g, f) {
610*a1157835SDaniel Fojt 		int res;
611*a1157835SDaniel Fojt 		const u8 *iface_addr = fst_iface_get_addr(f);
612*a1157835SDaniel Fojt 
613*a1157835SDaniel Fojt 		res = os_snprintf(buf + ret, buflen - ret,
614*a1157835SDaniel Fojt 				  "%s|" MACSTR "|%u|%u\n",
615*a1157835SDaniel Fojt 				  fst_iface_get_name(f),
616*a1157835SDaniel Fojt 				  MAC2STR(iface_addr),
617*a1157835SDaniel Fojt 				  fst_iface_get_priority(f),
618*a1157835SDaniel Fojt 				  fst_iface_get_llt(f));
619*a1157835SDaniel Fojt 		if (os_snprintf_error(buflen - ret, res))
620*a1157835SDaniel Fojt 			break;
621*a1157835SDaniel Fojt 		ret += res;
622*a1157835SDaniel Fojt 	}
623*a1157835SDaniel Fojt 
624*a1157835SDaniel Fojt 	return ret;
625*a1157835SDaniel Fojt }
626*a1157835SDaniel Fojt 
627*a1157835SDaniel Fojt 
628*a1157835SDaniel Fojt /* fst list_groups */
list_groups(const char * cmd,char * buf,size_t buflen)629*a1157835SDaniel Fojt static int list_groups(const char *cmd, char *buf, size_t buflen)
630*a1157835SDaniel Fojt {
631*a1157835SDaniel Fojt 	struct fst_group *g;
632*a1157835SDaniel Fojt 	int ret = 0;
633*a1157835SDaniel Fojt 
634*a1157835SDaniel Fojt 	foreach_fst_group(g) {
635*a1157835SDaniel Fojt 		int res;
636*a1157835SDaniel Fojt 
637*a1157835SDaniel Fojt 		res = os_snprintf(buf + ret, buflen - ret, "%s\n",
638*a1157835SDaniel Fojt 				  fst_group_get_id(g));
639*a1157835SDaniel Fojt 		if (os_snprintf_error(buflen - ret, res))
640*a1157835SDaniel Fojt 			break;
641*a1157835SDaniel Fojt 		ret += res;
642*a1157835SDaniel Fojt 	}
643*a1157835SDaniel Fojt 
644*a1157835SDaniel Fojt 	return ret;
645*a1157835SDaniel Fojt }
646*a1157835SDaniel Fojt 
647*a1157835SDaniel Fojt 
band_freq(enum mb_band_id band)648*a1157835SDaniel Fojt static const char * band_freq(enum mb_band_id band)
649*a1157835SDaniel Fojt {
650*a1157835SDaniel Fojt 	static const char *band_names[] = {
651*a1157835SDaniel Fojt 		[MB_BAND_ID_WIFI_2_4GHZ] = "2.4GHZ",
652*a1157835SDaniel Fojt 		[MB_BAND_ID_WIFI_5GHZ] = "5GHZ",
653*a1157835SDaniel Fojt 		[MB_BAND_ID_WIFI_60GHZ] = "60GHZ",
654*a1157835SDaniel Fojt 	};
655*a1157835SDaniel Fojt 
656*a1157835SDaniel Fojt 	return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
657*a1157835SDaniel Fojt }
658*a1157835SDaniel Fojt 
659*a1157835SDaniel Fojt 
print_band(unsigned num,struct fst_iface * iface,const u8 * addr,char * buf,size_t buflen)660*a1157835SDaniel Fojt static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
661*a1157835SDaniel Fojt 		      char *buf, size_t buflen)
662*a1157835SDaniel Fojt {
663*a1157835SDaniel Fojt 	const struct wpabuf *wpabuf;
664*a1157835SDaniel Fojt 	enum hostapd_hw_mode hw_mode;
665*a1157835SDaniel Fojt 	u8 channel;
666*a1157835SDaniel Fojt 	int ret = 0;
667*a1157835SDaniel Fojt 
668*a1157835SDaniel Fojt 	fst_iface_get_channel_info(iface, &hw_mode, &channel);
669*a1157835SDaniel Fojt 
670*a1157835SDaniel Fojt 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
671*a1157835SDaniel Fojt 			   num, band_freq(fst_hw_mode_to_band(hw_mode)));
672*a1157835SDaniel Fojt 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
673*a1157835SDaniel Fojt 			   num, fst_iface_get_name(iface));
674*a1157835SDaniel Fojt 	wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
675*a1157835SDaniel Fojt 	if (wpabuf) {
676*a1157835SDaniel Fojt 		ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
677*a1157835SDaniel Fojt 				   num);
678*a1157835SDaniel Fojt 		ret += wpa_snprintf_hex(buf + ret, buflen - ret,
679*a1157835SDaniel Fojt 					wpabuf_head(wpabuf),
680*a1157835SDaniel Fojt 					wpabuf_len(wpabuf));
681*a1157835SDaniel Fojt 		ret += os_snprintf(buf + ret, buflen - ret, "\n");
682*a1157835SDaniel Fojt 	}
683*a1157835SDaniel Fojt 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
684*a1157835SDaniel Fojt 			   num, fst_iface_get_group_id(iface));
685*a1157835SDaniel Fojt 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
686*a1157835SDaniel Fojt 			   num, fst_iface_get_priority(iface));
687*a1157835SDaniel Fojt 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
688*a1157835SDaniel Fojt 			   num, fst_iface_get_llt(iface));
689*a1157835SDaniel Fojt 
690*a1157835SDaniel Fojt 	return ret;
691*a1157835SDaniel Fojt }
692*a1157835SDaniel Fojt 
693*a1157835SDaniel Fojt 
fst_ctrl_iface_on_iface_state_changed(struct fst_iface * i,Boolean attached)694*a1157835SDaniel Fojt static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
695*a1157835SDaniel Fojt 						  Boolean attached)
696*a1157835SDaniel Fojt {
697*a1157835SDaniel Fojt 	union fst_event_extra extra;
698*a1157835SDaniel Fojt 
699*a1157835SDaniel Fojt 	os_memset(&extra, 0, sizeof(extra));
700*a1157835SDaniel Fojt 	extra.iface_state.attached = attached;
701*a1157835SDaniel Fojt 	os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
702*a1157835SDaniel Fojt 		   sizeof(extra.iface_state.ifname));
703*a1157835SDaniel Fojt 	os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
704*a1157835SDaniel Fojt 		   sizeof(extra.iface_state.group_id));
705*a1157835SDaniel Fojt 
706*a1157835SDaniel Fojt 	fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
707*a1157835SDaniel Fojt 			      EVENT_FST_IFACE_STATE_CHANGED, &extra);
708*a1157835SDaniel Fojt }
709*a1157835SDaniel Fojt 
710*a1157835SDaniel Fojt 
fst_ctrl_iface_on_iface_added(struct fst_iface * i)711*a1157835SDaniel Fojt static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
712*a1157835SDaniel Fojt {
713*a1157835SDaniel Fojt 	fst_ctrl_iface_on_iface_state_changed(i, TRUE);
714*a1157835SDaniel Fojt 	return 0;
715*a1157835SDaniel Fojt }
716*a1157835SDaniel Fojt 
717*a1157835SDaniel Fojt 
fst_ctrl_iface_on_iface_removed(struct fst_iface * i)718*a1157835SDaniel Fojt static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
719*a1157835SDaniel Fojt {
720*a1157835SDaniel Fojt 	fst_ctrl_iface_on_iface_state_changed(i, FALSE);
721*a1157835SDaniel Fojt }
722*a1157835SDaniel Fojt 
723*a1157835SDaniel Fojt 
fst_ctrl_iface_on_event(enum fst_event_type event_type,struct fst_iface * i,struct fst_session * s,const union fst_event_extra * extra)724*a1157835SDaniel Fojt static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
725*a1157835SDaniel Fojt 				    struct fst_iface *i, struct fst_session *s,
726*a1157835SDaniel Fojt 				    const union fst_event_extra *extra)
727*a1157835SDaniel Fojt {
728*a1157835SDaniel Fojt 	u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
729*a1157835SDaniel Fojt 
730*a1157835SDaniel Fojt 	fst_ctrl_iface_notify(i, session_id, event_type, extra);
731*a1157835SDaniel Fojt }
732*a1157835SDaniel Fojt 
733*a1157835SDaniel Fojt 
734*a1157835SDaniel Fojt static const struct fst_ctrl ctrl_cli = {
735*a1157835SDaniel Fojt 	.on_iface_added = fst_ctrl_iface_on_iface_added,
736*a1157835SDaniel Fojt 	.on_iface_removed =  fst_ctrl_iface_on_iface_removed,
737*a1157835SDaniel Fojt 	.on_event = fst_ctrl_iface_on_event,
738*a1157835SDaniel Fojt };
739*a1157835SDaniel Fojt 
740*a1157835SDaniel Fojt const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
741*a1157835SDaniel Fojt 
742*a1157835SDaniel Fojt 
fst_ctrl_iface_mb_info(const u8 * addr,char * buf,size_t buflen)743*a1157835SDaniel Fojt int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
744*a1157835SDaniel Fojt {
745*a1157835SDaniel Fojt 	struct fst_group *g;
746*a1157835SDaniel Fojt 	struct fst_iface *f;
747*a1157835SDaniel Fojt 	unsigned num = 0;
748*a1157835SDaniel Fojt 	int ret = 0;
749*a1157835SDaniel Fojt 
750*a1157835SDaniel Fojt 	foreach_fst_group(g) {
751*a1157835SDaniel Fojt 		foreach_fst_group_iface(g, f) {
752*a1157835SDaniel Fojt 			if (fst_iface_is_connected(f, addr, TRUE)) {
753*a1157835SDaniel Fojt 				ret += print_band(num++, f, addr,
754*a1157835SDaniel Fojt 						  buf + ret, buflen - ret);
755*a1157835SDaniel Fojt 			}
756*a1157835SDaniel Fojt 		}
757*a1157835SDaniel Fojt 	}
758*a1157835SDaniel Fojt 
759*a1157835SDaniel Fojt 	return ret;
760*a1157835SDaniel Fojt }
761*a1157835SDaniel Fojt 
762*a1157835SDaniel Fojt 
763*a1157835SDaniel Fojt /* fst ctrl processor */
fst_ctrl_iface_receive(const char * cmd,char * reply,size_t reply_size)764*a1157835SDaniel Fojt int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
765*a1157835SDaniel Fojt {
766*a1157835SDaniel Fojt 	static const struct fst_command {
767*a1157835SDaniel Fojt 		const char *name;
768*a1157835SDaniel Fojt 		unsigned has_param;
769*a1157835SDaniel Fojt 		int (*process)(const char *group_id, char *buf, size_t buflen);
770*a1157835SDaniel Fojt 	} commands[] = {
771*a1157835SDaniel Fojt 		{ FST_CMD_LIST_GROUPS, 0, list_groups},
772*a1157835SDaniel Fojt 		{ FST_CMD_LIST_IFACES, 1, list_ifaces},
773*a1157835SDaniel Fojt 		{ FST_CMD_IFACE_PEERS, 1, iface_peers},
774*a1157835SDaniel Fojt 		{ FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
775*a1157835SDaniel Fojt 		{ FST_CMD_LIST_SESSIONS, 1, list_sessions},
776*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_ADD, 1, session_add},
777*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_REMOVE, 1, session_remove},
778*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_GET, 1, session_get},
779*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_SET, 1, session_set},
780*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_INITIATE, 1, session_initiate},
781*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_RESPOND, 1, session_respond},
782*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_TRANSFER, 1, session_transfer},
783*a1157835SDaniel Fojt 		{ FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
784*a1157835SDaniel Fojt #ifdef CONFIG_FST_TEST
785*a1157835SDaniel Fojt 		{ FST_CMD_TEST_REQUEST, 1, test_request },
786*a1157835SDaniel Fojt #endif /* CONFIG_FST_TEST */
787*a1157835SDaniel Fojt 		{ NULL, 0, NULL }
788*a1157835SDaniel Fojt 	};
789*a1157835SDaniel Fojt 	const struct fst_command *c;
790*a1157835SDaniel Fojt 	const char *p;
791*a1157835SDaniel Fojt 	const char *temp;
792*a1157835SDaniel Fojt 	Boolean non_spaces_found;
793*a1157835SDaniel Fojt 
794*a1157835SDaniel Fojt 	for (c = commands; c->name; c++) {
795*a1157835SDaniel Fojt 		if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
796*a1157835SDaniel Fojt 			continue;
797*a1157835SDaniel Fojt 		p = cmd + os_strlen(c->name);
798*a1157835SDaniel Fojt 		if (c->has_param) {
799*a1157835SDaniel Fojt 			if (!isspace(p[0]))
800*a1157835SDaniel Fojt 				return os_snprintf(reply, reply_size, "FAIL\n");
801*a1157835SDaniel Fojt 			p++;
802*a1157835SDaniel Fojt 			temp = p;
803*a1157835SDaniel Fojt 			non_spaces_found = FALSE;
804*a1157835SDaniel Fojt 			while (*temp) {
805*a1157835SDaniel Fojt 				if (!isspace(*temp)) {
806*a1157835SDaniel Fojt 					non_spaces_found = TRUE;
807*a1157835SDaniel Fojt 					break;
808*a1157835SDaniel Fojt 				}
809*a1157835SDaniel Fojt 				temp++;
810*a1157835SDaniel Fojt 			}
811*a1157835SDaniel Fojt 			if (!non_spaces_found)
812*a1157835SDaniel Fojt 				return os_snprintf(reply, reply_size, "FAIL\n");
813*a1157835SDaniel Fojt 		}
814*a1157835SDaniel Fojt 		return c->process(p, reply, reply_size);
815*a1157835SDaniel Fojt 	}
816*a1157835SDaniel Fojt 
817*a1157835SDaniel Fojt 	return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
818*a1157835SDaniel Fojt }
819*a1157835SDaniel Fojt 
820*a1157835SDaniel Fojt 
fst_read_next_int_param(const char * params,Boolean * valid,char ** endp)821*a1157835SDaniel Fojt int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
822*a1157835SDaniel Fojt {
823*a1157835SDaniel Fojt 	int ret = -1;
824*a1157835SDaniel Fojt 	const char *curp;
825*a1157835SDaniel Fojt 
826*a1157835SDaniel Fojt 	*valid = FALSE;
827*a1157835SDaniel Fojt 	*endp = (char *) params;
828*a1157835SDaniel Fojt 	curp = params;
829*a1157835SDaniel Fojt 	if (*curp) {
830*a1157835SDaniel Fojt 		ret = (int) strtol(curp, endp, 0);
831*a1157835SDaniel Fojt 		if (!**endp || isspace(**endp))
832*a1157835SDaniel Fojt 			*valid = TRUE;
833*a1157835SDaniel Fojt 	}
834*a1157835SDaniel Fojt 
835*a1157835SDaniel Fojt 	return ret;
836*a1157835SDaniel Fojt }
837*a1157835SDaniel Fojt 
838*a1157835SDaniel Fojt 
fst_read_next_text_param(const char * params,char * buf,size_t buflen,char ** endp)839*a1157835SDaniel Fojt int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
840*a1157835SDaniel Fojt 			     char **endp)
841*a1157835SDaniel Fojt {
842*a1157835SDaniel Fojt 	size_t max_chars_to_copy;
843*a1157835SDaniel Fojt 	char *cur_dest;
844*a1157835SDaniel Fojt 
845*a1157835SDaniel Fojt 	*endp = (char *) params;
846*a1157835SDaniel Fojt 	while (isspace(**endp))
847*a1157835SDaniel Fojt 		(*endp)++;
848*a1157835SDaniel Fojt 	if (!**endp || buflen <= 1)
849*a1157835SDaniel Fojt 		return -EINVAL;
850*a1157835SDaniel Fojt 
851*a1157835SDaniel Fojt 	max_chars_to_copy = buflen - 1;
852*a1157835SDaniel Fojt 	/* We need 1 byte for the terminating zero */
853*a1157835SDaniel Fojt 	cur_dest = buf;
854*a1157835SDaniel Fojt 	while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
855*a1157835SDaniel Fojt 		*cur_dest = **endp;
856*a1157835SDaniel Fojt 		(*endp)++;
857*a1157835SDaniel Fojt 		cur_dest++;
858*a1157835SDaniel Fojt 		max_chars_to_copy--;
859*a1157835SDaniel Fojt 	}
860*a1157835SDaniel Fojt 	*cur_dest = 0;
861*a1157835SDaniel Fojt 
862*a1157835SDaniel Fojt 	return 0;
863*a1157835SDaniel Fojt }
864*a1157835SDaniel Fojt 
865*a1157835SDaniel Fojt 
fst_read_peer_addr(const char * mac,u8 * peer_addr)866*a1157835SDaniel Fojt int fst_read_peer_addr(const char *mac, u8 *peer_addr)
867*a1157835SDaniel Fojt {
868*a1157835SDaniel Fojt 	if (hwaddr_aton(mac, peer_addr)) {
869*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
870*a1157835SDaniel Fojt 			   mac);
871*a1157835SDaniel Fojt 		return -1;
872*a1157835SDaniel Fojt 	}
873*a1157835SDaniel Fojt 
874*a1157835SDaniel Fojt 	if (is_zero_ether_addr(peer_addr) ||
875*a1157835SDaniel Fojt 	    is_multicast_ether_addr(peer_addr)) {
876*a1157835SDaniel Fojt 		fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
877*a1157835SDaniel Fojt 			   mac);
878*a1157835SDaniel Fojt 		return -1;
879*a1157835SDaniel Fojt 	}
880*a1157835SDaniel Fojt 
881*a1157835SDaniel Fojt 	return 0;
882*a1157835SDaniel Fojt }
883*a1157835SDaniel Fojt 
884*a1157835SDaniel Fojt 
fst_parse_attach_command(const char * cmd,char * ifname,size_t ifname_size,struct fst_iface_cfg * cfg)885*a1157835SDaniel Fojt int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
886*a1157835SDaniel Fojt 			     struct fst_iface_cfg *cfg)
887*a1157835SDaniel Fojt {
888*a1157835SDaniel Fojt 	char *pos;
889*a1157835SDaniel Fojt 	char *endp;
890*a1157835SDaniel Fojt 	Boolean is_valid;
891*a1157835SDaniel Fojt 	int val;
892*a1157835SDaniel Fojt 
893*a1157835SDaniel Fojt 	if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
894*a1157835SDaniel Fojt 	    fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
895*a1157835SDaniel Fojt 				     &endp))
896*a1157835SDaniel Fojt 		return -EINVAL;
897*a1157835SDaniel Fojt 
898*a1157835SDaniel Fojt 	cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
899*a1157835SDaniel Fojt 	cfg->priority = 0;
900*a1157835SDaniel Fojt 	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
901*a1157835SDaniel Fojt 	if (pos) {
902*a1157835SDaniel Fojt 		pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
903*a1157835SDaniel Fojt 		if (*pos == '=') {
904*a1157835SDaniel Fojt 			val = fst_read_next_int_param(pos + 1, &is_valid,
905*a1157835SDaniel Fojt 						      &endp);
906*a1157835SDaniel Fojt 			if (is_valid)
907*a1157835SDaniel Fojt 				cfg->llt = val;
908*a1157835SDaniel Fojt 		}
909*a1157835SDaniel Fojt 	}
910*a1157835SDaniel Fojt 	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
911*a1157835SDaniel Fojt 	if (pos) {
912*a1157835SDaniel Fojt 		pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
913*a1157835SDaniel Fojt 		if (*pos == '=') {
914*a1157835SDaniel Fojt 			val = fst_read_next_int_param(pos + 1, &is_valid,
915*a1157835SDaniel Fojt 						      &endp);
916*a1157835SDaniel Fojt 			if (is_valid)
917*a1157835SDaniel Fojt 				cfg->priority = (u8) val;
918*a1157835SDaniel Fojt 		}
919*a1157835SDaniel Fojt 	}
920*a1157835SDaniel Fojt 
921*a1157835SDaniel Fojt 	return 0;
922*a1157835SDaniel Fojt }
923*a1157835SDaniel Fojt 
924*a1157835SDaniel Fojt 
fst_parse_detach_command(const char * cmd,char * ifname,size_t ifname_size)925*a1157835SDaniel Fojt int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
926*a1157835SDaniel Fojt {
927*a1157835SDaniel Fojt 	char *endp;
928*a1157835SDaniel Fojt 
929*a1157835SDaniel Fojt 	return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
930*a1157835SDaniel Fojt }
931*a1157835SDaniel Fojt 
932*a1157835SDaniel Fojt 
fst_iface_detach(const char * ifname)933*a1157835SDaniel Fojt int fst_iface_detach(const char *ifname)
934*a1157835SDaniel Fojt {
935*a1157835SDaniel Fojt 	struct fst_group *g;
936*a1157835SDaniel Fojt 
937*a1157835SDaniel Fojt 	foreach_fst_group(g) {
938*a1157835SDaniel Fojt 		struct fst_iface *f;
939*a1157835SDaniel Fojt 
940*a1157835SDaniel Fojt 		f = fst_group_get_iface_by_name(g, ifname);
941*a1157835SDaniel Fojt 		if (f) {
942*a1157835SDaniel Fojt 			fst_detach(f);
943*a1157835SDaniel Fojt 			return 0;
944*a1157835SDaniel Fojt 		}
945*a1157835SDaniel Fojt 	}
946*a1157835SDaniel Fojt 
947*a1157835SDaniel Fojt 	return -EINVAL;
948*a1157835SDaniel Fojt }
949