ctrl_iface_ap.c 15 KB

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