• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Control interface for shared AP commands
3  * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/sae.h"
14 #include "eapol_auth/eapol_auth_sm.h"
15 #include "fst/fst_ctrl_iface.h"
16 #include "hostapd.h"
17 #include "ieee802_1x.h"
18 #include "wpa_auth.h"
19 #include "ieee802_11.h"
20 #include "sta_info.h"
21 #include "wps_hostapd.h"
22 #include "p2p_hostapd.h"
23 #include "ctrl_iface_ap.h"
24 #include "ap_drv_ops.h"
25 #include "mbo_ap.h"
26 #include "taxonomy.h"
27 
28 
hostapd_get_sta_tx_rx(struct hostapd_data * hapd,struct sta_info * sta,char * buf,size_t buflen)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 
hostapd_get_sta_conn_time(struct sta_info * sta,char * buf,size_t buflen)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 
timeout_next_str(int val)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 
hostapd_ctrl_iface_sta_mib(struct hostapd_data * hapd,struct sta_info * sta,char * buf,size_t buflen)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 
hostapd_ctrl_iface_sta_first(struct hostapd_data * hapd,char * buf,size_t buflen)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 
hostapd_ctrl_iface_sta(struct hostapd_data * hapd,const char * txtaddr,char * buf,size_t buflen)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 
hostapd_ctrl_iface_sta_next(struct hostapd_data * hapd,const char * txtaddr,char * buf,size_t buflen)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
p2p_manager_disconnect(struct hostapd_data * hapd,u16 stype,u8 minor_reason_code,const u8 * addr)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 
hostapd_ctrl_iface_deauthenticate(struct hostapd_data * hapd,const char * txtaddr)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 
hostapd_ctrl_iface_disassociate(struct hostapd_data * hapd,const char * txtaddr)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
hostapd_ctrl_iface_signature(struct hostapd_data * hapd,const char * txtaddr,char * buf,size_t buflen)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 
hostapd_ctrl_iface_poll_sta(struct hostapd_data * hapd,const char * txtaddr)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 
hostapd_ctrl_iface_status(struct hostapd_data * hapd,char * buf,size_t buflen)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 
hostapd_parse_csa_settings(const char * pos,struct csa_settings * settings)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 
hostapd_ctrl_iface_stop_ap(struct hostapd_data * hapd)694 int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
695 {
696 	return hostapd_drv_stop_ap(hapd);
697 }
698 
699 
hostapd_ctrl_iface_pmksa_list(struct hostapd_data * hapd,char * buf,size_t len)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 
hostapd_ctrl_iface_pmksa_flush(struct hostapd_data * hapd)707 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
708 {
709 	wpa_auth_pmksa_flush(hapd->wpa_auth);
710 }
711 
712 
hostapd_ctrl_iface_pmksa_add(struct hostapd_data * hapd,char * cmd)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 
hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data * hapd,const u8 * addr,char * buf,size_t len)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 
hostapd_ctrl_iface_pmksa_create_entry(const u8 * aa,char * cmd)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