]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/ap/ctrl_iface_ap.c
Use 64-bit TX/RX byte counters for statistics
[thirdparty/hostap.git] / src / ap / ctrl_iface_ap.c
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=%llu\ntx_bytes=%llu\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 }