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