ctrl_iface_ap.c revision cce06667447b5aec83452adb0c15100ada531095
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
24static int hostapd_get_sta_conn_time(struct sta_info *sta,
25				     char *buf, size_t buflen)
26{
27	struct os_time now, age;
28	int len = 0, ret;
29
30	if (!sta->connected_time.sec)
31		return 0;
32
33	os_get_time(&now);
34	os_time_sub(&now, &sta->connected_time, &age);
35
36	ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
37			  (unsigned int) age.sec);
38	if (ret < 0 || (size_t) ret >= buflen - len)
39		return len;
40	len += ret;
41
42	return len;
43}
44
45
46static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
47				      struct sta_info *sta,
48				      char *buf, size_t buflen)
49{
50	int len, res, ret;
51
52	if (sta == NULL) {
53		ret = os_snprintf(buf, buflen, "FAIL\n");
54		if (ret < 0 || (size_t) ret >= buflen)
55			return 0;
56		return ret;
57	}
58
59	len = 0;
60	ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
61			  MAC2STR(sta->addr));
62	if (ret < 0 || (size_t) ret >= buflen - len)
63		return len;
64	len += ret;
65
66	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
67	if (res >= 0)
68		len += res;
69	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
70	if (res >= 0)
71		len += res;
72	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
73	if (res >= 0)
74		len += res;
75	res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
76				      buflen - len);
77	if (res >= 0)
78		len += res;
79	res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
80	if (res >= 0)
81		len += res;
82
83	res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
84	if (res >= 0)
85		len += res;
86
87	return len;
88}
89
90
91int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
92				 char *buf, size_t buflen)
93{
94	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
95}
96
97
98int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
99			   char *buf, size_t buflen)
100{
101	u8 addr[ETH_ALEN];
102	int ret;
103
104	if (hwaddr_aton(txtaddr, addr)) {
105		ret = os_snprintf(buf, buflen, "FAIL\n");
106		if (ret < 0 || (size_t) ret >= buflen)
107			return 0;
108		return ret;
109	}
110	return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
111					  buf, buflen);
112}
113
114
115int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
116				char *buf, size_t buflen)
117{
118	u8 addr[ETH_ALEN];
119	struct sta_info *sta;
120	int ret;
121
122	if (hwaddr_aton(txtaddr, addr) ||
123	    (sta = ap_get_sta(hapd, addr)) == NULL) {
124		ret = os_snprintf(buf, buflen, "FAIL\n");
125		if (ret < 0 || (size_t) ret >= buflen)
126			return 0;
127		return ret;
128	}
129	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
130}
131
132
133#ifdef CONFIG_P2P_MANAGER
134static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
135				  u8 minor_reason_code, const u8 *addr)
136{
137	struct ieee80211_mgmt *mgmt;
138	int ret;
139	u8 *pos;
140
141	if (hapd->driver->send_frame == NULL)
142		return -1;
143
144	mgmt = os_zalloc(sizeof(*mgmt) + 100);
145	if (mgmt == NULL)
146		return -1;
147
148	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
149		" with minor reason code %u (stype=%u)",
150		MAC2STR(addr), minor_reason_code, stype);
151
152	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
153	os_memcpy(mgmt->da, addr, ETH_ALEN);
154	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
155	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
156	if (stype == WLAN_FC_STYPE_DEAUTH) {
157		mgmt->u.deauth.reason_code =
158			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
159		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
160	} else {
161		mgmt->u.disassoc.reason_code =
162			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
163		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
164	}
165
166	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
167	*pos++ = 4 + 3 + 1;
168	WPA_PUT_BE24(pos, OUI_WFA);
169	pos += 3;
170	*pos++ = P2P_OUI_TYPE;
171
172	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
173	WPA_PUT_LE16(pos, 1);
174	pos += 2;
175	*pos++ = minor_reason_code;
176
177	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
178				       pos - (u8 *) mgmt, 1);
179	os_free(mgmt);
180
181	return ret < 0 ? -1 : 0;
182}
183#endif /* CONFIG_P2P_MANAGER */
184
185
186int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
187				      const char *txtaddr)
188{
189	u8 addr[ETH_ALEN];
190	struct sta_info *sta;
191	const char *pos;
192	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
193
194	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
195		txtaddr);
196
197	if (hwaddr_aton(txtaddr, addr))
198		return -1;
199
200	pos = os_strstr(txtaddr, " test=");
201	if (pos) {
202		struct ieee80211_mgmt mgmt;
203		int encrypt;
204		if (hapd->driver->send_frame == NULL)
205			return -1;
206		pos += 6;
207		encrypt = atoi(pos);
208		os_memset(&mgmt, 0, sizeof(mgmt));
209		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
210						  WLAN_FC_STYPE_DEAUTH);
211		os_memcpy(mgmt.da, addr, ETH_ALEN);
212		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
213		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
214		mgmt.u.deauth.reason_code =
215			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
216		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
217					     IEEE80211_HDRLEN +
218					     sizeof(mgmt.u.deauth),
219					     encrypt) < 0)
220			return -1;
221		return 0;
222	}
223
224#ifdef CONFIG_P2P_MANAGER
225	pos = os_strstr(txtaddr, " p2p=");
226	if (pos) {
227		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
228					      atoi(pos + 5), addr);
229	}
230#endif /* CONFIG_P2P_MANAGER */
231
232	pos = os_strstr(txtaddr, " reason=");
233	if (pos)
234		reason = atoi(pos + 8);
235
236	hostapd_drv_sta_deauth(hapd, addr, reason);
237	sta = ap_get_sta(hapd, addr);
238	if (sta)
239		ap_sta_deauthenticate(hapd, sta, reason);
240	else if (addr[0] == 0xff)
241		hostapd_free_stas(hapd);
242
243	return 0;
244}
245
246
247int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
248				    const char *txtaddr)
249{
250	u8 addr[ETH_ALEN];
251	struct sta_info *sta;
252	const char *pos;
253	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
254
255	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
256		txtaddr);
257
258	if (hwaddr_aton(txtaddr, addr))
259		return -1;
260
261	pos = os_strstr(txtaddr, " test=");
262	if (pos) {
263		struct ieee80211_mgmt mgmt;
264		int encrypt;
265		if (hapd->driver->send_frame == NULL)
266			return -1;
267		pos += 6;
268		encrypt = atoi(pos);
269		os_memset(&mgmt, 0, sizeof(mgmt));
270		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
271						  WLAN_FC_STYPE_DISASSOC);
272		os_memcpy(mgmt.da, addr, ETH_ALEN);
273		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
274		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
275		mgmt.u.disassoc.reason_code =
276			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
277		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
278					     IEEE80211_HDRLEN +
279					     sizeof(mgmt.u.deauth),
280					     encrypt) < 0)
281			return -1;
282		return 0;
283	}
284
285#ifdef CONFIG_P2P_MANAGER
286	pos = os_strstr(txtaddr, " p2p=");
287	if (pos) {
288		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
289					      atoi(pos + 5), addr);
290	}
291#endif /* CONFIG_P2P_MANAGER */
292
293	pos = os_strstr(txtaddr, " reason=");
294	if (pos)
295		reason = atoi(pos + 8);
296
297	hostapd_drv_sta_disassoc(hapd, addr, reason);
298	sta = ap_get_sta(hapd, addr);
299	if (sta)
300		ap_sta_disassociate(hapd, sta, reason);
301	else if (addr[0] == 0xff)
302		hostapd_free_stas(hapd);
303
304	return 0;
305}
306
307
308int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
309			      size_t buflen)
310{
311	struct hostapd_iface *iface = hapd->iface;
312	int len = 0, ret;
313	size_t i;
314
315	ret = os_snprintf(buf + len, buflen - len,
316			  "state=%s\n"
317			  "phy=%s\n"
318			  "freq=%d\n"
319			  "num_sta_non_erp=%d\n"
320			  "num_sta_no_short_slot_time=%d\n"
321			  "num_sta_no_short_preamble=%d\n"
322			  "olbc=%d\n"
323			  "num_sta_ht_no_gf=%d\n"
324			  "num_sta_no_ht=%d\n"
325			  "num_sta_ht_20_mhz=%d\n"
326			  "olbc_ht=%d\n"
327			  "ht_op_mode=0x%x\n",
328			  hostapd_state_text(iface->state),
329			  iface->phy,
330			  iface->freq,
331			  iface->num_sta_non_erp,
332			  iface->num_sta_no_short_slot_time,
333			  iface->num_sta_no_short_preamble,
334			  iface->olbc,
335			  iface->num_sta_ht_no_gf,
336			  iface->num_sta_no_ht,
337			  iface->num_sta_ht_20mhz,
338			  iface->olbc_ht,
339			  iface->ht_op_mode);
340	if (ret < 0 || (size_t) ret >= buflen - len)
341		return len;
342	len += ret;
343
344	ret = os_snprintf(buf + len, buflen - len,
345			  "channel=%u\n"
346			  "secondary_channel=%d\n"
347			  "ieee80211n=%d\n"
348			  "ieee80211ac=%d\n"
349			  "vht_oper_chwidth=%d\n"
350			  "vht_oper_centr_freq_seg0_idx=%d\n"
351			  "vht_oper_centr_freq_seg1_idx=%d\n",
352			  iface->conf->channel,
353			  iface->conf->secondary_channel,
354			  iface->conf->ieee80211n,
355			  iface->conf->ieee80211ac,
356			  iface->conf->vht_oper_chwidth,
357			  iface->conf->vht_oper_centr_freq_seg0_idx,
358			  iface->conf->vht_oper_centr_freq_seg1_idx);
359	if (ret < 0 || (size_t) ret >= buflen - len)
360		return len;
361	len += ret;
362
363	for (i = 0; i < iface->num_bss; i++) {
364		struct hostapd_data *bss = iface->bss[i];
365		ret = os_snprintf(buf + len, buflen - len,
366				  "bss[%d]=%s\n"
367				  "bssid[%d]=" MACSTR "\n"
368				  "ssid[%d]=%s\n"
369				  "num_sta[%d]=%d\n",
370				  (int) i, bss->conf->iface,
371				  (int) i, MAC2STR(bss->own_addr),
372				  (int) i,
373				  wpa_ssid_txt(bss->conf->ssid.ssid,
374					       bss->conf->ssid.ssid_len),
375				  (int) i, bss->num_sta);
376		if (ret < 0 || (size_t) ret >= buflen - len)
377			return len;
378		len += ret;
379	}
380
381	return len;
382}
383