Home | History | Annotate | Download | only in ap
      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