1 /* 2 * Control interface for shared AP commands 3 * Copyright (c) 2004-2014, Jouni Malinen <j (at) w1.fi> 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 11 #include "utils/common.h" 12 #include "common/ieee802_11_defs.h" 13 #include "common/sae.h" 14 #include "eapol_auth/eapol_auth_sm.h" 15 #include "fst/fst_ctrl_iface.h" 16 #include "hostapd.h" 17 #include "ieee802_1x.h" 18 #include "wpa_auth.h" 19 #include "ieee802_11.h" 20 #include "sta_info.h" 21 #include "wps_hostapd.h" 22 #include "p2p_hostapd.h" 23 #include "ctrl_iface_ap.h" 24 #include "ap_drv_ops.h" 25 #include "mbo_ap.h" 26 #include "taxonomy.h" 27 28 29 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, 30 struct sta_info *sta, 31 char *buf, size_t buflen) 32 { 33 struct hostap_sta_driver_data data; 34 int ret; 35 int len = 0; 36 37 if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) 38 return 0; 39 40 ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" 41 "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" 42 "signal=%d\n", 43 data.rx_packets, data.tx_packets, 44 data.rx_bytes, data.tx_bytes, data.inactive_msec, 45 data.signal); 46 if (os_snprintf_error(buflen, ret)) 47 return 0; 48 len += ret; 49 50 ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", 51 data.current_rx_rate); 52 if (os_snprintf_error(buflen - len, ret)) 53 return len; 54 len += ret; 55 if (data.flags & STA_DRV_DATA_RX_MCS) { 56 ret = os_snprintf(buf + len, buflen - len, " mcs %u", 57 data.rx_mcs); 58 if (!os_snprintf_error(buflen - len, ret)) 59 len += ret; 60 } 61 if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { 62 ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", 63 data.rx_vhtmcs); 64 if (!os_snprintf_error(buflen - len, ret)) 65 len += ret; 66 } 67 if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { 68 ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", 69 data.rx_vht_nss); 70 if (!os_snprintf_error(buflen - len, ret)) 71 len += ret; 72 } 73 if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { 74 ret = os_snprintf(buf + len, buflen - len, " shortGI"); 75 if (!os_snprintf_error(buflen - len, ret)) 76 len += ret; 77 } 78 ret = os_snprintf(buf + len, buflen - len, "\n"); 79 if (!os_snprintf_error(buflen - len, ret)) 80 len += ret; 81 82 ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", 83 data.current_tx_rate); 84 if (os_snprintf_error(buflen - len, ret)) 85 return len; 86 len += ret; 87 if (data.flags & STA_DRV_DATA_TX_MCS) { 88 ret = os_snprintf(buf + len, buflen - len, " mcs %u", 89 data.tx_mcs); 90 if (!os_snprintf_error(buflen - len, ret)) 91 len += ret; 92 } 93 if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { 94 ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", 95 data.tx_vhtmcs); 96 if (!os_snprintf_error(buflen - len, ret)) 97 len += ret; 98 } 99 if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { 100 ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", 101 data.tx_vht_nss); 102 if (!os_snprintf_error(buflen - len, ret)) 103 len += ret; 104 } 105 if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { 106 ret = os_snprintf(buf + len, buflen - len, " shortGI"); 107 if (!os_snprintf_error(buflen - len, ret)) 108 len += ret; 109 } 110 ret = os_snprintf(buf + len, buflen - len, "\n"); 111 if (!os_snprintf_error(buflen - len, ret)) 112 len += ret; 113 114 return len; 115 } 116 117 118 static int hostapd_get_sta_conn_time(struct sta_info *sta, 119 char *buf, size_t buflen) 120 { 121 struct os_reltime age; 122 int ret; 123 124 if (!sta->connected_time.sec) 125 return 0; 126 127 os_reltime_age(&sta->connected_time, &age); 128 129 ret = os_snprintf(buf, buflen, "connected_time=%u\n", 130 (unsigned int) age.sec); 131 if (os_snprintf_error(buflen, ret)) 132 return 0; 133 return ret; 134 } 135 136 137 static const char * timeout_next_str(int val) 138 { 139 switch (val) { 140 case STA_NULLFUNC: 141 return "NULLFUNC POLL"; 142 case STA_DISASSOC: 143 return "DISASSOC"; 144 case STA_DEAUTH: 145 return "DEAUTH"; 146 case STA_REMOVE: 147 return "REMOVE"; 148 case STA_DISASSOC_FROM_CLI: 149 return "DISASSOC_FROM_CLI"; 150 } 151 152 return "?"; 153 } 154 155 156 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, 157 struct sta_info *sta, 158 char *buf, size_t buflen) 159 { 160 int len, res, ret, i; 161 162 if (!sta) 163 return 0; 164 165 len = 0; 166 ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", 167 MAC2STR(sta->addr)); 168 if (os_snprintf_error(buflen - len, ret)) 169 return len; 170 len += ret; 171 172 ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); 173 if (ret < 0) 174 return len; 175 len += ret; 176 177 ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" 178 "listen_interval=%d\nsupported_rates=", 179 sta->aid, sta->capability, sta->listen_interval); 180 if (os_snprintf_error(buflen - len, ret)) 181 return len; 182 len += ret; 183 184 for (i = 0; i < sta->supported_rates_len; i++) { 185 ret = os_snprintf(buf + len, buflen - len, "%02x%s", 186 sta->supported_rates[i], 187 i + 1 < sta->supported_rates_len ? " " : ""); 188 if (os_snprintf_error(buflen - len, ret)) 189 return len; 190 len += ret; 191 } 192 193 ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", 194 timeout_next_str(sta->timeout_next)); 195 if (os_snprintf_error(buflen - len, ret)) 196 return len; 197 len += ret; 198 199 res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); 200 if (res >= 0) 201 len += res; 202 res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); 203 if (res >= 0) 204 len += res; 205 res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); 206 if (res >= 0) 207 len += res; 208 res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, 209 buflen - len); 210 if (res >= 0) 211 len += res; 212 res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); 213 if (res >= 0) 214 len += res; 215 216 len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); 217 len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); 218 219 #ifdef CONFIG_SAE 220 if (sta->sae && sta->sae->state == SAE_ACCEPTED) { 221 res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", 222 sta->sae->group); 223 if (!os_snprintf_error(buflen - len, res)) 224 len += res; 225 } 226 #endif /* CONFIG_SAE */ 227 228 if (sta->vlan_id > 0) { 229 res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", 230 sta->vlan_id); 231 if (!os_snprintf_error(buflen - len, res)) 232 len += res; 233 } 234 235 res = mbo_ap_get_info(sta, buf + len, buflen - len); 236 if (res >= 0) 237 len += res; 238 239 if (sta->supp_op_classes && 240 buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { 241 len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); 242 len += wpa_snprintf_hex(buf + len, buflen - len, 243 sta->supp_op_classes + 1, 244 sta->supp_op_classes[0]); 245 len += os_snprintf(buf + len, buflen - len, "\n"); 246 } 247 248 return len; 249 } 250 251 252 int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, 253 char *buf, size_t buflen) 254 { 255 return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); 256 } 257 258 259 int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, 260 char *buf, size_t buflen) 261 { 262 u8 addr[ETH_ALEN]; 263 int ret; 264 const char *pos; 265 struct sta_info *sta; 266 267 if (hwaddr_aton(txtaddr, addr)) { 268 ret = os_snprintf(buf, buflen, "FAIL\n"); 269 if (os_snprintf_error(buflen, ret)) 270 return 0; 271 return ret; 272 } 273 274 sta = ap_get_sta(hapd, addr); 275 if (sta == NULL) 276 return -1; 277 278 pos = os_strchr(txtaddr, ' '); 279 if (pos) { 280 pos++; 281 282 #ifdef HOSTAPD_DUMP_STATE 283 if (os_strcmp(pos, "eapol") == 0) { 284 if (sta->eapol_sm == NULL) 285 return -1; 286 return eapol_auth_dump_state(sta->eapol_sm, buf, 287 buflen); 288 } 289 #endif /* HOSTAPD_DUMP_STATE */ 290 291 return -1; 292 } 293 294 ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); 295 ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); 296 297 return ret; 298 } 299 300 301 int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, 302 char *buf, size_t buflen) 303 { 304 u8 addr[ETH_ALEN]; 305 struct sta_info *sta; 306 int ret; 307 308 if (hwaddr_aton(txtaddr, addr) || 309 (sta = ap_get_sta(hapd, addr)) == NULL) { 310 ret = os_snprintf(buf, buflen, "FAIL\n"); 311 if (os_snprintf_error(buflen, ret)) 312 return 0; 313 return ret; 314 } 315 316 if (!sta->next) 317 return 0; 318 319 return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); 320 } 321 322 323 #ifdef CONFIG_P2P_MANAGER 324 static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, 325 u8 minor_reason_code, const u8 *addr) 326 { 327 struct ieee80211_mgmt *mgmt; 328 int ret; 329 u8 *pos; 330 331 if (!hapd->drv_priv || !hapd->driver->send_frame) 332 return -1; 333 334 mgmt = os_zalloc(sizeof(*mgmt) + 100); 335 if (mgmt == NULL) 336 return -1; 337 338 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); 339 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR 340 " with minor reason code %u (stype=%u (%s))", 341 MAC2STR(addr), minor_reason_code, stype, 342 fc2str(le_to_host16(mgmt->frame_control))); 343 344 os_memcpy(mgmt->da, addr, ETH_ALEN); 345 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 346 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 347 if (stype == WLAN_FC_STYPE_DEAUTH) { 348 mgmt->u.deauth.reason_code = 349 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 350 pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); 351 } else { 352 mgmt->u.disassoc.reason_code = 353 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 354 pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); 355 } 356 357 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 358 *pos++ = 4 + 3 + 1; 359 WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE); 360 pos += 4; 361 362 *pos++ = P2P_ATTR_MINOR_REASON_CODE; 363 WPA_PUT_LE16(pos, 1); 364 pos += 2; 365 *pos++ = minor_reason_code; 366 367 ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, 368 pos - (u8 *) mgmt, 1); 369 os_free(mgmt); 370 371 return ret < 0 ? -1 : 0; 372 } 373 #endif /* CONFIG_P2P_MANAGER */ 374 375 376 int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, 377 const char *txtaddr) 378 { 379 u8 addr[ETH_ALEN]; 380 struct sta_info *sta; 381 const char *pos; 382 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 383 384 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", 385 txtaddr); 386 387 if (hwaddr_aton(txtaddr, addr)) 388 return -1; 389 390 pos = os_strstr(txtaddr, " reason="); 391 if (pos) 392 reason = atoi(pos + 8); 393 394 pos = os_strstr(txtaddr, " test="); 395 if (pos) { 396 struct ieee80211_mgmt mgmt; 397 int encrypt; 398 if (!hapd->drv_priv || !hapd->driver->send_frame) 399 return -1; 400 pos += 6; 401 encrypt = atoi(pos); 402 os_memset(&mgmt, 0, sizeof(mgmt)); 403 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 404 WLAN_FC_STYPE_DEAUTH); 405 os_memcpy(mgmt.da, addr, ETH_ALEN); 406 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 407 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 408 mgmt.u.deauth.reason_code = host_to_le16(reason); 409 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 410 IEEE80211_HDRLEN + 411 sizeof(mgmt.u.deauth), 412 encrypt) < 0) 413 return -1; 414 return 0; 415 } 416 417 #ifdef CONFIG_P2P_MANAGER 418 pos = os_strstr(txtaddr, " p2p="); 419 if (pos) { 420 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, 421 atoi(pos + 5), addr); 422 } 423 #endif /* CONFIG_P2P_MANAGER */ 424 425 if (os_strstr(txtaddr, " tx=0")) 426 hostapd_drv_sta_remove(hapd, addr); 427 else 428 hostapd_drv_sta_deauth(hapd, addr, reason); 429 sta = ap_get_sta(hapd, addr); 430 if (sta) 431 ap_sta_deauthenticate(hapd, sta, reason); 432 else if (addr[0] == 0xff) 433 hostapd_free_stas(hapd); 434 435 return 0; 436 } 437 438 439 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, 440 const char *txtaddr) 441 { 442 u8 addr[ETH_ALEN]; 443 struct sta_info *sta; 444 const char *pos; 445 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 446 447 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", 448 txtaddr); 449 450 if (hwaddr_aton(txtaddr, addr)) 451 return -1; 452 453 pos = os_strstr(txtaddr, " reason="); 454 if (pos) 455 reason = atoi(pos + 8); 456 457 pos = os_strstr(txtaddr, " test="); 458 if (pos) { 459 struct ieee80211_mgmt mgmt; 460 int encrypt; 461 if (!hapd->drv_priv || !hapd->driver->send_frame) 462 return -1; 463 pos += 6; 464 encrypt = atoi(pos); 465 os_memset(&mgmt, 0, sizeof(mgmt)); 466 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 467 WLAN_FC_STYPE_DISASSOC); 468 os_memcpy(mgmt.da, addr, ETH_ALEN); 469 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 470 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 471 mgmt.u.disassoc.reason_code = host_to_le16(reason); 472 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 473 IEEE80211_HDRLEN + 474 sizeof(mgmt.u.deauth), 475 encrypt) < 0) 476 return -1; 477 return 0; 478 } 479 480 #ifdef CONFIG_P2P_MANAGER 481 pos = os_strstr(txtaddr, " p2p="); 482 if (pos) { 483 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, 484 atoi(pos + 5), addr); 485 } 486 #endif /* CONFIG_P2P_MANAGER */ 487 488 if (os_strstr(txtaddr, " tx=0")) 489 hostapd_drv_sta_remove(hapd, addr); 490 else 491 hostapd_drv_sta_disassoc(hapd, addr, reason); 492 sta = ap_get_sta(hapd, addr); 493 if (sta) 494 ap_sta_disassociate(hapd, sta, reason); 495 else if (addr[0] == 0xff) 496 hostapd_free_stas(hapd); 497 498 return 0; 499 } 500 501 502 #ifdef CONFIG_TAXONOMY 503 int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, 504 const char *txtaddr, 505 char *buf, size_t buflen) 506 { 507 u8 addr[ETH_ALEN]; 508 struct sta_info *sta; 509 510 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); 511 512 if (hwaddr_aton(txtaddr, addr)) 513 return -1; 514 515 sta = ap_get_sta(hapd, addr); 516 if (!sta) 517 return -1; 518 519 return retrieve_sta_taxonomy(hapd, sta, buf, buflen); 520 } 521 #endif /* CONFIG_TAXONOMY */ 522 523 524 int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, 525 const char *txtaddr) 526 { 527 u8 addr[ETH_ALEN]; 528 struct sta_info *sta; 529 530 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); 531 532 if (hwaddr_aton(txtaddr, addr)) 533 return -1; 534 535 sta = ap_get_sta(hapd, addr); 536 if (!sta) 537 return -1; 538 539 hostapd_drv_poll_client(hapd, hapd->own_addr, addr, 540 sta->flags & WLAN_STA_WMM); 541 return 0; 542 } 543 544 545 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, 546 size_t buflen) 547 { 548 struct hostapd_iface *iface = hapd->iface; 549 int len = 0, ret; 550 size_t i; 551 552 ret = os_snprintf(buf + len, buflen - len, 553 "state=%s\n" 554 "phy=%s\n" 555 "freq=%d\n" 556 "num_sta_non_erp=%d\n" 557 "num_sta_no_short_slot_time=%d\n" 558 "num_sta_no_short_preamble=%d\n" 559 "olbc=%d\n" 560 "num_sta_ht_no_gf=%d\n" 561 "num_sta_no_ht=%d\n" 562 "num_sta_ht_20_mhz=%d\n" 563 "num_sta_ht40_intolerant=%d\n" 564 "olbc_ht=%d\n" 565 "ht_op_mode=0x%x\n", 566 hostapd_state_text(iface->state), 567 iface->phy, 568 iface->freq, 569 iface->num_sta_non_erp, 570 iface->num_sta_no_short_slot_time, 571 iface->num_sta_no_short_preamble, 572 iface->olbc, 573 iface->num_sta_ht_no_gf, 574 iface->num_sta_no_ht, 575 iface->num_sta_ht_20mhz, 576 iface->num_sta_ht40_intolerant, 577 iface->olbc_ht, 578 iface->ht_op_mode); 579 if (os_snprintf_error(buflen - len, ret)) 580 return len; 581 len += ret; 582 583 if (!iface->cac_started || !iface->dfs_cac_ms) { 584 ret = os_snprintf(buf + len, buflen - len, 585 "cac_time_seconds=%d\n" 586 "cac_time_left_seconds=N/A\n", 587 iface->dfs_cac_ms / 1000); 588 } else { 589 /* CAC started and CAC time set - calculate remaining time */ 590 struct os_reltime now; 591 unsigned int left_time; 592 593 os_reltime_age(&iface->dfs_cac_start, &now); 594 left_time = iface->dfs_cac_ms / 1000 - now.sec; 595 ret = os_snprintf(buf + len, buflen - len, 596 "cac_time_seconds=%u\n" 597 "cac_time_left_seconds=%u\n", 598 iface->dfs_cac_ms / 1000, 599 left_time); 600 } 601 if (os_snprintf_error(buflen - len, ret)) 602 return len; 603 len += ret; 604 605 ret = os_snprintf(buf + len, buflen - len, 606 "channel=%u\n" 607 "secondary_channel=%d\n" 608 "ieee80211n=%d\n" 609 "ieee80211ac=%d\n", 610 iface->conf->channel, 611 iface->conf->ieee80211n && !hapd->conf->disable_11n ? 612 iface->conf->secondary_channel : 0, 613 iface->conf->ieee80211n && !hapd->conf->disable_11n, 614 iface->conf->ieee80211ac && 615 !hapd->conf->disable_11ac); 616 if (os_snprintf_error(buflen - len, ret)) 617 return len; 618 len += ret; 619 if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { 620 ret = os_snprintf(buf + len, buflen - len, 621 "vht_oper_chwidth=%d\n" 622 "vht_oper_centr_freq_seg0_idx=%d\n" 623 "vht_oper_centr_freq_seg1_idx=%d\n", 624 iface->conf->vht_oper_chwidth, 625 iface->conf->vht_oper_centr_freq_seg0_idx, 626 iface->conf->vht_oper_centr_freq_seg1_idx); 627 if (os_snprintf_error(buflen - len, ret)) 628 return len; 629 len += ret; 630 } 631 632 for (i = 0; i < iface->num_bss; i++) { 633 struct hostapd_data *bss = iface->bss[i]; 634 ret = os_snprintf(buf + len, buflen - len, 635 "bss[%d]=%s\n" 636 "bssid[%d]=" MACSTR "\n" 637 "ssid[%d]=%s\n" 638 "num_sta[%d]=%d\n", 639 (int) i, bss->conf->iface, 640 (int) i, MAC2STR(bss->own_addr), 641 (int) i, 642 wpa_ssid_txt(bss->conf->ssid.ssid, 643 bss->conf->ssid.ssid_len), 644 (int) i, bss->num_sta); 645 if (os_snprintf_error(buflen - len, ret)) 646 return len; 647 len += ret; 648 } 649 650 return len; 651 } 652 653 654 int hostapd_parse_csa_settings(const char *pos, 655 struct csa_settings *settings) 656 { 657 char *end; 658 659 os_memset(settings, 0, sizeof(*settings)); 660 settings->cs_count = strtol(pos, &end, 10); 661 if (pos == end) { 662 wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); 663 return -1; 664 } 665 666 settings->freq_params.freq = atoi(end); 667 if (settings->freq_params.freq == 0) { 668 wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); 669 return -1; 670 } 671 672 #define SET_CSA_SETTING(str) \ 673 do { \ 674 const char *pos2 = os_strstr(pos, " " #str "="); \ 675 if (pos2) { \ 676 pos2 += sizeof(" " #str "=") - 1; \ 677 settings->freq_params.str = atoi(pos2); \ 678 } \ 679 } while (0) 680 681 SET_CSA_SETTING(center_freq1); 682 SET_CSA_SETTING(center_freq2); 683 SET_CSA_SETTING(bandwidth); 684 SET_CSA_SETTING(sec_channel_offset); 685 settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); 686 settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); 687 settings->block_tx = !!os_strstr(pos, " blocktx"); 688 #undef SET_CSA_SETTING 689 690 return 0; 691 } 692 693 694 int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) 695 { 696 return hostapd_drv_stop_ap(hapd); 697 } 698 699 700 int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, 701 size_t len) 702 { 703 return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); 704 } 705 706 707 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) 708 { 709 wpa_auth_pmksa_flush(hapd->wpa_auth); 710 } 711 712 713 int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) 714 { 715 u8 spa[ETH_ALEN]; 716 u8 pmkid[PMKID_LEN]; 717 u8 pmk[PMK_LEN_MAX]; 718 size_t pmk_len; 719 char *pos, *pos2; 720 int akmp = 0, expiration = 0; 721 722 /* 723 * Entry format: 724 * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp> 725 */ 726 727 if (hwaddr_aton(cmd, spa)) 728 return -1; 729 730 pos = os_strchr(cmd, ' '); 731 if (!pos) 732 return -1; 733 pos++; 734 735 if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) 736 return -1; 737 738 pos = os_strchr(pos, ' '); 739 if (!pos) 740 return -1; 741 pos++; 742 743 pos2 = os_strchr(pos, ' '); 744 if (!pos2) 745 return -1; 746 pmk_len = (pos2 - pos) / 2; 747 if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || 748 hexstr2bin(pos, pmk, pmk_len) < 0) 749 return -1; 750 751 pos = pos2 + 1; 752 753 if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) 754 return -1; 755 756 return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, 757 pmkid, expiration, akmp); 758 } 759 760 761 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL 762 #ifdef CONFIG_MESH 763 764 int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, 765 const u8 *addr, char *buf, size_t len) 766 { 767 return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); 768 } 769 770 771 void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) 772 { 773 u8 spa[ETH_ALEN]; 774 u8 pmkid[PMKID_LEN]; 775 u8 pmk[PMK_LEN_MAX]; 776 char *pos; 777 int expiration; 778 779 /* 780 * Entry format: 781 * <BSSID> <PMKID> <PMK> <expiration in seconds> 782 */ 783 784 if (hwaddr_aton(cmd, spa)) 785 return NULL; 786 787 pos = os_strchr(cmd, ' '); 788 if (!pos) 789 return NULL; 790 pos++; 791 792 if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) 793 return NULL; 794 795 pos = os_strchr(pos, ' '); 796 if (!pos) 797 return NULL; 798 pos++; 799 800 if (hexstr2bin(pos, pmk, PMK_LEN) < 0) 801 return NULL; 802 803 pos = os_strchr(pos, ' '); 804 if (!pos) 805 return NULL; 806 pos++; 807 808 if (sscanf(pos, "%d", &expiration) != 1) 809 return NULL; 810 811 return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); 812 } 813 814 #endif /* CONFIG_MESH */ 815 #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 816