1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * Driver interaction with generic Linux Wireless Extensions
3e5b75505Sopenharmony_ci * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci *
8e5b75505Sopenharmony_ci * This file implements a driver interface for the Linux Wireless Extensions.
9e5b75505Sopenharmony_ci * When used with WE-18 or newer, this interface can be used as-is with number
10e5b75505Sopenharmony_ci * of drivers. In addition to this, some of the common functions in this file
11e5b75505Sopenharmony_ci * can be used by other driver interface implementations that use generic WE
12e5b75505Sopenharmony_ci * ioctls, but require private ioctls for some of the functionality.
13e5b75505Sopenharmony_ci */
14e5b75505Sopenharmony_ci
15e5b75505Sopenharmony_ci#include "includes.h"
16e5b75505Sopenharmony_ci#include <sys/ioctl.h>
17e5b75505Sopenharmony_ci#include <sys/types.h>
18e5b75505Sopenharmony_ci#include <sys/stat.h>
19e5b75505Sopenharmony_ci#include <fcntl.h>
20e5b75505Sopenharmony_ci#include <net/if_arp.h>
21e5b75505Sopenharmony_ci#include <dirent.h>
22e5b75505Sopenharmony_ci
23e5b75505Sopenharmony_ci#include "linux_wext.h"
24e5b75505Sopenharmony_ci#include "common.h"
25e5b75505Sopenharmony_ci#include "eloop.h"
26e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h"
27e5b75505Sopenharmony_ci#include "common/wpa_common.h"
28e5b75505Sopenharmony_ci#include "priv_netlink.h"
29e5b75505Sopenharmony_ci#include "netlink.h"
30e5b75505Sopenharmony_ci#include "linux_ioctl.h"
31e5b75505Sopenharmony_ci#include "rfkill.h"
32e5b75505Sopenharmony_ci#include "driver.h"
33e5b75505Sopenharmony_ci#include "driver_wext.h"
34e5b75505Sopenharmony_ci
35e5b75505Sopenharmony_cistatic int wpa_driver_wext_flush_pmkid(void *priv);
36e5b75505Sopenharmony_cistatic int wpa_driver_wext_get_range(void *priv);
37e5b75505Sopenharmony_cistatic int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
38e5b75505Sopenharmony_cistatic void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
39e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
40e5b75505Sopenharmony_ci
41e5b75505Sopenharmony_ci
42e5b75505Sopenharmony_ciint wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
43e5b75505Sopenharmony_ci				   int idx, u32 value)
44e5b75505Sopenharmony_ci{
45e5b75505Sopenharmony_ci	struct iwreq iwr;
46e5b75505Sopenharmony_ci	int ret = 0;
47e5b75505Sopenharmony_ci
48e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
49e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
50e5b75505Sopenharmony_ci	iwr.u.param.flags = idx & IW_AUTH_INDEX;
51e5b75505Sopenharmony_ci	iwr.u.param.value = value;
52e5b75505Sopenharmony_ci
53e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
54e5b75505Sopenharmony_ci		if (errno != EOPNOTSUPP) {
55e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
56e5b75505Sopenharmony_ci				   "value 0x%x) failed: %s)",
57e5b75505Sopenharmony_ci				   idx, value, strerror(errno));
58e5b75505Sopenharmony_ci		}
59e5b75505Sopenharmony_ci		ret = errno == EOPNOTSUPP ? -2 : -1;
60e5b75505Sopenharmony_ci	}
61e5b75505Sopenharmony_ci
62e5b75505Sopenharmony_ci	return ret;
63e5b75505Sopenharmony_ci}
64e5b75505Sopenharmony_ci
65e5b75505Sopenharmony_ci
66e5b75505Sopenharmony_ci/**
67e5b75505Sopenharmony_ci * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
68e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
69e5b75505Sopenharmony_ci * @bssid: Buffer for BSSID
70e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
71e5b75505Sopenharmony_ci */
72e5b75505Sopenharmony_ciint wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
73e5b75505Sopenharmony_ci{
74e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
75e5b75505Sopenharmony_ci	struct iwreq iwr;
76e5b75505Sopenharmony_ci	int ret = 0;
77e5b75505Sopenharmony_ci
78e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
79e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
80e5b75505Sopenharmony_ci
81e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
82e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
83e5b75505Sopenharmony_ci		ret = -1;
84e5b75505Sopenharmony_ci	}
85e5b75505Sopenharmony_ci	os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
86e5b75505Sopenharmony_ci
87e5b75505Sopenharmony_ci	return ret;
88e5b75505Sopenharmony_ci}
89e5b75505Sopenharmony_ci
90e5b75505Sopenharmony_ci
91e5b75505Sopenharmony_ci/**
92e5b75505Sopenharmony_ci * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
93e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
94e5b75505Sopenharmony_ci * @bssid: BSSID
95e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
96e5b75505Sopenharmony_ci */
97e5b75505Sopenharmony_ciint wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
98e5b75505Sopenharmony_ci{
99e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
100e5b75505Sopenharmony_ci	struct iwreq iwr;
101e5b75505Sopenharmony_ci	int ret = 0;
102e5b75505Sopenharmony_ci
103e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
104e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
105e5b75505Sopenharmony_ci	iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
106e5b75505Sopenharmony_ci	if (bssid)
107e5b75505Sopenharmony_ci		os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
108e5b75505Sopenharmony_ci	else
109e5b75505Sopenharmony_ci		os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
110e5b75505Sopenharmony_ci
111e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
112e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
113e5b75505Sopenharmony_ci		ret = -1;
114e5b75505Sopenharmony_ci	}
115e5b75505Sopenharmony_ci
116e5b75505Sopenharmony_ci	return ret;
117e5b75505Sopenharmony_ci}
118e5b75505Sopenharmony_ci
119e5b75505Sopenharmony_ci
120e5b75505Sopenharmony_ci/**
121e5b75505Sopenharmony_ci * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
122e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
123e5b75505Sopenharmony_ci * @ssid: Buffer for the SSID; must be at least 32 bytes long
124e5b75505Sopenharmony_ci * Returns: SSID length on success, -1 on failure
125e5b75505Sopenharmony_ci */
126e5b75505Sopenharmony_ciint wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
127e5b75505Sopenharmony_ci{
128e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
129e5b75505Sopenharmony_ci	struct iwreq iwr;
130e5b75505Sopenharmony_ci	int ret = 0;
131e5b75505Sopenharmony_ci
132e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
133e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
134e5b75505Sopenharmony_ci	iwr.u.essid.pointer = (caddr_t) ssid;
135e5b75505Sopenharmony_ci	iwr.u.essid.length = SSID_MAX_LEN;
136e5b75505Sopenharmony_ci
137e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
138e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
139e5b75505Sopenharmony_ci			   strerror(errno));
140e5b75505Sopenharmony_ci		ret = -1;
141e5b75505Sopenharmony_ci	} else {
142e5b75505Sopenharmony_ci		ret = iwr.u.essid.length;
143e5b75505Sopenharmony_ci		if (ret > SSID_MAX_LEN)
144e5b75505Sopenharmony_ci			ret = SSID_MAX_LEN;
145e5b75505Sopenharmony_ci		/* Some drivers include nul termination in the SSID, so let's
146e5b75505Sopenharmony_ci		 * remove it here before further processing. WE-21 changes this
147e5b75505Sopenharmony_ci		 * to explicitly require the length _not_ to include nul
148e5b75505Sopenharmony_ci		 * termination. */
149e5b75505Sopenharmony_ci		if (ret > 0 && ssid[ret - 1] == '\0' &&
150e5b75505Sopenharmony_ci		    drv->we_version_compiled < 21)
151e5b75505Sopenharmony_ci			ret--;
152e5b75505Sopenharmony_ci	}
153e5b75505Sopenharmony_ci
154e5b75505Sopenharmony_ci	return ret;
155e5b75505Sopenharmony_ci}
156e5b75505Sopenharmony_ci
157e5b75505Sopenharmony_ci
158e5b75505Sopenharmony_ci/**
159e5b75505Sopenharmony_ci * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
160e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
161e5b75505Sopenharmony_ci * @ssid: SSID
162e5b75505Sopenharmony_ci * @ssid_len: Length of SSID (0..32)
163e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
164e5b75505Sopenharmony_ci */
165e5b75505Sopenharmony_ciint wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
166e5b75505Sopenharmony_ci{
167e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
168e5b75505Sopenharmony_ci	struct iwreq iwr;
169e5b75505Sopenharmony_ci	int ret = 0;
170e5b75505Sopenharmony_ci	char buf[33];
171e5b75505Sopenharmony_ci
172e5b75505Sopenharmony_ci	if (ssid_len > SSID_MAX_LEN)
173e5b75505Sopenharmony_ci		return -1;
174e5b75505Sopenharmony_ci
175e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
176e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
177e5b75505Sopenharmony_ci	/* flags: 1 = ESSID is active, 0 = not (promiscuous) */
178e5b75505Sopenharmony_ci	iwr.u.essid.flags = (ssid_len != 0);
179e5b75505Sopenharmony_ci	os_memset(buf, 0, sizeof(buf));
180e5b75505Sopenharmony_ci	os_memcpy(buf, ssid, ssid_len);
181e5b75505Sopenharmony_ci	iwr.u.essid.pointer = (caddr_t) buf;
182e5b75505Sopenharmony_ci	if (drv->we_version_compiled < 21) {
183e5b75505Sopenharmony_ci		/* For historic reasons, set SSID length to include one extra
184e5b75505Sopenharmony_ci		 * character, C string nul termination, even though SSID is
185e5b75505Sopenharmony_ci		 * really an octet string that should not be presented as a C
186e5b75505Sopenharmony_ci		 * string. Some Linux drivers decrement the length by one and
187e5b75505Sopenharmony_ci		 * can thus end up missing the last octet of the SSID if the
188e5b75505Sopenharmony_ci		 * length is not incremented here. WE-21 changes this to
189e5b75505Sopenharmony_ci		 * explicitly require the length _not_ to include nul
190e5b75505Sopenharmony_ci		 * termination. */
191e5b75505Sopenharmony_ci		if (ssid_len)
192e5b75505Sopenharmony_ci			ssid_len++;
193e5b75505Sopenharmony_ci	}
194e5b75505Sopenharmony_ci	iwr.u.essid.length = ssid_len;
195e5b75505Sopenharmony_ci
196e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
197e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
198e5b75505Sopenharmony_ci			   strerror(errno));
199e5b75505Sopenharmony_ci		ret = -1;
200e5b75505Sopenharmony_ci	}
201e5b75505Sopenharmony_ci
202e5b75505Sopenharmony_ci	return ret;
203e5b75505Sopenharmony_ci}
204e5b75505Sopenharmony_ci
205e5b75505Sopenharmony_ci
206e5b75505Sopenharmony_ci/**
207e5b75505Sopenharmony_ci * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
208e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
209e5b75505Sopenharmony_ci * @freq: Frequency in MHz
210e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
211e5b75505Sopenharmony_ci */
212e5b75505Sopenharmony_ciint wpa_driver_wext_set_freq(void *priv, int freq)
213e5b75505Sopenharmony_ci{
214e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
215e5b75505Sopenharmony_ci	struct iwreq iwr;
216e5b75505Sopenharmony_ci	int ret = 0;
217e5b75505Sopenharmony_ci
218e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
219e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
220e5b75505Sopenharmony_ci	iwr.u.freq.m = freq * 100000;
221e5b75505Sopenharmony_ci	iwr.u.freq.e = 1;
222e5b75505Sopenharmony_ci
223e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
224e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
225e5b75505Sopenharmony_ci			   strerror(errno));
226e5b75505Sopenharmony_ci		ret = -1;
227e5b75505Sopenharmony_ci	}
228e5b75505Sopenharmony_ci
229e5b75505Sopenharmony_ci	return ret;
230e5b75505Sopenharmony_ci}
231e5b75505Sopenharmony_ci
232e5b75505Sopenharmony_ci
233e5b75505Sopenharmony_cistatic void
234e5b75505Sopenharmony_ciwpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
235e5b75505Sopenharmony_ci{
236e5b75505Sopenharmony_ci	union wpa_event_data data;
237e5b75505Sopenharmony_ci
238e5b75505Sopenharmony_ci	wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
239e5b75505Sopenharmony_ci		   custom);
240e5b75505Sopenharmony_ci
241e5b75505Sopenharmony_ci	os_memset(&data, 0, sizeof(data));
242e5b75505Sopenharmony_ci	/* Host AP driver */
243e5b75505Sopenharmony_ci	if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
244e5b75505Sopenharmony_ci		data.michael_mic_failure.unicast =
245e5b75505Sopenharmony_ci			os_strstr(custom, " unicast ") != NULL;
246e5b75505Sopenharmony_ci		/* TODO: parse parameters(?) */
247e5b75505Sopenharmony_ci		wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
248e5b75505Sopenharmony_ci	} else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
249e5b75505Sopenharmony_ci		char *spos;
250e5b75505Sopenharmony_ci		int bytes;
251e5b75505Sopenharmony_ci		u8 *req_ies = NULL, *resp_ies = NULL;
252e5b75505Sopenharmony_ci
253e5b75505Sopenharmony_ci		spos = custom + 17;
254e5b75505Sopenharmony_ci
255e5b75505Sopenharmony_ci		bytes = strspn(spos, "0123456789abcdefABCDEF");
256e5b75505Sopenharmony_ci		if (!bytes || (bytes & 1))
257e5b75505Sopenharmony_ci			return;
258e5b75505Sopenharmony_ci		bytes /= 2;
259e5b75505Sopenharmony_ci
260e5b75505Sopenharmony_ci		req_ies = os_malloc(bytes);
261e5b75505Sopenharmony_ci		if (req_ies == NULL ||
262e5b75505Sopenharmony_ci		    hexstr2bin(spos, req_ies, bytes) < 0)
263e5b75505Sopenharmony_ci			goto done;
264e5b75505Sopenharmony_ci		data.assoc_info.req_ies = req_ies;
265e5b75505Sopenharmony_ci		data.assoc_info.req_ies_len = bytes;
266e5b75505Sopenharmony_ci
267e5b75505Sopenharmony_ci		spos += bytes * 2;
268e5b75505Sopenharmony_ci
269e5b75505Sopenharmony_ci		data.assoc_info.resp_ies = NULL;
270e5b75505Sopenharmony_ci		data.assoc_info.resp_ies_len = 0;
271e5b75505Sopenharmony_ci
272e5b75505Sopenharmony_ci		if (os_strncmp(spos, " RespIEs=", 9) == 0) {
273e5b75505Sopenharmony_ci			spos += 9;
274e5b75505Sopenharmony_ci
275e5b75505Sopenharmony_ci			bytes = strspn(spos, "0123456789abcdefABCDEF");
276e5b75505Sopenharmony_ci			if (!bytes || (bytes & 1))
277e5b75505Sopenharmony_ci				goto done;
278e5b75505Sopenharmony_ci			bytes /= 2;
279e5b75505Sopenharmony_ci
280e5b75505Sopenharmony_ci			resp_ies = os_malloc(bytes);
281e5b75505Sopenharmony_ci			if (resp_ies == NULL ||
282e5b75505Sopenharmony_ci			    hexstr2bin(spos, resp_ies, bytes) < 0)
283e5b75505Sopenharmony_ci				goto done;
284e5b75505Sopenharmony_ci			data.assoc_info.resp_ies = resp_ies;
285e5b75505Sopenharmony_ci			data.assoc_info.resp_ies_len = bytes;
286e5b75505Sopenharmony_ci		}
287e5b75505Sopenharmony_ci
288e5b75505Sopenharmony_ci		wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
289e5b75505Sopenharmony_ci
290e5b75505Sopenharmony_ci	done:
291e5b75505Sopenharmony_ci		os_free(resp_ies);
292e5b75505Sopenharmony_ci		os_free(req_ies);
293e5b75505Sopenharmony_ci	}
294e5b75505Sopenharmony_ci}
295e5b75505Sopenharmony_ci
296e5b75505Sopenharmony_ci
297e5b75505Sopenharmony_cistatic int wpa_driver_wext_event_wireless_michaelmicfailure(
298e5b75505Sopenharmony_ci	void *ctx, const char *ev, size_t len)
299e5b75505Sopenharmony_ci{
300e5b75505Sopenharmony_ci	const struct iw_michaelmicfailure *mic;
301e5b75505Sopenharmony_ci	union wpa_event_data data;
302e5b75505Sopenharmony_ci
303e5b75505Sopenharmony_ci	if (len < sizeof(*mic))
304e5b75505Sopenharmony_ci		return -1;
305e5b75505Sopenharmony_ci
306e5b75505Sopenharmony_ci	mic = (const struct iw_michaelmicfailure *) ev;
307e5b75505Sopenharmony_ci
308e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
309e5b75505Sopenharmony_ci		   "flags=0x%x src_addr=" MACSTR, mic->flags,
310e5b75505Sopenharmony_ci		   MAC2STR(mic->src_addr.sa_data));
311e5b75505Sopenharmony_ci
312e5b75505Sopenharmony_ci	os_memset(&data, 0, sizeof(data));
313e5b75505Sopenharmony_ci	data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
314e5b75505Sopenharmony_ci	wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
315e5b75505Sopenharmony_ci
316e5b75505Sopenharmony_ci	return 0;
317e5b75505Sopenharmony_ci}
318e5b75505Sopenharmony_ci
319e5b75505Sopenharmony_ci
320e5b75505Sopenharmony_cistatic int wpa_driver_wext_event_wireless_pmkidcand(
321e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv, const char *ev, size_t len)
322e5b75505Sopenharmony_ci{
323e5b75505Sopenharmony_ci	const struct iw_pmkid_cand *cand;
324e5b75505Sopenharmony_ci	union wpa_event_data data;
325e5b75505Sopenharmony_ci	const u8 *addr;
326e5b75505Sopenharmony_ci
327e5b75505Sopenharmony_ci	if (len < sizeof(*cand))
328e5b75505Sopenharmony_ci		return -1;
329e5b75505Sopenharmony_ci
330e5b75505Sopenharmony_ci	cand = (const struct iw_pmkid_cand *) ev;
331e5b75505Sopenharmony_ci	addr = (const u8 *) cand->bssid.sa_data;
332e5b75505Sopenharmony_ci
333e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
334e5b75505Sopenharmony_ci		   "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
335e5b75505Sopenharmony_ci		   cand->index, MAC2STR(addr));
336e5b75505Sopenharmony_ci
337e5b75505Sopenharmony_ci	os_memset(&data, 0, sizeof(data));
338e5b75505Sopenharmony_ci	os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
339e5b75505Sopenharmony_ci	data.pmkid_candidate.index = cand->index;
340e5b75505Sopenharmony_ci	data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
341e5b75505Sopenharmony_ci	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
342e5b75505Sopenharmony_ci
343e5b75505Sopenharmony_ci	return 0;
344e5b75505Sopenharmony_ci}
345e5b75505Sopenharmony_ci
346e5b75505Sopenharmony_ci
347e5b75505Sopenharmony_cistatic int wpa_driver_wext_event_wireless_assocreqie(
348e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv, const char *ev, int len)
349e5b75505Sopenharmony_ci{
350e5b75505Sopenharmony_ci	if (len < 0)
351e5b75505Sopenharmony_ci		return -1;
352e5b75505Sopenharmony_ci
353e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
354e5b75505Sopenharmony_ci		    len);
355e5b75505Sopenharmony_ci	os_free(drv->assoc_req_ies);
356e5b75505Sopenharmony_ci	drv->assoc_req_ies = os_memdup(ev, len);
357e5b75505Sopenharmony_ci	if (drv->assoc_req_ies == NULL) {
358e5b75505Sopenharmony_ci		drv->assoc_req_ies_len = 0;
359e5b75505Sopenharmony_ci		return -1;
360e5b75505Sopenharmony_ci	}
361e5b75505Sopenharmony_ci	drv->assoc_req_ies_len = len;
362e5b75505Sopenharmony_ci
363e5b75505Sopenharmony_ci	return 0;
364e5b75505Sopenharmony_ci}
365e5b75505Sopenharmony_ci
366e5b75505Sopenharmony_ci
367e5b75505Sopenharmony_cistatic int wpa_driver_wext_event_wireless_assocrespie(
368e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv, const char *ev, int len)
369e5b75505Sopenharmony_ci{
370e5b75505Sopenharmony_ci	if (len < 0)
371e5b75505Sopenharmony_ci		return -1;
372e5b75505Sopenharmony_ci
373e5b75505Sopenharmony_ci	wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
374e5b75505Sopenharmony_ci		    len);
375e5b75505Sopenharmony_ci	os_free(drv->assoc_resp_ies);
376e5b75505Sopenharmony_ci	drv->assoc_resp_ies = os_memdup(ev, len);
377e5b75505Sopenharmony_ci	if (drv->assoc_resp_ies == NULL) {
378e5b75505Sopenharmony_ci		drv->assoc_resp_ies_len = 0;
379e5b75505Sopenharmony_ci		return -1;
380e5b75505Sopenharmony_ci	}
381e5b75505Sopenharmony_ci	drv->assoc_resp_ies_len = len;
382e5b75505Sopenharmony_ci
383e5b75505Sopenharmony_ci	return 0;
384e5b75505Sopenharmony_ci}
385e5b75505Sopenharmony_ci
386e5b75505Sopenharmony_ci
387e5b75505Sopenharmony_cistatic void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
388e5b75505Sopenharmony_ci{
389e5b75505Sopenharmony_ci	union wpa_event_data data;
390e5b75505Sopenharmony_ci
391e5b75505Sopenharmony_ci	if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
392e5b75505Sopenharmony_ci		return;
393e5b75505Sopenharmony_ci
394e5b75505Sopenharmony_ci	os_memset(&data, 0, sizeof(data));
395e5b75505Sopenharmony_ci	if (drv->assoc_req_ies) {
396e5b75505Sopenharmony_ci		data.assoc_info.req_ies = drv->assoc_req_ies;
397e5b75505Sopenharmony_ci		data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
398e5b75505Sopenharmony_ci	}
399e5b75505Sopenharmony_ci	if (drv->assoc_resp_ies) {
400e5b75505Sopenharmony_ci		data.assoc_info.resp_ies = drv->assoc_resp_ies;
401e5b75505Sopenharmony_ci		data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
402e5b75505Sopenharmony_ci	}
403e5b75505Sopenharmony_ci
404e5b75505Sopenharmony_ci	wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
405e5b75505Sopenharmony_ci
406e5b75505Sopenharmony_ci	os_free(drv->assoc_req_ies);
407e5b75505Sopenharmony_ci	drv->assoc_req_ies = NULL;
408e5b75505Sopenharmony_ci	os_free(drv->assoc_resp_ies);
409e5b75505Sopenharmony_ci	drv->assoc_resp_ies = NULL;
410e5b75505Sopenharmony_ci}
411e5b75505Sopenharmony_ci
412e5b75505Sopenharmony_ci
413e5b75505Sopenharmony_cistatic void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
414e5b75505Sopenharmony_ci					   char *data, unsigned int len)
415e5b75505Sopenharmony_ci{
416e5b75505Sopenharmony_ci	struct iw_event iwe_buf, *iwe = &iwe_buf;
417e5b75505Sopenharmony_ci	char *pos, *end, *custom, *buf;
418e5b75505Sopenharmony_ci
419e5b75505Sopenharmony_ci	pos = data;
420e5b75505Sopenharmony_ci	end = data + len;
421e5b75505Sopenharmony_ci
422e5b75505Sopenharmony_ci	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
423e5b75505Sopenharmony_ci		/* Event data may be unaligned, so make a local, aligned copy
424e5b75505Sopenharmony_ci		 * before processing. */
425e5b75505Sopenharmony_ci		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
426e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
427e5b75505Sopenharmony_ci			   iwe->cmd, iwe->len);
428e5b75505Sopenharmony_ci		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
429e5b75505Sopenharmony_ci			return;
430e5b75505Sopenharmony_ci
431e5b75505Sopenharmony_ci		custom = pos + IW_EV_POINT_LEN;
432e5b75505Sopenharmony_ci		if (drv->we_version_compiled > 18 &&
433e5b75505Sopenharmony_ci		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
434e5b75505Sopenharmony_ci		     iwe->cmd == IWEVCUSTOM ||
435e5b75505Sopenharmony_ci		     iwe->cmd == IWEVASSOCREQIE ||
436e5b75505Sopenharmony_ci		     iwe->cmd == IWEVASSOCRESPIE ||
437e5b75505Sopenharmony_ci		     iwe->cmd == IWEVPMKIDCAND)) {
438e5b75505Sopenharmony_ci			/* WE-19 removed the pointer from struct iw_point */
439e5b75505Sopenharmony_ci			char *dpos = (char *) &iwe_buf.u.data.length;
440e5b75505Sopenharmony_ci			int dlen = dpos - (char *) &iwe_buf;
441e5b75505Sopenharmony_ci			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
442e5b75505Sopenharmony_ci				  sizeof(struct iw_event) - dlen);
443e5b75505Sopenharmony_ci		} else {
444e5b75505Sopenharmony_ci			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
445e5b75505Sopenharmony_ci			custom += IW_EV_POINT_OFF;
446e5b75505Sopenharmony_ci		}
447e5b75505Sopenharmony_ci
448e5b75505Sopenharmony_ci		switch (iwe->cmd) {
449e5b75505Sopenharmony_ci		case SIOCGIWAP:
450e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
451e5b75505Sopenharmony_ci				   MACSTR,
452e5b75505Sopenharmony_ci				   MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
453e5b75505Sopenharmony_ci			if (is_zero_ether_addr(
454e5b75505Sopenharmony_ci				    (const u8 *) iwe->u.ap_addr.sa_data) ||
455e5b75505Sopenharmony_ci			    os_memcmp(iwe->u.ap_addr.sa_data,
456e5b75505Sopenharmony_ci				      "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
457e5b75505Sopenharmony_ci			    0) {
458e5b75505Sopenharmony_ci				os_free(drv->assoc_req_ies);
459e5b75505Sopenharmony_ci				drv->assoc_req_ies = NULL;
460e5b75505Sopenharmony_ci				os_free(drv->assoc_resp_ies);
461e5b75505Sopenharmony_ci				drv->assoc_resp_ies = NULL;
462e5b75505Sopenharmony_ci				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
463e5b75505Sopenharmony_ci						     NULL);
464e5b75505Sopenharmony_ci
465e5b75505Sopenharmony_ci			} else {
466e5b75505Sopenharmony_ci				wpa_driver_wext_event_assoc_ies(drv);
467e5b75505Sopenharmony_ci				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
468e5b75505Sopenharmony_ci						     NULL);
469e5b75505Sopenharmony_ci			}
470e5b75505Sopenharmony_ci			break;
471e5b75505Sopenharmony_ci		case IWEVMICHAELMICFAILURE:
472e5b75505Sopenharmony_ci			if (iwe->u.data.length > end - custom) {
473e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
474e5b75505Sopenharmony_ci					   "IWEVMICHAELMICFAILURE length");
475e5b75505Sopenharmony_ci				return;
476e5b75505Sopenharmony_ci			}
477e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless_michaelmicfailure(
478e5b75505Sopenharmony_ci				drv->ctx, custom, iwe->u.data.length);
479e5b75505Sopenharmony_ci			break;
480e5b75505Sopenharmony_ci		case IWEVCUSTOM:
481e5b75505Sopenharmony_ci			if (iwe->u.data.length > end - custom) {
482e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
483e5b75505Sopenharmony_ci					   "IWEVCUSTOM length");
484e5b75505Sopenharmony_ci				return;
485e5b75505Sopenharmony_ci			}
486e5b75505Sopenharmony_ci			buf = dup_binstr(custom, iwe->u.data.length);
487e5b75505Sopenharmony_ci			if (buf == NULL)
488e5b75505Sopenharmony_ci				return;
489e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
490e5b75505Sopenharmony_ci			os_free(buf);
491e5b75505Sopenharmony_ci			break;
492e5b75505Sopenharmony_ci		case SIOCGIWSCAN:
493e5b75505Sopenharmony_ci			drv->scan_complete_events = 1;
494e5b75505Sopenharmony_ci			eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
495e5b75505Sopenharmony_ci					     drv, drv->ctx);
496e5b75505Sopenharmony_ci			wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
497e5b75505Sopenharmony_ci					     NULL);
498e5b75505Sopenharmony_ci			break;
499e5b75505Sopenharmony_ci		case IWEVASSOCREQIE:
500e5b75505Sopenharmony_ci			if (iwe->u.data.length > end - custom) {
501e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
502e5b75505Sopenharmony_ci					   "IWEVASSOCREQIE length");
503e5b75505Sopenharmony_ci				return;
504e5b75505Sopenharmony_ci			}
505e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless_assocreqie(
506e5b75505Sopenharmony_ci				drv, custom, iwe->u.data.length);
507e5b75505Sopenharmony_ci			break;
508e5b75505Sopenharmony_ci		case IWEVASSOCRESPIE:
509e5b75505Sopenharmony_ci			if (iwe->u.data.length > end - custom) {
510e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
511e5b75505Sopenharmony_ci					   "IWEVASSOCRESPIE length");
512e5b75505Sopenharmony_ci				return;
513e5b75505Sopenharmony_ci			}
514e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless_assocrespie(
515e5b75505Sopenharmony_ci				drv, custom, iwe->u.data.length);
516e5b75505Sopenharmony_ci			break;
517e5b75505Sopenharmony_ci		case IWEVPMKIDCAND:
518e5b75505Sopenharmony_ci			if (iwe->u.data.length > end - custom) {
519e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
520e5b75505Sopenharmony_ci					   "IWEVPMKIDCAND length");
521e5b75505Sopenharmony_ci				return;
522e5b75505Sopenharmony_ci			}
523e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless_pmkidcand(
524e5b75505Sopenharmony_ci				drv, custom, iwe->u.data.length);
525e5b75505Sopenharmony_ci			break;
526e5b75505Sopenharmony_ci		}
527e5b75505Sopenharmony_ci
528e5b75505Sopenharmony_ci		pos += iwe->len;
529e5b75505Sopenharmony_ci	}
530e5b75505Sopenharmony_ci}
531e5b75505Sopenharmony_ci
532e5b75505Sopenharmony_ci
533e5b75505Sopenharmony_cistatic void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
534e5b75505Sopenharmony_ci				       char *buf, size_t len, int del)
535e5b75505Sopenharmony_ci{
536e5b75505Sopenharmony_ci	union wpa_event_data event;
537e5b75505Sopenharmony_ci
538e5b75505Sopenharmony_ci	os_memset(&event, 0, sizeof(event));
539e5b75505Sopenharmony_ci	if (len > sizeof(event.interface_status.ifname))
540e5b75505Sopenharmony_ci		len = sizeof(event.interface_status.ifname) - 1;
541e5b75505Sopenharmony_ci	os_memcpy(event.interface_status.ifname, buf, len);
542e5b75505Sopenharmony_ci	event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
543e5b75505Sopenharmony_ci		EVENT_INTERFACE_ADDED;
544e5b75505Sopenharmony_ci
545e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
546e5b75505Sopenharmony_ci		   del ? "DEL" : "NEW",
547e5b75505Sopenharmony_ci		   event.interface_status.ifname,
548e5b75505Sopenharmony_ci		   del ? "removed" : "added");
549e5b75505Sopenharmony_ci
550e5b75505Sopenharmony_ci	if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
551e5b75505Sopenharmony_ci		if (del) {
552e5b75505Sopenharmony_ci			if (drv->if_removed) {
553e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
554e5b75505Sopenharmony_ci					   "already set - ignore event");
555e5b75505Sopenharmony_ci				return;
556e5b75505Sopenharmony_ci			}
557e5b75505Sopenharmony_ci			drv->if_removed = 1;
558e5b75505Sopenharmony_ci		} else {
559e5b75505Sopenharmony_ci			if (if_nametoindex(drv->ifname) == 0) {
560e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
561e5b75505Sopenharmony_ci					   "does not exist - ignore "
562e5b75505Sopenharmony_ci					   "RTM_NEWLINK",
563e5b75505Sopenharmony_ci					   drv->ifname);
564e5b75505Sopenharmony_ci				return;
565e5b75505Sopenharmony_ci			}
566e5b75505Sopenharmony_ci			if (!drv->if_removed) {
567e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
568e5b75505Sopenharmony_ci					   "already cleared - ignore event");
569e5b75505Sopenharmony_ci				return;
570e5b75505Sopenharmony_ci			}
571e5b75505Sopenharmony_ci			drv->if_removed = 0;
572e5b75505Sopenharmony_ci		}
573e5b75505Sopenharmony_ci	}
574e5b75505Sopenharmony_ci
575e5b75505Sopenharmony_ci	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
576e5b75505Sopenharmony_ci}
577e5b75505Sopenharmony_ci
578e5b75505Sopenharmony_ci
579e5b75505Sopenharmony_cistatic int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
580e5b75505Sopenharmony_ci				      u8 *buf, size_t len)
581e5b75505Sopenharmony_ci{
582e5b75505Sopenharmony_ci	int attrlen, rta_len;
583e5b75505Sopenharmony_ci	struct rtattr *attr;
584e5b75505Sopenharmony_ci
585e5b75505Sopenharmony_ci	attrlen = len;
586e5b75505Sopenharmony_ci	attr = (struct rtattr *) buf;
587e5b75505Sopenharmony_ci
588e5b75505Sopenharmony_ci	rta_len = RTA_ALIGN(sizeof(struct rtattr));
589e5b75505Sopenharmony_ci	while (RTA_OK(attr, attrlen)) {
590e5b75505Sopenharmony_ci		if (attr->rta_type == IFLA_IFNAME) {
591e5b75505Sopenharmony_ci			if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
592e5b75505Sopenharmony_ci			    == 0)
593e5b75505Sopenharmony_ci				return 1;
594e5b75505Sopenharmony_ci			else
595e5b75505Sopenharmony_ci				break;
596e5b75505Sopenharmony_ci		}
597e5b75505Sopenharmony_ci		attr = RTA_NEXT(attr, attrlen);
598e5b75505Sopenharmony_ci	}
599e5b75505Sopenharmony_ci
600e5b75505Sopenharmony_ci	return 0;
601e5b75505Sopenharmony_ci}
602e5b75505Sopenharmony_ci
603e5b75505Sopenharmony_ci
604e5b75505Sopenharmony_cistatic int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
605e5b75505Sopenharmony_ci				       int ifindex, u8 *buf, size_t len)
606e5b75505Sopenharmony_ci{
607e5b75505Sopenharmony_ci	if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
608e5b75505Sopenharmony_ci		return 1;
609e5b75505Sopenharmony_ci
610e5b75505Sopenharmony_ci	if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
611e5b75505Sopenharmony_ci		drv->ifindex = if_nametoindex(drv->ifname);
612e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
613e5b75505Sopenharmony_ci			   "interface");
614e5b75505Sopenharmony_ci		wpa_driver_wext_finish_drv_init(drv);
615e5b75505Sopenharmony_ci		return 1;
616e5b75505Sopenharmony_ci	}
617e5b75505Sopenharmony_ci
618e5b75505Sopenharmony_ci	return 0;
619e5b75505Sopenharmony_ci}
620e5b75505Sopenharmony_ci
621e5b75505Sopenharmony_ci
622e5b75505Sopenharmony_cistatic void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
623e5b75505Sopenharmony_ci					      u8 *buf, size_t len)
624e5b75505Sopenharmony_ci{
625e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = ctx;
626e5b75505Sopenharmony_ci	int attrlen, rta_len;
627e5b75505Sopenharmony_ci	struct rtattr *attr;
628e5b75505Sopenharmony_ci	char namebuf[IFNAMSIZ];
629e5b75505Sopenharmony_ci
630e5b75505Sopenharmony_ci	if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
631e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
632e5b75505Sopenharmony_ci			   ifi->ifi_index);
633e5b75505Sopenharmony_ci		return;
634e5b75505Sopenharmony_ci	}
635e5b75505Sopenharmony_ci
636e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
637e5b75505Sopenharmony_ci		   "(%s%s%s%s)",
638e5b75505Sopenharmony_ci		   drv->operstate, ifi->ifi_flags,
639e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
640e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
641e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
642e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
643e5b75505Sopenharmony_ci
644e5b75505Sopenharmony_ci	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
645e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: Interface down");
646e5b75505Sopenharmony_ci		drv->if_disabled = 1;
647e5b75505Sopenharmony_ci		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
648e5b75505Sopenharmony_ci	}
649e5b75505Sopenharmony_ci
650e5b75505Sopenharmony_ci	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
651e5b75505Sopenharmony_ci		if (if_indextoname(ifi->ifi_index, namebuf) &&
652e5b75505Sopenharmony_ci		    linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
653e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
654e5b75505Sopenharmony_ci				   "event since interface %s is down",
655e5b75505Sopenharmony_ci				   namebuf);
656e5b75505Sopenharmony_ci		} else if (if_nametoindex(drv->ifname) == 0) {
657e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
658e5b75505Sopenharmony_ci				   "event since interface %s does not exist",
659e5b75505Sopenharmony_ci				   drv->ifname);
660e5b75505Sopenharmony_ci		} else if (drv->if_removed) {
661e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
662e5b75505Sopenharmony_ci				   "event since interface %s is marked "
663e5b75505Sopenharmony_ci				   "removed", drv->ifname);
664e5b75505Sopenharmony_ci		} else {
665e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Interface up");
666e5b75505Sopenharmony_ci			drv->if_disabled = 0;
667e5b75505Sopenharmony_ci			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
668e5b75505Sopenharmony_ci					     NULL);
669e5b75505Sopenharmony_ci		}
670e5b75505Sopenharmony_ci	}
671e5b75505Sopenharmony_ci
672e5b75505Sopenharmony_ci	/*
673e5b75505Sopenharmony_ci	 * Some drivers send the association event before the operup event--in
674e5b75505Sopenharmony_ci	 * this case, lifting operstate in wpa_driver_wext_set_operstate()
675e5b75505Sopenharmony_ci	 * fails. This will hit us when wpa_supplicant does not need to do
676e5b75505Sopenharmony_ci	 * IEEE 802.1X authentication
677e5b75505Sopenharmony_ci	 */
678e5b75505Sopenharmony_ci	if (drv->operstate == 1 &&
679e5b75505Sopenharmony_ci	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
680e5b75505Sopenharmony_ci	    !(ifi->ifi_flags & IFF_RUNNING))
681e5b75505Sopenharmony_ci		netlink_send_oper_ifla(drv->netlink, drv->ifindex,
682e5b75505Sopenharmony_ci				       -1, IF_OPER_UP);
683e5b75505Sopenharmony_ci
684e5b75505Sopenharmony_ci	attrlen = len;
685e5b75505Sopenharmony_ci	attr = (struct rtattr *) buf;
686e5b75505Sopenharmony_ci
687e5b75505Sopenharmony_ci	rta_len = RTA_ALIGN(sizeof(struct rtattr));
688e5b75505Sopenharmony_ci	while (RTA_OK(attr, attrlen)) {
689e5b75505Sopenharmony_ci		if (attr->rta_type == IFLA_WIRELESS) {
690e5b75505Sopenharmony_ci			wpa_driver_wext_event_wireless(
691e5b75505Sopenharmony_ci				drv, ((char *) attr) + rta_len,
692e5b75505Sopenharmony_ci				attr->rta_len - rta_len);
693e5b75505Sopenharmony_ci		} else if (attr->rta_type == IFLA_IFNAME) {
694e5b75505Sopenharmony_ci			wpa_driver_wext_event_link(drv,
695e5b75505Sopenharmony_ci						   ((char *) attr) + rta_len,
696e5b75505Sopenharmony_ci						   attr->rta_len - rta_len, 0);
697e5b75505Sopenharmony_ci		}
698e5b75505Sopenharmony_ci		attr = RTA_NEXT(attr, attrlen);
699e5b75505Sopenharmony_ci	}
700e5b75505Sopenharmony_ci}
701e5b75505Sopenharmony_ci
702e5b75505Sopenharmony_ci
703e5b75505Sopenharmony_cistatic void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
704e5b75505Sopenharmony_ci					      u8 *buf, size_t len)
705e5b75505Sopenharmony_ci{
706e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = ctx;
707e5b75505Sopenharmony_ci	int attrlen, rta_len;
708e5b75505Sopenharmony_ci	struct rtattr *attr;
709e5b75505Sopenharmony_ci
710e5b75505Sopenharmony_ci	attrlen = len;
711e5b75505Sopenharmony_ci	attr = (struct rtattr *) buf;
712e5b75505Sopenharmony_ci
713e5b75505Sopenharmony_ci	rta_len = RTA_ALIGN(sizeof(struct rtattr));
714e5b75505Sopenharmony_ci	while (RTA_OK(attr, attrlen)) {
715e5b75505Sopenharmony_ci		if (attr->rta_type == IFLA_IFNAME) {
716e5b75505Sopenharmony_ci			wpa_driver_wext_event_link(drv,
717e5b75505Sopenharmony_ci						   ((char *) attr) + rta_len,
718e5b75505Sopenharmony_ci						   attr->rta_len - rta_len, 1);
719e5b75505Sopenharmony_ci		}
720e5b75505Sopenharmony_ci		attr = RTA_NEXT(attr, attrlen);
721e5b75505Sopenharmony_ci	}
722e5b75505Sopenharmony_ci}
723e5b75505Sopenharmony_ci
724e5b75505Sopenharmony_ci
725e5b75505Sopenharmony_cistatic void wpa_driver_wext_rfkill_blocked(void *ctx)
726e5b75505Sopenharmony_ci{
727e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
728e5b75505Sopenharmony_ci	/*
729e5b75505Sopenharmony_ci	 * This may be for any interface; use ifdown event to disable
730e5b75505Sopenharmony_ci	 * interface.
731e5b75505Sopenharmony_ci	 */
732e5b75505Sopenharmony_ci}
733e5b75505Sopenharmony_ci
734e5b75505Sopenharmony_ci
735e5b75505Sopenharmony_cistatic void wpa_driver_wext_rfkill_unblocked(void *ctx)
736e5b75505Sopenharmony_ci{
737e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = ctx;
738e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
739e5b75505Sopenharmony_ci	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
740e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
741e5b75505Sopenharmony_ci			   "after rfkill unblock");
742e5b75505Sopenharmony_ci		return;
743e5b75505Sopenharmony_ci	}
744e5b75505Sopenharmony_ci	/* rtnetlink ifup handler will report interface as enabled */
745e5b75505Sopenharmony_ci}
746e5b75505Sopenharmony_ci
747e5b75505Sopenharmony_ci
748e5b75505Sopenharmony_cistatic void wext_get_phy_name(struct wpa_driver_wext_data *drv)
749e5b75505Sopenharmony_ci{
750e5b75505Sopenharmony_ci	/* Find phy (radio) to which this interface belongs */
751e5b75505Sopenharmony_ci	char buf[90], *pos;
752e5b75505Sopenharmony_ci	int f, rv;
753e5b75505Sopenharmony_ci
754e5b75505Sopenharmony_ci	drv->phyname[0] = '\0';
755e5b75505Sopenharmony_ci	snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
756e5b75505Sopenharmony_ci		 drv->ifname);
757e5b75505Sopenharmony_ci	f = open(buf, O_RDONLY);
758e5b75505Sopenharmony_ci	if (f < 0) {
759e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
760e5b75505Sopenharmony_ci			   buf, strerror(errno));
761e5b75505Sopenharmony_ci		return;
762e5b75505Sopenharmony_ci	}
763e5b75505Sopenharmony_ci
764e5b75505Sopenharmony_ci	rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
765e5b75505Sopenharmony_ci	close(f);
766e5b75505Sopenharmony_ci	if (rv < 0) {
767e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
768e5b75505Sopenharmony_ci			   buf, strerror(errno));
769e5b75505Sopenharmony_ci		return;
770e5b75505Sopenharmony_ci	}
771e5b75505Sopenharmony_ci
772e5b75505Sopenharmony_ci	drv->phyname[rv] = '\0';
773e5b75505Sopenharmony_ci	pos = os_strchr(drv->phyname, '\n');
774e5b75505Sopenharmony_ci	if (pos)
775e5b75505Sopenharmony_ci		*pos = '\0';
776e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
777e5b75505Sopenharmony_ci		   drv->ifname, drv->phyname);
778e5b75505Sopenharmony_ci}
779e5b75505Sopenharmony_ci
780e5b75505Sopenharmony_ci
781e5b75505Sopenharmony_ci/**
782e5b75505Sopenharmony_ci * wpa_driver_wext_init - Initialize WE driver interface
783e5b75505Sopenharmony_ci * @ctx: context to be used when calling wpa_supplicant functions,
784e5b75505Sopenharmony_ci * e.g., wpa_supplicant_event()
785e5b75505Sopenharmony_ci * @ifname: interface name, e.g., wlan0
786e5b75505Sopenharmony_ci * Returns: Pointer to private data, %NULL on failure
787e5b75505Sopenharmony_ci */
788e5b75505Sopenharmony_civoid * wpa_driver_wext_init(void *ctx, const char *ifname)
789e5b75505Sopenharmony_ci{
790e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv;
791e5b75505Sopenharmony_ci	struct netlink_config *cfg;
792e5b75505Sopenharmony_ci	struct rfkill_config *rcfg;
793e5b75505Sopenharmony_ci	char path[128];
794e5b75505Sopenharmony_ci	struct stat buf;
795e5b75505Sopenharmony_ci
796e5b75505Sopenharmony_ci	drv = os_zalloc(sizeof(*drv));
797e5b75505Sopenharmony_ci	if (drv == NULL)
798e5b75505Sopenharmony_ci		return NULL;
799e5b75505Sopenharmony_ci	drv->ctx = ctx;
800e5b75505Sopenharmony_ci	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
801e5b75505Sopenharmony_ci
802e5b75505Sopenharmony_ci	os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
803e5b75505Sopenharmony_ci	if (stat(path, &buf) == 0) {
804e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
805e5b75505Sopenharmony_ci		drv->cfg80211 = 1;
806e5b75505Sopenharmony_ci		wext_get_phy_name(drv);
807e5b75505Sopenharmony_ci	}
808e5b75505Sopenharmony_ci
809e5b75505Sopenharmony_ci	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
810e5b75505Sopenharmony_ci	if (drv->ioctl_sock < 0) {
811e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
812e5b75505Sopenharmony_ci			   strerror(errno));
813e5b75505Sopenharmony_ci		goto err1;
814e5b75505Sopenharmony_ci	}
815e5b75505Sopenharmony_ci
816e5b75505Sopenharmony_ci	cfg = os_zalloc(sizeof(*cfg));
817e5b75505Sopenharmony_ci	if (cfg == NULL)
818e5b75505Sopenharmony_ci		goto err1;
819e5b75505Sopenharmony_ci	cfg->ctx = drv;
820e5b75505Sopenharmony_ci	cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
821e5b75505Sopenharmony_ci	cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
822e5b75505Sopenharmony_ci	drv->netlink = netlink_init(cfg);
823e5b75505Sopenharmony_ci	if (drv->netlink == NULL) {
824e5b75505Sopenharmony_ci		os_free(cfg);
825e5b75505Sopenharmony_ci		goto err2;
826e5b75505Sopenharmony_ci	}
827e5b75505Sopenharmony_ci
828e5b75505Sopenharmony_ci	rcfg = os_zalloc(sizeof(*rcfg));
829e5b75505Sopenharmony_ci	if (rcfg == NULL)
830e5b75505Sopenharmony_ci		goto err3;
831e5b75505Sopenharmony_ci	rcfg->ctx = drv;
832e5b75505Sopenharmony_ci	os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
833e5b75505Sopenharmony_ci	rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
834e5b75505Sopenharmony_ci	rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
835e5b75505Sopenharmony_ci	drv->rfkill = rfkill_init(rcfg);
836e5b75505Sopenharmony_ci	if (drv->rfkill == NULL) {
837e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
838e5b75505Sopenharmony_ci		os_free(rcfg);
839e5b75505Sopenharmony_ci	}
840e5b75505Sopenharmony_ci
841e5b75505Sopenharmony_ci	drv->mlme_sock = -1;
842e5b75505Sopenharmony_ci
843e5b75505Sopenharmony_ci	if (wpa_driver_wext_finish_drv_init(drv) < 0)
844e5b75505Sopenharmony_ci		goto err3;
845e5b75505Sopenharmony_ci
846e5b75505Sopenharmony_ci	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
847e5b75505Sopenharmony_ci
848e5b75505Sopenharmony_ci	return drv;
849e5b75505Sopenharmony_ci
850e5b75505Sopenharmony_cierr3:
851e5b75505Sopenharmony_ci	rfkill_deinit(drv->rfkill);
852e5b75505Sopenharmony_ci	netlink_deinit(drv->netlink);
853e5b75505Sopenharmony_cierr2:
854e5b75505Sopenharmony_ci	close(drv->ioctl_sock);
855e5b75505Sopenharmony_cierr1:
856e5b75505Sopenharmony_ci	os_free(drv);
857e5b75505Sopenharmony_ci	return NULL;
858e5b75505Sopenharmony_ci}
859e5b75505Sopenharmony_ci
860e5b75505Sopenharmony_ci
861e5b75505Sopenharmony_cistatic void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
862e5b75505Sopenharmony_ci{
863e5b75505Sopenharmony_ci	wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
864e5b75505Sopenharmony_ci}
865e5b75505Sopenharmony_ci
866e5b75505Sopenharmony_ci
867e5b75505Sopenharmony_cistatic int wext_hostap_ifname(struct wpa_driver_wext_data *drv,
868e5b75505Sopenharmony_ci			      const char *ifname)
869e5b75505Sopenharmony_ci{
870e5b75505Sopenharmony_ci	char buf[200], *res;
871e5b75505Sopenharmony_ci	int type, ret;
872e5b75505Sopenharmony_ci	FILE *f;
873e5b75505Sopenharmony_ci
874e5b75505Sopenharmony_ci	if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
875e5b75505Sopenharmony_ci		return -1;
876e5b75505Sopenharmony_ci
877e5b75505Sopenharmony_ci	ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
878e5b75505Sopenharmony_ci		       drv->ifname, ifname);
879e5b75505Sopenharmony_ci	if (os_snprintf_error(sizeof(buf), ret))
880e5b75505Sopenharmony_ci		return -1;
881e5b75505Sopenharmony_ci
882e5b75505Sopenharmony_ci	f = fopen(buf, "r");
883e5b75505Sopenharmony_ci	if (!f)
884e5b75505Sopenharmony_ci		return -1;
885e5b75505Sopenharmony_ci	res = fgets(buf, sizeof(buf), f);
886e5b75505Sopenharmony_ci	fclose(f);
887e5b75505Sopenharmony_ci
888e5b75505Sopenharmony_ci	type = res ? atoi(res) : -1;
889e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type);
890e5b75505Sopenharmony_ci
891e5b75505Sopenharmony_ci	if (type == ARPHRD_IEEE80211) {
892e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
893e5b75505Sopenharmony_ci			   "WEXT: Found hostap driver wifi# interface (%s)",
894e5b75505Sopenharmony_ci			   ifname);
895e5b75505Sopenharmony_ci		wpa_driver_wext_alternative_ifindex(drv, ifname);
896e5b75505Sopenharmony_ci		return 0;
897e5b75505Sopenharmony_ci	}
898e5b75505Sopenharmony_ci	return -1;
899e5b75505Sopenharmony_ci}
900e5b75505Sopenharmony_ci
901e5b75505Sopenharmony_ci
902e5b75505Sopenharmony_cistatic int wext_add_hostap(struct wpa_driver_wext_data *drv)
903e5b75505Sopenharmony_ci{
904e5b75505Sopenharmony_ci	char buf[200];
905e5b75505Sopenharmony_ci	int n;
906e5b75505Sopenharmony_ci	struct dirent **names;
907e5b75505Sopenharmony_ci	int ret = -1;
908e5b75505Sopenharmony_ci
909e5b75505Sopenharmony_ci	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname);
910e5b75505Sopenharmony_ci	n = scandir(buf, &names, NULL, alphasort);
911e5b75505Sopenharmony_ci	if (n < 0)
912e5b75505Sopenharmony_ci		return -1;
913e5b75505Sopenharmony_ci
914e5b75505Sopenharmony_ci	while (n--) {
915e5b75505Sopenharmony_ci		if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0)
916e5b75505Sopenharmony_ci			ret = 0;
917e5b75505Sopenharmony_ci		free(names[n]);
918e5b75505Sopenharmony_ci	}
919e5b75505Sopenharmony_ci	free(names);
920e5b75505Sopenharmony_ci
921e5b75505Sopenharmony_ci	return ret;
922e5b75505Sopenharmony_ci}
923e5b75505Sopenharmony_ci
924e5b75505Sopenharmony_ci
925e5b75505Sopenharmony_cistatic void wext_check_hostap(struct wpa_driver_wext_data *drv)
926e5b75505Sopenharmony_ci{
927e5b75505Sopenharmony_ci	char path[200], buf[200], *pos;
928e5b75505Sopenharmony_ci	ssize_t res;
929e5b75505Sopenharmony_ci
930e5b75505Sopenharmony_ci	/*
931e5b75505Sopenharmony_ci	 * Host AP driver may use both wlan# and wifi# interface in wireless
932e5b75505Sopenharmony_ci	 * events. Since some of the versions included WE-18 support, let's add
933e5b75505Sopenharmony_ci	 * the alternative ifindex also from driver_wext.c for the time being.
934e5b75505Sopenharmony_ci	 * This may be removed at some point once it is believed that old
935e5b75505Sopenharmony_ci	 * versions of the driver are not in use anymore. However, it looks like
936e5b75505Sopenharmony_ci	 * the wifi# interface is still used in the current kernel tree, so it
937e5b75505Sopenharmony_ci	 * may not really be possible to remove this before the Host AP driver
938e5b75505Sopenharmony_ci	 * gets removed from the kernel.
939e5b75505Sopenharmony_ci	 */
940e5b75505Sopenharmony_ci
941e5b75505Sopenharmony_ci	/* First, try to see if driver information is available from sysfs */
942e5b75505Sopenharmony_ci	snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
943e5b75505Sopenharmony_ci		 drv->ifname);
944e5b75505Sopenharmony_ci	res = readlink(path, buf, sizeof(buf) - 1);
945e5b75505Sopenharmony_ci	if (res > 0) {
946e5b75505Sopenharmony_ci		buf[res] = '\0';
947e5b75505Sopenharmony_ci		pos = strrchr(buf, '/');
948e5b75505Sopenharmony_ci		if (pos)
949e5b75505Sopenharmony_ci			pos++;
950e5b75505Sopenharmony_ci		else
951e5b75505Sopenharmony_ci			pos = buf;
952e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos);
953e5b75505Sopenharmony_ci		if (os_strncmp(pos, "hostap", 6) == 0 &&
954e5b75505Sopenharmony_ci		    wext_add_hostap(drv) == 0)
955e5b75505Sopenharmony_ci			return;
956e5b75505Sopenharmony_ci	}
957e5b75505Sopenharmony_ci
958e5b75505Sopenharmony_ci	/* Second, use the old design with hardcoded ifname */
959e5b75505Sopenharmony_ci	if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
960e5b75505Sopenharmony_ci		char ifname2[IFNAMSIZ + 1];
961e5b75505Sopenharmony_ci		os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
962e5b75505Sopenharmony_ci		os_memcpy(ifname2, "wifi", 4);
963e5b75505Sopenharmony_ci		wpa_driver_wext_alternative_ifindex(drv, ifname2);
964e5b75505Sopenharmony_ci	}
965e5b75505Sopenharmony_ci}
966e5b75505Sopenharmony_ci
967e5b75505Sopenharmony_ci
968e5b75505Sopenharmony_cistatic int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
969e5b75505Sopenharmony_ci{
970e5b75505Sopenharmony_ci	int send_rfkill_event = 0;
971e5b75505Sopenharmony_ci
972e5b75505Sopenharmony_ci	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
973e5b75505Sopenharmony_ci		if (rfkill_is_blocked(drv->rfkill)) {
974e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
975e5b75505Sopenharmony_ci				   "interface '%s' due to rfkill",
976e5b75505Sopenharmony_ci				   drv->ifname);
977e5b75505Sopenharmony_ci			drv->if_disabled = 1;
978e5b75505Sopenharmony_ci			send_rfkill_event = 1;
979e5b75505Sopenharmony_ci		} else {
980e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "WEXT: Could not set "
981e5b75505Sopenharmony_ci				   "interface '%s' UP", drv->ifname);
982e5b75505Sopenharmony_ci			return -1;
983e5b75505Sopenharmony_ci		}
984e5b75505Sopenharmony_ci	}
985e5b75505Sopenharmony_ci
986e5b75505Sopenharmony_ci	/*
987e5b75505Sopenharmony_ci	 * Make sure that the driver does not have any obsolete PMKID entries.
988e5b75505Sopenharmony_ci	 */
989e5b75505Sopenharmony_ci	wpa_driver_wext_flush_pmkid(drv);
990e5b75505Sopenharmony_ci
991e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_mode(drv, 0) < 0) {
992e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Could not configure driver to use "
993e5b75505Sopenharmony_ci			   "managed mode");
994e5b75505Sopenharmony_ci		/* Try to use it anyway */
995e5b75505Sopenharmony_ci	}
996e5b75505Sopenharmony_ci
997e5b75505Sopenharmony_ci	wpa_driver_wext_get_range(drv);
998e5b75505Sopenharmony_ci
999e5b75505Sopenharmony_ci	/*
1000e5b75505Sopenharmony_ci	 * Unlock the driver's BSSID and force to a random SSID to clear any
1001e5b75505Sopenharmony_ci	 * previous association the driver might have when the supplicant
1002e5b75505Sopenharmony_ci	 * starts up.
1003e5b75505Sopenharmony_ci	 */
1004e5b75505Sopenharmony_ci	wpa_driver_wext_disconnect(drv);
1005e5b75505Sopenharmony_ci
1006e5b75505Sopenharmony_ci	drv->ifindex = if_nametoindex(drv->ifname);
1007e5b75505Sopenharmony_ci
1008e5b75505Sopenharmony_ci	wext_check_hostap(drv);
1009e5b75505Sopenharmony_ci
1010e5b75505Sopenharmony_ci	netlink_send_oper_ifla(drv->netlink, drv->ifindex,
1011e5b75505Sopenharmony_ci			       1, IF_OPER_DORMANT);
1012e5b75505Sopenharmony_ci
1013e5b75505Sopenharmony_ci	if (send_rfkill_event) {
1014e5b75505Sopenharmony_ci		eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
1015e5b75505Sopenharmony_ci				       drv, drv->ctx);
1016e5b75505Sopenharmony_ci	}
1017e5b75505Sopenharmony_ci
1018e5b75505Sopenharmony_ci	return 0;
1019e5b75505Sopenharmony_ci}
1020e5b75505Sopenharmony_ci
1021e5b75505Sopenharmony_ci
1022e5b75505Sopenharmony_ci/**
1023e5b75505Sopenharmony_ci * wpa_driver_wext_deinit - Deinitialize WE driver interface
1024e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
1025e5b75505Sopenharmony_ci *
1026e5b75505Sopenharmony_ci * Shut down driver interface and processing of driver events. Free
1027e5b75505Sopenharmony_ci * private data buffer if one was allocated in wpa_driver_wext_init().
1028e5b75505Sopenharmony_ci */
1029e5b75505Sopenharmony_civoid wpa_driver_wext_deinit(void *priv)
1030e5b75505Sopenharmony_ci{
1031e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1032e5b75505Sopenharmony_ci
1033e5b75505Sopenharmony_ci	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
1034e5b75505Sopenharmony_ci
1035e5b75505Sopenharmony_ci	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
1036e5b75505Sopenharmony_ci	eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx);
1037e5b75505Sopenharmony_ci
1038e5b75505Sopenharmony_ci	/*
1039e5b75505Sopenharmony_ci	 * Clear possibly configured driver parameters in order to make it
1040e5b75505Sopenharmony_ci	 * easier to use the driver after wpa_supplicant has been terminated.
1041e5b75505Sopenharmony_ci	 */
1042e5b75505Sopenharmony_ci	wpa_driver_wext_disconnect(drv);
1043e5b75505Sopenharmony_ci
1044e5b75505Sopenharmony_ci	netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
1045e5b75505Sopenharmony_ci	netlink_deinit(drv->netlink);
1046e5b75505Sopenharmony_ci	rfkill_deinit(drv->rfkill);
1047e5b75505Sopenharmony_ci
1048e5b75505Sopenharmony_ci	if (drv->mlme_sock >= 0)
1049e5b75505Sopenharmony_ci		eloop_unregister_read_sock(drv->mlme_sock);
1050e5b75505Sopenharmony_ci
1051e5b75505Sopenharmony_ci	(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
1052e5b75505Sopenharmony_ci
1053e5b75505Sopenharmony_ci	close(drv->ioctl_sock);
1054e5b75505Sopenharmony_ci	if (drv->mlme_sock >= 0)
1055e5b75505Sopenharmony_ci		close(drv->mlme_sock);
1056e5b75505Sopenharmony_ci	os_free(drv->assoc_req_ies);
1057e5b75505Sopenharmony_ci	os_free(drv->assoc_resp_ies);
1058e5b75505Sopenharmony_ci	os_free(drv);
1059e5b75505Sopenharmony_ci}
1060e5b75505Sopenharmony_ci
1061e5b75505Sopenharmony_ci
1062e5b75505Sopenharmony_ci/**
1063e5b75505Sopenharmony_ci * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
1064e5b75505Sopenharmony_ci * @eloop_ctx: Unused
1065e5b75505Sopenharmony_ci * @timeout_ctx: ctx argument given to wpa_driver_wext_init()
1066e5b75505Sopenharmony_ci *
1067e5b75505Sopenharmony_ci * This function can be used as registered timeout when starting a scan to
1068e5b75505Sopenharmony_ci * generate a scan completed event if the driver does not report this.
1069e5b75505Sopenharmony_ci */
1070e5b75505Sopenharmony_civoid wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
1071e5b75505Sopenharmony_ci{
1072e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
1073e5b75505Sopenharmony_ci	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
1074e5b75505Sopenharmony_ci}
1075e5b75505Sopenharmony_ci
1076e5b75505Sopenharmony_ci
1077e5b75505Sopenharmony_ci/**
1078e5b75505Sopenharmony_ci * wpa_driver_wext_scan - Request the driver to initiate scan
1079e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
1080e5b75505Sopenharmony_ci * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
1081e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
1082e5b75505Sopenharmony_ci */
1083e5b75505Sopenharmony_ciint wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
1084e5b75505Sopenharmony_ci{
1085e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1086e5b75505Sopenharmony_ci	struct iwreq iwr;
1087e5b75505Sopenharmony_ci	int ret = 0, timeout;
1088e5b75505Sopenharmony_ci	struct iw_scan_req req;
1089e5b75505Sopenharmony_ci	const u8 *ssid = params->ssids[0].ssid;
1090e5b75505Sopenharmony_ci	size_t ssid_len = params->ssids[0].ssid_len;
1091e5b75505Sopenharmony_ci
1092e5b75505Sopenharmony_ci	if (ssid_len > IW_ESSID_MAX_SIZE) {
1093e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
1094e5b75505Sopenharmony_ci			   __FUNCTION__, (unsigned long) ssid_len);
1095e5b75505Sopenharmony_ci		return -1;
1096e5b75505Sopenharmony_ci	}
1097e5b75505Sopenharmony_ci
1098e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1099e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1100e5b75505Sopenharmony_ci
1101e5b75505Sopenharmony_ci	if (ssid && ssid_len) {
1102e5b75505Sopenharmony_ci		os_memset(&req, 0, sizeof(req));
1103e5b75505Sopenharmony_ci		req.essid_len = ssid_len;
1104e5b75505Sopenharmony_ci		req.bssid.sa_family = ARPHRD_ETHER;
1105e5b75505Sopenharmony_ci		os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
1106e5b75505Sopenharmony_ci		os_memcpy(req.essid, ssid, ssid_len);
1107e5b75505Sopenharmony_ci		iwr.u.data.pointer = (caddr_t) &req;
1108e5b75505Sopenharmony_ci		iwr.u.data.length = sizeof(req);
1109e5b75505Sopenharmony_ci		iwr.u.data.flags = IW_SCAN_THIS_ESSID;
1110e5b75505Sopenharmony_ci	}
1111e5b75505Sopenharmony_ci
1112e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
1113e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
1114e5b75505Sopenharmony_ci			   strerror(errno));
1115e5b75505Sopenharmony_ci		ret = -1;
1116e5b75505Sopenharmony_ci	}
1117e5b75505Sopenharmony_ci
1118e5b75505Sopenharmony_ci	/* Not all drivers generate "scan completed" wireless event, so try to
1119e5b75505Sopenharmony_ci	 * read results after a timeout. */
1120e5b75505Sopenharmony_ci	timeout = 10;
1121e5b75505Sopenharmony_ci	if (drv->scan_complete_events) {
1122e5b75505Sopenharmony_ci		/*
1123e5b75505Sopenharmony_ci		 * The driver seems to deliver SIOCGIWSCAN events to notify
1124e5b75505Sopenharmony_ci		 * when scan is complete, so use longer timeout to avoid race
1125e5b75505Sopenharmony_ci		 * conditions with scanning and following association request.
1126e5b75505Sopenharmony_ci		 */
1127e5b75505Sopenharmony_ci		timeout = 30;
1128e5b75505Sopenharmony_ci	}
1129e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
1130e5b75505Sopenharmony_ci		   "seconds", ret, timeout);
1131e5b75505Sopenharmony_ci	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
1132e5b75505Sopenharmony_ci	eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
1133e5b75505Sopenharmony_ci			       drv->ctx);
1134e5b75505Sopenharmony_ci
1135e5b75505Sopenharmony_ci	return ret;
1136e5b75505Sopenharmony_ci}
1137e5b75505Sopenharmony_ci
1138e5b75505Sopenharmony_ci
1139e5b75505Sopenharmony_cistatic u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
1140e5b75505Sopenharmony_ci				    size_t *len)
1141e5b75505Sopenharmony_ci{
1142e5b75505Sopenharmony_ci	struct iwreq iwr;
1143e5b75505Sopenharmony_ci	u8 *res_buf;
1144e5b75505Sopenharmony_ci	size_t res_buf_len;
1145e5b75505Sopenharmony_ci
1146e5b75505Sopenharmony_ci	res_buf_len = IW_SCAN_MAX_DATA;
1147e5b75505Sopenharmony_ci	for (;;) {
1148e5b75505Sopenharmony_ci		res_buf = os_malloc(res_buf_len);
1149e5b75505Sopenharmony_ci		if (res_buf == NULL)
1150e5b75505Sopenharmony_ci			return NULL;
1151e5b75505Sopenharmony_ci		os_memset(&iwr, 0, sizeof(iwr));
1152e5b75505Sopenharmony_ci		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1153e5b75505Sopenharmony_ci		iwr.u.data.pointer = res_buf;
1154e5b75505Sopenharmony_ci		iwr.u.data.length = res_buf_len;
1155e5b75505Sopenharmony_ci
1156e5b75505Sopenharmony_ci		if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
1157e5b75505Sopenharmony_ci			break;
1158e5b75505Sopenharmony_ci
1159e5b75505Sopenharmony_ci		if (errno == E2BIG && res_buf_len < 65535) {
1160e5b75505Sopenharmony_ci			os_free(res_buf);
1161e5b75505Sopenharmony_ci			res_buf = NULL;
1162e5b75505Sopenharmony_ci			res_buf_len *= 2;
1163e5b75505Sopenharmony_ci			if (res_buf_len > 65535)
1164e5b75505Sopenharmony_ci				res_buf_len = 65535; /* 16-bit length field */
1165e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "Scan results did not fit - "
1166e5b75505Sopenharmony_ci				   "trying larger buffer (%lu bytes)",
1167e5b75505Sopenharmony_ci				   (unsigned long) res_buf_len);
1168e5b75505Sopenharmony_ci		} else {
1169e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
1170e5b75505Sopenharmony_ci				   strerror(errno));
1171e5b75505Sopenharmony_ci			os_free(res_buf);
1172e5b75505Sopenharmony_ci			return NULL;
1173e5b75505Sopenharmony_ci		}
1174e5b75505Sopenharmony_ci	}
1175e5b75505Sopenharmony_ci
1176e5b75505Sopenharmony_ci	if (iwr.u.data.length > res_buf_len) {
1177e5b75505Sopenharmony_ci		os_free(res_buf);
1178e5b75505Sopenharmony_ci		return NULL;
1179e5b75505Sopenharmony_ci	}
1180e5b75505Sopenharmony_ci	*len = iwr.u.data.length;
1181e5b75505Sopenharmony_ci
1182e5b75505Sopenharmony_ci	return res_buf;
1183e5b75505Sopenharmony_ci}
1184e5b75505Sopenharmony_ci
1185e5b75505Sopenharmony_ci
1186e5b75505Sopenharmony_ci/*
1187e5b75505Sopenharmony_ci * Data structure for collecting WEXT scan results. This is needed to allow
1188e5b75505Sopenharmony_ci * the various methods of reporting IEs to be combined into a single IE buffer.
1189e5b75505Sopenharmony_ci */
1190e5b75505Sopenharmony_cistruct wext_scan_data {
1191e5b75505Sopenharmony_ci	struct wpa_scan_res res;
1192e5b75505Sopenharmony_ci	u8 *ie;
1193e5b75505Sopenharmony_ci	size_t ie_len;
1194e5b75505Sopenharmony_ci	u8 ssid[SSID_MAX_LEN];
1195e5b75505Sopenharmony_ci	size_t ssid_len;
1196e5b75505Sopenharmony_ci	int maxrate;
1197e5b75505Sopenharmony_ci};
1198e5b75505Sopenharmony_ci
1199e5b75505Sopenharmony_ci
1200e5b75505Sopenharmony_cistatic void wext_get_scan_mode(struct iw_event *iwe,
1201e5b75505Sopenharmony_ci			       struct wext_scan_data *res)
1202e5b75505Sopenharmony_ci{
1203e5b75505Sopenharmony_ci	if (iwe->u.mode == IW_MODE_ADHOC)
1204e5b75505Sopenharmony_ci		res->res.caps |= IEEE80211_CAP_IBSS;
1205e5b75505Sopenharmony_ci	else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
1206e5b75505Sopenharmony_ci		res->res.caps |= IEEE80211_CAP_ESS;
1207e5b75505Sopenharmony_ci}
1208e5b75505Sopenharmony_ci
1209e5b75505Sopenharmony_ci
1210e5b75505Sopenharmony_cistatic void wext_get_scan_ssid(struct iw_event *iwe,
1211e5b75505Sopenharmony_ci			       struct wext_scan_data *res, char *custom,
1212e5b75505Sopenharmony_ci			       char *end)
1213e5b75505Sopenharmony_ci{
1214e5b75505Sopenharmony_ci	int ssid_len = iwe->u.essid.length;
1215e5b75505Sopenharmony_ci	if (ssid_len > end - custom)
1216e5b75505Sopenharmony_ci		return;
1217e5b75505Sopenharmony_ci	if (iwe->u.essid.flags &&
1218e5b75505Sopenharmony_ci	    ssid_len > 0 &&
1219e5b75505Sopenharmony_ci	    ssid_len <= IW_ESSID_MAX_SIZE) {
1220e5b75505Sopenharmony_ci		os_memcpy(res->ssid, custom, ssid_len);
1221e5b75505Sopenharmony_ci		res->ssid_len = ssid_len;
1222e5b75505Sopenharmony_ci	}
1223e5b75505Sopenharmony_ci}
1224e5b75505Sopenharmony_ci
1225e5b75505Sopenharmony_ci
1226e5b75505Sopenharmony_cistatic void wext_get_scan_freq(struct iw_event *iwe,
1227e5b75505Sopenharmony_ci			       struct wext_scan_data *res)
1228e5b75505Sopenharmony_ci{
1229e5b75505Sopenharmony_ci	int divi = 1000000, i;
1230e5b75505Sopenharmony_ci
1231e5b75505Sopenharmony_ci	if (iwe->u.freq.e == 0) {
1232e5b75505Sopenharmony_ci		/*
1233e5b75505Sopenharmony_ci		 * Some drivers do not report frequency, but a channel.
1234e5b75505Sopenharmony_ci		 * Try to map this to frequency by assuming they are using
1235e5b75505Sopenharmony_ci		 * IEEE 802.11b/g.  But don't overwrite a previously parsed
1236e5b75505Sopenharmony_ci		 * frequency if the driver sends both frequency and channel,
1237e5b75505Sopenharmony_ci		 * since the driver may be sending an A-band channel that we
1238e5b75505Sopenharmony_ci		 * don't handle here.
1239e5b75505Sopenharmony_ci		 */
1240e5b75505Sopenharmony_ci
1241e5b75505Sopenharmony_ci		if (res->res.freq)
1242e5b75505Sopenharmony_ci			return;
1243e5b75505Sopenharmony_ci
1244e5b75505Sopenharmony_ci		if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
1245e5b75505Sopenharmony_ci			res->res.freq = 2407 + 5 * iwe->u.freq.m;
1246e5b75505Sopenharmony_ci			return;
1247e5b75505Sopenharmony_ci		} else if (iwe->u.freq.m == 14) {
1248e5b75505Sopenharmony_ci			res->res.freq = 2484;
1249e5b75505Sopenharmony_ci			return;
1250e5b75505Sopenharmony_ci		}
1251e5b75505Sopenharmony_ci	}
1252e5b75505Sopenharmony_ci
1253e5b75505Sopenharmony_ci	if (iwe->u.freq.e > 6) {
1254e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
1255e5b75505Sopenharmony_ci			   MACSTR " m=%d e=%d)",
1256e5b75505Sopenharmony_ci			   MAC2STR(res->res.bssid), iwe->u.freq.m,
1257e5b75505Sopenharmony_ci			   iwe->u.freq.e);
1258e5b75505Sopenharmony_ci		return;
1259e5b75505Sopenharmony_ci	}
1260e5b75505Sopenharmony_ci
1261e5b75505Sopenharmony_ci	for (i = 0; i < iwe->u.freq.e; i++)
1262e5b75505Sopenharmony_ci		divi /= 10;
1263e5b75505Sopenharmony_ci	res->res.freq = iwe->u.freq.m / divi;
1264e5b75505Sopenharmony_ci}
1265e5b75505Sopenharmony_ci
1266e5b75505Sopenharmony_ci
1267e5b75505Sopenharmony_cistatic void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
1268e5b75505Sopenharmony_ci			       struct iw_event *iwe,
1269e5b75505Sopenharmony_ci			       struct wext_scan_data *res)
1270e5b75505Sopenharmony_ci{
1271e5b75505Sopenharmony_ci	res->res.qual = iwe->u.qual.qual;
1272e5b75505Sopenharmony_ci	res->res.noise = iwe->u.qual.noise;
1273e5b75505Sopenharmony_ci	res->res.level = iwe->u.qual.level;
1274e5b75505Sopenharmony_ci	if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID)
1275e5b75505Sopenharmony_ci		res->res.flags |= WPA_SCAN_QUAL_INVALID;
1276e5b75505Sopenharmony_ci	if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID)
1277e5b75505Sopenharmony_ci		res->res.flags |= WPA_SCAN_LEVEL_INVALID;
1278e5b75505Sopenharmony_ci	if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID)
1279e5b75505Sopenharmony_ci		res->res.flags |= WPA_SCAN_NOISE_INVALID;
1280e5b75505Sopenharmony_ci	if (iwe->u.qual.updated & IW_QUAL_DBM)
1281e5b75505Sopenharmony_ci		res->res.flags |= WPA_SCAN_LEVEL_DBM;
1282e5b75505Sopenharmony_ci	if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
1283e5b75505Sopenharmony_ci	    ((iwe->u.qual.level != 0) &&
1284e5b75505Sopenharmony_ci	     (iwe->u.qual.level > drv->max_level))) {
1285e5b75505Sopenharmony_ci		if (iwe->u.qual.level >= 64)
1286e5b75505Sopenharmony_ci			res->res.level -= 0x100;
1287e5b75505Sopenharmony_ci		if (iwe->u.qual.noise >= 64)
1288e5b75505Sopenharmony_ci			res->res.noise -= 0x100;
1289e5b75505Sopenharmony_ci	}
1290e5b75505Sopenharmony_ci}
1291e5b75505Sopenharmony_ci
1292e5b75505Sopenharmony_ci
1293e5b75505Sopenharmony_cistatic void wext_get_scan_encode(struct iw_event *iwe,
1294e5b75505Sopenharmony_ci				 struct wext_scan_data *res)
1295e5b75505Sopenharmony_ci{
1296e5b75505Sopenharmony_ci	if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
1297e5b75505Sopenharmony_ci		res->res.caps |= IEEE80211_CAP_PRIVACY;
1298e5b75505Sopenharmony_ci}
1299e5b75505Sopenharmony_ci
1300e5b75505Sopenharmony_ci
1301e5b75505Sopenharmony_cistatic void wext_get_scan_rate(struct iw_event *iwe,
1302e5b75505Sopenharmony_ci			       struct wext_scan_data *res, char *pos,
1303e5b75505Sopenharmony_ci			       char *end)
1304e5b75505Sopenharmony_ci{
1305e5b75505Sopenharmony_ci	int maxrate;
1306e5b75505Sopenharmony_ci	char *custom = pos + IW_EV_LCP_LEN;
1307e5b75505Sopenharmony_ci	struct iw_param p;
1308e5b75505Sopenharmony_ci	size_t clen;
1309e5b75505Sopenharmony_ci
1310e5b75505Sopenharmony_ci	clen = iwe->len;
1311e5b75505Sopenharmony_ci	if (clen > (size_t) (end - custom))
1312e5b75505Sopenharmony_ci		return;
1313e5b75505Sopenharmony_ci	maxrate = 0;
1314e5b75505Sopenharmony_ci	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
1315e5b75505Sopenharmony_ci		/* Note: may be misaligned, make a local, aligned copy */
1316e5b75505Sopenharmony_ci		os_memcpy(&p, custom, sizeof(struct iw_param));
1317e5b75505Sopenharmony_ci		if (p.value > maxrate)
1318e5b75505Sopenharmony_ci			maxrate = p.value;
1319e5b75505Sopenharmony_ci		clen -= sizeof(struct iw_param);
1320e5b75505Sopenharmony_ci		custom += sizeof(struct iw_param);
1321e5b75505Sopenharmony_ci	}
1322e5b75505Sopenharmony_ci
1323e5b75505Sopenharmony_ci	/* Convert the maxrate from WE-style (b/s units) to
1324e5b75505Sopenharmony_ci	 * 802.11 rates (500000 b/s units).
1325e5b75505Sopenharmony_ci	 */
1326e5b75505Sopenharmony_ci	res->maxrate = maxrate / 500000;
1327e5b75505Sopenharmony_ci}
1328e5b75505Sopenharmony_ci
1329e5b75505Sopenharmony_ci
1330e5b75505Sopenharmony_cistatic void wext_get_scan_iwevgenie(struct iw_event *iwe,
1331e5b75505Sopenharmony_ci				    struct wext_scan_data *res, char *custom,
1332e5b75505Sopenharmony_ci				    char *end)
1333e5b75505Sopenharmony_ci{
1334e5b75505Sopenharmony_ci	char *genie, *gpos, *gend;
1335e5b75505Sopenharmony_ci	u8 *tmp;
1336e5b75505Sopenharmony_ci
1337e5b75505Sopenharmony_ci	if (iwe->u.data.length == 0)
1338e5b75505Sopenharmony_ci		return;
1339e5b75505Sopenharmony_ci
1340e5b75505Sopenharmony_ci	gpos = genie = custom;
1341e5b75505Sopenharmony_ci	gend = genie + iwe->u.data.length;
1342e5b75505Sopenharmony_ci	if (gend > end) {
1343e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "IWEVGENIE overflow");
1344e5b75505Sopenharmony_ci		return;
1345e5b75505Sopenharmony_ci	}
1346e5b75505Sopenharmony_ci
1347e5b75505Sopenharmony_ci	tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
1348e5b75505Sopenharmony_ci	if (tmp == NULL)
1349e5b75505Sopenharmony_ci		return;
1350e5b75505Sopenharmony_ci	os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
1351e5b75505Sopenharmony_ci	res->ie = tmp;
1352e5b75505Sopenharmony_ci	res->ie_len += gend - gpos;
1353e5b75505Sopenharmony_ci}
1354e5b75505Sopenharmony_ci
1355e5b75505Sopenharmony_ci
1356e5b75505Sopenharmony_cistatic void wext_get_scan_custom(struct iw_event *iwe,
1357e5b75505Sopenharmony_ci				 struct wext_scan_data *res, char *custom,
1358e5b75505Sopenharmony_ci				 char *end)
1359e5b75505Sopenharmony_ci{
1360e5b75505Sopenharmony_ci	size_t clen;
1361e5b75505Sopenharmony_ci	u8 *tmp;
1362e5b75505Sopenharmony_ci
1363e5b75505Sopenharmony_ci	clen = iwe->u.data.length;
1364e5b75505Sopenharmony_ci	if (clen > (size_t) (end - custom))
1365e5b75505Sopenharmony_ci		return;
1366e5b75505Sopenharmony_ci
1367e5b75505Sopenharmony_ci	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
1368e5b75505Sopenharmony_ci		char *spos;
1369e5b75505Sopenharmony_ci		int bytes;
1370e5b75505Sopenharmony_ci		spos = custom + 7;
1371e5b75505Sopenharmony_ci		bytes = custom + clen - spos;
1372e5b75505Sopenharmony_ci		if (bytes & 1 || bytes == 0)
1373e5b75505Sopenharmony_ci			return;
1374e5b75505Sopenharmony_ci		bytes /= 2;
1375e5b75505Sopenharmony_ci		tmp = os_realloc(res->ie, res->ie_len + bytes);
1376e5b75505Sopenharmony_ci		if (tmp == NULL)
1377e5b75505Sopenharmony_ci			return;
1378e5b75505Sopenharmony_ci		res->ie = tmp;
1379e5b75505Sopenharmony_ci		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
1380e5b75505Sopenharmony_ci			return;
1381e5b75505Sopenharmony_ci		res->ie_len += bytes;
1382e5b75505Sopenharmony_ci	} else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
1383e5b75505Sopenharmony_ci		char *spos;
1384e5b75505Sopenharmony_ci		int bytes;
1385e5b75505Sopenharmony_ci		spos = custom + 7;
1386e5b75505Sopenharmony_ci		bytes = custom + clen - spos;
1387e5b75505Sopenharmony_ci		if (bytes & 1 || bytes == 0)
1388e5b75505Sopenharmony_ci			return;
1389e5b75505Sopenharmony_ci		bytes /= 2;
1390e5b75505Sopenharmony_ci		tmp = os_realloc(res->ie, res->ie_len + bytes);
1391e5b75505Sopenharmony_ci		if (tmp == NULL)
1392e5b75505Sopenharmony_ci			return;
1393e5b75505Sopenharmony_ci		res->ie = tmp;
1394e5b75505Sopenharmony_ci		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
1395e5b75505Sopenharmony_ci			return;
1396e5b75505Sopenharmony_ci		res->ie_len += bytes;
1397e5b75505Sopenharmony_ci	} else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
1398e5b75505Sopenharmony_ci		char *spos;
1399e5b75505Sopenharmony_ci		int bytes;
1400e5b75505Sopenharmony_ci		u8 bin[8];
1401e5b75505Sopenharmony_ci		spos = custom + 4;
1402e5b75505Sopenharmony_ci		bytes = custom + clen - spos;
1403e5b75505Sopenharmony_ci		if (bytes != 16) {
1404e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
1405e5b75505Sopenharmony_ci			return;
1406e5b75505Sopenharmony_ci		}
1407e5b75505Sopenharmony_ci		bytes /= 2;
1408e5b75505Sopenharmony_ci		if (hexstr2bin(spos, bin, bytes) < 0) {
1409e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
1410e5b75505Sopenharmony_ci			return;
1411e5b75505Sopenharmony_ci		}
1412e5b75505Sopenharmony_ci		res->res.tsf += WPA_GET_BE64(bin);
1413e5b75505Sopenharmony_ci	}
1414e5b75505Sopenharmony_ci}
1415e5b75505Sopenharmony_ci
1416e5b75505Sopenharmony_ci
1417e5b75505Sopenharmony_cistatic int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
1418e5b75505Sopenharmony_ci{
1419e5b75505Sopenharmony_ci	return drv->we_version_compiled > 18 &&
1420e5b75505Sopenharmony_ci		(cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
1421e5b75505Sopenharmony_ci		 cmd == IWEVGENIE || cmd == IWEVCUSTOM);
1422e5b75505Sopenharmony_ci}
1423e5b75505Sopenharmony_ci
1424e5b75505Sopenharmony_ci
1425e5b75505Sopenharmony_cistatic void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
1426e5b75505Sopenharmony_ci					   struct wext_scan_data *data)
1427e5b75505Sopenharmony_ci{
1428e5b75505Sopenharmony_ci	struct wpa_scan_res **tmp;
1429e5b75505Sopenharmony_ci	struct wpa_scan_res *r;
1430e5b75505Sopenharmony_ci	size_t extra_len;
1431e5b75505Sopenharmony_ci	u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
1432e5b75505Sopenharmony_ci
1433e5b75505Sopenharmony_ci	/* Figure out whether we need to fake any IEs */
1434e5b75505Sopenharmony_ci	pos = data->ie;
1435e5b75505Sopenharmony_ci	end = pos + data->ie_len;
1436e5b75505Sopenharmony_ci	while (pos && end - pos > 1) {
1437e5b75505Sopenharmony_ci		if (2 + pos[1] > end - pos)
1438e5b75505Sopenharmony_ci			break;
1439e5b75505Sopenharmony_ci		if (pos[0] == WLAN_EID_SSID)
1440e5b75505Sopenharmony_ci			ssid_ie = pos;
1441e5b75505Sopenharmony_ci		else if (pos[0] == WLAN_EID_SUPP_RATES)
1442e5b75505Sopenharmony_ci			rate_ie = pos;
1443e5b75505Sopenharmony_ci		else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
1444e5b75505Sopenharmony_ci			rate_ie = pos;
1445e5b75505Sopenharmony_ci		pos += 2 + pos[1];
1446e5b75505Sopenharmony_ci	}
1447e5b75505Sopenharmony_ci
1448e5b75505Sopenharmony_ci	extra_len = 0;
1449e5b75505Sopenharmony_ci	if (ssid_ie == NULL)
1450e5b75505Sopenharmony_ci		extra_len += 2 + data->ssid_len;
1451e5b75505Sopenharmony_ci	if (rate_ie == NULL && data->maxrate)
1452e5b75505Sopenharmony_ci		extra_len += 3;
1453e5b75505Sopenharmony_ci
1454e5b75505Sopenharmony_ci	r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
1455e5b75505Sopenharmony_ci	if (r == NULL)
1456e5b75505Sopenharmony_ci		return;
1457e5b75505Sopenharmony_ci	os_memcpy(r, &data->res, sizeof(*r));
1458e5b75505Sopenharmony_ci	r->ie_len = extra_len + data->ie_len;
1459e5b75505Sopenharmony_ci	pos = (u8 *) (r + 1);
1460e5b75505Sopenharmony_ci	if (ssid_ie == NULL) {
1461e5b75505Sopenharmony_ci		/*
1462e5b75505Sopenharmony_ci		 * Generate a fake SSID IE since the driver did not report
1463e5b75505Sopenharmony_ci		 * a full IE list.
1464e5b75505Sopenharmony_ci		 */
1465e5b75505Sopenharmony_ci		*pos++ = WLAN_EID_SSID;
1466e5b75505Sopenharmony_ci		*pos++ = data->ssid_len;
1467e5b75505Sopenharmony_ci		os_memcpy(pos, data->ssid, data->ssid_len);
1468e5b75505Sopenharmony_ci		pos += data->ssid_len;
1469e5b75505Sopenharmony_ci	}
1470e5b75505Sopenharmony_ci	if (rate_ie == NULL && data->maxrate) {
1471e5b75505Sopenharmony_ci		/*
1472e5b75505Sopenharmony_ci		 * Generate a fake Supported Rates IE since the driver did not
1473e5b75505Sopenharmony_ci		 * report a full IE list.
1474e5b75505Sopenharmony_ci		 */
1475e5b75505Sopenharmony_ci		*pos++ = WLAN_EID_SUPP_RATES;
1476e5b75505Sopenharmony_ci		*pos++ = 1;
1477e5b75505Sopenharmony_ci		*pos++ = data->maxrate;
1478e5b75505Sopenharmony_ci	}
1479e5b75505Sopenharmony_ci	if (data->ie)
1480e5b75505Sopenharmony_ci		os_memcpy(pos, data->ie, data->ie_len);
1481e5b75505Sopenharmony_ci
1482e5b75505Sopenharmony_ci	tmp = os_realloc_array(res->res, res->num + 1,
1483e5b75505Sopenharmony_ci			       sizeof(struct wpa_scan_res *));
1484e5b75505Sopenharmony_ci	if (tmp == NULL) {
1485e5b75505Sopenharmony_ci		os_free(r);
1486e5b75505Sopenharmony_ci		return;
1487e5b75505Sopenharmony_ci	}
1488e5b75505Sopenharmony_ci	tmp[res->num++] = r;
1489e5b75505Sopenharmony_ci	res->res = tmp;
1490e5b75505Sopenharmony_ci}
1491e5b75505Sopenharmony_ci
1492e5b75505Sopenharmony_ci
1493e5b75505Sopenharmony_ci/**
1494e5b75505Sopenharmony_ci * wpa_driver_wext_get_scan_results - Fetch the latest scan results
1495e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
1496e5b75505Sopenharmony_ci * Returns: Scan results on success, -1 on failure
1497e5b75505Sopenharmony_ci */
1498e5b75505Sopenharmony_cistruct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
1499e5b75505Sopenharmony_ci{
1500e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1501e5b75505Sopenharmony_ci	size_t len;
1502e5b75505Sopenharmony_ci	int first;
1503e5b75505Sopenharmony_ci	u8 *res_buf;
1504e5b75505Sopenharmony_ci	struct iw_event iwe_buf, *iwe = &iwe_buf;
1505e5b75505Sopenharmony_ci	char *pos, *end, *custom;
1506e5b75505Sopenharmony_ci	struct wpa_scan_results *res;
1507e5b75505Sopenharmony_ci	struct wext_scan_data data;
1508e5b75505Sopenharmony_ci
1509e5b75505Sopenharmony_ci	res_buf = wpa_driver_wext_giwscan(drv, &len);
1510e5b75505Sopenharmony_ci	if (res_buf == NULL)
1511e5b75505Sopenharmony_ci		return NULL;
1512e5b75505Sopenharmony_ci
1513e5b75505Sopenharmony_ci	first = 1;
1514e5b75505Sopenharmony_ci
1515e5b75505Sopenharmony_ci	res = os_zalloc(sizeof(*res));
1516e5b75505Sopenharmony_ci	if (res == NULL) {
1517e5b75505Sopenharmony_ci		os_free(res_buf);
1518e5b75505Sopenharmony_ci		return NULL;
1519e5b75505Sopenharmony_ci	}
1520e5b75505Sopenharmony_ci
1521e5b75505Sopenharmony_ci	pos = (char *) res_buf;
1522e5b75505Sopenharmony_ci	end = (char *) res_buf + len;
1523e5b75505Sopenharmony_ci	os_memset(&data, 0, sizeof(data));
1524e5b75505Sopenharmony_ci
1525e5b75505Sopenharmony_ci	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
1526e5b75505Sopenharmony_ci		/* Event data may be unaligned, so make a local, aligned copy
1527e5b75505Sopenharmony_ci		 * before processing. */
1528e5b75505Sopenharmony_ci		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
1529e5b75505Sopenharmony_ci		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
1530e5b75505Sopenharmony_ci			break;
1531e5b75505Sopenharmony_ci
1532e5b75505Sopenharmony_ci		custom = pos + IW_EV_POINT_LEN;
1533e5b75505Sopenharmony_ci		if (wext_19_iw_point(drv, iwe->cmd)) {
1534e5b75505Sopenharmony_ci			/* WE-19 removed the pointer from struct iw_point */
1535e5b75505Sopenharmony_ci			char *dpos = (char *) &iwe_buf.u.data.length;
1536e5b75505Sopenharmony_ci			int dlen = dpos - (char *) &iwe_buf;
1537e5b75505Sopenharmony_ci			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
1538e5b75505Sopenharmony_ci				  sizeof(struct iw_event) - dlen);
1539e5b75505Sopenharmony_ci		} else {
1540e5b75505Sopenharmony_ci			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
1541e5b75505Sopenharmony_ci			custom += IW_EV_POINT_OFF;
1542e5b75505Sopenharmony_ci		}
1543e5b75505Sopenharmony_ci
1544e5b75505Sopenharmony_ci		switch (iwe->cmd) {
1545e5b75505Sopenharmony_ci		case SIOCGIWAP:
1546e5b75505Sopenharmony_ci			if (!first)
1547e5b75505Sopenharmony_ci				wpa_driver_wext_add_scan_entry(res, &data);
1548e5b75505Sopenharmony_ci			first = 0;
1549e5b75505Sopenharmony_ci			os_free(data.ie);
1550e5b75505Sopenharmony_ci			os_memset(&data, 0, sizeof(data));
1551e5b75505Sopenharmony_ci			os_memcpy(data.res.bssid,
1552e5b75505Sopenharmony_ci				  iwe->u.ap_addr.sa_data, ETH_ALEN);
1553e5b75505Sopenharmony_ci			break;
1554e5b75505Sopenharmony_ci		case SIOCGIWMODE:
1555e5b75505Sopenharmony_ci			wext_get_scan_mode(iwe, &data);
1556e5b75505Sopenharmony_ci			break;
1557e5b75505Sopenharmony_ci		case SIOCGIWESSID:
1558e5b75505Sopenharmony_ci			wext_get_scan_ssid(iwe, &data, custom, end);
1559e5b75505Sopenharmony_ci			break;
1560e5b75505Sopenharmony_ci		case SIOCGIWFREQ:
1561e5b75505Sopenharmony_ci			wext_get_scan_freq(iwe, &data);
1562e5b75505Sopenharmony_ci			break;
1563e5b75505Sopenharmony_ci		case IWEVQUAL:
1564e5b75505Sopenharmony_ci			wext_get_scan_qual(drv, iwe, &data);
1565e5b75505Sopenharmony_ci			break;
1566e5b75505Sopenharmony_ci		case SIOCGIWENCODE:
1567e5b75505Sopenharmony_ci			wext_get_scan_encode(iwe, &data);
1568e5b75505Sopenharmony_ci			break;
1569e5b75505Sopenharmony_ci		case SIOCGIWRATE:
1570e5b75505Sopenharmony_ci			wext_get_scan_rate(iwe, &data, pos, end);
1571e5b75505Sopenharmony_ci			break;
1572e5b75505Sopenharmony_ci		case IWEVGENIE:
1573e5b75505Sopenharmony_ci			wext_get_scan_iwevgenie(iwe, &data, custom, end);
1574e5b75505Sopenharmony_ci			break;
1575e5b75505Sopenharmony_ci		case IWEVCUSTOM:
1576e5b75505Sopenharmony_ci			wext_get_scan_custom(iwe, &data, custom, end);
1577e5b75505Sopenharmony_ci			break;
1578e5b75505Sopenharmony_ci		}
1579e5b75505Sopenharmony_ci
1580e5b75505Sopenharmony_ci		pos += iwe->len;
1581e5b75505Sopenharmony_ci	}
1582e5b75505Sopenharmony_ci	os_free(res_buf);
1583e5b75505Sopenharmony_ci	res_buf = NULL;
1584e5b75505Sopenharmony_ci	if (!first)
1585e5b75505Sopenharmony_ci		wpa_driver_wext_add_scan_entry(res, &data);
1586e5b75505Sopenharmony_ci	os_free(data.ie);
1587e5b75505Sopenharmony_ci
1588e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
1589e5b75505Sopenharmony_ci		   (unsigned long) len, (unsigned long) res->num);
1590e5b75505Sopenharmony_ci
1591e5b75505Sopenharmony_ci	return res;
1592e5b75505Sopenharmony_ci}
1593e5b75505Sopenharmony_ci
1594e5b75505Sopenharmony_ci
1595e5b75505Sopenharmony_cistatic int wpa_driver_wext_get_range(void *priv)
1596e5b75505Sopenharmony_ci{
1597e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1598e5b75505Sopenharmony_ci	struct iw_range *range;
1599e5b75505Sopenharmony_ci	struct iwreq iwr;
1600e5b75505Sopenharmony_ci	int minlen;
1601e5b75505Sopenharmony_ci	size_t buflen;
1602e5b75505Sopenharmony_ci
1603e5b75505Sopenharmony_ci	/*
1604e5b75505Sopenharmony_ci	 * Use larger buffer than struct iw_range in order to allow the
1605e5b75505Sopenharmony_ci	 * structure to grow in the future.
1606e5b75505Sopenharmony_ci	 */
1607e5b75505Sopenharmony_ci	buflen = sizeof(struct iw_range) + 500;
1608e5b75505Sopenharmony_ci	range = os_zalloc(buflen);
1609e5b75505Sopenharmony_ci	if (range == NULL)
1610e5b75505Sopenharmony_ci		return -1;
1611e5b75505Sopenharmony_ci
1612e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1613e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1614e5b75505Sopenharmony_ci	iwr.u.data.pointer = (caddr_t) range;
1615e5b75505Sopenharmony_ci	iwr.u.data.length = buflen;
1616e5b75505Sopenharmony_ci
1617e5b75505Sopenharmony_ci	minlen = ((char *) &range->enc_capa) - (char *) range +
1618e5b75505Sopenharmony_ci		sizeof(range->enc_capa);
1619e5b75505Sopenharmony_ci
1620e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
1621e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
1622e5b75505Sopenharmony_ci			   strerror(errno));
1623e5b75505Sopenharmony_ci		os_free(range);
1624e5b75505Sopenharmony_ci		return -1;
1625e5b75505Sopenharmony_ci	} else if (iwr.u.data.length >= minlen &&
1626e5b75505Sopenharmony_ci		   range->we_version_compiled >= 18) {
1627e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
1628e5b75505Sopenharmony_ci			   "WE(source)=%d enc_capa=0x%x",
1629e5b75505Sopenharmony_ci			   range->we_version_compiled,
1630e5b75505Sopenharmony_ci			   range->we_version_source,
1631e5b75505Sopenharmony_ci			   range->enc_capa);
1632e5b75505Sopenharmony_ci		drv->has_capability = 1;
1633e5b75505Sopenharmony_ci		drv->we_version_compiled = range->we_version_compiled;
1634e5b75505Sopenharmony_ci		if (range->enc_capa & IW_ENC_CAPA_WPA) {
1635e5b75505Sopenharmony_ci			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
1636e5b75505Sopenharmony_ci				WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
1637e5b75505Sopenharmony_ci		}
1638e5b75505Sopenharmony_ci		if (range->enc_capa & IW_ENC_CAPA_WPA2) {
1639e5b75505Sopenharmony_ci			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
1640e5b75505Sopenharmony_ci				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
1641e5b75505Sopenharmony_ci		}
1642e5b75505Sopenharmony_ci		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
1643e5b75505Sopenharmony_ci			WPA_DRIVER_CAPA_ENC_WEP104;
1644e5b75505Sopenharmony_ci		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
1645e5b75505Sopenharmony_ci		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
1646e5b75505Sopenharmony_ci			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
1647e5b75505Sopenharmony_ci		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
1648e5b75505Sopenharmony_ci			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
1649e5b75505Sopenharmony_ci		if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
1650e5b75505Sopenharmony_ci			drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
1651e5b75505Sopenharmony_ci				WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
1652e5b75505Sopenharmony_ci		drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
1653e5b75505Sopenharmony_ci			WPA_DRIVER_AUTH_SHARED |
1654e5b75505Sopenharmony_ci			WPA_DRIVER_AUTH_LEAP;
1655e5b75505Sopenharmony_ci		drv->capa.max_scan_ssids = 1;
1656e5b75505Sopenharmony_ci
1657e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
1658e5b75505Sopenharmony_ci			   "flags 0x%llx",
1659e5b75505Sopenharmony_ci			   drv->capa.key_mgmt, drv->capa.enc,
1660e5b75505Sopenharmony_ci			   (unsigned long long) drv->capa.flags);
1661e5b75505Sopenharmony_ci	} else {
1662e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
1663e5b75505Sopenharmony_ci			   "assuming WPA is not supported");
1664e5b75505Sopenharmony_ci	}
1665e5b75505Sopenharmony_ci
1666e5b75505Sopenharmony_ci	drv->max_level = range->max_qual.level;
1667e5b75505Sopenharmony_ci
1668e5b75505Sopenharmony_ci	os_free(range);
1669e5b75505Sopenharmony_ci	return 0;
1670e5b75505Sopenharmony_ci}
1671e5b75505Sopenharmony_ci
1672e5b75505Sopenharmony_ci
1673e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
1674e5b75505Sopenharmony_ci				   const u8 *psk)
1675e5b75505Sopenharmony_ci{
1676e5b75505Sopenharmony_ci	struct iw_encode_ext *ext;
1677e5b75505Sopenharmony_ci	struct iwreq iwr;
1678e5b75505Sopenharmony_ci	int ret;
1679e5b75505Sopenharmony_ci
1680e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
1681e5b75505Sopenharmony_ci
1682e5b75505Sopenharmony_ci	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
1683e5b75505Sopenharmony_ci		return 0;
1684e5b75505Sopenharmony_ci
1685e5b75505Sopenharmony_ci	if (!psk)
1686e5b75505Sopenharmony_ci		return 0;
1687e5b75505Sopenharmony_ci
1688e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1689e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1690e5b75505Sopenharmony_ci
1691e5b75505Sopenharmony_ci	ext = os_zalloc(sizeof(*ext) + PMK_LEN);
1692e5b75505Sopenharmony_ci	if (ext == NULL)
1693e5b75505Sopenharmony_ci		return -1;
1694e5b75505Sopenharmony_ci
1695e5b75505Sopenharmony_ci	iwr.u.encoding.pointer = (caddr_t) ext;
1696e5b75505Sopenharmony_ci	iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
1697e5b75505Sopenharmony_ci	ext->key_len = PMK_LEN;
1698e5b75505Sopenharmony_ci	os_memcpy(&ext->key, psk, ext->key_len);
1699e5b75505Sopenharmony_ci	ext->alg = IW_ENCODE_ALG_PMK;
1700e5b75505Sopenharmony_ci
1701e5b75505Sopenharmony_ci	ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
1702e5b75505Sopenharmony_ci	if (ret < 0)
1703e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
1704e5b75505Sopenharmony_ci			   strerror(errno));
1705e5b75505Sopenharmony_ci	os_free(ext);
1706e5b75505Sopenharmony_ci
1707e5b75505Sopenharmony_ci	return ret;
1708e5b75505Sopenharmony_ci}
1709e5b75505Sopenharmony_ci
1710e5b75505Sopenharmony_ci
1711e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
1712e5b75505Sopenharmony_ci				       const u8 *addr, int key_idx,
1713e5b75505Sopenharmony_ci				       int set_tx, const u8 *seq,
1714e5b75505Sopenharmony_ci				       size_t seq_len,
1715e5b75505Sopenharmony_ci				       const u8 *key, size_t key_len)
1716e5b75505Sopenharmony_ci{
1717e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1718e5b75505Sopenharmony_ci	struct iwreq iwr;
1719e5b75505Sopenharmony_ci	int ret = 0;
1720e5b75505Sopenharmony_ci	struct iw_encode_ext *ext;
1721e5b75505Sopenharmony_ci
1722e5b75505Sopenharmony_ci	if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
1723e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
1724e5b75505Sopenharmony_ci			   __FUNCTION__, (unsigned long) seq_len);
1725e5b75505Sopenharmony_ci		return -1;
1726e5b75505Sopenharmony_ci	}
1727e5b75505Sopenharmony_ci
1728e5b75505Sopenharmony_ci	ext = os_zalloc(sizeof(*ext) + key_len);
1729e5b75505Sopenharmony_ci	if (ext == NULL)
1730e5b75505Sopenharmony_ci		return -1;
1731e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1732e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1733e5b75505Sopenharmony_ci	iwr.u.encoding.flags = key_idx + 1;
1734e5b75505Sopenharmony_ci	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
1735e5b75505Sopenharmony_ci	if (alg == WPA_ALG_NONE)
1736e5b75505Sopenharmony_ci		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
1737e5b75505Sopenharmony_ci	iwr.u.encoding.pointer = (caddr_t) ext;
1738e5b75505Sopenharmony_ci	iwr.u.encoding.length = sizeof(*ext) + key_len;
1739e5b75505Sopenharmony_ci
1740e5b75505Sopenharmony_ci	if (addr == NULL || is_broadcast_ether_addr(addr))
1741e5b75505Sopenharmony_ci		ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
1742e5b75505Sopenharmony_ci	if (set_tx)
1743e5b75505Sopenharmony_ci		ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
1744e5b75505Sopenharmony_ci
1745e5b75505Sopenharmony_ci	ext->addr.sa_family = ARPHRD_ETHER;
1746e5b75505Sopenharmony_ci	if (addr)
1747e5b75505Sopenharmony_ci		os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
1748e5b75505Sopenharmony_ci	else
1749e5b75505Sopenharmony_ci		os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
1750e5b75505Sopenharmony_ci	if (key && key_len) {
1751e5b75505Sopenharmony_ci		os_memcpy(ext + 1, key, key_len);
1752e5b75505Sopenharmony_ci		ext->key_len = key_len;
1753e5b75505Sopenharmony_ci	}
1754e5b75505Sopenharmony_ci	switch (alg) {
1755e5b75505Sopenharmony_ci	case WPA_ALG_NONE:
1756e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_NONE;
1757e5b75505Sopenharmony_ci		break;
1758e5b75505Sopenharmony_ci	case WPA_ALG_WEP:
1759e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_WEP;
1760e5b75505Sopenharmony_ci		break;
1761e5b75505Sopenharmony_ci	case WPA_ALG_TKIP:
1762e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_TKIP;
1763e5b75505Sopenharmony_ci		break;
1764e5b75505Sopenharmony_ci	case WPA_ALG_CCMP:
1765e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_CCMP;
1766e5b75505Sopenharmony_ci		break;
1767e5b75505Sopenharmony_ci	case WPA_ALG_PMK:
1768e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_PMK;
1769e5b75505Sopenharmony_ci		break;
1770e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211W
1771e5b75505Sopenharmony_ci	case WPA_ALG_IGTK:
1772e5b75505Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_AES_CMAC;
1773e5b75505Sopenharmony_ci		break;
1774e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211W */
1775e5b75505Sopenharmony_ci	default:
1776e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
1777e5b75505Sopenharmony_ci			   __FUNCTION__, alg);
1778e5b75505Sopenharmony_ci		os_free(ext);
1779e5b75505Sopenharmony_ci		return -1;
1780e5b75505Sopenharmony_ci	}
1781e5b75505Sopenharmony_ci
1782e5b75505Sopenharmony_ci	if (seq && seq_len) {
1783e5b75505Sopenharmony_ci		ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
1784e5b75505Sopenharmony_ci		os_memcpy(ext->rx_seq, seq, seq_len);
1785e5b75505Sopenharmony_ci	}
1786e5b75505Sopenharmony_ci
1787e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
1788e5b75505Sopenharmony_ci		ret = errno == EOPNOTSUPP ? -2 : -1;
1789e5b75505Sopenharmony_ci		if (errno == ENODEV) {
1790e5b75505Sopenharmony_ci			/*
1791e5b75505Sopenharmony_ci			 * ndiswrapper seems to be returning incorrect error
1792e5b75505Sopenharmony_ci			 * code.. */
1793e5b75505Sopenharmony_ci			ret = -2;
1794e5b75505Sopenharmony_ci		}
1795e5b75505Sopenharmony_ci
1796e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
1797e5b75505Sopenharmony_ci			   strerror(errno));
1798e5b75505Sopenharmony_ci	}
1799e5b75505Sopenharmony_ci
1800e5b75505Sopenharmony_ci	os_free(ext);
1801e5b75505Sopenharmony_ci	return ret;
1802e5b75505Sopenharmony_ci}
1803e5b75505Sopenharmony_ci
1804e5b75505Sopenharmony_ci
1805e5b75505Sopenharmony_ci/**
1806e5b75505Sopenharmony_ci * wpa_driver_wext_set_key - Configure encryption key
1807e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
1808e5b75505Sopenharmony_ci * @priv: Private driver interface data
1809e5b75505Sopenharmony_ci * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
1810e5b75505Sopenharmony_ci *	%WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
1811e5b75505Sopenharmony_ci * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for
1812e5b75505Sopenharmony_ci *	broadcast/default keys
1813e5b75505Sopenharmony_ci * @key_idx: key index (0..3), usually 0 for unicast keys
1814e5b75505Sopenharmony_ci * @set_tx: Configure this key as the default Tx key (only used when
1815e5b75505Sopenharmony_ci *	driver does not support separate unicast/individual key
1816e5b75505Sopenharmony_ci * @seq: Sequence number/packet number, seq_len octets, the next
1817e5b75505Sopenharmony_ci *	packet number to be used for in replay protection; configured
1818e5b75505Sopenharmony_ci *	for Rx keys (in most cases, this is only used with broadcast
1819e5b75505Sopenharmony_ci *	keys and set to zero for unicast keys)
1820e5b75505Sopenharmony_ci * @seq_len: Length of the seq, depends on the algorithm:
1821e5b75505Sopenharmony_ci *	TKIP: 6 octets, CCMP: 6 octets
1822e5b75505Sopenharmony_ci * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
1823e5b75505Sopenharmony_ci *	8-byte Rx Mic Key
1824e5b75505Sopenharmony_ci * @key_len: Length of the key buffer in octets (WEP: 5 or 13,
1825e5b75505Sopenharmony_ci *	TKIP: 32, CCMP: 16)
1826e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
1827e5b75505Sopenharmony_ci *
1828e5b75505Sopenharmony_ci * This function uses SIOCSIWENCODEEXT by default, but tries to use
1829e5b75505Sopenharmony_ci * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
1830e5b75505Sopenharmony_ci */
1831e5b75505Sopenharmony_ciint wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
1832e5b75505Sopenharmony_ci			    const u8 *addr, int key_idx,
1833e5b75505Sopenharmony_ci			    int set_tx, const u8 *seq, size_t seq_len,
1834e5b75505Sopenharmony_ci			    const u8 *key, size_t key_len)
1835e5b75505Sopenharmony_ci{
1836e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1837e5b75505Sopenharmony_ci	struct iwreq iwr;
1838e5b75505Sopenharmony_ci	int ret = 0;
1839e5b75505Sopenharmony_ci
1840e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
1841e5b75505Sopenharmony_ci		   "key_len=%lu",
1842e5b75505Sopenharmony_ci		   __FUNCTION__, alg, key_idx, set_tx,
1843e5b75505Sopenharmony_ci		   (unsigned long) seq_len, (unsigned long) key_len);
1844e5b75505Sopenharmony_ci
1845e5b75505Sopenharmony_ci	ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
1846e5b75505Sopenharmony_ci					  seq, seq_len, key, key_len);
1847e5b75505Sopenharmony_ci	if (ret == 0)
1848e5b75505Sopenharmony_ci		return 0;
1849e5b75505Sopenharmony_ci
1850e5b75505Sopenharmony_ci	if (ret == -2 &&
1851e5b75505Sopenharmony_ci	    (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
1852e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Driver did not support "
1853e5b75505Sopenharmony_ci			   "SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
1854e5b75505Sopenharmony_ci		ret = 0;
1855e5b75505Sopenharmony_ci	} else {
1856e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Driver did not support "
1857e5b75505Sopenharmony_ci			   "SIOCSIWENCODEEXT");
1858e5b75505Sopenharmony_ci		return ret;
1859e5b75505Sopenharmony_ci	}
1860e5b75505Sopenharmony_ci
1861e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1862e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1863e5b75505Sopenharmony_ci	iwr.u.encoding.flags = key_idx + 1;
1864e5b75505Sopenharmony_ci	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
1865e5b75505Sopenharmony_ci	if (alg == WPA_ALG_NONE)
1866e5b75505Sopenharmony_ci		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
1867e5b75505Sopenharmony_ci	iwr.u.encoding.pointer = (caddr_t) key;
1868e5b75505Sopenharmony_ci	iwr.u.encoding.length = key_len;
1869e5b75505Sopenharmony_ci
1870e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
1871e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
1872e5b75505Sopenharmony_ci			   strerror(errno));
1873e5b75505Sopenharmony_ci		ret = -1;
1874e5b75505Sopenharmony_ci	}
1875e5b75505Sopenharmony_ci
1876e5b75505Sopenharmony_ci	if (set_tx && alg != WPA_ALG_NONE) {
1877e5b75505Sopenharmony_ci		os_memset(&iwr, 0, sizeof(iwr));
1878e5b75505Sopenharmony_ci		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1879e5b75505Sopenharmony_ci		iwr.u.encoding.flags = key_idx + 1;
1880e5b75505Sopenharmony_ci		iwr.u.encoding.flags |= IW_ENCODE_TEMP;
1881e5b75505Sopenharmony_ci		iwr.u.encoding.pointer = (caddr_t) NULL;
1882e5b75505Sopenharmony_ci		iwr.u.encoding.length = 0;
1883e5b75505Sopenharmony_ci		if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
1884e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR,
1885e5b75505Sopenharmony_ci				   "ioctl[SIOCSIWENCODE] (set_tx): %s",
1886e5b75505Sopenharmony_ci				   strerror(errno));
1887e5b75505Sopenharmony_ci			ret = -1;
1888e5b75505Sopenharmony_ci		}
1889e5b75505Sopenharmony_ci	}
1890e5b75505Sopenharmony_ci
1891e5b75505Sopenharmony_ci	return ret;
1892e5b75505Sopenharmony_ci}
1893e5b75505Sopenharmony_ci
1894e5b75505Sopenharmony_ci
1895e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_countermeasures(void *priv,
1896e5b75505Sopenharmony_ci					       int enabled)
1897e5b75505Sopenharmony_ci{
1898e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1899e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
1900e5b75505Sopenharmony_ci	return wpa_driver_wext_set_auth_param(drv,
1901e5b75505Sopenharmony_ci					      IW_AUTH_TKIP_COUNTERMEASURES,
1902e5b75505Sopenharmony_ci					      enabled);
1903e5b75505Sopenharmony_ci}
1904e5b75505Sopenharmony_ci
1905e5b75505Sopenharmony_ci
1906e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_drop_unencrypted(void *priv,
1907e5b75505Sopenharmony_ci						int enabled)
1908e5b75505Sopenharmony_ci{
1909e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
1910e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
1911e5b75505Sopenharmony_ci	drv->use_crypt = enabled;
1912e5b75505Sopenharmony_ci	return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
1913e5b75505Sopenharmony_ci					      enabled);
1914e5b75505Sopenharmony_ci}
1915e5b75505Sopenharmony_ci
1916e5b75505Sopenharmony_ci
1917e5b75505Sopenharmony_cistatic int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
1918e5b75505Sopenharmony_ci				const u8 *addr, int cmd, u16 reason_code)
1919e5b75505Sopenharmony_ci{
1920e5b75505Sopenharmony_ci	struct iwreq iwr;
1921e5b75505Sopenharmony_ci	struct iw_mlme mlme;
1922e5b75505Sopenharmony_ci	int ret = 0;
1923e5b75505Sopenharmony_ci
1924e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1925e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1926e5b75505Sopenharmony_ci	os_memset(&mlme, 0, sizeof(mlme));
1927e5b75505Sopenharmony_ci	mlme.cmd = cmd;
1928e5b75505Sopenharmony_ci	mlme.reason_code = reason_code;
1929e5b75505Sopenharmony_ci	mlme.addr.sa_family = ARPHRD_ETHER;
1930e5b75505Sopenharmony_ci	os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN);
1931e5b75505Sopenharmony_ci	iwr.u.data.pointer = (caddr_t) &mlme;
1932e5b75505Sopenharmony_ci	iwr.u.data.length = sizeof(mlme);
1933e5b75505Sopenharmony_ci
1934e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
1935e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
1936e5b75505Sopenharmony_ci			   strerror(errno));
1937e5b75505Sopenharmony_ci		ret = -1;
1938e5b75505Sopenharmony_ci	}
1939e5b75505Sopenharmony_ci
1940e5b75505Sopenharmony_ci	return ret;
1941e5b75505Sopenharmony_ci}
1942e5b75505Sopenharmony_ci
1943e5b75505Sopenharmony_ci
1944e5b75505Sopenharmony_cistatic void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
1945e5b75505Sopenharmony_ci{
1946e5b75505Sopenharmony_ci	struct iwreq iwr;
1947e5b75505Sopenharmony_ci	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
1948e5b75505Sopenharmony_ci	u8 ssid[SSID_MAX_LEN];
1949e5b75505Sopenharmony_ci	int i;
1950e5b75505Sopenharmony_ci
1951e5b75505Sopenharmony_ci	/*
1952e5b75505Sopenharmony_ci	 * Only force-disconnect when the card is in infrastructure mode,
1953e5b75505Sopenharmony_ci	 * otherwise the driver might interpret the cleared BSSID and random
1954e5b75505Sopenharmony_ci	 * SSID as an attempt to create a new ad-hoc network.
1955e5b75505Sopenharmony_ci	 */
1956e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
1957e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
1958e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
1959e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
1960e5b75505Sopenharmony_ci			   strerror(errno));
1961e5b75505Sopenharmony_ci		iwr.u.mode = IW_MODE_INFRA;
1962e5b75505Sopenharmony_ci	}
1963e5b75505Sopenharmony_ci
1964e5b75505Sopenharmony_ci	if (iwr.u.mode == IW_MODE_INFRA) {
1965e5b75505Sopenharmony_ci		/* Clear the BSSID selection */
1966e5b75505Sopenharmony_ci		if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
1967e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
1968e5b75505Sopenharmony_ci				   "selection on disconnect");
1969e5b75505Sopenharmony_ci		}
1970e5b75505Sopenharmony_ci
1971e5b75505Sopenharmony_ci		if (drv->cfg80211) {
1972e5b75505Sopenharmony_ci			/*
1973e5b75505Sopenharmony_ci			 * cfg80211 supports SIOCSIWMLME commands, so there is
1974e5b75505Sopenharmony_ci			 * no need for the random SSID hack, but clear the
1975e5b75505Sopenharmony_ci			 * SSID.
1976e5b75505Sopenharmony_ci			 */
1977e5b75505Sopenharmony_ci			if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
1978e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
1979e5b75505Sopenharmony_ci					   "SSID on disconnect");
1980e5b75505Sopenharmony_ci			}
1981e5b75505Sopenharmony_ci			return;
1982e5b75505Sopenharmony_ci		}
1983e5b75505Sopenharmony_ci
1984e5b75505Sopenharmony_ci		/*
1985e5b75505Sopenharmony_ci		 * Set a random SSID to make sure the driver will not be trying
1986e5b75505Sopenharmony_ci		 * to associate with something even if it does not understand
1987e5b75505Sopenharmony_ci		 * SIOCSIWMLME commands (or tries to associate automatically
1988e5b75505Sopenharmony_ci		 * after deauth/disassoc).
1989e5b75505Sopenharmony_ci		 */
1990e5b75505Sopenharmony_ci		for (i = 0; i < SSID_MAX_LEN; i++)
1991e5b75505Sopenharmony_ci			ssid[i] = rand() & 0xFF;
1992e5b75505Sopenharmony_ci		if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) {
1993e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
1994e5b75505Sopenharmony_ci				   "SSID to disconnect");
1995e5b75505Sopenharmony_ci		}
1996e5b75505Sopenharmony_ci	}
1997e5b75505Sopenharmony_ci}
1998e5b75505Sopenharmony_ci
1999e5b75505Sopenharmony_ci
2000e5b75505Sopenharmony_cistatic int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
2001e5b75505Sopenharmony_ci					  u16 reason_code)
2002e5b75505Sopenharmony_ci{
2003e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2004e5b75505Sopenharmony_ci	int ret;
2005e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
2006e5b75505Sopenharmony_ci	ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
2007e5b75505Sopenharmony_ci	wpa_driver_wext_disconnect(drv);
2008e5b75505Sopenharmony_ci	return ret;
2009e5b75505Sopenharmony_ci}
2010e5b75505Sopenharmony_ci
2011e5b75505Sopenharmony_ci
2012e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
2013e5b75505Sopenharmony_ci				      size_t ie_len)
2014e5b75505Sopenharmony_ci{
2015e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2016e5b75505Sopenharmony_ci	struct iwreq iwr;
2017e5b75505Sopenharmony_ci	int ret = 0;
2018e5b75505Sopenharmony_ci
2019e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
2020e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
2021e5b75505Sopenharmony_ci	iwr.u.data.pointer = (caddr_t) ie;
2022e5b75505Sopenharmony_ci	iwr.u.data.length = ie_len;
2023e5b75505Sopenharmony_ci
2024e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
2025e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
2026e5b75505Sopenharmony_ci			   strerror(errno));
2027e5b75505Sopenharmony_ci		ret = -1;
2028e5b75505Sopenharmony_ci	}
2029e5b75505Sopenharmony_ci
2030e5b75505Sopenharmony_ci	return ret;
2031e5b75505Sopenharmony_ci}
2032e5b75505Sopenharmony_ci
2033e5b75505Sopenharmony_ci
2034e5b75505Sopenharmony_ciint wpa_driver_wext_cipher2wext(int cipher)
2035e5b75505Sopenharmony_ci{
2036e5b75505Sopenharmony_ci	switch (cipher) {
2037e5b75505Sopenharmony_ci	case WPA_CIPHER_NONE:
2038e5b75505Sopenharmony_ci		return IW_AUTH_CIPHER_NONE;
2039e5b75505Sopenharmony_ci	case WPA_CIPHER_WEP40:
2040e5b75505Sopenharmony_ci		return IW_AUTH_CIPHER_WEP40;
2041e5b75505Sopenharmony_ci	case WPA_CIPHER_TKIP:
2042e5b75505Sopenharmony_ci		return IW_AUTH_CIPHER_TKIP;
2043e5b75505Sopenharmony_ci	case WPA_CIPHER_CCMP:
2044e5b75505Sopenharmony_ci		return IW_AUTH_CIPHER_CCMP;
2045e5b75505Sopenharmony_ci	case WPA_CIPHER_WEP104:
2046e5b75505Sopenharmony_ci		return IW_AUTH_CIPHER_WEP104;
2047e5b75505Sopenharmony_ci	default:
2048e5b75505Sopenharmony_ci		return 0;
2049e5b75505Sopenharmony_ci	}
2050e5b75505Sopenharmony_ci}
2051e5b75505Sopenharmony_ci
2052e5b75505Sopenharmony_ci
2053e5b75505Sopenharmony_ciint wpa_driver_wext_keymgmt2wext(int keymgmt)
2054e5b75505Sopenharmony_ci{
2055e5b75505Sopenharmony_ci	switch (keymgmt) {
2056e5b75505Sopenharmony_ci	case WPA_KEY_MGMT_IEEE8021X:
2057e5b75505Sopenharmony_ci	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
2058e5b75505Sopenharmony_ci		return IW_AUTH_KEY_MGMT_802_1X;
2059e5b75505Sopenharmony_ci	case WPA_KEY_MGMT_PSK:
2060e5b75505Sopenharmony_ci		return IW_AUTH_KEY_MGMT_PSK;
2061e5b75505Sopenharmony_ci	default:
2062e5b75505Sopenharmony_ci		return 0;
2063e5b75505Sopenharmony_ci	}
2064e5b75505Sopenharmony_ci}
2065e5b75505Sopenharmony_ci
2066e5b75505Sopenharmony_ci
2067e5b75505Sopenharmony_cistatic int
2068e5b75505Sopenharmony_ciwpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
2069e5b75505Sopenharmony_ci				  struct wpa_driver_associate_params *params)
2070e5b75505Sopenharmony_ci{
2071e5b75505Sopenharmony_ci	struct iwreq iwr;
2072e5b75505Sopenharmony_ci	int ret = 0;
2073e5b75505Sopenharmony_ci
2074e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
2075e5b75505Sopenharmony_ci		   "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
2076e5b75505Sopenharmony_ci
2077e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
2078e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
2079e5b75505Sopenharmony_ci	/* Just changing mode, not actual keys */
2080e5b75505Sopenharmony_ci	iwr.u.encoding.flags = 0;
2081e5b75505Sopenharmony_ci	iwr.u.encoding.pointer = (caddr_t) NULL;
2082e5b75505Sopenharmony_ci	iwr.u.encoding.length = 0;
2083e5b75505Sopenharmony_ci
2084e5b75505Sopenharmony_ci	/*
2085e5b75505Sopenharmony_ci	 * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
2086e5b75505Sopenharmony_ci	 * different things. Here they are used to indicate Open System vs.
2087e5b75505Sopenharmony_ci	 * Shared Key authentication algorithm. However, some drivers may use
2088e5b75505Sopenharmony_ci	 * them to select between open/restricted WEP encrypted (open = allow
2089e5b75505Sopenharmony_ci	 * both unencrypted and encrypted frames; restricted = only allow
2090e5b75505Sopenharmony_ci	 * encrypted frames).
2091e5b75505Sopenharmony_ci	 */
2092e5b75505Sopenharmony_ci
2093e5b75505Sopenharmony_ci	if (!drv->use_crypt) {
2094e5b75505Sopenharmony_ci		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
2095e5b75505Sopenharmony_ci	} else {
2096e5b75505Sopenharmony_ci		if (params->auth_alg & WPA_AUTH_ALG_OPEN)
2097e5b75505Sopenharmony_ci			iwr.u.encoding.flags |= IW_ENCODE_OPEN;
2098e5b75505Sopenharmony_ci		if (params->auth_alg & WPA_AUTH_ALG_SHARED)
2099e5b75505Sopenharmony_ci			iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
2100e5b75505Sopenharmony_ci	}
2101e5b75505Sopenharmony_ci
2102e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
2103e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
2104e5b75505Sopenharmony_ci			   strerror(errno));
2105e5b75505Sopenharmony_ci		ret = -1;
2106e5b75505Sopenharmony_ci	}
2107e5b75505Sopenharmony_ci
2108e5b75505Sopenharmony_ci	return ret;
2109e5b75505Sopenharmony_ci}
2110e5b75505Sopenharmony_ci
2111e5b75505Sopenharmony_ci
2112e5b75505Sopenharmony_ciint wpa_driver_wext_associate(void *priv,
2113e5b75505Sopenharmony_ci			      struct wpa_driver_associate_params *params)
2114e5b75505Sopenharmony_ci{
2115e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2116e5b75505Sopenharmony_ci	int ret = 0;
2117e5b75505Sopenharmony_ci	int allow_unencrypted_eapol;
2118e5b75505Sopenharmony_ci	int value;
2119e5b75505Sopenharmony_ci
2120e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
2121e5b75505Sopenharmony_ci
2122e5b75505Sopenharmony_ci	if (drv->cfg80211) {
2123e5b75505Sopenharmony_ci		/*
2124e5b75505Sopenharmony_ci		 * Stop cfg80211 from trying to associate before we are done
2125e5b75505Sopenharmony_ci		 * with all parameters.
2126e5b75505Sopenharmony_ci		 */
2127e5b75505Sopenharmony_ci		if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
2128e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG,
2129e5b75505Sopenharmony_ci				   "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
2130e5b75505Sopenharmony_ci			/* continue anyway */
2131e5b75505Sopenharmony_ci		}
2132e5b75505Sopenharmony_ci	}
2133e5b75505Sopenharmony_ci
2134e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
2135e5b75505Sopenharmony_ci	    < 0)
2136e5b75505Sopenharmony_ci		ret = -1;
2137e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
2138e5b75505Sopenharmony_ci		ret = -1;
2139e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
2140e5b75505Sopenharmony_ci		ret = -1;
2141e5b75505Sopenharmony_ci
2142e5b75505Sopenharmony_ci	/*
2143e5b75505Sopenharmony_ci	 * If the driver did not support SIOCSIWAUTH, fallback to
2144e5b75505Sopenharmony_ci	 * SIOCSIWENCODE here.
2145e5b75505Sopenharmony_ci	 */
2146e5b75505Sopenharmony_ci	if (drv->auth_alg_fallback &&
2147e5b75505Sopenharmony_ci	    wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
2148e5b75505Sopenharmony_ci		ret = -1;
2149e5b75505Sopenharmony_ci
2150e5b75505Sopenharmony_ci	if (!params->bssid &&
2151e5b75505Sopenharmony_ci	    wpa_driver_wext_set_bssid(drv, NULL) < 0)
2152e5b75505Sopenharmony_ci		ret = -1;
2153e5b75505Sopenharmony_ci
2154e5b75505Sopenharmony_ci	/* TODO: should consider getting wpa version and cipher/key_mgmt suites
2155e5b75505Sopenharmony_ci	 * from configuration, not from here, where only the selected suite is
2156e5b75505Sopenharmony_ci	 * available */
2157e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
2158e5b75505Sopenharmony_ci	    < 0)
2159e5b75505Sopenharmony_ci		ret = -1;
2160e5b75505Sopenharmony_ci	if (params->wpa_proto & WPA_PROTO_RSN)
2161e5b75505Sopenharmony_ci		value = IW_AUTH_WPA_VERSION_WPA2;
2162e5b75505Sopenharmony_ci	else if (params->wpa_proto & WPA_PROTO_WPA)
2163e5b75505Sopenharmony_ci		value = IW_AUTH_WPA_VERSION_WPA;
2164e5b75505Sopenharmony_ci	else
2165e5b75505Sopenharmony_ci		value = IW_AUTH_WPA_VERSION_DISABLED;
2166e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2167e5b75505Sopenharmony_ci					   IW_AUTH_WPA_VERSION, value) < 0)
2168e5b75505Sopenharmony_ci		ret = -1;
2169e5b75505Sopenharmony_ci	value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
2170e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2171e5b75505Sopenharmony_ci					   IW_AUTH_CIPHER_PAIRWISE, value) < 0)
2172e5b75505Sopenharmony_ci		ret = -1;
2173e5b75505Sopenharmony_ci	value = wpa_driver_wext_cipher2wext(params->group_suite);
2174e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2175e5b75505Sopenharmony_ci					   IW_AUTH_CIPHER_GROUP, value) < 0)
2176e5b75505Sopenharmony_ci		ret = -1;
2177e5b75505Sopenharmony_ci	value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
2178e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2179e5b75505Sopenharmony_ci					   IW_AUTH_KEY_MGMT, value) < 0)
2180e5b75505Sopenharmony_ci		ret = -1;
2181e5b75505Sopenharmony_ci	value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
2182e5b75505Sopenharmony_ci		params->pairwise_suite != WPA_CIPHER_NONE ||
2183e5b75505Sopenharmony_ci		params->group_suite != WPA_CIPHER_NONE ||
2184e5b75505Sopenharmony_ci		(params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
2185e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2186e5b75505Sopenharmony_ci					   IW_AUTH_PRIVACY_INVOKED, value) < 0)
2187e5b75505Sopenharmony_ci		ret = -1;
2188e5b75505Sopenharmony_ci
2189e5b75505Sopenharmony_ci	/* Allow unencrypted EAPOL messages even if pairwise keys are set when
2190e5b75505Sopenharmony_ci	 * not using WPA. IEEE 802.1X specifies that these frames are not
2191e5b75505Sopenharmony_ci	 * encrypted, but WPA encrypts them when pairwise keys are in use. */
2192e5b75505Sopenharmony_ci	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
2193e5b75505Sopenharmony_ci	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
2194e5b75505Sopenharmony_ci		allow_unencrypted_eapol = 0;
2195e5b75505Sopenharmony_ci	else
2196e5b75505Sopenharmony_ci		allow_unencrypted_eapol = 1;
2197e5b75505Sopenharmony_ci
2198e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
2199e5b75505Sopenharmony_ci		ret = -1;
2200e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv,
2201e5b75505Sopenharmony_ci					   IW_AUTH_RX_UNENCRYPTED_EAPOL,
2202e5b75505Sopenharmony_ci					   allow_unencrypted_eapol) < 0)
2203e5b75505Sopenharmony_ci		ret = -1;
2204e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211W
2205e5b75505Sopenharmony_ci	switch (params->mgmt_frame_protection) {
2206e5b75505Sopenharmony_ci	case NO_MGMT_FRAME_PROTECTION:
2207e5b75505Sopenharmony_ci		value = IW_AUTH_MFP_DISABLED;
2208e5b75505Sopenharmony_ci		break;
2209e5b75505Sopenharmony_ci	case MGMT_FRAME_PROTECTION_OPTIONAL:
2210e5b75505Sopenharmony_ci		value = IW_AUTH_MFP_OPTIONAL;
2211e5b75505Sopenharmony_ci		break;
2212e5b75505Sopenharmony_ci	case MGMT_FRAME_PROTECTION_REQUIRED:
2213e5b75505Sopenharmony_ci		value = IW_AUTH_MFP_REQUIRED;
2214e5b75505Sopenharmony_ci		break;
2215e5b75505Sopenharmony_ci	};
2216e5b75505Sopenharmony_ci	if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
2217e5b75505Sopenharmony_ci		ret = -1;
2218e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211W */
2219e5b75505Sopenharmony_ci	if (params->freq.freq &&
2220e5b75505Sopenharmony_ci	    wpa_driver_wext_set_freq(drv, params->freq.freq) < 0)
2221e5b75505Sopenharmony_ci		ret = -1;
2222e5b75505Sopenharmony_ci	if (!drv->cfg80211 &&
2223e5b75505Sopenharmony_ci	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
2224e5b75505Sopenharmony_ci		ret = -1;
2225e5b75505Sopenharmony_ci	if (params->bssid &&
2226e5b75505Sopenharmony_ci	    wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
2227e5b75505Sopenharmony_ci		ret = -1;
2228e5b75505Sopenharmony_ci	if (drv->cfg80211 &&
2229e5b75505Sopenharmony_ci	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
2230e5b75505Sopenharmony_ci		ret = -1;
2231e5b75505Sopenharmony_ci
2232e5b75505Sopenharmony_ci	return ret;
2233e5b75505Sopenharmony_ci}
2234e5b75505Sopenharmony_ci
2235e5b75505Sopenharmony_ci
2236e5b75505Sopenharmony_cistatic int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
2237e5b75505Sopenharmony_ci{
2238e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2239e5b75505Sopenharmony_ci	int algs = 0, res;
2240e5b75505Sopenharmony_ci
2241e5b75505Sopenharmony_ci	if (auth_alg & WPA_AUTH_ALG_OPEN)
2242e5b75505Sopenharmony_ci		algs |= IW_AUTH_ALG_OPEN_SYSTEM;
2243e5b75505Sopenharmony_ci	if (auth_alg & WPA_AUTH_ALG_SHARED)
2244e5b75505Sopenharmony_ci		algs |= IW_AUTH_ALG_SHARED_KEY;
2245e5b75505Sopenharmony_ci	if (auth_alg & WPA_AUTH_ALG_LEAP)
2246e5b75505Sopenharmony_ci		algs |= IW_AUTH_ALG_LEAP;
2247e5b75505Sopenharmony_ci	if (algs == 0) {
2248e5b75505Sopenharmony_ci		/* at least one algorithm should be set */
2249e5b75505Sopenharmony_ci		algs = IW_AUTH_ALG_OPEN_SYSTEM;
2250e5b75505Sopenharmony_ci	}
2251e5b75505Sopenharmony_ci
2252e5b75505Sopenharmony_ci	res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
2253e5b75505Sopenharmony_ci					     algs);
2254e5b75505Sopenharmony_ci	drv->auth_alg_fallback = res == -2;
2255e5b75505Sopenharmony_ci	return res;
2256e5b75505Sopenharmony_ci}
2257e5b75505Sopenharmony_ci
2258e5b75505Sopenharmony_ci
2259e5b75505Sopenharmony_ci/**
2260e5b75505Sopenharmony_ci * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
2261e5b75505Sopenharmony_ci * @priv: Pointer to private wext data from wpa_driver_wext_init()
2262e5b75505Sopenharmony_ci * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
2263e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
2264e5b75505Sopenharmony_ci */
2265e5b75505Sopenharmony_ciint wpa_driver_wext_set_mode(void *priv, int mode)
2266e5b75505Sopenharmony_ci{
2267e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2268e5b75505Sopenharmony_ci	struct iwreq iwr;
2269e5b75505Sopenharmony_ci	int ret = -1;
2270e5b75505Sopenharmony_ci	unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
2271e5b75505Sopenharmony_ci
2272e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
2273e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
2274e5b75505Sopenharmony_ci	iwr.u.mode = new_mode;
2275e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
2276e5b75505Sopenharmony_ci		ret = 0;
2277e5b75505Sopenharmony_ci		goto done;
2278e5b75505Sopenharmony_ci	}
2279e5b75505Sopenharmony_ci
2280e5b75505Sopenharmony_ci	if (errno != EBUSY) {
2281e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
2282e5b75505Sopenharmony_ci			   strerror(errno));
2283e5b75505Sopenharmony_ci		goto done;
2284e5b75505Sopenharmony_ci	}
2285e5b75505Sopenharmony_ci
2286e5b75505Sopenharmony_ci	/* mac80211 doesn't allow mode changes while the device is up, so if
2287e5b75505Sopenharmony_ci	 * the device isn't in the mode we're about to change to, take device
2288e5b75505Sopenharmony_ci	 * down, try to set the mode again, and bring it back up.
2289e5b75505Sopenharmony_ci	 */
2290e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
2291e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
2292e5b75505Sopenharmony_ci			   strerror(errno));
2293e5b75505Sopenharmony_ci		goto done;
2294e5b75505Sopenharmony_ci	}
2295e5b75505Sopenharmony_ci
2296e5b75505Sopenharmony_ci	if (iwr.u.mode == new_mode) {
2297e5b75505Sopenharmony_ci		ret = 0;
2298e5b75505Sopenharmony_ci		goto done;
2299e5b75505Sopenharmony_ci	}
2300e5b75505Sopenharmony_ci
2301e5b75505Sopenharmony_ci	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
2302e5b75505Sopenharmony_ci		/* Try to set the mode again while the interface is down */
2303e5b75505Sopenharmony_ci		iwr.u.mode = new_mode;
2304e5b75505Sopenharmony_ci		if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
2305e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
2306e5b75505Sopenharmony_ci				   strerror(errno));
2307e5b75505Sopenharmony_ci		else
2308e5b75505Sopenharmony_ci			ret = 0;
2309e5b75505Sopenharmony_ci
2310e5b75505Sopenharmony_ci		(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
2311e5b75505Sopenharmony_ci	}
2312e5b75505Sopenharmony_ci
2313e5b75505Sopenharmony_cidone:
2314e5b75505Sopenharmony_ci	return ret;
2315e5b75505Sopenharmony_ci}
2316e5b75505Sopenharmony_ci
2317e5b75505Sopenharmony_ci
2318e5b75505Sopenharmony_cistatic int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
2319e5b75505Sopenharmony_ci				 u32 cmd, const u8 *bssid, const u8 *pmkid)
2320e5b75505Sopenharmony_ci{
2321e5b75505Sopenharmony_ci	struct iwreq iwr;
2322e5b75505Sopenharmony_ci	struct iw_pmksa pmksa;
2323e5b75505Sopenharmony_ci	int ret = 0;
2324e5b75505Sopenharmony_ci
2325e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
2326e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
2327e5b75505Sopenharmony_ci	os_memset(&pmksa, 0, sizeof(pmksa));
2328e5b75505Sopenharmony_ci	pmksa.cmd = cmd;
2329e5b75505Sopenharmony_ci	pmksa.bssid.sa_family = ARPHRD_ETHER;
2330e5b75505Sopenharmony_ci	if (bssid)
2331e5b75505Sopenharmony_ci		os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
2332e5b75505Sopenharmony_ci	if (pmkid)
2333e5b75505Sopenharmony_ci		os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
2334e5b75505Sopenharmony_ci	iwr.u.data.pointer = (caddr_t) &pmksa;
2335e5b75505Sopenharmony_ci	iwr.u.data.length = sizeof(pmksa);
2336e5b75505Sopenharmony_ci
2337e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
2338e5b75505Sopenharmony_ci		if (errno != EOPNOTSUPP)
2339e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
2340e5b75505Sopenharmony_ci				   strerror(errno));
2341e5b75505Sopenharmony_ci		ret = -1;
2342e5b75505Sopenharmony_ci	}
2343e5b75505Sopenharmony_ci
2344e5b75505Sopenharmony_ci	return ret;
2345e5b75505Sopenharmony_ci}
2346e5b75505Sopenharmony_ci
2347e5b75505Sopenharmony_ci
2348e5b75505Sopenharmony_cistatic int wpa_driver_wext_add_pmkid(void *priv,
2349e5b75505Sopenharmony_ci				     struct wpa_pmkid_params *params)
2350e5b75505Sopenharmony_ci{
2351e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2352e5b75505Sopenharmony_ci	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
2353e5b75505Sopenharmony_ci				     params->pmkid);
2354e5b75505Sopenharmony_ci}
2355e5b75505Sopenharmony_ci
2356e5b75505Sopenharmony_ci
2357e5b75505Sopenharmony_cistatic int wpa_driver_wext_remove_pmkid(void *priv,
2358e5b75505Sopenharmony_ci					struct wpa_pmkid_params *params)
2359e5b75505Sopenharmony_ci{
2360e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2361e5b75505Sopenharmony_ci	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
2362e5b75505Sopenharmony_ci				     params->pmkid);
2363e5b75505Sopenharmony_ci}
2364e5b75505Sopenharmony_ci
2365e5b75505Sopenharmony_ci
2366e5b75505Sopenharmony_cistatic int wpa_driver_wext_flush_pmkid(void *priv)
2367e5b75505Sopenharmony_ci{
2368e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2369e5b75505Sopenharmony_ci	return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
2370e5b75505Sopenharmony_ci}
2371e5b75505Sopenharmony_ci
2372e5b75505Sopenharmony_ci
2373e5b75505Sopenharmony_ciint wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
2374e5b75505Sopenharmony_ci{
2375e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2376e5b75505Sopenharmony_ci	if (!drv->has_capability)
2377e5b75505Sopenharmony_ci		return -1;
2378e5b75505Sopenharmony_ci	os_memcpy(capa, &drv->capa, sizeof(*capa));
2379e5b75505Sopenharmony_ci	return 0;
2380e5b75505Sopenharmony_ci}
2381e5b75505Sopenharmony_ci
2382e5b75505Sopenharmony_ci
2383e5b75505Sopenharmony_ciint wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
2384e5b75505Sopenharmony_ci					const char *ifname)
2385e5b75505Sopenharmony_ci{
2386e5b75505Sopenharmony_ci	if (ifname == NULL) {
2387e5b75505Sopenharmony_ci		drv->ifindex2 = -1;
2388e5b75505Sopenharmony_ci		return 0;
2389e5b75505Sopenharmony_ci	}
2390e5b75505Sopenharmony_ci
2391e5b75505Sopenharmony_ci	drv->ifindex2 = if_nametoindex(ifname);
2392e5b75505Sopenharmony_ci	if (drv->ifindex2 <= 0)
2393e5b75505Sopenharmony_ci		return -1;
2394e5b75505Sopenharmony_ci
2395e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
2396e5b75505Sopenharmony_ci		   "wireless events", drv->ifindex2, ifname);
2397e5b75505Sopenharmony_ci
2398e5b75505Sopenharmony_ci	return 0;
2399e5b75505Sopenharmony_ci}
2400e5b75505Sopenharmony_ci
2401e5b75505Sopenharmony_ci
2402e5b75505Sopenharmony_ciint wpa_driver_wext_set_operstate(void *priv, int state)
2403e5b75505Sopenharmony_ci{
2404e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2405e5b75505Sopenharmony_ci
2406e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
2407e5b75505Sopenharmony_ci		   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
2408e5b75505Sopenharmony_ci	drv->operstate = state;
2409e5b75505Sopenharmony_ci	return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
2410e5b75505Sopenharmony_ci				      state ? IF_OPER_UP : IF_OPER_DORMANT);
2411e5b75505Sopenharmony_ci}
2412e5b75505Sopenharmony_ci
2413e5b75505Sopenharmony_ci
2414e5b75505Sopenharmony_ciint wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
2415e5b75505Sopenharmony_ci{
2416e5b75505Sopenharmony_ci	return drv->we_version_compiled;
2417e5b75505Sopenharmony_ci}
2418e5b75505Sopenharmony_ci
2419e5b75505Sopenharmony_ci
2420e5b75505Sopenharmony_cistatic const char * wext_get_radio_name(void *priv)
2421e5b75505Sopenharmony_ci{
2422e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2423e5b75505Sopenharmony_ci	return drv->phyname;
2424e5b75505Sopenharmony_ci}
2425e5b75505Sopenharmony_ci
2426e5b75505Sopenharmony_ci
2427e5b75505Sopenharmony_cistatic int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
2428e5b75505Sopenharmony_ci{
2429e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2430e5b75505Sopenharmony_ci	struct iw_statistics stats;
2431e5b75505Sopenharmony_ci	struct iwreq iwr;
2432e5b75505Sopenharmony_ci
2433e5b75505Sopenharmony_ci	os_memset(si, 0, sizeof(*si));
2434e5b75505Sopenharmony_ci	si->current_signal = -WPA_INVALID_NOISE;
2435e5b75505Sopenharmony_ci	si->current_noise = WPA_INVALID_NOISE;
2436e5b75505Sopenharmony_ci	si->chanwidth = CHAN_WIDTH_UNKNOWN;
2437e5b75505Sopenharmony_ci
2438e5b75505Sopenharmony_ci	os_memset(&iwr, 0, sizeof(iwr));
2439e5b75505Sopenharmony_ci	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
2440e5b75505Sopenharmony_ci	iwr.u.data.pointer = (caddr_t) &stats;
2441e5b75505Sopenharmony_ci	iwr.u.data.length = sizeof(stats);
2442e5b75505Sopenharmony_ci	iwr.u.data.flags = 1;
2443e5b75505Sopenharmony_ci
2444e5b75505Sopenharmony_ci	if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
2445e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
2446e5b75505Sopenharmony_ci			   strerror(errno));
2447e5b75505Sopenharmony_ci		return -1;
2448e5b75505Sopenharmony_ci	}
2449e5b75505Sopenharmony_ci
2450e5b75505Sopenharmony_ci	si->current_signal = stats.qual.level -
2451e5b75505Sopenharmony_ci		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
2452e5b75505Sopenharmony_ci	si->current_noise = stats.qual.noise -
2453e5b75505Sopenharmony_ci		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
2454e5b75505Sopenharmony_ci	return 0;
2455e5b75505Sopenharmony_ci}
2456e5b75505Sopenharmony_ci
2457e5b75505Sopenharmony_ci
2458e5b75505Sopenharmony_cistatic int wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
2459e5b75505Sopenharmony_ci{
2460e5b75505Sopenharmony_ci	struct wpa_driver_wext_data *drv = priv;
2461e5b75505Sopenharmony_ci	int res;
2462e5b75505Sopenharmony_ci	char *pos, *end;
2463e5b75505Sopenharmony_ci	unsigned char addr[ETH_ALEN];
2464e5b75505Sopenharmony_ci
2465e5b75505Sopenharmony_ci	pos = buf;
2466e5b75505Sopenharmony_ci	end = buf + buflen;
2467e5b75505Sopenharmony_ci
2468e5b75505Sopenharmony_ci	if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
2469e5b75505Sopenharmony_ci		return -1;
2470e5b75505Sopenharmony_ci
2471e5b75505Sopenharmony_ci	res = os_snprintf(pos, end - pos,
2472e5b75505Sopenharmony_ci			  "ifindex=%d\n"
2473e5b75505Sopenharmony_ci			  "ifname=%s\n"
2474e5b75505Sopenharmony_ci			  "addr=" MACSTR "\n",
2475e5b75505Sopenharmony_ci			  drv->ifindex,
2476e5b75505Sopenharmony_ci			  drv->ifname,
2477e5b75505Sopenharmony_ci			  MAC2STR(addr));
2478e5b75505Sopenharmony_ci	if (os_snprintf_error(end - pos, res))
2479e5b75505Sopenharmony_ci		return pos - buf;
2480e5b75505Sopenharmony_ci	pos += res;
2481e5b75505Sopenharmony_ci
2482e5b75505Sopenharmony_ci	return pos - buf;
2483e5b75505Sopenharmony_ci}
2484e5b75505Sopenharmony_ci
2485e5b75505Sopenharmony_ciconst struct wpa_driver_ops wpa_driver_wext_ops = {
2486e5b75505Sopenharmony_ci	.name = "wext",
2487e5b75505Sopenharmony_ci	.desc = "Linux wireless extensions (generic)",
2488e5b75505Sopenharmony_ci	.get_bssid = wpa_driver_wext_get_bssid,
2489e5b75505Sopenharmony_ci	.get_ssid = wpa_driver_wext_get_ssid,
2490e5b75505Sopenharmony_ci	.set_key = wpa_driver_wext_set_key,
2491e5b75505Sopenharmony_ci	.set_countermeasures = wpa_driver_wext_set_countermeasures,
2492e5b75505Sopenharmony_ci	.scan2 = wpa_driver_wext_scan,
2493e5b75505Sopenharmony_ci	.get_scan_results2 = wpa_driver_wext_get_scan_results,
2494e5b75505Sopenharmony_ci	.deauthenticate = wpa_driver_wext_deauthenticate,
2495e5b75505Sopenharmony_ci	.associate = wpa_driver_wext_associate,
2496e5b75505Sopenharmony_ci	.init = wpa_driver_wext_init,
2497e5b75505Sopenharmony_ci	.deinit = wpa_driver_wext_deinit,
2498e5b75505Sopenharmony_ci	.add_pmkid = wpa_driver_wext_add_pmkid,
2499e5b75505Sopenharmony_ci	.remove_pmkid = wpa_driver_wext_remove_pmkid,
2500e5b75505Sopenharmony_ci	.flush_pmkid = wpa_driver_wext_flush_pmkid,
2501e5b75505Sopenharmony_ci	.get_capa = wpa_driver_wext_get_capa,
2502e5b75505Sopenharmony_ci	.set_operstate = wpa_driver_wext_set_operstate,
2503e5b75505Sopenharmony_ci	.get_radio_name = wext_get_radio_name,
2504e5b75505Sopenharmony_ci	.signal_poll = wpa_driver_wext_signal_poll,
2505e5b75505Sopenharmony_ci	.status = wpa_driver_wext_status,
2506e5b75505Sopenharmony_ci};
2507