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