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