xref: /freebsd/contrib/wpa/src/fst/fst_ctrl_iface.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1325151a3SRui Paulo /*
2325151a3SRui Paulo  * FST module - Control Interface implementation
3325151a3SRui Paulo  * Copyright (c) 2014, Qualcomm Atheros, Inc.
4325151a3SRui Paulo  *
5325151a3SRui Paulo  * This software may be distributed under the terms of the BSD license.
6325151a3SRui Paulo  * See README for more details.
7325151a3SRui Paulo  */
8325151a3SRui Paulo 
9325151a3SRui Paulo #include "utils/includes.h"
10325151a3SRui Paulo #include "utils/common.h"
11325151a3SRui Paulo #include "common/defs.h"
12325151a3SRui Paulo #include "list.h"
13325151a3SRui Paulo #include "fst/fst.h"
14325151a3SRui Paulo #include "fst/fst_internal.h"
15325151a3SRui Paulo #include "fst_ctrl_defs.h"
16325151a3SRui Paulo #include "fst_ctrl_iface.h"
17325151a3SRui Paulo 
18325151a3SRui Paulo 
get_fst_group_by_id(const char * id)19325151a3SRui Paulo static struct fst_group * get_fst_group_by_id(const char *id)
20325151a3SRui Paulo {
21325151a3SRui Paulo 	struct fst_group *g;
22325151a3SRui Paulo 
23325151a3SRui Paulo 	foreach_fst_group(g) {
24325151a3SRui Paulo 		const char *group_id = fst_group_get_id(g);
25325151a3SRui Paulo 
26325151a3SRui Paulo 		if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
27325151a3SRui Paulo 			return g;
28325151a3SRui Paulo 	}
29325151a3SRui Paulo 
30325151a3SRui Paulo 	return NULL;
31325151a3SRui Paulo }
32325151a3SRui Paulo 
33325151a3SRui Paulo 
34325151a3SRui Paulo /* notifications */
format_session_state_extra(const union fst_event_extra * extra,char * buffer,size_t size)35*c1d255d3SCy Schubert static bool format_session_state_extra(const union fst_event_extra *extra,
36325151a3SRui Paulo 				       char *buffer, size_t size)
37325151a3SRui Paulo {
38325151a3SRui Paulo 	int len;
39325151a3SRui Paulo 	char reject_str[32] = FST_CTRL_PVAL_NONE;
40325151a3SRui Paulo 	const char *initiator = FST_CTRL_PVAL_NONE;
41325151a3SRui Paulo 	const struct fst_event_extra_session_state *ss;
42325151a3SRui Paulo 
43325151a3SRui Paulo 	ss = &extra->session_state;
44325151a3SRui Paulo 	if (ss->new_state != FST_SESSION_STATE_INITIAL)
45*c1d255d3SCy Schubert 		return true;
46325151a3SRui Paulo 
47325151a3SRui Paulo 	switch (ss->extra.to_initial.reason) {
48325151a3SRui Paulo 	case REASON_REJECT:
49325151a3SRui Paulo 		if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
50325151a3SRui Paulo 			os_snprintf(reject_str, sizeof(reject_str), "%u",
51325151a3SRui Paulo 				    ss->extra.to_initial.reject_code);
5285732ac8SCy Schubert 		/* fall through */
53325151a3SRui Paulo 	case REASON_TEARDOWN:
54325151a3SRui Paulo 	case REASON_SWITCH:
55325151a3SRui Paulo 		switch (ss->extra.to_initial.initiator) {
56325151a3SRui Paulo 		case FST_INITIATOR_LOCAL:
57325151a3SRui Paulo 			initiator = FST_CS_PVAL_INITIATOR_LOCAL;
58325151a3SRui Paulo 			break;
59325151a3SRui Paulo 		case FST_INITIATOR_REMOTE:
60325151a3SRui Paulo 			initiator = FST_CS_PVAL_INITIATOR_REMOTE;
61325151a3SRui Paulo 			break;
62325151a3SRui Paulo 		default:
63325151a3SRui Paulo 			break;
64325151a3SRui Paulo 		}
65325151a3SRui Paulo 		break;
66325151a3SRui Paulo 	default:
67325151a3SRui Paulo 		break;
68325151a3SRui Paulo 	}
69325151a3SRui Paulo 
70325151a3SRui Paulo 	len = os_snprintf(buffer, size,
71325151a3SRui Paulo 			  FST_CES_PNAME_REASON "=%s "
72325151a3SRui Paulo 			  FST_CES_PNAME_REJECT_CODE "=%s "
73325151a3SRui Paulo 			  FST_CES_PNAME_INITIATOR "=%s",
74325151a3SRui Paulo 			  fst_reason_name(ss->extra.to_initial.reason),
75325151a3SRui Paulo 			  reject_str, initiator);
76325151a3SRui Paulo 
77325151a3SRui Paulo 	return !os_snprintf_error(size, len);
78325151a3SRui Paulo }
79325151a3SRui Paulo 
80325151a3SRui Paulo 
fst_ctrl_iface_notify(struct fst_iface * f,u32 session_id,enum fst_event_type event_type,const union fst_event_extra * extra)81325151a3SRui Paulo static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
82325151a3SRui Paulo 				  enum fst_event_type event_type,
83325151a3SRui Paulo 				  const union fst_event_extra *extra)
84325151a3SRui Paulo {
85325151a3SRui Paulo 	struct fst_group *g;
86325151a3SRui Paulo 	char extra_str[128] = "";
87325151a3SRui Paulo 	const struct fst_event_extra_session_state *ss;
88325151a3SRui Paulo 	const struct fst_event_extra_iface_state *is;
89325151a3SRui Paulo 	const struct fst_event_extra_peer_state *ps;
90325151a3SRui Paulo 
91325151a3SRui Paulo 	/*
92325151a3SRui Paulo 	 * FST can use any of interface objects as it only sends messages
93325151a3SRui Paulo 	 * on global Control Interface, so we just pick the 1st one.
94325151a3SRui Paulo 	 */
95325151a3SRui Paulo 
96325151a3SRui Paulo 	if (!f) {
97325151a3SRui Paulo 		foreach_fst_group(g) {
98325151a3SRui Paulo 			f = fst_group_first_iface(g);
99325151a3SRui Paulo 			if (f)
100325151a3SRui Paulo 				break;
101325151a3SRui Paulo 		}
102325151a3SRui Paulo 		if (!f)
103325151a3SRui Paulo 			return;
104325151a3SRui Paulo 	}
105325151a3SRui Paulo 
106325151a3SRui Paulo 	WPA_ASSERT(f->iface_obj.ctx);
107325151a3SRui Paulo 
108325151a3SRui Paulo 	switch (event_type) {
109325151a3SRui Paulo 	case EVENT_FST_IFACE_STATE_CHANGED:
110325151a3SRui Paulo 		if (!extra)
111325151a3SRui Paulo 			return;
112325151a3SRui Paulo 		is = &extra->iface_state;
113325151a3SRui Paulo 		wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
114325151a3SRui Paulo 				    FST_CTRL_EVENT_IFACE " %s "
115325151a3SRui Paulo 				    FST_CEI_PNAME_IFNAME "=%s "
116325151a3SRui Paulo 				    FST_CEI_PNAME_GROUP "=%s",
117325151a3SRui Paulo 				    is->attached ? FST_CEI_PNAME_ATTACHED :
118325151a3SRui Paulo 				    FST_CEI_PNAME_DETACHED,
119325151a3SRui Paulo 				    is->ifname, is->group_id);
120325151a3SRui Paulo 		break;
121325151a3SRui Paulo 	case EVENT_PEER_STATE_CHANGED:
122325151a3SRui Paulo 		if (!extra)
123325151a3SRui Paulo 			return;
124325151a3SRui Paulo 		ps = &extra->peer_state;
125325151a3SRui Paulo 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
126325151a3SRui Paulo 				    FST_CTRL_EVENT_PEER " %s "
127325151a3SRui Paulo 				    FST_CEP_PNAME_IFNAME "=%s "
128325151a3SRui Paulo 				    FST_CEP_PNAME_ADDR "=" MACSTR,
129325151a3SRui Paulo 				    ps->connected ? FST_CEP_PNAME_CONNECTED :
130325151a3SRui Paulo 				    FST_CEP_PNAME_DISCONNECTED,
131325151a3SRui Paulo 				    ps->ifname, MAC2STR(ps->addr));
132325151a3SRui Paulo 		break;
133325151a3SRui Paulo 	case EVENT_FST_SESSION_STATE_CHANGED:
134325151a3SRui Paulo 		if (!extra)
135325151a3SRui Paulo 			return;
136325151a3SRui Paulo 		if (!format_session_state_extra(extra, extra_str,
137325151a3SRui Paulo 						sizeof(extra_str))) {
138325151a3SRui Paulo 			fst_printf(MSG_ERROR,
139325151a3SRui Paulo 				   "CTRL: Cannot format STATE_CHANGE extra");
140325151a3SRui Paulo 			extra_str[0] = 0;
141325151a3SRui Paulo 		}
142325151a3SRui Paulo 		ss = &extra->session_state;
143325151a3SRui Paulo 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
144325151a3SRui Paulo 				    FST_CTRL_EVENT_SESSION " "
145325151a3SRui Paulo 				    FST_CES_PNAME_SESSION_ID "=%u "
146325151a3SRui Paulo 				    FST_CES_PNAME_EVT_TYPE "=%s "
147325151a3SRui Paulo 				    FST_CES_PNAME_OLD_STATE "=%s "
148325151a3SRui Paulo 				    FST_CES_PNAME_NEW_STATE "=%s %s",
149325151a3SRui Paulo 				    session_id,
150325151a3SRui Paulo 				    fst_session_event_type_name(event_type),
151325151a3SRui Paulo 				    fst_session_state_name(ss->old_state),
152325151a3SRui Paulo 				    fst_session_state_name(ss->new_state),
153325151a3SRui Paulo 				    extra_str);
154325151a3SRui Paulo 		break;
155325151a3SRui Paulo 	case EVENT_FST_ESTABLISHED:
156325151a3SRui Paulo 	case EVENT_FST_SETUP:
157325151a3SRui Paulo 		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
158325151a3SRui Paulo 				    FST_CTRL_EVENT_SESSION " "
159325151a3SRui Paulo 				    FST_CES_PNAME_SESSION_ID "=%u "
160325151a3SRui Paulo 				    FST_CES_PNAME_EVT_TYPE "=%s",
161325151a3SRui Paulo 				    session_id,
162325151a3SRui Paulo 				    fst_session_event_type_name(event_type));
163325151a3SRui Paulo 		break;
164325151a3SRui Paulo 	}
165325151a3SRui Paulo }
166325151a3SRui Paulo 
167325151a3SRui Paulo 
168325151a3SRui Paulo /* command processors */
169325151a3SRui Paulo 
170325151a3SRui Paulo /* fst session_get */
session_get(const char * session_id,char * buf,size_t buflen)171325151a3SRui Paulo static int session_get(const char *session_id, char *buf, size_t buflen)
172325151a3SRui Paulo {
173325151a3SRui Paulo 	struct fst_session *s;
174325151a3SRui Paulo 	struct fst_iface *new_iface, *old_iface;
175325151a3SRui Paulo 	const u8 *old_peer_addr, *new_peer_addr;
176325151a3SRui Paulo 	u32 id;
177325151a3SRui Paulo 
178325151a3SRui Paulo 	id = strtoul(session_id, NULL, 0);
179325151a3SRui Paulo 
180325151a3SRui Paulo 	s = fst_session_get_by_id(id);
181325151a3SRui Paulo 	if (!s) {
182325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
183325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
184325151a3SRui Paulo 	}
185325151a3SRui Paulo 
186*c1d255d3SCy Schubert 	old_peer_addr = fst_session_get_peer_addr(s, true);
187*c1d255d3SCy Schubert 	new_peer_addr = fst_session_get_peer_addr(s, false);
188*c1d255d3SCy Schubert 	new_iface = fst_session_get_iface(s, false);
189*c1d255d3SCy Schubert 	old_iface = fst_session_get_iface(s, true);
190325151a3SRui Paulo 
191325151a3SRui Paulo 	return os_snprintf(buf, buflen,
192325151a3SRui Paulo 			   FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
193325151a3SRui Paulo 			   FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
194325151a3SRui Paulo 			   FST_CSG_PNAME_NEW_IFNAME "=%s\n"
195325151a3SRui Paulo 			   FST_CSG_PNAME_OLD_IFNAME "=%s\n"
196325151a3SRui Paulo 			   FST_CSG_PNAME_LLT "=%u\n"
197325151a3SRui Paulo 			   FST_CSG_PNAME_STATE "=%s\n",
198325151a3SRui Paulo 			   MAC2STR(old_peer_addr),
199325151a3SRui Paulo 			   MAC2STR(new_peer_addr),
200325151a3SRui Paulo 			   new_iface ? fst_iface_get_name(new_iface) :
201325151a3SRui Paulo 			   FST_CTRL_PVAL_NONE,
202325151a3SRui Paulo 			   old_iface ? fst_iface_get_name(old_iface) :
203325151a3SRui Paulo 			   FST_CTRL_PVAL_NONE,
204325151a3SRui Paulo 			   fst_session_get_llt(s),
205325151a3SRui Paulo 			   fst_session_state_name(fst_session_get_state(s)));
206325151a3SRui Paulo }
207325151a3SRui Paulo 
208325151a3SRui Paulo 
209325151a3SRui Paulo /* fst session_set */
session_set(const char * session_id,char * buf,size_t buflen)210325151a3SRui Paulo static int session_set(const char *session_id, char *buf, size_t buflen)
211325151a3SRui Paulo {
212325151a3SRui Paulo 	struct fst_session *s;
213325151a3SRui Paulo 	char *p, *q;
214325151a3SRui Paulo 	u32 id;
215325151a3SRui Paulo 	int ret;
216325151a3SRui Paulo 
217325151a3SRui Paulo 	id = strtoul(session_id, &p, 0);
218325151a3SRui Paulo 
219325151a3SRui Paulo 	s = fst_session_get_by_id(id);
220325151a3SRui Paulo 	if (!s) {
221325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
222325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
223325151a3SRui Paulo 	}
224325151a3SRui Paulo 
225325151a3SRui Paulo 	if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
226325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
227325151a3SRui Paulo 	p++;
228325151a3SRui Paulo 
229325151a3SRui Paulo 	if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
230*c1d255d3SCy Schubert 		ret = fst_session_set_str_ifname(s, q + 1, true);
231325151a3SRui Paulo 	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
232*c1d255d3SCy Schubert 		ret = fst_session_set_str_ifname(s, q + 1, false);
233325151a3SRui Paulo 	} else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
234*c1d255d3SCy Schubert 		ret = fst_session_set_str_peer_addr(s, q + 1, true);
235325151a3SRui Paulo 	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
236*c1d255d3SCy Schubert 		ret = fst_session_set_str_peer_addr(s, q + 1, false);
237325151a3SRui Paulo 	} else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
238325151a3SRui Paulo 		ret = fst_session_set_str_llt(s, q + 1);
239325151a3SRui Paulo 	} else {
240325151a3SRui Paulo 		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
241325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
242325151a3SRui Paulo 	}
243325151a3SRui Paulo 
244325151a3SRui Paulo 	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
245325151a3SRui Paulo }
246325151a3SRui Paulo 
247325151a3SRui Paulo 
248325151a3SRui Paulo /* fst session_add/remove */
session_add(const char * group_id,char * buf,size_t buflen)249325151a3SRui Paulo static int session_add(const char *group_id, char *buf, size_t buflen)
250325151a3SRui Paulo {
251325151a3SRui Paulo 	struct fst_group *g;
252325151a3SRui Paulo 	struct fst_session *s;
253325151a3SRui Paulo 
254325151a3SRui Paulo 	g = get_fst_group_by_id(group_id);
255325151a3SRui Paulo 	if (!g) {
256325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
257325151a3SRui Paulo 			   group_id);
258325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
259325151a3SRui Paulo 	}
260325151a3SRui Paulo 
261325151a3SRui Paulo 	s = fst_session_create(g);
262325151a3SRui Paulo 	if (!s) {
263325151a3SRui Paulo 		fst_printf(MSG_ERROR,
264325151a3SRui Paulo 			   "CTRL: Cannot create session for group '%s'",
265325151a3SRui Paulo 			   group_id);
266325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
267325151a3SRui Paulo 	}
268325151a3SRui Paulo 
269325151a3SRui Paulo 	return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
270325151a3SRui Paulo }
271325151a3SRui Paulo 
272325151a3SRui Paulo 
session_remove(const char * session_id,char * buf,size_t buflen)273325151a3SRui Paulo static int session_remove(const char *session_id, char *buf, size_t buflen)
274325151a3SRui Paulo {
275325151a3SRui Paulo 	struct fst_session *s;
276325151a3SRui Paulo 	struct fst_group *g;
277325151a3SRui Paulo 	u32 id;
278325151a3SRui Paulo 
279325151a3SRui Paulo 	id = strtoul(session_id, NULL, 0);
280325151a3SRui Paulo 
281325151a3SRui Paulo 	s = fst_session_get_by_id(id);
282325151a3SRui Paulo 	if (!s) {
283325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
284325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
285325151a3SRui Paulo 	}
286325151a3SRui Paulo 
287325151a3SRui Paulo 	g = fst_session_get_group(s);
288325151a3SRui Paulo 	fst_session_reset(s);
289325151a3SRui Paulo 	fst_session_delete(s);
290325151a3SRui Paulo 	fst_group_delete_if_empty(g);
291325151a3SRui Paulo 
292325151a3SRui Paulo 	return os_snprintf(buf, buflen, "OK\n");
293325151a3SRui Paulo }
294325151a3SRui Paulo 
295325151a3SRui Paulo 
296325151a3SRui Paulo /* fst session_initiate */
session_initiate(const char * session_id,char * buf,size_t buflen)297325151a3SRui Paulo static int session_initiate(const char *session_id, char *buf, size_t buflen)
298325151a3SRui Paulo {
299325151a3SRui Paulo 	struct fst_session *s;
300325151a3SRui Paulo 	u32 id;
301325151a3SRui Paulo 
302325151a3SRui Paulo 	id = strtoul(session_id, NULL, 0);
303325151a3SRui Paulo 
304325151a3SRui Paulo 	s = fst_session_get_by_id(id);
305325151a3SRui Paulo 	if (!s) {
306325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
307325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
308325151a3SRui Paulo 	}
309325151a3SRui Paulo 
310325151a3SRui Paulo 	if (fst_session_initiate_setup(s)) {
311325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
312325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
313325151a3SRui Paulo 	}
314325151a3SRui Paulo 
315325151a3SRui Paulo 	return os_snprintf(buf, buflen, "OK\n");
316325151a3SRui Paulo }
317325151a3SRui Paulo 
318325151a3SRui Paulo 
319325151a3SRui Paulo /* fst session_respond */
session_respond(const char * session_id,char * buf,size_t buflen)320325151a3SRui Paulo static int session_respond(const char *session_id, char *buf, size_t buflen)
321325151a3SRui Paulo {
322325151a3SRui Paulo 	struct fst_session *s;
323325151a3SRui Paulo 	char *p;
324325151a3SRui Paulo 	u32 id;
325325151a3SRui Paulo 	u8 status_code;
326325151a3SRui Paulo 
327325151a3SRui Paulo 	id = strtoul(session_id, &p, 0);
328325151a3SRui Paulo 
329325151a3SRui Paulo 	s = fst_session_get_by_id(id);
330325151a3SRui Paulo 	if (!s) {
331325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
332325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
333325151a3SRui Paulo 	}
334325151a3SRui Paulo 
335325151a3SRui Paulo 	if (*p != ' ')
336325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
337325151a3SRui Paulo 	p++;
338325151a3SRui Paulo 
339325151a3SRui Paulo 	if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
340325151a3SRui Paulo 		status_code = WLAN_STATUS_SUCCESS;
341325151a3SRui Paulo 	} else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
342325151a3SRui Paulo 		status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
343325151a3SRui Paulo 	} else {
344325151a3SRui Paulo 		fst_printf(MSG_WARNING,
345325151a3SRui Paulo 			   "CTRL: session %u: unknown response status: %s",
346325151a3SRui Paulo 			   id, p);
347325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
348325151a3SRui Paulo 	}
349325151a3SRui Paulo 
350325151a3SRui Paulo 	if (fst_session_respond(s, status_code)) {
351325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
352325151a3SRui Paulo 			   id);
353325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
354325151a3SRui Paulo 	}
355325151a3SRui Paulo 
356325151a3SRui Paulo 	fst_printf(MSG_INFO, "CTRL: session %u responded", id);
357325151a3SRui Paulo 
358325151a3SRui Paulo 	return os_snprintf(buf, buflen, "OK\n");
359325151a3SRui Paulo }
360325151a3SRui Paulo 
361325151a3SRui Paulo 
362325151a3SRui Paulo /* fst session_transfer */
session_transfer(const char * session_id,char * buf,size_t buflen)363325151a3SRui Paulo static int session_transfer(const char *session_id, char *buf, size_t buflen)
364325151a3SRui Paulo {
365325151a3SRui Paulo 	struct fst_session *s;
366325151a3SRui Paulo 	u32 id;
367325151a3SRui Paulo 
368325151a3SRui Paulo 	id = strtoul(session_id, NULL, 0);
369325151a3SRui Paulo 
370325151a3SRui Paulo 	s = fst_session_get_by_id(id);
371325151a3SRui Paulo 	if (!s) {
372325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
373325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
374325151a3SRui Paulo 	}
375325151a3SRui Paulo 
376325151a3SRui Paulo 	if (fst_session_initiate_switch(s)) {
377325151a3SRui Paulo 		fst_printf(MSG_WARNING,
378325151a3SRui Paulo 			   "CTRL: Cannot initiate ST for session %u", id);
379325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
380325151a3SRui Paulo 	}
381325151a3SRui Paulo 
382325151a3SRui Paulo 	return os_snprintf(buf, buflen, "OK\n");
383325151a3SRui Paulo }
384325151a3SRui Paulo 
385325151a3SRui Paulo 
386325151a3SRui Paulo /* fst session_teardown */
session_teardown(const char * session_id,char * buf,size_t buflen)387325151a3SRui Paulo static int session_teardown(const char *session_id, char *buf, size_t buflen)
388325151a3SRui Paulo {
389325151a3SRui Paulo 	struct fst_session *s;
390325151a3SRui Paulo 	u32 id;
391325151a3SRui Paulo 
392325151a3SRui Paulo 	id = strtoul(session_id, NULL, 0);
393325151a3SRui Paulo 
394325151a3SRui Paulo 	s = fst_session_get_by_id(id);
395325151a3SRui Paulo 	if (!s) {
396325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
397325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
398325151a3SRui Paulo 	}
399325151a3SRui Paulo 
400325151a3SRui Paulo 	if (fst_session_tear_down_setup(s)) {
401325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
402325151a3SRui Paulo 			   id);
403325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
404325151a3SRui Paulo 	}
405325151a3SRui Paulo 
406325151a3SRui Paulo 	return os_snprintf(buf, buflen, "OK\n");
407325151a3SRui Paulo }
408325151a3SRui Paulo 
409325151a3SRui Paulo 
410325151a3SRui Paulo #ifdef CONFIG_FST_TEST
411325151a3SRui Paulo /* fst test_request */
test_request(const char * request,char * buf,size_t buflen)412325151a3SRui Paulo static int test_request(const char *request, char *buf, size_t buflen)
413325151a3SRui Paulo {
414325151a3SRui Paulo 	const char *p = request;
415325151a3SRui Paulo 	int ret;
416325151a3SRui Paulo 
417325151a3SRui Paulo 	if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
418325151a3SRui Paulo 			    os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
419325151a3SRui Paulo 		ret = fst_test_req_send_fst_request(
420325151a3SRui Paulo 			p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
421325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
422325151a3SRui Paulo 				   os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
423325151a3SRui Paulo 		ret = fst_test_req_send_fst_response(
424325151a3SRui Paulo 			p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
425325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
426325151a3SRui Paulo 				   os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
427325151a3SRui Paulo 		ret = fst_test_req_send_ack_request(
428325151a3SRui Paulo 			p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
429325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
430325151a3SRui Paulo 				   os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
431325151a3SRui Paulo 		ret = fst_test_req_send_ack_response(
432325151a3SRui Paulo 			p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
433325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
434325151a3SRui Paulo 				   os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
435325151a3SRui Paulo 		ret = fst_test_req_send_tear_down(
436325151a3SRui Paulo 			p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
437325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
438325151a3SRui Paulo 				   os_strlen(FST_CTR_GET_FSTS_ID))) {
439325151a3SRui Paulo 		u32 fsts_id = fst_test_req_get_fsts_id(
440325151a3SRui Paulo 			p + os_strlen(FST_CTR_GET_FSTS_ID));
441325151a3SRui Paulo 		if (fsts_id != FST_FSTS_ID_NOT_FOUND)
442325151a3SRui Paulo 			return os_snprintf(buf, buflen, "%u\n", fsts_id);
443325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
444325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
445325151a3SRui Paulo 				   os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
446325151a3SRui Paulo 		return fst_test_req_get_local_mbies(
447325151a3SRui Paulo 			p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
448325151a3SRui Paulo 	} else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
449325151a3SRui Paulo 				   os_strlen(FST_CTR_IS_SUPPORTED))) {
450325151a3SRui Paulo 		ret = 0;
451325151a3SRui Paulo 	} else {
452325151a3SRui Paulo 		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
453325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
454325151a3SRui Paulo 	}
455325151a3SRui Paulo 
456325151a3SRui Paulo 	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
457325151a3SRui Paulo }
458325151a3SRui Paulo #endif /* CONFIG_FST_TEST */
459325151a3SRui Paulo 
460325151a3SRui Paulo 
461325151a3SRui Paulo /* fst list_sessions */
462325151a3SRui Paulo struct list_sessions_cb_ctx {
463325151a3SRui Paulo 	char *buf;
464325151a3SRui Paulo 	size_t buflen;
465325151a3SRui Paulo 	size_t reply_len;
466325151a3SRui Paulo };
467325151a3SRui Paulo 
468325151a3SRui Paulo 
list_session_enum_cb(struct fst_group * g,struct fst_session * s,void * ctx)469325151a3SRui Paulo static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
470325151a3SRui Paulo 				 void *ctx)
471325151a3SRui Paulo {
472325151a3SRui Paulo 	struct list_sessions_cb_ctx *c = ctx;
473325151a3SRui Paulo 	int ret;
474325151a3SRui Paulo 
475325151a3SRui Paulo 	ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
476325151a3SRui Paulo 
477325151a3SRui Paulo 	c->buf += ret;
478325151a3SRui Paulo 	c->buflen -= ret;
479325151a3SRui Paulo 	c->reply_len += ret;
480325151a3SRui Paulo }
481325151a3SRui Paulo 
482325151a3SRui Paulo 
list_sessions(const char * group_id,char * buf,size_t buflen)483325151a3SRui Paulo static int list_sessions(const char *group_id, char *buf, size_t buflen)
484325151a3SRui Paulo {
485325151a3SRui Paulo 	struct list_sessions_cb_ctx ctx;
486325151a3SRui Paulo 	struct fst_group *g;
487325151a3SRui Paulo 
488325151a3SRui Paulo 	g = get_fst_group_by_id(group_id);
489325151a3SRui Paulo 	if (!g) {
490325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
491325151a3SRui Paulo 			   group_id);
492325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
493325151a3SRui Paulo 	}
494325151a3SRui Paulo 
495325151a3SRui Paulo 	ctx.buf = buf;
496325151a3SRui Paulo 	ctx.buflen = buflen;
497325151a3SRui Paulo 	ctx.reply_len = 0;
498325151a3SRui Paulo 
499325151a3SRui Paulo 	fst_session_enum(g, list_session_enum_cb, &ctx);
500325151a3SRui Paulo 
501325151a3SRui Paulo 	ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
502325151a3SRui Paulo 
503325151a3SRui Paulo 	return ctx.reply_len;
504325151a3SRui Paulo }
505325151a3SRui Paulo 
506325151a3SRui Paulo 
507325151a3SRui Paulo /* fst iface_peers */
iface_peers(const char * group_id,char * buf,size_t buflen)508325151a3SRui Paulo static int iface_peers(const char *group_id, char *buf, size_t buflen)
509325151a3SRui Paulo {
510325151a3SRui Paulo 	const char *ifname;
511325151a3SRui Paulo 	struct fst_group *g;
512325151a3SRui Paulo 	struct fst_iface *f;
513325151a3SRui Paulo 	struct fst_get_peer_ctx *ctx;
514325151a3SRui Paulo 	const u8 *addr;
515325151a3SRui Paulo 	unsigned found = 0;
516325151a3SRui Paulo 	int ret = 0;
517325151a3SRui Paulo 
518325151a3SRui Paulo 	g = get_fst_group_by_id(group_id);
519325151a3SRui Paulo 	if (!g) {
520325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
521325151a3SRui Paulo 			   group_id);
522325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
523325151a3SRui Paulo 	}
524325151a3SRui Paulo 
525325151a3SRui Paulo 	ifname = os_strchr(group_id, ' ');
526325151a3SRui Paulo 	if (!ifname)
527325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
528325151a3SRui Paulo 	ifname++;
529325151a3SRui Paulo 
530325151a3SRui Paulo 	foreach_fst_group_iface(g, f) {
531325151a3SRui Paulo 		const char *in = fst_iface_get_name(f);
532325151a3SRui Paulo 
533325151a3SRui Paulo 		if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
534325151a3SRui Paulo 			found = 1;
535325151a3SRui Paulo 			break;
536325151a3SRui Paulo 		}
537325151a3SRui Paulo 	}
538325151a3SRui Paulo 
539325151a3SRui Paulo 	if (!found)
540325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
541325151a3SRui Paulo 
542*c1d255d3SCy Schubert 	addr = fst_iface_get_peer_first(f, &ctx, false);
543*c1d255d3SCy Schubert 	for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, false)) {
544325151a3SRui Paulo 		int res;
545325151a3SRui Paulo 
546325151a3SRui Paulo 		res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
547325151a3SRui Paulo 				  MAC2STR(addr));
548325151a3SRui Paulo 		if (os_snprintf_error(buflen - ret, res))
549325151a3SRui Paulo 			break;
550325151a3SRui Paulo 		ret += res;
551325151a3SRui Paulo 	}
552325151a3SRui Paulo 
553325151a3SRui Paulo 	return ret;
554325151a3SRui Paulo }
555325151a3SRui Paulo 
556325151a3SRui Paulo 
get_peer_mbies(const char * params,char * buf,size_t buflen)557325151a3SRui Paulo static int get_peer_mbies(const char *params, char *buf, size_t buflen)
558325151a3SRui Paulo {
559325151a3SRui Paulo 	char *endp;
560325151a3SRui Paulo 	char ifname[FST_MAX_INTERFACE_SIZE];
561325151a3SRui Paulo 	u8 peer_addr[ETH_ALEN];
562325151a3SRui Paulo 	struct fst_group *g;
563325151a3SRui Paulo 	struct fst_iface *iface = NULL;
564325151a3SRui Paulo 	const struct wpabuf *mbies;
565325151a3SRui Paulo 
566325151a3SRui Paulo 	if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
567325151a3SRui Paulo 	    !*ifname)
568325151a3SRui Paulo 		goto problem;
569325151a3SRui Paulo 
570325151a3SRui Paulo 	while (isspace(*endp))
571325151a3SRui Paulo 		endp++;
572325151a3SRui Paulo 	if (fst_read_peer_addr(endp, peer_addr))
573325151a3SRui Paulo 		goto problem;
574325151a3SRui Paulo 
575325151a3SRui Paulo 	foreach_fst_group(g) {
576325151a3SRui Paulo 		iface = fst_group_get_iface_by_name(g, ifname);
577325151a3SRui Paulo 		if (iface)
578325151a3SRui Paulo 			break;
579325151a3SRui Paulo 	}
580325151a3SRui Paulo 	if (!iface)
581325151a3SRui Paulo 		goto problem;
582325151a3SRui Paulo 
583325151a3SRui Paulo 	mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
584325151a3SRui Paulo 	if (!mbies)
585325151a3SRui Paulo 		goto problem;
586325151a3SRui Paulo 
587325151a3SRui Paulo 	return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
588325151a3SRui Paulo 				wpabuf_len(mbies));
589325151a3SRui Paulo 
590325151a3SRui Paulo problem:
591325151a3SRui Paulo 	return os_snprintf(buf, buflen, "FAIL\n");
592325151a3SRui Paulo }
593325151a3SRui Paulo 
594325151a3SRui Paulo 
595325151a3SRui Paulo /* fst list_ifaces */
list_ifaces(const char * group_id,char * buf,size_t buflen)596325151a3SRui Paulo static int list_ifaces(const char *group_id, char *buf, size_t buflen)
597325151a3SRui Paulo {
598325151a3SRui Paulo 	struct fst_group *g;
599325151a3SRui Paulo 	struct fst_iface *f;
600325151a3SRui Paulo 	int ret = 0;
601325151a3SRui Paulo 
602325151a3SRui Paulo 	g = get_fst_group_by_id(group_id);
603325151a3SRui Paulo 	if (!g) {
604325151a3SRui Paulo 		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
605325151a3SRui Paulo 			   group_id);
606325151a3SRui Paulo 		return os_snprintf(buf, buflen, "FAIL\n");
607325151a3SRui Paulo 	}
608325151a3SRui Paulo 
609325151a3SRui Paulo 	foreach_fst_group_iface(g, f) {
610325151a3SRui Paulo 		int res;
611325151a3SRui Paulo 		const u8 *iface_addr = fst_iface_get_addr(f);
612325151a3SRui Paulo 
613325151a3SRui Paulo 		res = os_snprintf(buf + ret, buflen - ret,
614325151a3SRui Paulo 				  "%s|" MACSTR "|%u|%u\n",
615325151a3SRui Paulo 				  fst_iface_get_name(f),
616325151a3SRui Paulo 				  MAC2STR(iface_addr),
617325151a3SRui Paulo 				  fst_iface_get_priority(f),
618325151a3SRui Paulo 				  fst_iface_get_llt(f));
619325151a3SRui Paulo 		if (os_snprintf_error(buflen - ret, res))
620325151a3SRui Paulo 			break;
621325151a3SRui Paulo 		ret += res;
622325151a3SRui Paulo 	}
623325151a3SRui Paulo 
624325151a3SRui Paulo 	return ret;
625325151a3SRui Paulo }
626325151a3SRui Paulo 
627325151a3SRui Paulo 
628325151a3SRui Paulo /* fst list_groups */
list_groups(const char * cmd,char * buf,size_t buflen)629325151a3SRui Paulo static int list_groups(const char *cmd, char *buf, size_t buflen)
630325151a3SRui Paulo {
631325151a3SRui Paulo 	struct fst_group *g;
632325151a3SRui Paulo 	int ret = 0;
633325151a3SRui Paulo 
634325151a3SRui Paulo 	foreach_fst_group(g) {
635325151a3SRui Paulo 		int res;
636325151a3SRui Paulo 
637325151a3SRui Paulo 		res = os_snprintf(buf + ret, buflen - ret, "%s\n",
638325151a3SRui Paulo 				  fst_group_get_id(g));
639325151a3SRui Paulo 		if (os_snprintf_error(buflen - ret, res))
640325151a3SRui Paulo 			break;
641325151a3SRui Paulo 		ret += res;
642325151a3SRui Paulo 	}
643325151a3SRui Paulo 
644325151a3SRui Paulo 	return ret;
645325151a3SRui Paulo }
646325151a3SRui Paulo 
647325151a3SRui Paulo 
band_freq(enum mb_band_id band)648325151a3SRui Paulo static const char * band_freq(enum mb_band_id band)
649325151a3SRui Paulo {
650325151a3SRui Paulo 	static const char *band_names[] = {
651780fb4a2SCy Schubert 		[MB_BAND_ID_WIFI_2_4GHZ] = "2.4GHZ",
652780fb4a2SCy Schubert 		[MB_BAND_ID_WIFI_5GHZ] = "5GHZ",
653780fb4a2SCy Schubert 		[MB_BAND_ID_WIFI_60GHZ] = "60GHZ",
654325151a3SRui Paulo 	};
655325151a3SRui Paulo 
656325151a3SRui Paulo 	return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
657325151a3SRui Paulo }
658325151a3SRui Paulo 
659325151a3SRui Paulo 
print_band(unsigned num,struct fst_iface * iface,const u8 * addr,char * buf,size_t buflen)660325151a3SRui Paulo static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
661325151a3SRui Paulo 		      char *buf, size_t buflen)
662325151a3SRui Paulo {
663325151a3SRui Paulo 	const struct wpabuf *wpabuf;
664325151a3SRui Paulo 	enum hostapd_hw_mode hw_mode;
665325151a3SRui Paulo 	u8 channel;
666325151a3SRui Paulo 	int ret = 0;
667325151a3SRui Paulo 
668325151a3SRui Paulo 	fst_iface_get_channel_info(iface, &hw_mode, &channel);
669325151a3SRui Paulo 
670325151a3SRui Paulo 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
671325151a3SRui Paulo 			   num, band_freq(fst_hw_mode_to_band(hw_mode)));
672325151a3SRui Paulo 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
673325151a3SRui Paulo 			   num, fst_iface_get_name(iface));
674325151a3SRui Paulo 	wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
675325151a3SRui Paulo 	if (wpabuf) {
676325151a3SRui Paulo 		ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
677325151a3SRui Paulo 				   num);
678325151a3SRui Paulo 		ret += wpa_snprintf_hex(buf + ret, buflen - ret,
679325151a3SRui Paulo 					wpabuf_head(wpabuf),
680325151a3SRui Paulo 					wpabuf_len(wpabuf));
681325151a3SRui Paulo 		ret += os_snprintf(buf + ret, buflen - ret, "\n");
682325151a3SRui Paulo 	}
683325151a3SRui Paulo 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
684325151a3SRui Paulo 			   num, fst_iface_get_group_id(iface));
685325151a3SRui Paulo 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
686325151a3SRui Paulo 			   num, fst_iface_get_priority(iface));
687325151a3SRui Paulo 	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
688325151a3SRui Paulo 			   num, fst_iface_get_llt(iface));
689325151a3SRui Paulo 
690325151a3SRui Paulo 	return ret;
691325151a3SRui Paulo }
692325151a3SRui Paulo 
693325151a3SRui Paulo 
fst_ctrl_iface_on_iface_state_changed(struct fst_iface * i,bool attached)694325151a3SRui Paulo static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
695*c1d255d3SCy Schubert 						  bool attached)
696325151a3SRui Paulo {
697325151a3SRui Paulo 	union fst_event_extra extra;
698325151a3SRui Paulo 
699325151a3SRui Paulo 	os_memset(&extra, 0, sizeof(extra));
700325151a3SRui Paulo 	extra.iface_state.attached = attached;
701325151a3SRui Paulo 	os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
702325151a3SRui Paulo 		   sizeof(extra.iface_state.ifname));
703325151a3SRui Paulo 	os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
704325151a3SRui Paulo 		   sizeof(extra.iface_state.group_id));
705325151a3SRui Paulo 
706325151a3SRui Paulo 	fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
707325151a3SRui Paulo 			      EVENT_FST_IFACE_STATE_CHANGED, &extra);
708325151a3SRui Paulo }
709325151a3SRui Paulo 
710325151a3SRui Paulo 
fst_ctrl_iface_on_iface_added(struct fst_iface * i)711325151a3SRui Paulo static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
712325151a3SRui Paulo {
713*c1d255d3SCy Schubert 	fst_ctrl_iface_on_iface_state_changed(i, true);
714325151a3SRui Paulo 	return 0;
715325151a3SRui Paulo }
716325151a3SRui Paulo 
717325151a3SRui Paulo 
fst_ctrl_iface_on_iface_removed(struct fst_iface * i)718325151a3SRui Paulo static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
719325151a3SRui Paulo {
720*c1d255d3SCy Schubert 	fst_ctrl_iface_on_iface_state_changed(i, false);
721325151a3SRui Paulo }
722325151a3SRui Paulo 
723325151a3SRui Paulo 
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)724325151a3SRui Paulo static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
725325151a3SRui Paulo 				    struct fst_iface *i, struct fst_session *s,
726325151a3SRui Paulo 				    const union fst_event_extra *extra)
727325151a3SRui Paulo {
728325151a3SRui Paulo 	u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
729325151a3SRui Paulo 
730325151a3SRui Paulo 	fst_ctrl_iface_notify(i, session_id, event_type, extra);
731325151a3SRui Paulo }
732325151a3SRui Paulo 
733325151a3SRui Paulo 
734325151a3SRui Paulo static const struct fst_ctrl ctrl_cli = {
735325151a3SRui Paulo 	.on_iface_added = fst_ctrl_iface_on_iface_added,
736325151a3SRui Paulo 	.on_iface_removed =  fst_ctrl_iface_on_iface_removed,
737325151a3SRui Paulo 	.on_event = fst_ctrl_iface_on_event,
738325151a3SRui Paulo };
739325151a3SRui Paulo 
740325151a3SRui Paulo const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
741325151a3SRui Paulo 
742325151a3SRui Paulo 
fst_ctrl_iface_mb_info(const u8 * addr,char * buf,size_t buflen)743325151a3SRui Paulo int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
744325151a3SRui Paulo {
745325151a3SRui Paulo 	struct fst_group *g;
746325151a3SRui Paulo 	struct fst_iface *f;
747325151a3SRui Paulo 	unsigned num = 0;
748325151a3SRui Paulo 	int ret = 0;
749325151a3SRui Paulo 
750325151a3SRui Paulo 	foreach_fst_group(g) {
751325151a3SRui Paulo 		foreach_fst_group_iface(g, f) {
752*c1d255d3SCy Schubert 			if (fst_iface_is_connected(f, addr, true)) {
753325151a3SRui Paulo 				ret += print_band(num++, f, addr,
754325151a3SRui Paulo 						  buf + ret, buflen - ret);
755325151a3SRui Paulo 			}
756325151a3SRui Paulo 		}
757325151a3SRui Paulo 	}
758325151a3SRui Paulo 
759325151a3SRui Paulo 	return ret;
760325151a3SRui Paulo }
761325151a3SRui Paulo 
762325151a3SRui Paulo 
763325151a3SRui Paulo /* fst ctrl processor */
fst_ctrl_iface_receive(const char * cmd,char * reply,size_t reply_size)764325151a3SRui Paulo int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
765325151a3SRui Paulo {
766325151a3SRui Paulo 	static const struct fst_command {
767325151a3SRui Paulo 		const char *name;
768325151a3SRui Paulo 		unsigned has_param;
769325151a3SRui Paulo 		int (*process)(const char *group_id, char *buf, size_t buflen);
770325151a3SRui Paulo 	} commands[] = {
771325151a3SRui Paulo 		{ FST_CMD_LIST_GROUPS, 0, list_groups},
772325151a3SRui Paulo 		{ FST_CMD_LIST_IFACES, 1, list_ifaces},
773325151a3SRui Paulo 		{ FST_CMD_IFACE_PEERS, 1, iface_peers},
774325151a3SRui Paulo 		{ FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
775325151a3SRui Paulo 		{ FST_CMD_LIST_SESSIONS, 1, list_sessions},
776325151a3SRui Paulo 		{ FST_CMD_SESSION_ADD, 1, session_add},
777325151a3SRui Paulo 		{ FST_CMD_SESSION_REMOVE, 1, session_remove},
778325151a3SRui Paulo 		{ FST_CMD_SESSION_GET, 1, session_get},
779325151a3SRui Paulo 		{ FST_CMD_SESSION_SET, 1, session_set},
780325151a3SRui Paulo 		{ FST_CMD_SESSION_INITIATE, 1, session_initiate},
781325151a3SRui Paulo 		{ FST_CMD_SESSION_RESPOND, 1, session_respond},
782325151a3SRui Paulo 		{ FST_CMD_SESSION_TRANSFER, 1, session_transfer},
783325151a3SRui Paulo 		{ FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
784325151a3SRui Paulo #ifdef CONFIG_FST_TEST
785325151a3SRui Paulo 		{ FST_CMD_TEST_REQUEST, 1, test_request },
786325151a3SRui Paulo #endif /* CONFIG_FST_TEST */
787325151a3SRui Paulo 		{ NULL, 0, NULL }
788325151a3SRui Paulo 	};
789325151a3SRui Paulo 	const struct fst_command *c;
790325151a3SRui Paulo 	const char *p;
791325151a3SRui Paulo 	const char *temp;
792*c1d255d3SCy Schubert 	bool non_spaces_found;
793325151a3SRui Paulo 
794325151a3SRui Paulo 	for (c = commands; c->name; c++) {
795325151a3SRui Paulo 		if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
796325151a3SRui Paulo 			continue;
797325151a3SRui Paulo 		p = cmd + os_strlen(c->name);
798325151a3SRui Paulo 		if (c->has_param) {
799325151a3SRui Paulo 			if (!isspace(p[0]))
800325151a3SRui Paulo 				return os_snprintf(reply, reply_size, "FAIL\n");
801325151a3SRui Paulo 			p++;
802325151a3SRui Paulo 			temp = p;
803*c1d255d3SCy Schubert 			non_spaces_found = false;
804325151a3SRui Paulo 			while (*temp) {
805325151a3SRui Paulo 				if (!isspace(*temp)) {
806*c1d255d3SCy Schubert 					non_spaces_found = true;
807325151a3SRui Paulo 					break;
808325151a3SRui Paulo 				}
809325151a3SRui Paulo 				temp++;
810325151a3SRui Paulo 			}
811325151a3SRui Paulo 			if (!non_spaces_found)
812325151a3SRui Paulo 				return os_snprintf(reply, reply_size, "FAIL\n");
813325151a3SRui Paulo 		}
814325151a3SRui Paulo 		return c->process(p, reply, reply_size);
815325151a3SRui Paulo 	}
816325151a3SRui Paulo 
817325151a3SRui Paulo 	return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
818325151a3SRui Paulo }
819325151a3SRui Paulo 
820325151a3SRui Paulo 
fst_read_next_int_param(const char * params,bool * valid,char ** endp)821*c1d255d3SCy Schubert int fst_read_next_int_param(const char *params, bool *valid, char **endp)
822325151a3SRui Paulo {
823325151a3SRui Paulo 	int ret = -1;
824325151a3SRui Paulo 	const char *curp;
825325151a3SRui Paulo 
826*c1d255d3SCy Schubert 	*valid = false;
827325151a3SRui Paulo 	*endp = (char *) params;
828325151a3SRui Paulo 	curp = params;
829325151a3SRui Paulo 	if (*curp) {
830325151a3SRui Paulo 		ret = (int) strtol(curp, endp, 0);
831325151a3SRui Paulo 		if (!**endp || isspace(**endp))
832*c1d255d3SCy Schubert 			*valid = true;
833325151a3SRui Paulo 	}
834325151a3SRui Paulo 
835325151a3SRui Paulo 	return ret;
836325151a3SRui Paulo }
837325151a3SRui Paulo 
838325151a3SRui Paulo 
fst_read_next_text_param(const char * params,char * buf,size_t buflen,char ** endp)839325151a3SRui Paulo int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
840325151a3SRui Paulo 			     char **endp)
841325151a3SRui Paulo {
842325151a3SRui Paulo 	size_t max_chars_to_copy;
843325151a3SRui Paulo 	char *cur_dest;
844325151a3SRui Paulo 
845325151a3SRui Paulo 	*endp = (char *) params;
846325151a3SRui Paulo 	while (isspace(**endp))
847325151a3SRui Paulo 		(*endp)++;
848325151a3SRui Paulo 	if (!**endp || buflen <= 1)
849325151a3SRui Paulo 		return -EINVAL;
850325151a3SRui Paulo 
851325151a3SRui Paulo 	max_chars_to_copy = buflen - 1;
852325151a3SRui Paulo 	/* We need 1 byte for the terminating zero */
853325151a3SRui Paulo 	cur_dest = buf;
854325151a3SRui Paulo 	while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
855325151a3SRui Paulo 		*cur_dest = **endp;
856325151a3SRui Paulo 		(*endp)++;
857325151a3SRui Paulo 		cur_dest++;
858325151a3SRui Paulo 		max_chars_to_copy--;
859325151a3SRui Paulo 	}
860325151a3SRui Paulo 	*cur_dest = 0;
861325151a3SRui Paulo 
862325151a3SRui Paulo 	return 0;
863325151a3SRui Paulo }
864325151a3SRui Paulo 
865325151a3SRui Paulo 
fst_read_peer_addr(const char * mac,u8 * peer_addr)866325151a3SRui Paulo int fst_read_peer_addr(const char *mac, u8 *peer_addr)
867325151a3SRui Paulo {
868325151a3SRui Paulo 	if (hwaddr_aton(mac, peer_addr)) {
869325151a3SRui Paulo 		fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
870325151a3SRui Paulo 			   mac);
871325151a3SRui Paulo 		return -1;
872325151a3SRui Paulo 	}
873325151a3SRui Paulo 
874325151a3SRui Paulo 	if (is_zero_ether_addr(peer_addr) ||
875325151a3SRui Paulo 	    is_multicast_ether_addr(peer_addr)) {
876325151a3SRui Paulo 		fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
877325151a3SRui Paulo 			   mac);
878325151a3SRui Paulo 		return -1;
879325151a3SRui Paulo 	}
880325151a3SRui Paulo 
881325151a3SRui Paulo 	return 0;
882325151a3SRui Paulo }
883325151a3SRui Paulo 
884325151a3SRui Paulo 
fst_parse_attach_command(const char * cmd,char * ifname,size_t ifname_size,struct fst_iface_cfg * cfg)885325151a3SRui Paulo int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
886325151a3SRui Paulo 			     struct fst_iface_cfg *cfg)
887325151a3SRui Paulo {
888325151a3SRui Paulo 	char *pos;
889325151a3SRui Paulo 	char *endp;
890*c1d255d3SCy Schubert 	bool is_valid;
891325151a3SRui Paulo 	int val;
892325151a3SRui Paulo 
893325151a3SRui Paulo 	if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
894325151a3SRui Paulo 	    fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
895325151a3SRui Paulo 				     &endp))
896325151a3SRui Paulo 		return -EINVAL;
897325151a3SRui Paulo 
898325151a3SRui Paulo 	cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
899325151a3SRui Paulo 	cfg->priority = 0;
900325151a3SRui Paulo 	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
901325151a3SRui Paulo 	if (pos) {
902325151a3SRui Paulo 		pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
903325151a3SRui Paulo 		if (*pos == '=') {
904325151a3SRui Paulo 			val = fst_read_next_int_param(pos + 1, &is_valid,
905325151a3SRui Paulo 						      &endp);
906325151a3SRui Paulo 			if (is_valid)
907325151a3SRui Paulo 				cfg->llt = val;
908325151a3SRui Paulo 		}
909325151a3SRui Paulo 	}
910325151a3SRui Paulo 	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
911325151a3SRui Paulo 	if (pos) {
912325151a3SRui Paulo 		pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
913325151a3SRui Paulo 		if (*pos == '=') {
914325151a3SRui Paulo 			val = fst_read_next_int_param(pos + 1, &is_valid,
915325151a3SRui Paulo 						      &endp);
916325151a3SRui Paulo 			if (is_valid)
917325151a3SRui Paulo 				cfg->priority = (u8) val;
918325151a3SRui Paulo 		}
919325151a3SRui Paulo 	}
920325151a3SRui Paulo 
921325151a3SRui Paulo 	return 0;
922325151a3SRui Paulo }
923325151a3SRui Paulo 
924325151a3SRui Paulo 
fst_parse_detach_command(const char * cmd,char * ifname,size_t ifname_size)925325151a3SRui Paulo int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
926325151a3SRui Paulo {
927325151a3SRui Paulo 	char *endp;
928325151a3SRui Paulo 
929325151a3SRui Paulo 	return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
930325151a3SRui Paulo }
931325151a3SRui Paulo 
932325151a3SRui Paulo 
fst_iface_detach(const char * ifname)933325151a3SRui Paulo int fst_iface_detach(const char *ifname)
934325151a3SRui Paulo {
935325151a3SRui Paulo 	struct fst_group *g;
936325151a3SRui Paulo 
937325151a3SRui Paulo 	foreach_fst_group(g) {
938325151a3SRui Paulo 		struct fst_iface *f;
939325151a3SRui Paulo 
940325151a3SRui Paulo 		f = fst_group_get_iface_by_name(g, ifname);
941325151a3SRui Paulo 		if (f) {
942325151a3SRui Paulo 			fst_detach(f);
943325151a3SRui Paulo 			return 0;
944325151a3SRui Paulo 		}
945325151a3SRui Paulo 	}
946325151a3SRui Paulo 
947325151a3SRui Paulo 	return -EINVAL;
948325151a3SRui Paulo }
949