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
get_fst_group_by_id(const char * id)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 */
format_session_state_extra(const union fst_event_extra * extra,char * buffer,size_t size)35 static bool 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 /* fall through */
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
fst_ctrl_iface_notify(struct fst_iface * f,u32 session_id,enum fst_event_type event_type,const union fst_event_extra * extra)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 */
session_get(const char * session_id,char * buf,size_t buflen)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 */
session_set(const char * session_id,char * buf,size_t buflen)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 */
session_add(const char * group_id,char * buf,size_t buflen)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
session_remove(const char * session_id,char * buf,size_t buflen)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 */
session_initiate(const char * session_id,char * buf,size_t buflen)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 */
session_respond(const char * session_id,char * buf,size_t buflen)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 */
session_transfer(const char * session_id,char * buf,size_t buflen)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 */
session_teardown(const char * session_id,char * buf,size_t buflen)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 */
test_request(const char * request,char * buf,size_t buflen)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
list_session_enum_cb(struct fst_group * g,struct fst_session * s,void * ctx)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
list_sessions(const char * group_id,char * buf,size_t buflen)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 */
iface_peers(const char * group_id,char * buf,size_t buflen)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
get_peer_mbies(const char * params,char * buf,size_t buflen)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 */
list_ifaces(const char * group_id,char * buf,size_t buflen)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 */
list_groups(const char * cmd,char * buf,size_t buflen)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
band_freq(enum mb_band_id band)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
print_band(unsigned num,struct fst_iface * iface,const u8 * addr,char * buf,size_t buflen)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
fst_ctrl_iface_on_iface_state_changed(struct fst_iface * i,bool attached)694 static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
695 bool 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
fst_ctrl_iface_on_iface_added(struct fst_iface * i)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
fst_ctrl_iface_on_iface_removed(struct fst_iface * i)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
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 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
fst_ctrl_iface_mb_info(const u8 * addr,char * buf,size_t buflen)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 */
fst_ctrl_iface_receive(const char * cmd,char * reply,size_t reply_size)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 bool 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
fst_read_next_int_param(const char * params,bool * valid,char ** endp)821 int fst_read_next_int_param(const char *params, bool *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
fst_read_next_text_param(const char * params,char * buf,size_t buflen,char ** endp)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
fst_read_peer_addr(const char * mac,u8 * peer_addr)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
fst_parse_attach_command(const char * cmd,char * ifname,size_t ifname_size,struct fst_iface_cfg * cfg)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 bool 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
fst_parse_detach_command(const char * cmd,char * ifname,size_t ifname_size)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
fst_iface_detach(const char * ifname)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