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