Line data Source code
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 202 : 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 202 : if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
35 1 : return 0;
36 :
37 201 : 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 201 : if (os_snprintf_error(buflen, ret))
42 0 : return 0;
43 201 : return ret;
44 : }
45 :
46 :
47 202 : 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 202 : if (!sta->connected_time.sec)
54 0 : return 0;
55 :
56 202 : os_reltime_age(&sta->connected_time, &age);
57 :
58 202 : ret = os_snprintf(buf, buflen, "connected_time=%u\n",
59 202 : (unsigned int) age.sec);
60 202 : if (os_snprintf_error(buflen, ret))
61 0 : return 0;
62 202 : return ret;
63 : }
64 :
65 :
66 202 : static const char * timeout_next_str(int val)
67 : {
68 202 : switch (val) {
69 : case STA_NULLFUNC:
70 201 : return "NULLFUNC POLL";
71 : case STA_DISASSOC:
72 1 : return "DISASSOC";
73 : case STA_DEAUTH:
74 0 : return "DEAUTH";
75 : case STA_REMOVE:
76 0 : return "REMOVE";
77 : case STA_DISASSOC_FROM_CLI:
78 0 : return "DISASSOC_FROM_CLI";
79 : }
80 :
81 0 : return "?";
82 : }
83 :
84 :
85 202 : 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 202 : if (!sta)
92 0 : return 0;
93 :
94 202 : len = 0;
95 1212 : ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
96 1212 : MAC2STR(sta->addr));
97 202 : if (os_snprintf_error(buflen - len, ret))
98 0 : return len;
99 202 : len += ret;
100 :
101 202 : ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
102 202 : if (ret < 0)
103 0 : return len;
104 202 : len += ret;
105 :
106 606 : ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
107 : "listen_interval=%d\nsupported_rates=",
108 606 : sta->aid, sta->capability, sta->listen_interval);
109 202 : if (os_snprintf_error(buflen - len, ret))
110 0 : return len;
111 202 : len += ret;
112 :
113 2374 : for (i = 0; i < sta->supported_rates_len; i++) {
114 4344 : ret = os_snprintf(buf + len, buflen - len, "%02x%s",
115 2172 : sta->supported_rates[i],
116 2172 : i + 1 < sta->supported_rates_len ? " " : "");
117 2172 : if (os_snprintf_error(buflen - len, ret))
118 0 : return len;
119 2172 : len += ret;
120 : }
121 :
122 202 : ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
123 202 : timeout_next_str(sta->timeout_next));
124 202 : if (os_snprintf_error(buflen - len, ret))
125 0 : return len;
126 202 : len += ret;
127 :
128 202 : res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
129 202 : if (res >= 0)
130 202 : len += res;
131 202 : res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
132 202 : if (res >= 0)
133 202 : len += res;
134 202 : res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
135 202 : if (res >= 0)
136 202 : len += res;
137 202 : res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
138 : buflen - len);
139 202 : if (res >= 0)
140 202 : len += res;
141 202 : res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
142 202 : if (res >= 0)
143 202 : len += res;
144 :
145 202 : len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
146 202 : len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
147 :
148 : #ifdef CONFIG_SAE
149 202 : if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
150 0 : res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
151 0 : sta->sae->group);
152 0 : if (!os_snprintf_error(buflen - len, res))
153 0 : len += res;
154 : }
155 : #endif /* CONFIG_SAE */
156 :
157 202 : if (sta->vlan_id > 0) {
158 2 : res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
159 : sta->vlan_id);
160 2 : if (!os_snprintf_error(buflen - len, res))
161 2 : len += res;
162 : }
163 :
164 202 : return len;
165 : }
166 :
167 :
168 115 : int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
169 : char *buf, size_t buflen)
170 : {
171 115 : return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
172 : }
173 :
174 :
175 109 : 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 109 : if (hwaddr_aton(txtaddr, addr)) {
184 1 : ret = os_snprintf(buf, buflen, "FAIL\n");
185 1 : if (os_snprintf_error(buflen, ret))
186 0 : return 0;
187 1 : return ret;
188 : }
189 :
190 108 : sta = ap_get_sta(hapd, addr);
191 108 : if (sta == NULL)
192 4 : return -1;
193 :
194 104 : pos = os_strchr(txtaddr, ' ');
195 104 : if (pos) {
196 19 : pos++;
197 :
198 : #ifdef HOSTAPD_DUMP_STATE
199 19 : if (os_strcmp(pos, "eapol") == 0) {
200 19 : if (sta->eapol_sm == NULL)
201 1 : return -1;
202 18 : return eapol_auth_dump_state(sta->eapol_sm, buf,
203 : buflen);
204 : }
205 : #endif /* HOSTAPD_DUMP_STATE */
206 :
207 0 : return -1;
208 : }
209 :
210 85 : ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
211 85 : ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
212 :
213 85 : return ret;
214 : }
215 :
216 :
217 6 : 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 6 : if (hwaddr_aton(txtaddr, addr) ||
225 : (sta = ap_get_sta(hapd, addr)) == NULL) {
226 1 : ret = os_snprintf(buf, buflen, "FAIL\n");
227 1 : if (os_snprintf_error(buflen, ret))
228 0 : return 0;
229 1 : return ret;
230 : }
231 :
232 5 : if (!sta->next)
233 3 : return 0;
234 :
235 2 : return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
236 : }
237 :
238 :
239 : #ifdef CONFIG_P2P_MANAGER
240 2 : 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 2 : if (hapd->driver->send_frame == NULL)
248 0 : return -1;
249 :
250 2 : mgmt = os_zalloc(sizeof(*mgmt) + 100);
251 2 : if (mgmt == NULL)
252 0 : return -1;
253 :
254 2 : mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
255 2 : 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 2 : os_memcpy(mgmt->da, addr, ETH_ALEN);
261 2 : os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
262 2 : os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
263 2 : if (stype == WLAN_FC_STYPE_DEAUTH) {
264 1 : mgmt->u.deauth.reason_code =
265 : host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
266 1 : pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
267 : } else {
268 1 : mgmt->u.disassoc.reason_code =
269 : host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
270 1 : pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
271 : }
272 :
273 2 : *pos++ = WLAN_EID_VENDOR_SPECIFIC;
274 2 : *pos++ = 4 + 3 + 1;
275 2 : WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
276 2 : pos += 4;
277 :
278 2 : *pos++ = P2P_ATTR_MINOR_REASON_CODE;
279 2 : WPA_PUT_LE16(pos, 1);
280 2 : pos += 2;
281 2 : *pos++ = minor_reason_code;
282 :
283 4 : ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
284 2 : pos - (u8 *) mgmt, 1);
285 2 : os_free(mgmt);
286 :
287 2 : return ret < 0 ? -1 : 0;
288 : }
289 : #endif /* CONFIG_P2P_MANAGER */
290 :
291 :
292 36 : 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 36 : u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
299 :
300 36 : wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
301 : txtaddr);
302 :
303 36 : if (hwaddr_aton(txtaddr, addr))
304 2 : return -1;
305 :
306 34 : pos = os_strstr(txtaddr, " reason=");
307 34 : if (pos)
308 20 : reason = atoi(pos + 8);
309 :
310 34 : pos = os_strstr(txtaddr, " test=");
311 34 : if (pos) {
312 : struct ieee80211_mgmt mgmt;
313 : int encrypt;
314 20 : if (hapd->driver->send_frame == NULL)
315 0 : return -1;
316 20 : pos += 6;
317 20 : encrypt = atoi(pos);
318 20 : os_memset(&mgmt, 0, sizeof(mgmt));
319 20 : mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
320 : WLAN_FC_STYPE_DEAUTH);
321 20 : os_memcpy(mgmt.da, addr, ETH_ALEN);
322 20 : os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
323 20 : os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
324 20 : mgmt.u.deauth.reason_code = host_to_le16(reason);
325 20 : if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
326 : IEEE80211_HDRLEN +
327 : sizeof(mgmt.u.deauth),
328 : encrypt) < 0)
329 0 : return -1;
330 20 : return 0;
331 : }
332 :
333 : #ifdef CONFIG_P2P_MANAGER
334 13 : pos = os_strstr(txtaddr, " p2p=");
335 13 : if (pos) {
336 1 : return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
337 1 : atoi(pos + 5), addr);
338 : }
339 : #endif /* CONFIG_P2P_MANAGER */
340 :
341 13 : hostapd_drv_sta_deauth(hapd, addr, reason);
342 13 : sta = ap_get_sta(hapd, addr);
343 13 : if (sta)
344 8 : ap_sta_deauthenticate(hapd, sta, reason);
345 5 : else if (addr[0] == 0xff)
346 5 : hostapd_free_stas(hapd);
347 :
348 13 : return 0;
349 : }
350 :
351 :
352 25 : 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 25 : u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
359 :
360 25 : wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
361 : txtaddr);
362 :
363 25 : if (hwaddr_aton(txtaddr, addr))
364 2 : return -1;
365 :
366 23 : pos = os_strstr(txtaddr, " reason=");
367 23 : if (pos)
368 17 : reason = atoi(pos + 8);
369 :
370 23 : pos = os_strstr(txtaddr, " test=");
371 23 : if (pos) {
372 : struct ieee80211_mgmt mgmt;
373 : int encrypt;
374 19 : if (hapd->driver->send_frame == NULL)
375 0 : return -1;
376 19 : pos += 6;
377 19 : encrypt = atoi(pos);
378 19 : os_memset(&mgmt, 0, sizeof(mgmt));
379 19 : mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
380 : WLAN_FC_STYPE_DISASSOC);
381 19 : os_memcpy(mgmt.da, addr, ETH_ALEN);
382 19 : os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
383 19 : os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
384 19 : mgmt.u.disassoc.reason_code = host_to_le16(reason);
385 19 : if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
386 : IEEE80211_HDRLEN +
387 : sizeof(mgmt.u.deauth),
388 : encrypt) < 0)
389 0 : return -1;
390 19 : return 0;
391 : }
392 :
393 : #ifdef CONFIG_P2P_MANAGER
394 2 : pos = os_strstr(txtaddr, " p2p=");
395 2 : if (pos) {
396 1 : return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
397 1 : atoi(pos + 5), addr);
398 : }
399 : #endif /* CONFIG_P2P_MANAGER */
400 :
401 3 : hostapd_drv_sta_disassoc(hapd, addr, reason);
402 3 : sta = ap_get_sta(hapd, addr);
403 3 : if (sta)
404 2 : ap_sta_disassociate(hapd, sta, reason);
405 1 : else if (addr[0] == 0xff)
406 1 : hostapd_free_stas(hapd);
407 :
408 3 : return 0;
409 : }
410 :
411 :
412 687 : int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
413 : size_t buflen)
414 : {
415 687 : struct hostapd_iface *iface = hapd->iface;
416 687 : int len = 0, ret;
417 : size_t i;
418 :
419 1374 : 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 687 : 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 687 : iface->ht_op_mode);
446 687 : if (os_snprintf_error(buflen - len, ret))
447 0 : return len;
448 687 : len += ret;
449 :
450 687 : if (!iface->cac_started || !iface->dfs_cac_ms) {
451 677 : ret = os_snprintf(buf + len, buflen - len,
452 : "cac_time_seconds=%d\n"
453 : "cac_time_left_seconds=N/A\n",
454 677 : 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 10 : os_reltime_age(&iface->dfs_cac_start, &now);
461 10 : left_time = iface->dfs_cac_ms / 1000 - now.sec;
462 10 : ret = os_snprintf(buf + len, buflen - len,
463 : "cac_time_seconds=%u\n"
464 : "cac_time_left_seconds=%u\n",
465 10 : iface->dfs_cac_ms / 1000,
466 : left_time);
467 : }
468 687 : if (os_snprintf_error(buflen - len, ret))
469 0 : return len;
470 687 : len += ret;
471 :
472 4809 : 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 687 : iface->conf->channel,
481 687 : iface->conf->secondary_channel,
482 687 : iface->conf->ieee80211n,
483 687 : iface->conf->ieee80211ac,
484 687 : iface->conf->vht_oper_chwidth,
485 687 : iface->conf->vht_oper_centr_freq_seg0_idx,
486 687 : iface->conf->vht_oper_centr_freq_seg1_idx);
487 687 : if (os_snprintf_error(buflen - len, ret))
488 0 : return len;
489 687 : len += ret;
490 :
491 1384 : for (i = 0; i < iface->num_bss; i++) {
492 697 : struct hostapd_data *bss = iface->bss[i];
493 6970 : 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 697 : (int) i, bss->conf->iface,
499 4182 : (int) i, MAC2STR(bss->own_addr),
500 : (int) i,
501 697 : wpa_ssid_txt(bss->conf->ssid.ssid,
502 697 : bss->conf->ssid.ssid_len),
503 : (int) i, bss->num_sta);
504 697 : if (os_snprintf_error(buflen - len, ret))
505 0 : return len;
506 697 : len += ret;
507 : }
508 :
509 687 : return len;
510 : }
511 :
512 :
513 25 : int hostapd_parse_csa_settings(const char *pos,
514 : struct csa_settings *settings)
515 : {
516 : char *end;
517 :
518 25 : os_memset(settings, 0, sizeof(*settings));
519 25 : settings->cs_count = strtol(pos, &end, 10);
520 25 : if (pos == end) {
521 2 : wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
522 2 : return -1;
523 : }
524 :
525 23 : settings->freq_params.freq = atoi(end);
526 23 : if (settings->freq_params.freq == 0) {
527 1 : wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
528 1 : 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 22 : SET_CSA_SETTING(center_freq1);
541 22 : SET_CSA_SETTING(center_freq2);
542 22 : SET_CSA_SETTING(bandwidth);
543 22 : SET_CSA_SETTING(sec_channel_offset);
544 22 : settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
545 22 : settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
546 22 : settings->block_tx = !!os_strstr(pos, " blocktx");
547 : #undef SET_CSA_SETTING
548 :
549 22 : return 0;
550 : }
551 :
552 :
553 2 : int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
554 : {
555 2 : return hostapd_drv_stop_ap(hapd);
556 : }
|