1/* 2 * Control interface for shared AP commands 3 * Copyright (c) 2004-2014, Jouni Malinen <j@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 27 28static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, 29 struct sta_info *sta, 30 char *buf, size_t buflen) 31{ 32 struct hostap_sta_driver_data data; 33 int ret; 34 35 if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) 36 return 0; 37 38 ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" 39 "rx_bytes=%llu\ntx_bytes=%llu\n", 40 data.rx_packets, data.tx_packets, 41 data.rx_bytes, data.tx_bytes); 42 if (os_snprintf_error(buflen, ret)) 43 return 0; 44 return ret; 45} 46 47 48static int hostapd_get_sta_conn_time(struct sta_info *sta, 49 char *buf, size_t buflen) 50{ 51 struct os_reltime age; 52 int ret; 53 54 if (!sta->connected_time.sec) 55 return 0; 56 57 os_reltime_age(&sta->connected_time, &age); 58 59 ret = os_snprintf(buf, buflen, "connected_time=%u\n", 60 (unsigned int) age.sec); 61 if (os_snprintf_error(buflen, ret)) 62 return 0; 63 return ret; 64} 65 66 67static const char * timeout_next_str(int val) 68{ 69 switch (val) { 70 case STA_NULLFUNC: 71 return "NULLFUNC POLL"; 72 case STA_DISASSOC: 73 return "DISASSOC"; 74 case STA_DEAUTH: 75 return "DEAUTH"; 76 case STA_REMOVE: 77 return "REMOVE"; 78 case STA_DISASSOC_FROM_CLI: 79 return "DISASSOC_FROM_CLI"; 80 } 81 82 return "?"; 83} 84 85 86static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, 87 struct sta_info *sta, 88 char *buf, size_t buflen) 89{ 90 int len, res, ret, i; 91 92 if (!sta) 93 return 0; 94 95 len = 0; 96 ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", 97 MAC2STR(sta->addr)); 98 if (os_snprintf_error(buflen - len, ret)) 99 return len; 100 len += ret; 101 102 ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); 103 if (ret < 0) 104 return len; 105 len += ret; 106 107 ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" 108 "listen_interval=%d\nsupported_rates=", 109 sta->aid, sta->capability, sta->listen_interval); 110 if (os_snprintf_error(buflen - len, ret)) 111 return len; 112 len += ret; 113 114 for (i = 0; i < sta->supported_rates_len; i++) { 115 ret = os_snprintf(buf + len, buflen - len, "%02x%s", 116 sta->supported_rates[i], 117 i + 1 < sta->supported_rates_len ? " " : ""); 118 if (os_snprintf_error(buflen - len, ret)) 119 return len; 120 len += ret; 121 } 122 123 ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", 124 timeout_next_str(sta->timeout_next)); 125 if (os_snprintf_error(buflen - len, ret)) 126 return len; 127 len += ret; 128 129 res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); 130 if (res >= 0) 131 len += res; 132 res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); 133 if (res >= 0) 134 len += res; 135 res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); 136 if (res >= 0) 137 len += res; 138 res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, 139 buflen - len); 140 if (res >= 0) 141 len += res; 142 res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); 143 if (res >= 0) 144 len += res; 145 146 len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); 147 len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); 148 149#ifdef CONFIG_SAE 150 if (sta->sae && sta->sae->state == SAE_ACCEPTED) { 151 res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", 152 sta->sae->group); 153 if (!os_snprintf_error(buflen - len, res)) 154 len += res; 155 } 156#endif /* CONFIG_SAE */ 157 158 if (sta->vlan_id > 0) { 159 res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", 160 sta->vlan_id); 161 if (!os_snprintf_error(buflen - len, res)) 162 len += res; 163 } 164 165 res = mbo_ap_get_info(sta, buf + len, buflen - len); 166 if (res >= 0) 167 len += res; 168 169 if (sta->supp_op_classes && 170 buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { 171 len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); 172 len += wpa_snprintf_hex(buf + len, buflen - len, 173 sta->supp_op_classes + 1, 174 sta->supp_op_classes[0]); 175 len += os_snprintf(buf + len, buflen - len, "\n"); 176 } 177 178 return len; 179} 180 181 182int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, 183 char *buf, size_t buflen) 184{ 185 return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); 186} 187 188 189int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, 190 char *buf, size_t buflen) 191{ 192 u8 addr[ETH_ALEN]; 193 int ret; 194 const char *pos; 195 struct sta_info *sta; 196 197 if (hwaddr_aton(txtaddr, addr)) { 198 ret = os_snprintf(buf, buflen, "FAIL\n"); 199 if (os_snprintf_error(buflen, ret)) 200 return 0; 201 return ret; 202 } 203 204 sta = ap_get_sta(hapd, addr); 205 if (sta == NULL) 206 return -1; 207 208 pos = os_strchr(txtaddr, ' '); 209 if (pos) { 210 pos++; 211 212#ifdef HOSTAPD_DUMP_STATE 213 if (os_strcmp(pos, "eapol") == 0) { 214 if (sta->eapol_sm == NULL) 215 return -1; 216 return eapol_auth_dump_state(sta->eapol_sm, buf, 217 buflen); 218 } 219#endif /* HOSTAPD_DUMP_STATE */ 220 221 return -1; 222 } 223 224 ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); 225 ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); 226 227 return ret; 228} 229 230 231int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, 232 char *buf, size_t buflen) 233{ 234 u8 addr[ETH_ALEN]; 235 struct sta_info *sta; 236 int ret; 237 238 if (hwaddr_aton(txtaddr, addr) || 239 (sta = ap_get_sta(hapd, addr)) == NULL) { 240 ret = os_snprintf(buf, buflen, "FAIL\n"); 241 if (os_snprintf_error(buflen, ret)) 242 return 0; 243 return ret; 244 } 245 246 if (!sta->next) 247 return 0; 248 249 return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); 250} 251 252 253#ifdef CONFIG_P2P_MANAGER 254static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, 255 u8 minor_reason_code, const u8 *addr) 256{ 257 struct ieee80211_mgmt *mgmt; 258 int ret; 259 u8 *pos; 260 261 if (hapd->driver->send_frame == NULL) 262 return -1; 263 264 mgmt = os_zalloc(sizeof(*mgmt) + 100); 265 if (mgmt == NULL) 266 return -1; 267 268 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); 269 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR 270 " with minor reason code %u (stype=%u (%s))", 271 MAC2STR(addr), minor_reason_code, stype, 272 fc2str(mgmt->frame_control)); 273 274 os_memcpy(mgmt->da, addr, ETH_ALEN); 275 os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 276 os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 277 if (stype == WLAN_FC_STYPE_DEAUTH) { 278 mgmt->u.deauth.reason_code = 279 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 280 pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); 281 } else { 282 mgmt->u.disassoc.reason_code = 283 host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 284 pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); 285 } 286 287 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 288 *pos++ = 4 + 3 + 1; 289 WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE); 290 pos += 4; 291 292 *pos++ = P2P_ATTR_MINOR_REASON_CODE; 293 WPA_PUT_LE16(pos, 1); 294 pos += 2; 295 *pos++ = minor_reason_code; 296 297 ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, 298 pos - (u8 *) mgmt, 1); 299 os_free(mgmt); 300 301 return ret < 0 ? -1 : 0; 302} 303#endif /* CONFIG_P2P_MANAGER */ 304 305 306int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, 307 const char *txtaddr) 308{ 309 u8 addr[ETH_ALEN]; 310 struct sta_info *sta; 311 const char *pos; 312 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 313 314 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", 315 txtaddr); 316 317 if (hwaddr_aton(txtaddr, addr)) 318 return -1; 319 320 pos = os_strstr(txtaddr, " reason="); 321 if (pos) 322 reason = atoi(pos + 8); 323 324 pos = os_strstr(txtaddr, " test="); 325 if (pos) { 326 struct ieee80211_mgmt mgmt; 327 int encrypt; 328 if (hapd->driver->send_frame == NULL) 329 return -1; 330 pos += 6; 331 encrypt = atoi(pos); 332 os_memset(&mgmt, 0, sizeof(mgmt)); 333 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 334 WLAN_FC_STYPE_DEAUTH); 335 os_memcpy(mgmt.da, addr, ETH_ALEN); 336 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 337 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 338 mgmt.u.deauth.reason_code = host_to_le16(reason); 339 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 340 IEEE80211_HDRLEN + 341 sizeof(mgmt.u.deauth), 342 encrypt) < 0) 343 return -1; 344 return 0; 345 } 346 347#ifdef CONFIG_P2P_MANAGER 348 pos = os_strstr(txtaddr, " p2p="); 349 if (pos) { 350 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, 351 atoi(pos + 5), addr); 352 } 353#endif /* CONFIG_P2P_MANAGER */ 354 355 hostapd_drv_sta_deauth(hapd, addr, reason); 356 sta = ap_get_sta(hapd, addr); 357 if (sta) 358 ap_sta_deauthenticate(hapd, sta, reason); 359 else if (addr[0] == 0xff) 360 hostapd_free_stas(hapd); 361 362 return 0; 363} 364 365 366int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, 367 const char *txtaddr) 368{ 369 u8 addr[ETH_ALEN]; 370 struct sta_info *sta; 371 const char *pos; 372 u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 373 374 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", 375 txtaddr); 376 377 if (hwaddr_aton(txtaddr, addr)) 378 return -1; 379 380 pos = os_strstr(txtaddr, " reason="); 381 if (pos) 382 reason = atoi(pos + 8); 383 384 pos = os_strstr(txtaddr, " test="); 385 if (pos) { 386 struct ieee80211_mgmt mgmt; 387 int encrypt; 388 if (hapd->driver->send_frame == NULL) 389 return -1; 390 pos += 6; 391 encrypt = atoi(pos); 392 os_memset(&mgmt, 0, sizeof(mgmt)); 393 mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 394 WLAN_FC_STYPE_DISASSOC); 395 os_memcpy(mgmt.da, addr, ETH_ALEN); 396 os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 397 os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 398 mgmt.u.disassoc.reason_code = host_to_le16(reason); 399 if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 400 IEEE80211_HDRLEN + 401 sizeof(mgmt.u.deauth), 402 encrypt) < 0) 403 return -1; 404 return 0; 405 } 406 407#ifdef CONFIG_P2P_MANAGER 408 pos = os_strstr(txtaddr, " p2p="); 409 if (pos) { 410 return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, 411 atoi(pos + 5), addr); 412 } 413#endif /* CONFIG_P2P_MANAGER */ 414 415 hostapd_drv_sta_disassoc(hapd, addr, reason); 416 sta = ap_get_sta(hapd, addr); 417 if (sta) 418 ap_sta_disassociate(hapd, sta, reason); 419 else if (addr[0] == 0xff) 420 hostapd_free_stas(hapd); 421 422 return 0; 423} 424 425 426int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, 427 size_t buflen) 428{ 429 struct hostapd_iface *iface = hapd->iface; 430 int len = 0, ret; 431 size_t i; 432 433 ret = os_snprintf(buf + len, buflen - len, 434 "state=%s\n" 435 "phy=%s\n" 436 "freq=%d\n" 437 "num_sta_non_erp=%d\n" 438 "num_sta_no_short_slot_time=%d\n" 439 "num_sta_no_short_preamble=%d\n" 440 "olbc=%d\n" 441 "num_sta_ht_no_gf=%d\n" 442 "num_sta_no_ht=%d\n" 443 "num_sta_ht_20_mhz=%d\n" 444 "num_sta_ht40_intolerant=%d\n" 445 "olbc_ht=%d\n" 446 "ht_op_mode=0x%x\n", 447 hostapd_state_text(iface->state), 448 iface->phy, 449 iface->freq, 450 iface->num_sta_non_erp, 451 iface->num_sta_no_short_slot_time, 452 iface->num_sta_no_short_preamble, 453 iface->olbc, 454 iface->num_sta_ht_no_gf, 455 iface->num_sta_no_ht, 456 iface->num_sta_ht_20mhz, 457 iface->num_sta_ht40_intolerant, 458 iface->olbc_ht, 459 iface->ht_op_mode); 460 if (os_snprintf_error(buflen - len, ret)) 461 return len; 462 len += ret; 463 464 if (!iface->cac_started || !iface->dfs_cac_ms) { 465 ret = os_snprintf(buf + len, buflen - len, 466 "cac_time_seconds=%d\n" 467 "cac_time_left_seconds=N/A\n", 468 iface->dfs_cac_ms / 1000); 469 } else { 470 /* CAC started and CAC time set - calculate remaining time */ 471 struct os_reltime now; 472 unsigned int left_time; 473 474 os_reltime_age(&iface->dfs_cac_start, &now); 475 left_time = iface->dfs_cac_ms / 1000 - now.sec; 476 ret = os_snprintf(buf + len, buflen - len, 477 "cac_time_seconds=%u\n" 478 "cac_time_left_seconds=%u\n", 479 iface->dfs_cac_ms / 1000, 480 left_time); 481 } 482 if (os_snprintf_error(buflen - len, ret)) 483 return len; 484 len += ret; 485 486 ret = os_snprintf(buf + len, buflen - len, 487 "channel=%u\n" 488 "secondary_channel=%d\n" 489 "ieee80211n=%d\n" 490 "ieee80211ac=%d\n" 491 "vht_oper_chwidth=%d\n" 492 "vht_oper_centr_freq_seg0_idx=%d\n" 493 "vht_oper_centr_freq_seg1_idx=%d\n", 494 iface->conf->channel, 495 iface->conf->secondary_channel, 496 iface->conf->ieee80211n, 497 iface->conf->ieee80211ac, 498 iface->conf->vht_oper_chwidth, 499 iface->conf->vht_oper_centr_freq_seg0_idx, 500 iface->conf->vht_oper_centr_freq_seg1_idx); 501 if (os_snprintf_error(buflen - len, ret)) 502 return len; 503 len += ret; 504 505 for (i = 0; i < iface->num_bss; i++) { 506 struct hostapd_data *bss = iface->bss[i]; 507 ret = os_snprintf(buf + len, buflen - len, 508 "bss[%d]=%s\n" 509 "bssid[%d]=" MACSTR "\n" 510 "ssid[%d]=%s\n" 511 "num_sta[%d]=%d\n", 512 (int) i, bss->conf->iface, 513 (int) i, MAC2STR(bss->own_addr), 514 (int) i, 515 wpa_ssid_txt(bss->conf->ssid.ssid, 516 bss->conf->ssid.ssid_len), 517 (int) i, bss->num_sta); 518 if (os_snprintf_error(buflen - len, ret)) 519 return len; 520 len += ret; 521 } 522 523 return len; 524} 525 526 527int hostapd_parse_csa_settings(const char *pos, 528 struct csa_settings *settings) 529{ 530 char *end; 531 532 os_memset(settings, 0, sizeof(*settings)); 533 settings->cs_count = strtol(pos, &end, 10); 534 if (pos == end) { 535 wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); 536 return -1; 537 } 538 539 settings->freq_params.freq = atoi(end); 540 if (settings->freq_params.freq == 0) { 541 wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); 542 return -1; 543 } 544 545#define SET_CSA_SETTING(str) \ 546 do { \ 547 const char *pos2 = os_strstr(pos, " " #str "="); \ 548 if (pos2) { \ 549 pos2 += sizeof(" " #str "=") - 1; \ 550 settings->freq_params.str = atoi(pos2); \ 551 } \ 552 } while (0) 553 554 SET_CSA_SETTING(center_freq1); 555 SET_CSA_SETTING(center_freq2); 556 SET_CSA_SETTING(bandwidth); 557 SET_CSA_SETTING(sec_channel_offset); 558 settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); 559 settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); 560 settings->block_tx = !!os_strstr(pos, " blocktx"); 561#undef SET_CSA_SETTING 562 563 return 0; 564} 565 566 567int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) 568{ 569 return hostapd_drv_stop_ap(hapd); 570} 571 572 573int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, 574 size_t len) 575{ 576 return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); 577} 578 579 580void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) 581{ 582 wpa_auth_pmksa_flush(hapd->wpa_auth); 583} 584