Line data Source code
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 626 : static struct fst_group * get_fst_group_by_id(const char *id)
20 : {
21 : struct fst_group *g;
22 :
23 662 : foreach_fst_group(g) {
24 653 : const char *group_id = fst_group_get_id(g);
25 :
26 653 : if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
27 617 : return g;
28 : }
29 :
30 9 : return NULL;
31 : }
32 :
33 :
34 : /* notifications */
35 1275 : static Boolean format_session_state_extra(const union fst_event_extra *extra,
36 : char *buffer, size_t size)
37 : {
38 : int len;
39 1275 : char reject_str[32] = FST_CTRL_PVAL_NONE;
40 1275 : const char *initiator = FST_CTRL_PVAL_NONE;
41 : const struct fst_event_extra_session_state *ss;
42 :
43 1275 : ss = &extra->session_state;
44 1275 : if (ss->new_state != FST_SESSION_STATE_INITIAL)
45 648 : return TRUE;
46 :
47 627 : switch (ss->extra.to_initial.reason) {
48 : case REASON_REJECT:
49 5 : if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
50 5 : os_snprintf(reject_str, sizeof(reject_str), "%u",
51 5 : ss->extra.to_initial.reject_code);
52 : /* no break */
53 : case REASON_TEARDOWN:
54 : case REASON_SWITCH:
55 590 : switch (ss->extra.to_initial.initiator) {
56 : case FST_INITIATOR_LOCAL:
57 317 : initiator = FST_CS_PVAL_INITIATOR_LOCAL;
58 317 : break;
59 : case FST_INITIATOR_REMOTE:
60 273 : initiator = FST_CS_PVAL_INITIATOR_REMOTE;
61 273 : break;
62 : default:
63 0 : break;
64 : }
65 590 : break;
66 : default:
67 37 : break;
68 : }
69 :
70 627 : 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 627 : return !os_snprintf_error(size, len);
78 : }
79 :
80 :
81 3963 : 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 3963 : 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 3963 : if (!f) {
97 1894 : foreach_fst_group(g) {
98 1894 : f = fst_group_first_iface(g);
99 1894 : if (f)
100 1879 : break;
101 : }
102 1879 : if (!f)
103 0 : return;
104 : }
105 :
106 : WPA_ASSERT(f->iface_obj.ctx);
107 :
108 3963 : switch (event_type) {
109 : case EVENT_FST_IFACE_STATE_CHANGED:
110 1088 : if (!extra)
111 0 : return;
112 1088 : is = &extra->iface_state;
113 1088 : 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 1088 : is->attached ? FST_CEI_PNAME_ATTACHED :
118 : FST_CEI_PNAME_DETACHED,
119 1088 : is->ifname, is->group_id);
120 1088 : break;
121 : case EVENT_PEER_STATE_CHANGED:
122 996 : if (!extra)
123 0 : return;
124 996 : ps = &extra->peer_state;
125 6972 : 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 996 : ps->connected ? FST_CEP_PNAME_CONNECTED :
130 : FST_CEP_PNAME_DISCONNECTED,
131 6972 : ps->ifname, MAC2STR(ps->addr));
132 996 : break;
133 : case EVENT_FST_SESSION_STATE_CHANGED:
134 1275 : if (!extra)
135 0 : return;
136 1275 : if (!format_session_state_extra(extra, extra_str,
137 : sizeof(extra_str))) {
138 0 : fst_printf(MSG_ERROR,
139 : "CTRL: Cannot format STATE_CHANGE extra");
140 0 : extra_str[0] = 0;
141 : }
142 1275 : ss = &extra->session_state;
143 1275 : 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 1275 : break;
155 : case EVENT_FST_ESTABLISHED:
156 : case EVENT_FST_SETUP:
157 604 : 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 604 : break;
164 : }
165 : }
166 :
167 :
168 : /* command processors */
169 :
170 : /* fst session_get */
171 3 : 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 3 : id = strtoul(session_id, NULL, 0);
179 :
180 3 : s = fst_session_get_by_id(id);
181 3 : if (!s) {
182 1 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
183 1 : return os_snprintf(buf, buflen, "FAIL\n");
184 : }
185 :
186 2 : old_peer_addr = fst_session_get_peer_addr(s, TRUE);
187 2 : new_peer_addr = fst_session_get_peer_addr(s, FALSE);
188 2 : new_iface = fst_session_get_iface(s, FALSE);
189 2 : old_iface = fst_session_get_iface(s, TRUE);
190 :
191 26 : 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 12 : MAC2STR(old_peer_addr),
199 12 : 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 534 : 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 534 : id = strtoul(session_id, &p, 0);
218 :
219 534 : s = fst_session_get_by_id(id);
220 534 : if (!s) {
221 4 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
222 4 : return os_snprintf(buf, buflen, "FAIL\n");
223 : }
224 :
225 530 : if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
226 3 : return os_snprintf(buf, buflen, "FAIL\n");
227 527 : p++;
228 :
229 527 : if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
230 111 : ret = fst_session_set_str_ifname(s, q + 1, TRUE);
231 416 : } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
232 106 : ret = fst_session_set_str_ifname(s, q + 1, FALSE);
233 310 : } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
234 108 : ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
235 202 : } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
236 106 : ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
237 96 : } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
238 94 : ret = fst_session_set_str_llt(s, q + 1);
239 : } else {
240 2 : fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
241 2 : return os_snprintf(buf, buflen, "FAIL\n");
242 : }
243 :
244 525 : return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
245 : }
246 :
247 :
248 : /* fst session_add/remove */
249 118 : 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 118 : g = get_fst_group_by_id(group_id);
255 118 : if (!g) {
256 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
257 : group_id);
258 2 : return os_snprintf(buf, buflen, "FAIL\n");
259 : }
260 :
261 116 : s = fst_session_create(g);
262 116 : if (!s) {
263 1 : fst_printf(MSG_ERROR,
264 : "CTRL: Cannot create session for group '%s'",
265 : group_id);
266 1 : return os_snprintf(buf, buflen, "FAIL\n");
267 : }
268 :
269 115 : return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
270 : }
271 :
272 :
273 429 : 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 429 : id = strtoul(session_id, NULL, 0);
280 :
281 429 : s = fst_session_get_by_id(id);
282 429 : if (!s) {
283 3 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
284 3 : return os_snprintf(buf, buflen, "FAIL\n");
285 : }
286 :
287 426 : g = fst_session_get_group(s);
288 426 : fst_session_reset(s);
289 426 : fst_session_delete(s);
290 426 : fst_group_delete_if_empty(g);
291 :
292 426 : return os_snprintf(buf, buflen, "OK\n");
293 : }
294 :
295 :
296 : /* fst session_initiate */
297 327 : static int session_initiate(const char *session_id, char *buf, size_t buflen)
298 : {
299 : struct fst_session *s;
300 : u32 id;
301 :
302 327 : id = strtoul(session_id, NULL, 0);
303 :
304 327 : s = fst_session_get_by_id(id);
305 327 : if (!s) {
306 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
307 2 : return os_snprintf(buf, buflen, "FAIL\n");
308 : }
309 :
310 325 : if (fst_session_initiate_setup(s)) {
311 14 : fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
312 14 : return os_snprintf(buf, buflen, "FAIL\n");
313 : }
314 :
315 311 : return os_snprintf(buf, buflen, "OK\n");
316 : }
317 :
318 :
319 : /* fst session_respond */
320 297 : 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 297 : id = strtoul(session_id, &p, 0);
328 :
329 297 : s = fst_session_get_by_id(id);
330 297 : if (!s) {
331 1 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
332 1 : return os_snprintf(buf, buflen, "FAIL\n");
333 : }
334 :
335 296 : if (*p != ' ')
336 1 : return os_snprintf(buf, buflen, "FAIL\n");
337 295 : p++;
338 :
339 295 : if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
340 291 : status_code = WLAN_STATUS_SUCCESS;
341 4 : } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
342 3 : status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
343 : } else {
344 1 : fst_printf(MSG_WARNING,
345 : "CTRL: session %u: unknown response status: %s",
346 : id, p);
347 1 : return os_snprintf(buf, buflen, "FAIL\n");
348 : }
349 :
350 294 : if (fst_session_respond(s, status_code)) {
351 4 : fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
352 : id);
353 4 : return os_snprintf(buf, buflen, "FAIL\n");
354 : }
355 :
356 290 : fst_printf(MSG_INFO, "CTRL: session %u responded", id);
357 :
358 290 : return os_snprintf(buf, buflen, "OK\n");
359 : }
360 :
361 :
362 : /* fst session_transfer */
363 7 : static int session_transfer(const char *session_id, char *buf, size_t buflen)
364 : {
365 : struct fst_session *s;
366 : u32 id;
367 :
368 7 : id = strtoul(session_id, NULL, 0);
369 :
370 7 : s = fst_session_get_by_id(id);
371 7 : if (!s) {
372 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
373 2 : return os_snprintf(buf, buflen, "FAIL\n");
374 : }
375 :
376 5 : if (fst_session_initiate_switch(s)) {
377 2 : fst_printf(MSG_WARNING,
378 : "CTRL: Cannot initiate ST for session %u", id);
379 2 : return os_snprintf(buf, buflen, "FAIL\n");
380 : }
381 :
382 3 : return os_snprintf(buf, buflen, "OK\n");
383 : }
384 :
385 :
386 : /* fst session_teardown */
387 265 : static int session_teardown(const char *session_id, char *buf, size_t buflen)
388 : {
389 : struct fst_session *s;
390 : u32 id;
391 :
392 265 : id = strtoul(session_id, NULL, 0);
393 :
394 265 : s = fst_session_get_by_id(id);
395 265 : if (!s) {
396 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
397 2 : return os_snprintf(buf, buflen, "FAIL\n");
398 : }
399 :
400 263 : if (fst_session_tear_down_setup(s)) {
401 4 : fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
402 : id);
403 4 : return os_snprintf(buf, buflen, "FAIL\n");
404 : }
405 :
406 259 : return os_snprintf(buf, buflen, "OK\n");
407 : }
408 :
409 :
410 : #ifdef CONFIG_FST_TEST
411 : /* fst test_request */
412 658 : static int test_request(const char *request, char *buf, size_t buflen)
413 : {
414 658 : const char *p = request;
415 : int ret;
416 :
417 658 : if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
418 : os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
419 6 : ret = fst_test_req_send_fst_request(
420 : p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
421 652 : } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
422 : os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
423 8 : ret = fst_test_req_send_fst_response(
424 : p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
425 644 : } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
426 : os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
427 12 : ret = fst_test_req_send_ack_request(
428 : p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
429 632 : } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
430 : os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
431 12 : ret = fst_test_req_send_ack_response(
432 : p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
433 620 : } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
434 : os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
435 6 : ret = fst_test_req_send_tear_down(
436 : p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
437 614 : } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
438 : os_strlen(FST_CTR_GET_FSTS_ID))) {
439 11 : u32 fsts_id = fst_test_req_get_fsts_id(
440 : p + os_strlen(FST_CTR_GET_FSTS_ID));
441 11 : if (fsts_id != FST_FSTS_ID_NOT_FOUND)
442 8 : return os_snprintf(buf, buflen, "%u\n", fsts_id);
443 3 : return os_snprintf(buf, buflen, "FAIL\n");
444 603 : } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
445 : os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
446 44 : return fst_test_req_get_local_mbies(
447 : p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
448 559 : } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
449 : os_strlen(FST_CTR_IS_SUPPORTED))) {
450 558 : ret = 0;
451 : } else {
452 1 : fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
453 1 : return os_snprintf(buf, buflen, "FAIL\n");
454 : }
455 :
456 602 : 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 160 : static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
470 : void *ctx)
471 : {
472 160 : struct list_sessions_cb_ctx *c = ctx;
473 : int ret;
474 :
475 160 : ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
476 :
477 160 : c->buf += ret;
478 160 : c->buflen -= ret;
479 160 : c->reply_len += ret;
480 160 : }
481 :
482 :
483 499 : 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 499 : g = get_fst_group_by_id(group_id);
489 499 : if (!g) {
490 3 : fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
491 : group_id);
492 3 : return os_snprintf(buf, buflen, "FAIL\n");
493 : }
494 :
495 496 : ctx.buf = buf;
496 496 : ctx.buflen = buflen;
497 496 : ctx.reply_len = 0;
498 :
499 496 : fst_session_enum(g, list_session_enum_cb, &ctx);
500 :
501 496 : ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
502 :
503 496 : return ctx.reply_len;
504 : }
505 :
506 :
507 : /* fst iface_peers */
508 5 : 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 5 : unsigned found = 0;
516 5 : int ret = 0;
517 :
518 5 : g = get_fst_group_by_id(group_id);
519 5 : if (!g) {
520 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
521 : group_id);
522 2 : return os_snprintf(buf, buflen, "FAIL\n");
523 : }
524 :
525 3 : ifname = os_strchr(group_id, ' ');
526 3 : if (!ifname)
527 1 : return os_snprintf(buf, buflen, "FAIL\n");
528 2 : ifname++;
529 :
530 5 : foreach_fst_group_iface(g, f) {
531 4 : const char *in = fst_iface_get_name(f);
532 :
533 4 : if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
534 1 : found = 1;
535 1 : break;
536 : }
537 : }
538 :
539 2 : if (!found)
540 1 : return os_snprintf(buf, buflen, "FAIL\n");
541 :
542 1 : addr = fst_iface_get_peer_first(f, &ctx, FALSE);
543 2 : for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
544 : int res;
545 :
546 6 : res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
547 6 : MAC2STR(addr));
548 1 : if (os_snprintf_error(buflen - ret, res))
549 0 : break;
550 1 : ret += res;
551 : }
552 :
553 1 : return ret;
554 : }
555 :
556 :
557 7 : 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 7 : struct fst_iface *iface = NULL;
564 : const struct wpabuf *mbies;
565 :
566 14 : if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
567 7 : !*ifname)
568 : goto problem;
569 :
570 21 : while (isspace(*endp))
571 7 : endp++;
572 7 : if (fst_read_peer_addr(endp, peer_addr))
573 4 : goto problem;
574 :
575 7 : foreach_fst_group(g) {
576 6 : iface = fst_group_get_iface_by_name(g, ifname);
577 6 : if (iface)
578 2 : break;
579 : }
580 3 : if (!iface)
581 1 : goto problem;
582 :
583 2 : mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
584 2 : if (!mbies)
585 1 : goto problem;
586 :
587 1 : return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
588 : wpabuf_len(mbies));
589 :
590 : problem:
591 6 : return os_snprintf(buf, buflen, "FAIL\n");
592 : }
593 :
594 :
595 : /* fst list_ifaces */
596 4 : 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 4 : int ret = 0;
601 :
602 4 : g = get_fst_group_by_id(group_id);
603 4 : if (!g) {
604 2 : fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
605 : group_id);
606 2 : return os_snprintf(buf, buflen, "FAIL\n");
607 : }
608 :
609 6 : foreach_fst_group_iface(g, f) {
610 : int res;
611 4 : const u8 *iface_addr = fst_iface_get_addr(f);
612 :
613 32 : res = os_snprintf(buf + ret, buflen - ret,
614 : "%s|" MACSTR "|%u|%u\n",
615 : fst_iface_get_name(f),
616 24 : MAC2STR(iface_addr),
617 4 : fst_iface_get_priority(f),
618 : fst_iface_get_llt(f));
619 4 : if (os_snprintf_error(buflen - ret, res))
620 0 : break;
621 4 : ret += res;
622 : }
623 :
624 2 : return ret;
625 : }
626 :
627 :
628 : /* fst list_groups */
629 2 : static int list_groups(const char *cmd, char *buf, size_t buflen)
630 : {
631 : struct fst_group *g;
632 2 : int ret = 0;
633 :
634 5 : foreach_fst_group(g) {
635 : int res;
636 :
637 3 : res = os_snprintf(buf + ret, buflen - ret, "%s\n",
638 : fst_group_get_id(g));
639 3 : if (os_snprintf_error(buflen - ret, res))
640 0 : break;
641 3 : ret += res;
642 : }
643 :
644 2 : return ret;
645 : }
646 :
647 :
648 5 : 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 5 : return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
657 : }
658 :
659 :
660 5 : 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 5 : int ret = 0;
667 :
668 5 : fst_iface_get_channel_info(iface, &hw_mode, &channel);
669 :
670 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
671 : num, band_freq(fst_hw_mode_to_band(hw_mode)));
672 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
673 : num, fst_iface_get_name(iface));
674 5 : wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
675 5 : if (wpabuf) {
676 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
677 : num);
678 10 : ret += wpa_snprintf_hex(buf + ret, buflen - ret,
679 5 : wpabuf_head(wpabuf),
680 : wpabuf_len(wpabuf));
681 5 : ret += os_snprintf(buf + ret, buflen - ret, "\n");
682 : }
683 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
684 : num, fst_iface_get_group_id(iface));
685 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
686 5 : num, fst_iface_get_priority(iface));
687 5 : ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
688 : num, fst_iface_get_llt(iface));
689 :
690 5 : return ret;
691 : }
692 :
693 :
694 1088 : 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 1088 : os_memset(&extra, 0, sizeof(extra));
700 1088 : extra.iface_state.attached = attached;
701 1088 : os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
702 : sizeof(extra.iface_state.ifname));
703 1088 : os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
704 : sizeof(extra.iface_state.group_id));
705 :
706 1088 : fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
707 : EVENT_FST_IFACE_STATE_CHANGED, &extra);
708 1088 : }
709 :
710 :
711 544 : static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
712 : {
713 544 : fst_ctrl_iface_on_iface_state_changed(i, TRUE);
714 544 : return 0;
715 : }
716 :
717 :
718 544 : static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
719 : {
720 544 : fst_ctrl_iface_on_iface_state_changed(i, FALSE);
721 544 : }
722 :
723 :
724 2875 : 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 2875 : u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
729 :
730 2875 : fst_ctrl_iface_notify(i, session_id, event_type, extra);
731 2875 : }
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 2290 : 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 2290 : unsigned num = 0;
748 2290 : int ret = 0;
749 :
750 2531 : foreach_fst_group(g) {
751 723 : foreach_fst_group_iface(g, f) {
752 482 : if (fst_iface_is_connected(f, addr)) {
753 5 : ret += print_band(num++, f, addr,
754 : buf + ret, buflen - ret);
755 : }
756 : }
757 : }
758 :
759 2290 : return ret;
760 : }
761 :
762 :
763 : /* fst ctrl processor */
764 3173 : 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 61038 : for (c = commands; c->name; c++) {
795 30518 : if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
796 27346 : continue;
797 3172 : p = cmd + os_strlen(c->name);
798 3172 : if (c->has_param) {
799 3170 : if (!isspace(p[0]))
800 14 : return os_snprintf(reply, reply_size, "FAIL\n");
801 3156 : p++;
802 3156 : temp = p;
803 3156 : non_spaces_found = FALSE;
804 6316 : while (*temp) {
805 3157 : if (!isspace(*temp)) {
806 3153 : non_spaces_found = TRUE;
807 3153 : break;
808 : }
809 4 : temp++;
810 : }
811 3156 : if (!non_spaces_found)
812 3 : return os_snprintf(reply, reply_size, "FAIL\n");
813 : }
814 3155 : return c->process(p, reply, reply_size);
815 : }
816 :
817 1 : return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
818 : }
819 :
820 :
821 1057 : int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
822 : {
823 1057 : int ret = -1;
824 : const char *curp;
825 :
826 1057 : *valid = FALSE;
827 1057 : *endp = (char *) params;
828 1057 : curp = params;
829 1057 : if (*curp) {
830 1057 : ret = (int) strtol(curp, endp, 0);
831 1057 : if (!**endp || isspace(**endp))
832 1051 : *valid = TRUE;
833 : }
834 :
835 1057 : return ret;
836 : }
837 :
838 :
839 1654 : 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 1654 : *endp = (char *) params;
846 3854 : while (isspace(**endp))
847 546 : (*endp)++;
848 1654 : if (!**endp || buflen <= 1)
849 9 : return -EINVAL;
850 :
851 1645 : max_chars_to_copy = buflen - 1;
852 : /* We need 1 byte for the terminating zero */
853 1645 : cur_dest = buf;
854 11582 : while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
855 8292 : *cur_dest = **endp;
856 8292 : (*endp)++;
857 8292 : cur_dest++;
858 8292 : max_chars_to_copy--;
859 : }
860 1645 : *cur_dest = 0;
861 :
862 1645 : return 0;
863 : }
864 :
865 :
866 221 : int fst_read_peer_addr(const char *mac, u8 *peer_addr)
867 : {
868 221 : if (hwaddr_aton(mac, peer_addr)) {
869 4 : fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
870 : mac);
871 4 : return -1;
872 : }
873 :
874 433 : if (is_zero_ether_addr(peer_addr) ||
875 216 : is_multicast_ether_addr(peer_addr)) {
876 6 : fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
877 : mac);
878 6 : return -1;
879 : }
880 :
881 211 : return 0;
882 : }
883 :
884 :
885 538 : 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 1075 : if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
894 537 : fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
895 : &endp))
896 2 : return -EINVAL;
897 :
898 536 : cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
899 536 : cfg->priority = 0;
900 536 : pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
901 536 : if (pos) {
902 504 : pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
903 504 : if (*pos == '=') {
904 504 : val = fst_read_next_int_param(pos + 1, &is_valid,
905 : &endp);
906 504 : if (is_valid)
907 504 : cfg->llt = val;
908 : }
909 : }
910 536 : pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
911 536 : if (pos) {
912 504 : pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
913 504 : if (*pos == '=') {
914 504 : val = fst_read_next_int_param(pos + 1, &is_valid,
915 : &endp);
916 504 : if (is_valid)
917 504 : cfg->priority = (u8) val;
918 : }
919 : }
920 :
921 536 : return 0;
922 : }
923 :
924 :
925 513 : int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
926 : {
927 : char *endp;
928 :
929 513 : return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
930 : }
931 :
932 :
933 511 : int fst_iface_detach(const char *ifname)
934 : {
935 : struct fst_group *g;
936 :
937 529 : foreach_fst_group(g) {
938 : struct fst_iface *f;
939 :
940 526 : f = fst_group_get_iface_by_name(g, ifname);
941 526 : if (f) {
942 508 : fst_detach(f);
943 508 : return 0;
944 : }
945 : }
946 :
947 3 : return -EINVAL;
948 : }
|