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