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