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 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 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 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 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 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 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 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 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 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