1214501Srpaulo/* 2214501Srpaulo * Control interface for shared AP commands 3346981Scy * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "utils/includes.h" 10214501Srpaulo 11214501Srpaulo#include "utils/common.h" 12252726Srpaulo#include "common/ieee802_11_defs.h" 13281806Srpaulo#include "common/sae.h" 14281806Srpaulo#include "eapol_auth/eapol_auth_sm.h" 15289549Srpaulo#include "fst/fst_ctrl_iface.h" 16214501Srpaulo#include "hostapd.h" 17214501Srpaulo#include "ieee802_1x.h" 18214501Srpaulo#include "wpa_auth.h" 19214501Srpaulo#include "ieee802_11.h" 20214501Srpaulo#include "sta_info.h" 21214501Srpaulo#include "wps_hostapd.h" 22252726Srpaulo#include "p2p_hostapd.h" 23214501Srpaulo#include "ctrl_iface_ap.h" 24252726Srpaulo#include "ap_drv_ops.h" 25337817Scy#include "mbo_ap.h" 26337817Scy#include "taxonomy.h" 27214501Srpaulo 28214501Srpaulo 29346981Scystatic size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, 30346981Scy size_t curr_len, const u8 *mcs_set) 31346981Scy{ 32346981Scy int ret; 33346981Scy size_t len = curr_len; 34346981Scy 35346981Scy ret = os_snprintf(buf + len, buflen - len, 36346981Scy "ht_mcs_bitmask="); 37346981Scy if (os_snprintf_error(buflen - len, ret)) 38346981Scy return len; 39346981Scy len += ret; 40346981Scy 41346981Scy /* 77 first bits (+ 3 reserved bits) */ 42346981Scy len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10); 43346981Scy 44346981Scy ret = os_snprintf(buf + len, buflen - len, "\n"); 45346981Scy if (os_snprintf_error(buflen - len, ret)) 46346981Scy return curr_len; 47346981Scy len += ret; 48346981Scy 49346981Scy return len; 50346981Scy} 51346981Scy 52346981Scy 53281806Srpaulostatic int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, 54281806Srpaulo struct sta_info *sta, 55281806Srpaulo char *buf, size_t buflen) 56281806Srpaulo{ 57281806Srpaulo struct hostap_sta_driver_data data; 58281806Srpaulo int ret; 59346981Scy int len = 0; 60281806Srpaulo 61281806Srpaulo if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) 62281806Srpaulo return 0; 63281806Srpaulo 64281806Srpaulo ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" 65346981Scy "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" 66346981Scy "signal=%d\n", 67281806Srpaulo data.rx_packets, data.tx_packets, 68346981Scy data.rx_bytes, data.tx_bytes, data.inactive_msec, 69346981Scy data.signal); 70281806Srpaulo if (os_snprintf_error(buflen, ret)) 71281806Srpaulo return 0; 72346981Scy len += ret; 73346981Scy 74346981Scy ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", 75346981Scy data.current_rx_rate); 76346981Scy if (os_snprintf_error(buflen - len, ret)) 77346981Scy return len; 78346981Scy len += ret; 79346981Scy if (data.flags & STA_DRV_DATA_RX_MCS) { 80346981Scy ret = os_snprintf(buf + len, buflen - len, " mcs %u", 81346981Scy data.rx_mcs); 82346981Scy if (!os_snprintf_error(buflen - len, ret)) 83346981Scy len += ret; 84346981Scy } 85346981Scy if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { 86346981Scy ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", 87346981Scy data.rx_vhtmcs); 88346981Scy if (!os_snprintf_error(buflen - len, ret)) 89346981Scy len += ret; 90346981Scy } 91346981Scy if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { 92346981Scy ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", 93346981Scy data.rx_vht_nss); 94346981Scy if (!os_snprintf_error(buflen - len, ret)) 95346981Scy len += ret; 96346981Scy } 97346981Scy if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { 98346981Scy ret = os_snprintf(buf + len, buflen - len, " shortGI"); 99346981Scy if (!os_snprintf_error(buflen - len, ret)) 100346981Scy len += ret; 101346981Scy } 102346981Scy ret = os_snprintf(buf + len, buflen - len, "\n"); 103346981Scy if (!os_snprintf_error(buflen - len, ret)) 104346981Scy len += ret; 105346981Scy 106346981Scy ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", 107346981Scy data.current_tx_rate); 108346981Scy if (os_snprintf_error(buflen - len, ret)) 109346981Scy return len; 110346981Scy len += ret; 111346981Scy if (data.flags & STA_DRV_DATA_TX_MCS) { 112346981Scy ret = os_snprintf(buf + len, buflen - len, " mcs %u", 113346981Scy data.tx_mcs); 114346981Scy if (!os_snprintf_error(buflen - len, ret)) 115346981Scy len += ret; 116346981Scy } 117346981Scy if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { 118346981Scy ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", 119346981Scy data.tx_vhtmcs); 120346981Scy if (!os_snprintf_error(buflen - len, ret)) 121346981Scy len += ret; 122346981Scy } 123346981Scy if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { 124346981Scy ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", 125346981Scy data.tx_vht_nss); 126346981Scy if (!os_snprintf_error(buflen - len, ret)) 127346981Scy len += ret; 128346981Scy } 129346981Scy if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { 130346981Scy ret = os_snprintf(buf + len, buflen - len, " shortGI"); 131346981Scy if (!os_snprintf_error(buflen - len, ret)) 132346981Scy len += ret; 133346981Scy } 134346981Scy ret = os_snprintf(buf + len, buflen - len, "\n"); 135346981Scy if (!os_snprintf_error(buflen - len, ret)) 136346981Scy len += ret; 137346981Scy 138346981Scy if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { 139346981Scy ret = os_snprintf(buf + len, buflen - len, 140346981Scy "rx_vht_mcs_map=%04x\n" 141346981Scy "tx_vht_mcs_map=%04x\n", 142346981Scy le_to_host16(sta->vht_capabilities-> 143346981Scy vht_supported_mcs_set.rx_map), 144346981Scy le_to_host16(sta->vht_capabilities-> 145346981Scy vht_supported_mcs_set.tx_map)); 146346981Scy if (!os_snprintf_error(buflen - len, ret)) 147346981Scy len += ret; 148346981Scy } 149346981Scy 150346981Scy if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { 151346981Scy len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, 152346981Scy sta->ht_capabilities-> 153346981Scy supported_mcs_set); 154346981Scy } 155346981Scy 156346981Scy if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { 157346981Scy ret = os_snprintf(buf + len, buflen - len, 158346981Scy "last_ack_signal=%d\n", data.last_ack_rssi); 159346981Scy if (!os_snprintf_error(buflen - len, ret)) 160346981Scy len += ret; 161346981Scy } 162346981Scy 163346981Scy return len; 164281806Srpaulo} 165281806Srpaulo 166281806Srpaulo 167252726Srpaulostatic int hostapd_get_sta_conn_time(struct sta_info *sta, 168252726Srpaulo char *buf, size_t buflen) 169252726Srpaulo{ 170281806Srpaulo struct os_reltime age; 171281806Srpaulo int ret; 172252726Srpaulo 173252726Srpaulo if (!sta->connected_time.sec) 174252726Srpaulo return 0; 175252726Srpaulo 176281806Srpaulo os_reltime_age(&sta->connected_time, &age); 177252726Srpaulo 178281806Srpaulo ret = os_snprintf(buf, buflen, "connected_time=%u\n", 179252726Srpaulo (unsigned int) age.sec); 180281806Srpaulo if (os_snprintf_error(buflen, ret)) 181281806Srpaulo return 0; 182281806Srpaulo return ret; 183281806Srpaulo} 184252726Srpaulo 185281806Srpaulo 186281806Srpaulostatic const char * timeout_next_str(int val) 187281806Srpaulo{ 188281806Srpaulo switch (val) { 189281806Srpaulo case STA_NULLFUNC: 190281806Srpaulo return "NULLFUNC POLL"; 191281806Srpaulo case STA_DISASSOC: 192281806Srpaulo return "DISASSOC"; 193281806Srpaulo case STA_DEAUTH: 194281806Srpaulo return "DEAUTH"; 195281806Srpaulo case STA_REMOVE: 196281806Srpaulo return "REMOVE"; 197281806Srpaulo case STA_DISASSOC_FROM_CLI: 198281806Srpaulo return "DISASSOC_FROM_CLI"; 199281806Srpaulo } 200281806Srpaulo 201281806Srpaulo return "?"; 202252726Srpaulo} 203252726Srpaulo 204252726Srpaulo 205214501Srpaulostatic int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, 206214501Srpaulo struct sta_info *sta, 207214501Srpaulo char *buf, size_t buflen) 208214501Srpaulo{ 209281806Srpaulo int len, res, ret, i; 210346981Scy const char *keyid; 211214501Srpaulo 212281806Srpaulo if (!sta) 213281806Srpaulo return 0; 214214501Srpaulo 215214501Srpaulo len = 0; 216281806Srpaulo ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", 217214501Srpaulo MAC2STR(sta->addr)); 218281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 219214501Srpaulo return len; 220214501Srpaulo len += ret; 221214501Srpaulo 222281806Srpaulo ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); 223281806Srpaulo if (ret < 0) 224281806Srpaulo return len; 225281806Srpaulo len += ret; 226281806Srpaulo 227281806Srpaulo ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" 228281806Srpaulo "listen_interval=%d\nsupported_rates=", 229281806Srpaulo sta->aid, sta->capability, sta->listen_interval); 230281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 231281806Srpaulo return len; 232281806Srpaulo len += ret; 233281806Srpaulo 234281806Srpaulo for (i = 0; i < sta->supported_rates_len; i++) { 235281806Srpaulo ret = os_snprintf(buf + len, buflen - len, "%02x%s", 236281806Srpaulo sta->supported_rates[i], 237281806Srpaulo i + 1 < sta->supported_rates_len ? " " : ""); 238281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 239281806Srpaulo return len; 240281806Srpaulo len += ret; 241281806Srpaulo } 242281806Srpaulo 243281806Srpaulo ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", 244281806Srpaulo timeout_next_str(sta->timeout_next)); 245281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 246281806Srpaulo return len; 247281806Srpaulo len += ret; 248281806Srpaulo 249214501Srpaulo res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); 250214501Srpaulo if (res >= 0) 251214501Srpaulo len += res; 252214501Srpaulo res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); 253214501Srpaulo if (res >= 0) 254214501Srpaulo len += res; 255214501Srpaulo res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); 256214501Srpaulo if (res >= 0) 257214501Srpaulo len += res; 258214501Srpaulo res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, 259214501Srpaulo buflen - len); 260214501Srpaulo if (res >= 0) 261214501Srpaulo len += res; 262252726Srpaulo res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); 263252726Srpaulo if (res >= 0) 264252726Srpaulo len += res; 265214501Srpaulo 266281806Srpaulo len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); 267281806Srpaulo len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); 268252726Srpaulo 269281806Srpaulo#ifdef CONFIG_SAE 270281806Srpaulo if (sta->sae && sta->sae->state == SAE_ACCEPTED) { 271281806Srpaulo res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", 272281806Srpaulo sta->sae->group); 273281806Srpaulo if (!os_snprintf_error(buflen - len, res)) 274281806Srpaulo len += res; 275281806Srpaulo } 276281806Srpaulo#endif /* CONFIG_SAE */ 277281806Srpaulo 278289549Srpaulo if (sta->vlan_id > 0) { 279289549Srpaulo res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n", 280289549Srpaulo sta->vlan_id); 281289549Srpaulo if (!os_snprintf_error(buflen - len, res)) 282289549Srpaulo len += res; 283289549Srpaulo } 284289549Srpaulo 285337817Scy res = mbo_ap_get_info(sta, buf + len, buflen - len); 286337817Scy if (res >= 0) 287337817Scy len += res; 288337817Scy 289337817Scy if (sta->supp_op_classes && 290337817Scy buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { 291337817Scy len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); 292337817Scy len += wpa_snprintf_hex(buf + len, buflen - len, 293337817Scy sta->supp_op_classes + 1, 294337817Scy sta->supp_op_classes[0]); 295337817Scy len += os_snprintf(buf + len, buflen - len, "\n"); 296337817Scy } 297337817Scy 298346981Scy if (sta->power_capab) { 299346981Scy ret = os_snprintf(buf + len, buflen - len, 300346981Scy "min_txpower=%d\n" 301346981Scy "max_txpower=%d\n", 302346981Scy sta->min_tx_power, sta->max_tx_power); 303346981Scy if (!os_snprintf_error(buflen - len, ret)) 304346981Scy len += ret; 305346981Scy } 306346981Scy 307346981Scy#ifdef CONFIG_IEEE80211AC 308346981Scy if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { 309346981Scy res = os_snprintf(buf + len, buflen - len, 310346981Scy "vht_caps_info=0x%08x\n", 311346981Scy le_to_host32(sta->vht_capabilities-> 312346981Scy vht_capabilities_info)); 313346981Scy if (!os_snprintf_error(buflen - len, res)) 314346981Scy len += res; 315346981Scy } 316346981Scy#endif /* CONFIG_IEEE80211AC */ 317346981Scy 318346981Scy#ifdef CONFIG_IEEE80211N 319346981Scy if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { 320346981Scy res = os_snprintf(buf + len, buflen - len, 321346981Scy "ht_caps_info=0x%04x\n", 322346981Scy le_to_host16(sta->ht_capabilities-> 323346981Scy ht_capabilities_info)); 324346981Scy if (!os_snprintf_error(buflen - len, res)) 325346981Scy len += res; 326346981Scy } 327346981Scy#endif /* CONFIG_IEEE80211N */ 328346981Scy 329346981Scy if (sta->ext_capability && 330346981Scy buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) { 331346981Scy len += os_snprintf(buf + len, buflen - len, "ext_capab="); 332346981Scy len += wpa_snprintf_hex(buf + len, buflen - len, 333346981Scy sta->ext_capability + 1, 334346981Scy sta->ext_capability[0]); 335346981Scy len += os_snprintf(buf + len, buflen - len, "\n"); 336346981Scy } 337346981Scy 338346981Scy if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) { 339346981Scy ret = os_snprintf(buf + len, buflen - len, 340346981Scy "wds_sta_ifname=%s\n", sta->ifname_wds); 341346981Scy if (!os_snprintf_error(buflen - len, ret)) 342346981Scy len += ret; 343346981Scy } 344346981Scy 345346981Scy keyid = ap_sta_wpa_get_keyid(hapd, sta); 346346981Scy if (keyid) { 347346981Scy ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid); 348346981Scy if (!os_snprintf_error(buflen - len, ret)) 349346981Scy len += ret; 350346981Scy } 351346981Scy 352214501Srpaulo return len; 353214501Srpaulo} 354214501Srpaulo 355214501Srpaulo 356214501Srpauloint hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, 357214501Srpaulo char *buf, size_t buflen) 358214501Srpaulo{ 359214501Srpaulo return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); 360214501Srpaulo} 361214501Srpaulo 362214501Srpaulo 363214501Srpauloint hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, 364214501Srpaulo char *buf, size_t buflen) 365214501Srpaulo{ 366214501Srpaulo u8 addr[ETH_ALEN]; 367214501Srpaulo int ret; 368281806Srpaulo const char *pos; 369281806Srpaulo struct sta_info *sta; 370214501Srpaulo 371214501Srpaulo if (hwaddr_aton(txtaddr, addr)) { 372214501Srpaulo ret = os_snprintf(buf, buflen, "FAIL\n"); 373281806Srpaulo if (os_snprintf_error(buflen, ret)) 374214501Srpaulo return 0; 375214501Srpaulo return ret; 376214501Srpaulo } 377281806Srpaulo 378281806Srpaulo sta = ap_get_sta(hapd, addr); 379281806Srpaulo if (sta == NULL) 380281806Srpaulo return -1; 381281806Srpaulo 382281806Srpaulo pos = os_strchr(txtaddr, ' '); 383281806Srpaulo if (pos) { 384281806Srpaulo pos++; 385281806Srpaulo 386281806Srpaulo#ifdef HOSTAPD_DUMP_STATE 387281806Srpaulo if (os_strcmp(pos, "eapol") == 0) { 388281806Srpaulo if (sta->eapol_sm == NULL) 389281806Srpaulo return -1; 390281806Srpaulo return eapol_auth_dump_state(sta->eapol_sm, buf, 391281806Srpaulo buflen); 392281806Srpaulo } 393281806Srpaulo#endif /* HOSTAPD_DUMP_STATE */ 394281806Srpaulo 395281806Srpaulo return -1; 396281806Srpaulo } 397281806Srpaulo 398289549Srpaulo ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); 399289549Srpaulo ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret); 400289549Srpaulo 401289549Srpaulo return ret; 402214501Srpaulo} 403214501Srpaulo 404214501Srpaulo 405214501Srpauloint hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, 406214501Srpaulo char *buf, size_t buflen) 407214501Srpaulo{ 408214501Srpaulo u8 addr[ETH_ALEN]; 409214501Srpaulo struct sta_info *sta; 410214501Srpaulo int ret; 411214501Srpaulo 412214501Srpaulo if (hwaddr_aton(txtaddr, addr) || 413214501Srpaulo (sta = ap_get_sta(hapd, addr)) == NULL) { 414214501Srpaulo ret = os_snprintf(buf, buflen, "FAIL\n"); 415281806Srpaulo if (os_snprintf_error(buflen, ret)) 416214501Srpaulo return 0; 417214501Srpaulo return ret; 418281806Srpaulo } 419281806Srpaulo 420281806Srpaulo if (!sta->next) 421281806Srpaulo return 0; 422281806Srpaulo 423214501Srpaulo return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); 424214501Srpaulo} 425252726Srpaulo 426252726Srpaulo 427252726Srpaulo#ifdef CONFIG_P2P_MANAGER 428252726Srpaulostatic int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, 429252726Srpaulo u8 minor_reason_code, const u8 *addr) 430252726Srpaulo{ 431252726Srpaulo struct ieee80211_mgmt *mgmt; 432252726Srpaulo int ret; 433252726Srpaulo u8 *pos; 434252726Srpaulo 435337817Scy if (!hapd->drv_priv || !hapd->driver->send_frame) 436252726Srpaulo return -1; 437252726Srpaulo 438252726Srpaulo mgmt = os_zalloc(sizeof(*mgmt) + 100); 439252726Srpaulo if (mgmt == NULL) 440252726Srpaulo return -1; 441252726Srpaulo 442281806Srpaulo mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); 443252726Srpaulo wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR 444281806Srpaulo " with minor reason code %u (stype=%u (%s))", 445281806Srpaulo MAC2STR(addr), minor_reason_code, stype, 446337817Scy fc2str(le_to_host16(mgmt->frame_control))); 447252726Srpaulo 448252726Srpaulo os_memcpy(mgmt->da, addr, ETH_ALEN); 449252726Srpaulo os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); 450252726Srpaulo os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); 451252726Srpaulo if (stype == WLAN_FC_STYPE_DEAUTH) { 452252726Srpaulo mgmt->u.deauth.reason_code = 453252726Srpaulo host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 454346981Scy pos = mgmt->u.deauth.variable; 455252726Srpaulo } else { 456252726Srpaulo mgmt->u.disassoc.reason_code = 457252726Srpaulo host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); 458346981Scy pos = mgmt->u.disassoc.variable; 459252726Srpaulo } 460252726Srpaulo 461252726Srpaulo *pos++ = WLAN_EID_VENDOR_SPECIFIC; 462252726Srpaulo *pos++ = 4 + 3 + 1; 463281806Srpaulo WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE); 464281806Srpaulo pos += 4; 465252726Srpaulo 466252726Srpaulo *pos++ = P2P_ATTR_MINOR_REASON_CODE; 467252726Srpaulo WPA_PUT_LE16(pos, 1); 468252726Srpaulo pos += 2; 469252726Srpaulo *pos++ = minor_reason_code; 470252726Srpaulo 471252726Srpaulo ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, 472252726Srpaulo pos - (u8 *) mgmt, 1); 473252726Srpaulo os_free(mgmt); 474252726Srpaulo 475252726Srpaulo return ret < 0 ? -1 : 0; 476252726Srpaulo} 477252726Srpaulo#endif /* CONFIG_P2P_MANAGER */ 478252726Srpaulo 479252726Srpaulo 480252726Srpauloint hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, 481252726Srpaulo const char *txtaddr) 482252726Srpaulo{ 483252726Srpaulo u8 addr[ETH_ALEN]; 484252726Srpaulo struct sta_info *sta; 485252726Srpaulo const char *pos; 486281806Srpaulo u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 487252726Srpaulo 488252726Srpaulo wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", 489252726Srpaulo txtaddr); 490252726Srpaulo 491252726Srpaulo if (hwaddr_aton(txtaddr, addr)) 492252726Srpaulo return -1; 493252726Srpaulo 494281806Srpaulo pos = os_strstr(txtaddr, " reason="); 495281806Srpaulo if (pos) 496281806Srpaulo reason = atoi(pos + 8); 497281806Srpaulo 498252726Srpaulo pos = os_strstr(txtaddr, " test="); 499252726Srpaulo if (pos) { 500252726Srpaulo struct ieee80211_mgmt mgmt; 501252726Srpaulo int encrypt; 502337817Scy if (!hapd->drv_priv || !hapd->driver->send_frame) 503252726Srpaulo return -1; 504252726Srpaulo pos += 6; 505252726Srpaulo encrypt = atoi(pos); 506252726Srpaulo os_memset(&mgmt, 0, sizeof(mgmt)); 507252726Srpaulo mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 508252726Srpaulo WLAN_FC_STYPE_DEAUTH); 509252726Srpaulo os_memcpy(mgmt.da, addr, ETH_ALEN); 510252726Srpaulo os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 511252726Srpaulo os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 512281806Srpaulo mgmt.u.deauth.reason_code = host_to_le16(reason); 513252726Srpaulo if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 514252726Srpaulo IEEE80211_HDRLEN + 515252726Srpaulo sizeof(mgmt.u.deauth), 516252726Srpaulo encrypt) < 0) 517252726Srpaulo return -1; 518252726Srpaulo return 0; 519252726Srpaulo } 520252726Srpaulo 521252726Srpaulo#ifdef CONFIG_P2P_MANAGER 522252726Srpaulo pos = os_strstr(txtaddr, " p2p="); 523252726Srpaulo if (pos) { 524252726Srpaulo return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, 525252726Srpaulo atoi(pos + 5), addr); 526252726Srpaulo } 527252726Srpaulo#endif /* CONFIG_P2P_MANAGER */ 528252726Srpaulo 529337817Scy if (os_strstr(txtaddr, " tx=0")) 530337817Scy hostapd_drv_sta_remove(hapd, addr); 531337817Scy else 532337817Scy hostapd_drv_sta_deauth(hapd, addr, reason); 533252726Srpaulo sta = ap_get_sta(hapd, addr); 534252726Srpaulo if (sta) 535281806Srpaulo ap_sta_deauthenticate(hapd, sta, reason); 536252726Srpaulo else if (addr[0] == 0xff) 537252726Srpaulo hostapd_free_stas(hapd); 538252726Srpaulo 539252726Srpaulo return 0; 540252726Srpaulo} 541252726Srpaulo 542252726Srpaulo 543252726Srpauloint hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, 544252726Srpaulo const char *txtaddr) 545252726Srpaulo{ 546252726Srpaulo u8 addr[ETH_ALEN]; 547252726Srpaulo struct sta_info *sta; 548252726Srpaulo const char *pos; 549281806Srpaulo u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; 550252726Srpaulo 551252726Srpaulo wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", 552252726Srpaulo txtaddr); 553252726Srpaulo 554252726Srpaulo if (hwaddr_aton(txtaddr, addr)) 555252726Srpaulo return -1; 556252726Srpaulo 557281806Srpaulo pos = os_strstr(txtaddr, " reason="); 558281806Srpaulo if (pos) 559281806Srpaulo reason = atoi(pos + 8); 560281806Srpaulo 561252726Srpaulo pos = os_strstr(txtaddr, " test="); 562252726Srpaulo if (pos) { 563252726Srpaulo struct ieee80211_mgmt mgmt; 564252726Srpaulo int encrypt; 565337817Scy if (!hapd->drv_priv || !hapd->driver->send_frame) 566252726Srpaulo return -1; 567252726Srpaulo pos += 6; 568252726Srpaulo encrypt = atoi(pos); 569252726Srpaulo os_memset(&mgmt, 0, sizeof(mgmt)); 570252726Srpaulo mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 571252726Srpaulo WLAN_FC_STYPE_DISASSOC); 572252726Srpaulo os_memcpy(mgmt.da, addr, ETH_ALEN); 573252726Srpaulo os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); 574252726Srpaulo os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); 575281806Srpaulo mgmt.u.disassoc.reason_code = host_to_le16(reason); 576252726Srpaulo if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, 577252726Srpaulo IEEE80211_HDRLEN + 578252726Srpaulo sizeof(mgmt.u.deauth), 579252726Srpaulo encrypt) < 0) 580252726Srpaulo return -1; 581252726Srpaulo return 0; 582252726Srpaulo } 583252726Srpaulo 584252726Srpaulo#ifdef CONFIG_P2P_MANAGER 585252726Srpaulo pos = os_strstr(txtaddr, " p2p="); 586252726Srpaulo if (pos) { 587252726Srpaulo return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, 588252726Srpaulo atoi(pos + 5), addr); 589252726Srpaulo } 590252726Srpaulo#endif /* CONFIG_P2P_MANAGER */ 591252726Srpaulo 592337817Scy if (os_strstr(txtaddr, " tx=0")) 593337817Scy hostapd_drv_sta_remove(hapd, addr); 594337817Scy else 595337817Scy hostapd_drv_sta_disassoc(hapd, addr, reason); 596252726Srpaulo sta = ap_get_sta(hapd, addr); 597252726Srpaulo if (sta) 598281806Srpaulo ap_sta_disassociate(hapd, sta, reason); 599252726Srpaulo else if (addr[0] == 0xff) 600252726Srpaulo hostapd_free_stas(hapd); 601252726Srpaulo 602252726Srpaulo return 0; 603252726Srpaulo} 604281806Srpaulo 605281806Srpaulo 606337817Scy#ifdef CONFIG_TAXONOMY 607337817Scyint hostapd_ctrl_iface_signature(struct hostapd_data *hapd, 608337817Scy const char *txtaddr, 609337817Scy char *buf, size_t buflen) 610337817Scy{ 611337817Scy u8 addr[ETH_ALEN]; 612337817Scy struct sta_info *sta; 613337817Scy 614337817Scy wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); 615337817Scy 616337817Scy if (hwaddr_aton(txtaddr, addr)) 617337817Scy return -1; 618337817Scy 619337817Scy sta = ap_get_sta(hapd, addr); 620337817Scy if (!sta) 621337817Scy return -1; 622337817Scy 623337817Scy return retrieve_sta_taxonomy(hapd, sta, buf, buflen); 624337817Scy} 625337817Scy#endif /* CONFIG_TAXONOMY */ 626337817Scy 627337817Scy 628337817Scyint hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, 629337817Scy const char *txtaddr) 630337817Scy{ 631337817Scy u8 addr[ETH_ALEN]; 632337817Scy struct sta_info *sta; 633337817Scy 634337817Scy wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); 635337817Scy 636337817Scy if (hwaddr_aton(txtaddr, addr)) 637337817Scy return -1; 638337817Scy 639337817Scy sta = ap_get_sta(hapd, addr); 640337817Scy if (!sta) 641337817Scy return -1; 642337817Scy 643337817Scy hostapd_drv_poll_client(hapd, hapd->own_addr, addr, 644337817Scy sta->flags & WLAN_STA_WMM); 645337817Scy return 0; 646337817Scy} 647337817Scy 648337817Scy 649281806Srpauloint hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, 650281806Srpaulo size_t buflen) 651281806Srpaulo{ 652281806Srpaulo struct hostapd_iface *iface = hapd->iface; 653346981Scy struct hostapd_hw_modes *mode = iface->current_mode; 654346981Scy int len = 0, ret, j; 655281806Srpaulo size_t i; 656281806Srpaulo 657281806Srpaulo ret = os_snprintf(buf + len, buflen - len, 658281806Srpaulo "state=%s\n" 659281806Srpaulo "phy=%s\n" 660281806Srpaulo "freq=%d\n" 661281806Srpaulo "num_sta_non_erp=%d\n" 662281806Srpaulo "num_sta_no_short_slot_time=%d\n" 663281806Srpaulo "num_sta_no_short_preamble=%d\n" 664281806Srpaulo "olbc=%d\n" 665281806Srpaulo "num_sta_ht_no_gf=%d\n" 666281806Srpaulo "num_sta_no_ht=%d\n" 667281806Srpaulo "num_sta_ht_20_mhz=%d\n" 668281806Srpaulo "num_sta_ht40_intolerant=%d\n" 669281806Srpaulo "olbc_ht=%d\n" 670281806Srpaulo "ht_op_mode=0x%x\n", 671281806Srpaulo hostapd_state_text(iface->state), 672281806Srpaulo iface->phy, 673281806Srpaulo iface->freq, 674281806Srpaulo iface->num_sta_non_erp, 675281806Srpaulo iface->num_sta_no_short_slot_time, 676281806Srpaulo iface->num_sta_no_short_preamble, 677281806Srpaulo iface->olbc, 678281806Srpaulo iface->num_sta_ht_no_gf, 679281806Srpaulo iface->num_sta_no_ht, 680281806Srpaulo iface->num_sta_ht_20mhz, 681281806Srpaulo iface->num_sta_ht40_intolerant, 682281806Srpaulo iface->olbc_ht, 683281806Srpaulo iface->ht_op_mode); 684281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 685281806Srpaulo return len; 686281806Srpaulo len += ret; 687281806Srpaulo 688281806Srpaulo if (!iface->cac_started || !iface->dfs_cac_ms) { 689281806Srpaulo ret = os_snprintf(buf + len, buflen - len, 690281806Srpaulo "cac_time_seconds=%d\n" 691281806Srpaulo "cac_time_left_seconds=N/A\n", 692281806Srpaulo iface->dfs_cac_ms / 1000); 693281806Srpaulo } else { 694281806Srpaulo /* CAC started and CAC time set - calculate remaining time */ 695281806Srpaulo struct os_reltime now; 696281806Srpaulo unsigned int left_time; 697281806Srpaulo 698281806Srpaulo os_reltime_age(&iface->dfs_cac_start, &now); 699281806Srpaulo left_time = iface->dfs_cac_ms / 1000 - now.sec; 700281806Srpaulo ret = os_snprintf(buf + len, buflen - len, 701281806Srpaulo "cac_time_seconds=%u\n" 702281806Srpaulo "cac_time_left_seconds=%u\n", 703281806Srpaulo iface->dfs_cac_ms / 1000, 704281806Srpaulo left_time); 705281806Srpaulo } 706281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 707281806Srpaulo return len; 708281806Srpaulo len += ret; 709281806Srpaulo 710281806Srpaulo ret = os_snprintf(buf + len, buflen - len, 711281806Srpaulo "channel=%u\n" 712281806Srpaulo "secondary_channel=%d\n" 713281806Srpaulo "ieee80211n=%d\n" 714346981Scy "ieee80211ac=%d\n" 715351611Scy "ieee80211ax=%d\n" 716346981Scy "beacon_int=%u\n" 717346981Scy "dtim_period=%d\n", 718281806Srpaulo iface->conf->channel, 719337817Scy iface->conf->ieee80211n && !hapd->conf->disable_11n ? 720337817Scy iface->conf->secondary_channel : 0, 721337817Scy iface->conf->ieee80211n && !hapd->conf->disable_11n, 722337817Scy iface->conf->ieee80211ac && 723346981Scy !hapd->conf->disable_11ac, 724351611Scy iface->conf->ieee80211ax, 725346981Scy iface->conf->beacon_int, 726346981Scy hapd->conf->dtim_period); 727281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 728281806Srpaulo return len; 729281806Srpaulo len += ret; 730337817Scy if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { 731337817Scy ret = os_snprintf(buf + len, buflen - len, 732337817Scy "vht_oper_chwidth=%d\n" 733337817Scy "vht_oper_centr_freq_seg0_idx=%d\n" 734346981Scy "vht_oper_centr_freq_seg1_idx=%d\n" 735346981Scy "vht_caps_info=%08x\n", 736337817Scy iface->conf->vht_oper_chwidth, 737337817Scy iface->conf->vht_oper_centr_freq_seg0_idx, 738346981Scy iface->conf->vht_oper_centr_freq_seg1_idx, 739346981Scy iface->conf->vht_capab); 740337817Scy if (os_snprintf_error(buflen - len, ret)) 741337817Scy return len; 742337817Scy len += ret; 743337817Scy } 744281806Srpaulo 745346981Scy if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { 746346981Scy u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); 747346981Scy u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); 748346981Scy 749346981Scy ret = os_snprintf(buf + len, buflen - len, 750346981Scy "rx_vht_mcs_map=%04x\n" 751346981Scy "tx_vht_mcs_map=%04x\n", 752346981Scy rxmap, txmap); 753346981Scy if (os_snprintf_error(buflen - len, ret)) 754346981Scy return len; 755346981Scy len += ret; 756346981Scy } 757346981Scy 758346981Scy if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { 759346981Scy ret = os_snprintf(buf + len, buflen - len, 760346981Scy "ht_caps_info=%04x\n", 761346981Scy hapd->iconf->ht_capab); 762346981Scy if (os_snprintf_error(buflen - len, ret)) 763346981Scy return len; 764346981Scy len += ret; 765346981Scy } 766346981Scy 767346981Scy if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { 768346981Scy len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, 769346981Scy mode->mcs_set); 770346981Scy } 771346981Scy 772346981Scy if (iface->current_rates && iface->num_rates) { 773346981Scy ret = os_snprintf(buf + len, buflen - len, "supported_rates="); 774346981Scy if (os_snprintf_error(buflen - len, ret)) 775346981Scy return len; 776346981Scy len += ret; 777346981Scy 778346981Scy for (j = 0; j < iface->num_rates; j++) { 779346981Scy ret = os_snprintf(buf + len, buflen - len, "%s%02x", 780346981Scy j > 0 ? " " : "", 781346981Scy iface->current_rates[j].rate / 5); 782346981Scy if (os_snprintf_error(buflen - len, ret)) 783346981Scy return len; 784346981Scy len += ret; 785346981Scy } 786346981Scy ret = os_snprintf(buf + len, buflen - len, "\n"); 787346981Scy if (os_snprintf_error(buflen - len, ret)) 788346981Scy return len; 789346981Scy len += ret; 790346981Scy } 791346981Scy 792346981Scy for (j = 0; mode && j < mode->num_channels; j++) { 793346981Scy if (mode->channels[j].freq == iface->freq) { 794346981Scy ret = os_snprintf(buf + len, buflen - len, 795346981Scy "max_txpower=%u\n", 796346981Scy mode->channels[j].max_tx_power); 797346981Scy if (os_snprintf_error(buflen - len, ret)) 798346981Scy return len; 799346981Scy len += ret; 800346981Scy break; 801346981Scy } 802346981Scy } 803346981Scy 804281806Srpaulo for (i = 0; i < iface->num_bss; i++) { 805281806Srpaulo struct hostapd_data *bss = iface->bss[i]; 806281806Srpaulo ret = os_snprintf(buf + len, buflen - len, 807281806Srpaulo "bss[%d]=%s\n" 808281806Srpaulo "bssid[%d]=" MACSTR "\n" 809281806Srpaulo "ssid[%d]=%s\n" 810281806Srpaulo "num_sta[%d]=%d\n", 811281806Srpaulo (int) i, bss->conf->iface, 812281806Srpaulo (int) i, MAC2STR(bss->own_addr), 813281806Srpaulo (int) i, 814281806Srpaulo wpa_ssid_txt(bss->conf->ssid.ssid, 815281806Srpaulo bss->conf->ssid.ssid_len), 816281806Srpaulo (int) i, bss->num_sta); 817281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 818281806Srpaulo return len; 819281806Srpaulo len += ret; 820281806Srpaulo } 821281806Srpaulo 822346981Scy if (hapd->conf->chan_util_avg_period) { 823346981Scy ret = os_snprintf(buf + len, buflen - len, 824346981Scy "chan_util_avg=%u\n", 825346981Scy iface->chan_util_average); 826346981Scy if (os_snprintf_error(buflen - len, ret)) 827346981Scy return len; 828346981Scy len += ret; 829346981Scy } 830346981Scy 831281806Srpaulo return len; 832281806Srpaulo} 833281806Srpaulo 834281806Srpaulo 835281806Srpauloint hostapd_parse_csa_settings(const char *pos, 836281806Srpaulo struct csa_settings *settings) 837281806Srpaulo{ 838281806Srpaulo char *end; 839281806Srpaulo 840281806Srpaulo os_memset(settings, 0, sizeof(*settings)); 841281806Srpaulo settings->cs_count = strtol(pos, &end, 10); 842281806Srpaulo if (pos == end) { 843281806Srpaulo wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); 844281806Srpaulo return -1; 845281806Srpaulo } 846281806Srpaulo 847281806Srpaulo settings->freq_params.freq = atoi(end); 848281806Srpaulo if (settings->freq_params.freq == 0) { 849281806Srpaulo wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); 850281806Srpaulo return -1; 851281806Srpaulo } 852281806Srpaulo 853281806Srpaulo#define SET_CSA_SETTING(str) \ 854281806Srpaulo do { \ 855281806Srpaulo const char *pos2 = os_strstr(pos, " " #str "="); \ 856281806Srpaulo if (pos2) { \ 857281806Srpaulo pos2 += sizeof(" " #str "=") - 1; \ 858281806Srpaulo settings->freq_params.str = atoi(pos2); \ 859281806Srpaulo } \ 860281806Srpaulo } while (0) 861281806Srpaulo 862281806Srpaulo SET_CSA_SETTING(center_freq1); 863281806Srpaulo SET_CSA_SETTING(center_freq2); 864281806Srpaulo SET_CSA_SETTING(bandwidth); 865281806Srpaulo SET_CSA_SETTING(sec_channel_offset); 866281806Srpaulo settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); 867281806Srpaulo settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); 868281806Srpaulo settings->block_tx = !!os_strstr(pos, " blocktx"); 869281806Srpaulo#undef SET_CSA_SETTING 870281806Srpaulo 871281806Srpaulo return 0; 872281806Srpaulo} 873281806Srpaulo 874281806Srpaulo 875281806Srpauloint hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) 876281806Srpaulo{ 877281806Srpaulo return hostapd_drv_stop_ap(hapd); 878281806Srpaulo} 879337817Scy 880337817Scy 881337817Scyint hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, 882337817Scy size_t len) 883337817Scy{ 884337817Scy return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); 885337817Scy} 886337817Scy 887337817Scy 888337817Scyvoid hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) 889337817Scy{ 890337817Scy wpa_auth_pmksa_flush(hapd->wpa_auth); 891337817Scy} 892346981Scy 893346981Scy 894346981Scyint hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) 895346981Scy{ 896346981Scy u8 spa[ETH_ALEN]; 897346981Scy u8 pmkid[PMKID_LEN]; 898346981Scy u8 pmk[PMK_LEN_MAX]; 899346981Scy size_t pmk_len; 900346981Scy char *pos, *pos2; 901346981Scy int akmp = 0, expiration = 0; 902346981Scy 903346981Scy /* 904346981Scy * Entry format: 905346981Scy * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp> 906346981Scy */ 907346981Scy 908346981Scy if (hwaddr_aton(cmd, spa)) 909346981Scy return -1; 910346981Scy 911346981Scy pos = os_strchr(cmd, ' '); 912346981Scy if (!pos) 913346981Scy return -1; 914346981Scy pos++; 915346981Scy 916346981Scy if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) 917346981Scy return -1; 918346981Scy 919346981Scy pos = os_strchr(pos, ' '); 920346981Scy if (!pos) 921346981Scy return -1; 922346981Scy pos++; 923346981Scy 924346981Scy pos2 = os_strchr(pos, ' '); 925346981Scy if (!pos2) 926346981Scy return -1; 927346981Scy pmk_len = (pos2 - pos) / 2; 928346981Scy if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || 929346981Scy hexstr2bin(pos, pmk, pmk_len) < 0) 930346981Scy return -1; 931346981Scy 932346981Scy pos = pos2 + 1; 933346981Scy 934346981Scy if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) 935346981Scy return -1; 936346981Scy 937346981Scy return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, 938346981Scy pmkid, expiration, akmp); 939346981Scy} 940346981Scy 941346981Scy 942346981Scy#ifdef CONFIG_PMKSA_CACHE_EXTERNAL 943346981Scy#ifdef CONFIG_MESH 944346981Scy 945346981Scyint hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, 946346981Scy const u8 *addr, char *buf, size_t len) 947346981Scy{ 948346981Scy return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); 949346981Scy} 950346981Scy 951346981Scy 952346981Scyvoid * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) 953346981Scy{ 954346981Scy u8 spa[ETH_ALEN]; 955346981Scy u8 pmkid[PMKID_LEN]; 956346981Scy u8 pmk[PMK_LEN_MAX]; 957346981Scy char *pos; 958346981Scy int expiration; 959346981Scy 960346981Scy /* 961346981Scy * Entry format: 962346981Scy * <BSSID> <PMKID> <PMK> <expiration in seconds> 963346981Scy */ 964346981Scy 965346981Scy if (hwaddr_aton(cmd, spa)) 966346981Scy return NULL; 967346981Scy 968346981Scy pos = os_strchr(cmd, ' '); 969346981Scy if (!pos) 970346981Scy return NULL; 971346981Scy pos++; 972346981Scy 973346981Scy if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) 974346981Scy return NULL; 975346981Scy 976346981Scy pos = os_strchr(pos, ' '); 977346981Scy if (!pos) 978346981Scy return NULL; 979346981Scy pos++; 980346981Scy 981346981Scy if (hexstr2bin(pos, pmk, PMK_LEN) < 0) 982346981Scy return NULL; 983346981Scy 984346981Scy pos = os_strchr(pos, ' '); 985346981Scy if (!pos) 986346981Scy return NULL; 987346981Scy pos++; 988346981Scy 989346981Scy if (sscanf(pos, "%d", &expiration) != 1) 990346981Scy return NULL; 991346981Scy 992346981Scy return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); 993346981Scy} 994346981Scy 995346981Scy#endif /* CONFIG_MESH */ 996346981Scy#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ 997