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