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