]>
Commit | Line | Data |
---|---|---|
6d7fb691 JM |
1 | /* |
2 | * hostapd / Driver interaction with Atheros driver | |
3 | * Copyright (c) 2004, Sam Leffler <sam@errno.com> | |
4 | * Copyright (c) 2004, Video54 Technologies | |
5 | * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> | |
6 | * Copyright (c) 2009, Atheros Communications | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * Alternatively, this software may be distributed under the terms of BSD | |
13 | * license. | |
14 | * | |
15 | * See README and COPYING for more details. | |
16 | */ | |
17 | ||
18 | #include "includes.h" | |
19 | #include <net/if.h> | |
20 | #include <sys/ioctl.h> | |
21 | ||
22 | #include "common.h" | |
23 | #ifndef _BYTE_ORDER | |
24 | #ifdef WORDS_BIGENDIAN | |
25 | #define _BYTE_ORDER _BIG_ENDIAN | |
26 | #else | |
27 | #define _BYTE_ORDER _LITTLE_ENDIAN | |
28 | #endif | |
29 | #endif /* _BYTE_ORDER */ | |
30 | ||
31 | #include <net80211/ieee80211.h> | |
32 | #include <net80211/_ieee80211.h> | |
33 | #include <net80211/ieee80211_crypto.h> | |
34 | ||
35 | /* | |
36 | * Note, the ATH_WPS_IE setting must match with the driver build.. If the | |
37 | * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. | |
38 | */ | |
39 | #define ATH_WPS_IE | |
40 | #include <net80211/ieee80211_ioctl.h> | |
41 | ||
42 | #ifdef CONFIG_WPS | |
43 | #ifdef IEEE80211_IOCTL_FILTERFRAME | |
44 | #include <netpacket/packet.h> | |
45 | ||
46 | #ifndef ETH_P_80211_RAW | |
47 | #define ETH_P_80211_RAW 0x0019 | |
48 | #endif | |
49 | #endif /* IEEE80211_IOCTL_FILTERFRAME */ | |
50 | #endif /* CONFIG_WPS */ | |
51 | ||
52 | /* | |
53 | * Avoid conflicts with hostapd definitions by undefining couple of defines | |
54 | * from madwifi header files. | |
55 | */ | |
56 | #undef WPA_OUI_TYPE | |
57 | #undef WME_OUI_TYPE | |
58 | ||
59 | #include "wireless_copy.h" | |
60 | ||
c5121837 JM |
61 | #include "../hostapd/hostapd.h" |
62 | #include "../hostapd/config.h" | |
63 | #include "../hostapd/sta_flags.h" | |
6d7fb691 JM |
64 | #include "driver.h" |
65 | #include "eloop.h" | |
66 | #include "priv_netlink.h" | |
67 | #include "l2_packet/l2_packet.h" | |
68 | ||
c5121837 | 69 | #include "../hostapd/wps_hostapd.h" |
90973fb2 | 70 | #include "common/ieee802_11_defs.h" |
6d7fb691 JM |
71 | |
72 | ||
73 | struct madwifi_driver_data { | |
74 | struct hostapd_data *hapd; /* back pointer */ | |
75 | ||
76 | char iface[IFNAMSIZ + 1]; | |
77 | int ifindex; | |
78 | struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ | |
79 | struct l2_packet_data *sock_recv; /* raw packet recv socket */ | |
80 | int ioctl_sock; /* socket for ioctl() use */ | |
81 | int wext_sock; /* socket for wireless events */ | |
82 | int we_version; | |
83 | u8 acct_mac[ETH_ALEN]; | |
84 | struct hostap_sta_driver_data acct_data; | |
85 | ||
86 | struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ | |
87 | }; | |
88 | ||
731723a5 JM |
89 | static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, |
90 | int reason_code); | |
6d7fb691 JM |
91 | |
92 | static int | |
93 | set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) | |
94 | { | |
95 | struct iwreq iwr; | |
96 | int do_inline = len < IFNAMSIZ; | |
97 | ||
98 | /* Certain ioctls must use the non-inlined method */ | |
99 | if (op == IEEE80211_IOCTL_SET_APPIEBUF || | |
100 | op == IEEE80211_IOCTL_FILTERFRAME) | |
101 | do_inline = 0; | |
102 | ||
103 | memset(&iwr, 0, sizeof(iwr)); | |
104 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
105 | if (do_inline) { | |
106 | /* | |
107 | * Argument data fits inline; put it there. | |
108 | */ | |
109 | memcpy(iwr.u.name, data, len); | |
110 | } else { | |
111 | /* | |
112 | * Argument data too big for inline transfer; setup a | |
113 | * parameter block instead; the kernel will transfer | |
114 | * the data for the driver. | |
115 | */ | |
116 | iwr.u.data.pointer = data; | |
117 | iwr.u.data.length = len; | |
118 | } | |
119 | ||
120 | if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { | |
121 | int first = IEEE80211_IOCTL_SETPARAM; | |
122 | static const char *opnames[] = { | |
123 | "ioctl[IEEE80211_IOCTL_SETPARAM]", | |
124 | "ioctl[IEEE80211_IOCTL_GETPARAM]", | |
125 | "ioctl[IEEE80211_IOCTL_SETKEY]", | |
126 | "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", | |
127 | "ioctl[IEEE80211_IOCTL_DELKEY]", | |
128 | "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", | |
129 | "ioctl[IEEE80211_IOCTL_SETMLME]", | |
130 | "ioctl[IEEE80211_IOCTL_GETCHANINFO]", | |
131 | "ioctl[IEEE80211_IOCTL_SETOPTIE]", | |
132 | "ioctl[IEEE80211_IOCTL_GETOPTIE]", | |
133 | "ioctl[IEEE80211_IOCTL_ADDMAC]", | |
134 | "ioctl[IEEE80211_IOCTL_DELMAC]", | |
135 | "ioctl[IEEE80211_IOCTL_GETCHANLIST]", | |
136 | "ioctl[IEEE80211_IOCTL_SETCHANLIST]", | |
137 | "ioctl[IEEE80211_IOCTL_KICKMAC]", | |
138 | "ioctl[IEEE80211_IOCTL_CHANSWITCH]", | |
139 | "ioctl[IEEE80211_IOCTL_GETMODE]", | |
140 | "ioctl[IEEE80211_IOCTL_SETMODE]", | |
141 | "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", | |
142 | "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", | |
143 | NULL, | |
144 | "ioctl[IEEE80211_IOCTL_FILTERFRAME]", | |
145 | }; | |
146 | int idx = op - first; | |
147 | if (first <= op && | |
148 | idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && | |
149 | opnames[idx]) | |
150 | perror(opnames[idx]); | |
151 | else { | |
152 | perror("ioctl[unknown???]"); | |
153 | wpa_printf(MSG_DEBUG, "Failed ioctl: 0x%x", op); | |
154 | } | |
155 | return -1; | |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | static int | |
161 | set80211param(struct madwifi_driver_data *drv, int op, int arg) | |
162 | { | |
163 | struct iwreq iwr; | |
164 | ||
165 | memset(&iwr, 0, sizeof(iwr)); | |
166 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
167 | iwr.u.mode = op; | |
168 | memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); | |
169 | ||
170 | if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { | |
171 | perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); | |
172 | wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " | |
173 | "arg %d)", __func__, op, arg); | |
174 | return -1; | |
175 | } | |
176 | return 0; | |
177 | } | |
178 | ||
179 | #ifndef CONFIG_NO_STDOUT_DEBUG | |
180 | static const char * | |
181 | ether_sprintf(const u8 *addr) | |
182 | { | |
183 | static char buf[sizeof(MACSTR)]; | |
184 | ||
185 | if (addr != NULL) | |
186 | snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); | |
187 | else | |
188 | snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); | |
189 | return buf; | |
190 | } | |
191 | #endif /* CONFIG_NO_STDOUT_DEBUG */ | |
192 | ||
193 | /* | |
194 | * Configure WPA parameters. | |
195 | */ | |
196 | static int | |
197 | madwifi_configure_wpa(struct madwifi_driver_data *drv) | |
198 | { | |
199 | struct hostapd_data *hapd = drv->hapd; | |
200 | struct hostapd_bss_config *conf = hapd->conf; | |
201 | int v; | |
202 | ||
203 | switch (conf->wpa_group) { | |
204 | case WPA_CIPHER_CCMP: | |
205 | v = IEEE80211_CIPHER_AES_CCM; | |
206 | break; | |
207 | case WPA_CIPHER_TKIP: | |
208 | v = IEEE80211_CIPHER_TKIP; | |
209 | break; | |
210 | case WPA_CIPHER_WEP104: | |
211 | v = IEEE80211_CIPHER_WEP; | |
212 | break; | |
213 | case WPA_CIPHER_WEP40: | |
214 | v = IEEE80211_CIPHER_WEP; | |
215 | break; | |
216 | case WPA_CIPHER_NONE: | |
217 | v = IEEE80211_CIPHER_NONE; | |
218 | break; | |
219 | default: | |
220 | wpa_printf(MSG_ERROR, "Unknown group key cipher %u", | |
221 | conf->wpa_group); | |
222 | return -1; | |
223 | } | |
224 | wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); | |
225 | if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { | |
226 | printf("Unable to set group key cipher to %u\n", v); | |
227 | return -1; | |
228 | } | |
229 | if (v == IEEE80211_CIPHER_WEP) { | |
230 | /* key length is done only for specific ciphers */ | |
231 | v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); | |
232 | if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { | |
233 | printf("Unable to set group key length to %u\n", v); | |
234 | return -1; | |
235 | } | |
236 | } | |
237 | ||
238 | v = 0; | |
239 | if (conf->wpa_pairwise & WPA_CIPHER_CCMP) | |
240 | v |= 1<<IEEE80211_CIPHER_AES_CCM; | |
241 | if (conf->wpa_pairwise & WPA_CIPHER_TKIP) | |
242 | v |= 1<<IEEE80211_CIPHER_TKIP; | |
243 | if (conf->wpa_pairwise & WPA_CIPHER_NONE) | |
244 | v |= 1<<IEEE80211_CIPHER_NONE; | |
245 | wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); | |
246 | if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { | |
247 | printf("Unable to set pairwise key ciphers to 0x%x\n", v); | |
248 | return -1; | |
249 | } | |
250 | ||
251 | wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", | |
252 | __func__, conf->wpa_key_mgmt); | |
253 | if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { | |
254 | printf("Unable to set key management algorithms to 0x%x\n", | |
255 | conf->wpa_key_mgmt); | |
256 | return -1; | |
257 | } | |
258 | ||
259 | v = 0; | |
260 | if (conf->rsn_preauth) | |
261 | v |= BIT(0); | |
262 | wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", | |
263 | __func__, conf->rsn_preauth); | |
264 | if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { | |
265 | printf("Unable to set RSN capabilities to 0x%x\n", v); | |
266 | return -1; | |
267 | } | |
268 | ||
269 | wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, conf->wpa); | |
270 | if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { | |
271 | printf("Unable to set WPA to %u\n", conf->wpa); | |
272 | return -1; | |
273 | } | |
274 | return 0; | |
275 | } | |
276 | ||
277 | ||
278 | static int | |
279 | madwifi_set_iface_flags(void *priv, int dev_up) | |
280 | { | |
281 | struct madwifi_driver_data *drv = priv; | |
282 | struct ifreq ifr; | |
283 | ||
284 | wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); | |
285 | ||
286 | if (drv->ioctl_sock < 0) | |
287 | return -1; | |
288 | ||
289 | memset(&ifr, 0, sizeof(ifr)); | |
290 | os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); | |
291 | ||
292 | if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { | |
293 | perror("ioctl[SIOCGIFFLAGS]"); | |
294 | return -1; | |
295 | } | |
296 | ||
297 | if (dev_up) | |
298 | ifr.ifr_flags |= IFF_UP; | |
299 | else | |
300 | ifr.ifr_flags &= ~IFF_UP; | |
301 | ||
302 | if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { | |
303 | perror("ioctl[SIOCSIFFLAGS]"); | |
304 | return -1; | |
305 | } | |
306 | ||
6d7fb691 JM |
307 | return 0; |
308 | } | |
309 | ||
310 | static int | |
311 | madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) | |
312 | { | |
313 | struct madwifi_driver_data *drv = priv; | |
314 | struct hostapd_data *hapd = drv->hapd; | |
315 | struct hostapd_bss_config *conf = hapd->conf; | |
316 | ||
317 | wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); | |
318 | ||
319 | if (!enabled) { | |
320 | /* XXX restore state */ | |
321 | return set80211param(priv, IEEE80211_PARAM_AUTHMODE, | |
322 | IEEE80211_AUTH_AUTO); | |
323 | } | |
324 | if (!conf->wpa && !conf->ieee802_1x) { | |
325 | hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, | |
326 | HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); | |
327 | return -1; | |
328 | } | |
329 | if (conf->wpa && madwifi_configure_wpa(drv) != 0) { | |
330 | hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, | |
331 | HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); | |
332 | return -1; | |
333 | } | |
334 | if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, | |
335 | (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { | |
336 | hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, | |
337 | HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); | |
338 | return -1; | |
339 | } | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | static int | |
345 | madwifi_set_privacy(const char *ifname, void *priv, int enabled) | |
346 | { | |
347 | struct madwifi_driver_data *drv = priv; | |
348 | ||
349 | wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); | |
350 | ||
351 | return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); | |
352 | } | |
353 | ||
354 | static int | |
355 | madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) | |
356 | { | |
357 | struct madwifi_driver_data *drv = priv; | |
358 | struct ieee80211req_mlme mlme; | |
359 | int ret; | |
360 | ||
361 | wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", | |
362 | __func__, ether_sprintf(addr), authorized); | |
363 | ||
364 | if (authorized) | |
365 | mlme.im_op = IEEE80211_MLME_AUTHORIZE; | |
366 | else | |
367 | mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; | |
368 | mlme.im_reason = 0; | |
369 | memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); | |
370 | ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); | |
371 | if (ret < 0) { | |
372 | wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, | |
373 | __func__, authorized ? "" : "un", MAC2STR(addr)); | |
374 | } | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
379 | static int | |
380 | madwifi_sta_set_flags(void *priv, const u8 *addr, int total_flags, | |
381 | int flags_or, int flags_and) | |
382 | { | |
383 | /* For now, only support setting Authorized flag */ | |
384 | if (flags_or & WLAN_STA_AUTHORIZED) | |
385 | return madwifi_set_sta_authorized(priv, addr, 1); | |
386 | if (!(flags_and & WLAN_STA_AUTHORIZED)) | |
387 | return madwifi_set_sta_authorized(priv, addr, 0); | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static int | |
392 | madwifi_del_key(void *priv, const u8 *addr, int key_idx) | |
393 | { | |
394 | struct madwifi_driver_data *drv = priv; | |
395 | struct ieee80211req_del_key wk; | |
396 | int ret; | |
397 | ||
398 | wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", | |
399 | __func__, ether_sprintf(addr), key_idx); | |
400 | ||
401 | memset(&wk, 0, sizeof(wk)); | |
402 | if (addr != NULL) { | |
403 | memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); | |
404 | wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; | |
405 | } else { | |
406 | wk.idk_keyix = key_idx; | |
407 | } | |
408 | ||
409 | ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); | |
410 | if (ret < 0) { | |
411 | wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" | |
412 | " key_idx %d)", __func__, ether_sprintf(addr), | |
413 | key_idx); | |
414 | } | |
415 | ||
416 | return ret; | |
417 | } | |
418 | ||
419 | static int | |
89d39d9d JM |
420 | madwifi_set_key(const char *ifname, void *priv, wpa_alg alg, const u8 *addr, |
421 | int key_idx, int set_tx, const u8 *seq, size_t seq_len, | |
422 | const u8 *key, size_t key_len) | |
6d7fb691 JM |
423 | { |
424 | struct madwifi_driver_data *drv = priv; | |
425 | struct ieee80211req_key wk; | |
426 | u_int8_t cipher; | |
427 | int ret; | |
428 | ||
89d39d9d | 429 | if (alg == WPA_ALG_NONE) |
6d7fb691 JM |
430 | return madwifi_del_key(drv, addr, key_idx); |
431 | ||
89d39d9d | 432 | wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", |
6d7fb691 JM |
433 | __func__, alg, ether_sprintf(addr), key_idx); |
434 | ||
89d39d9d JM |
435 | switch (alg) { |
436 | case WPA_ALG_WEP: | |
6d7fb691 | 437 | cipher = IEEE80211_CIPHER_WEP; |
89d39d9d JM |
438 | break; |
439 | case WPA_ALG_TKIP: | |
6d7fb691 | 440 | cipher = IEEE80211_CIPHER_TKIP; |
89d39d9d JM |
441 | break; |
442 | case WPA_ALG_CCMP: | |
6d7fb691 | 443 | cipher = IEEE80211_CIPHER_AES_CCM; |
89d39d9d JM |
444 | break; |
445 | default: | |
446 | printf("%s: unknown/unsupported algorithm %d\n", | |
6d7fb691 JM |
447 | __func__, alg); |
448 | return -1; | |
449 | } | |
450 | ||
451 | if (key_len > sizeof(wk.ik_keydata)) { | |
452 | printf("%s: key length %lu too big\n", __func__, | |
453 | (unsigned long) key_len); | |
454 | return -3; | |
455 | } | |
456 | ||
457 | memset(&wk, 0, sizeof(wk)); | |
458 | wk.ik_type = cipher; | |
459 | wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; | |
460 | if (addr == NULL) { | |
461 | memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); | |
462 | wk.ik_keyix = key_idx; | |
463 | wk.ik_flags |= IEEE80211_KEY_DEFAULT; | |
464 | } else { | |
465 | memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); | |
466 | wk.ik_keyix = IEEE80211_KEYIX_NONE; | |
467 | } | |
468 | wk.ik_keylen = key_len; | |
469 | memcpy(wk.ik_keydata, key, key_len); | |
470 | ||
471 | ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); | |
472 | if (ret < 0) { | |
473 | wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" | |
89d39d9d | 474 | " key_idx %d alg %d key_len %lu set_tx %d)", |
6d7fb691 | 475 | __func__, ether_sprintf(wk.ik_macaddr), key_idx, |
89d39d9d | 476 | alg, (unsigned long) key_len, set_tx); |
6d7fb691 JM |
477 | } |
478 | ||
479 | return ret; | |
480 | } | |
481 | ||
482 | ||
483 | static int | |
484 | madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, | |
485 | u8 *seq) | |
486 | { | |
487 | struct madwifi_driver_data *drv = priv; | |
488 | struct ieee80211req_key wk; | |
489 | ||
490 | wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", | |
491 | __func__, ether_sprintf(addr), idx); | |
492 | ||
493 | memset(&wk, 0, sizeof(wk)); | |
494 | if (addr == NULL) | |
495 | memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); | |
496 | else | |
497 | memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); | |
498 | wk.ik_keyix = idx; | |
499 | ||
500 | if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { | |
501 | wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " | |
502 | "(addr " MACSTR " key_idx %d)", | |
503 | __func__, MAC2STR(wk.ik_macaddr), idx); | |
504 | return -1; | |
505 | } | |
506 | ||
507 | #ifdef WORDS_BIGENDIAN | |
508 | { | |
509 | /* | |
510 | * wk.ik_keytsc is in host byte order (big endian), need to | |
511 | * swap it to match with the byte order used in WPA. | |
512 | */ | |
513 | int i; | |
514 | u8 tmp[WPA_KEY_RSC_LEN]; | |
515 | memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); | |
516 | for (i = 0; i < WPA_KEY_RSC_LEN; i++) { | |
517 | seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; | |
518 | } | |
519 | } | |
520 | #else /* WORDS_BIGENDIAN */ | |
521 | memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); | |
522 | #endif /* WORDS_BIGENDIAN */ | |
523 | return 0; | |
524 | } | |
525 | ||
526 | ||
527 | static int | |
528 | madwifi_flush(void *priv) | |
529 | { | |
530 | u8 allsta[IEEE80211_ADDR_LEN]; | |
531 | memset(allsta, 0xff, IEEE80211_ADDR_LEN); | |
731723a5 JM |
532 | return madwifi_sta_deauth(priv, NULL, allsta, |
533 | IEEE80211_REASON_AUTH_LEAVE); | |
6d7fb691 JM |
534 | } |
535 | ||
536 | ||
537 | static int | |
538 | madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, | |
539 | const u8 *addr) | |
540 | { | |
541 | struct madwifi_driver_data *drv = priv; | |
542 | struct ieee80211req_sta_stats stats; | |
543 | ||
544 | memset(data, 0, sizeof(*data)); | |
545 | ||
546 | /* | |
547 | * Fetch statistics for station from the system. | |
548 | */ | |
549 | memset(&stats, 0, sizeof(stats)); | |
550 | memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); | |
551 | if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, | |
552 | &stats, sizeof(stats))) { | |
553 | wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " | |
554 | MACSTR ")", __func__, MAC2STR(addr)); | |
555 | if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { | |
556 | memcpy(data, &drv->acct_data, sizeof(*data)); | |
557 | return 0; | |
558 | } | |
559 | ||
560 | printf("Failed to get station stats information element.\n"); | |
561 | return -1; | |
562 | } | |
563 | ||
564 | data->rx_packets = stats.is_stats.ns_rx_data; | |
565 | data->rx_bytes = stats.is_stats.ns_rx_bytes; | |
566 | data->tx_packets = stats.is_stats.ns_tx_data; | |
567 | data->tx_bytes = stats.is_stats.ns_tx_bytes; | |
568 | return 0; | |
569 | } | |
570 | ||
571 | ||
572 | static int | |
573 | madwifi_sta_clear_stats(void *priv, const u8 *addr) | |
574 | { | |
575 | struct madwifi_driver_data *drv = priv; | |
576 | struct ieee80211req_mlme mlme; | |
577 | int ret; | |
578 | ||
579 | wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); | |
580 | ||
581 | mlme.im_op = IEEE80211_MLME_CLEAR_STATS; | |
582 | memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); | |
583 | ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, | |
584 | sizeof(mlme)); | |
585 | if (ret < 0) { | |
586 | wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " | |
587 | MACSTR ")", __func__, MAC2STR(addr)); | |
588 | } | |
589 | ||
590 | return ret; | |
591 | } | |
592 | ||
593 | ||
594 | static int | |
595 | madwifi_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) | |
596 | { | |
597 | /* | |
598 | * Do nothing; we setup parameters at startup that define the | |
599 | * contents of the beacon information element. | |
600 | */ | |
601 | return 0; | |
602 | } | |
603 | ||
604 | static int | |
731723a5 JM |
605 | madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, |
606 | int reason_code) | |
6d7fb691 JM |
607 | { |
608 | struct madwifi_driver_data *drv = priv; | |
609 | struct ieee80211req_mlme mlme; | |
610 | int ret; | |
611 | ||
612 | wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", | |
613 | __func__, ether_sprintf(addr), reason_code); | |
614 | ||
615 | mlme.im_op = IEEE80211_MLME_DEAUTH; | |
616 | mlme.im_reason = reason_code; | |
617 | memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); | |
618 | ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); | |
619 | if (ret < 0) { | |
620 | wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR | |
621 | " reason %d)", | |
622 | __func__, MAC2STR(addr), reason_code); | |
623 | } | |
624 | ||
625 | return ret; | |
626 | } | |
627 | ||
628 | static int | |
731723a5 JM |
629 | madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, |
630 | int reason_code) | |
6d7fb691 JM |
631 | { |
632 | struct madwifi_driver_data *drv = priv; | |
633 | struct ieee80211req_mlme mlme; | |
634 | int ret; | |
635 | ||
636 | wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", | |
637 | __func__, ether_sprintf(addr), reason_code); | |
638 | ||
639 | mlme.im_op = IEEE80211_MLME_DISASSOC; | |
640 | mlme.im_reason = reason_code; | |
641 | memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); | |
642 | ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); | |
643 | if (ret < 0) { | |
644 | wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " | |
645 | MACSTR " reason %d)", | |
646 | __func__, MAC2STR(addr), reason_code); | |
647 | } | |
648 | ||
649 | return ret; | |
650 | } | |
651 | ||
652 | #ifdef CONFIG_WPS | |
653 | static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, | |
654 | size_t len) | |
655 | { | |
656 | struct madwifi_driver_data *drv = ctx; | |
657 | const struct ieee80211_mgmt *mgmt; | |
658 | const u8 *end, *ie; | |
659 | u16 fc; | |
660 | size_t ie_len; | |
661 | ||
662 | /* Send Probe Request information to WPS processing */ | |
663 | ||
664 | if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) | |
665 | return; | |
666 | mgmt = (const struct ieee80211_mgmt *) buf; | |
667 | ||
668 | fc = le_to_host16(mgmt->frame_control); | |
669 | if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || | |
670 | WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) | |
671 | return; | |
672 | ||
673 | end = buf + len; | |
674 | ie = mgmt->u.probe_req.variable; | |
675 | ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); | |
676 | ||
3fed6f25 | 677 | hostapd_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); |
6d7fb691 JM |
678 | } |
679 | #endif /* CONFIG_WPS */ | |
680 | ||
681 | static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) | |
682 | { | |
683 | int ret = 0; | |
684 | #ifdef CONFIG_WPS | |
685 | struct ieee80211req_set_filter filt; | |
686 | ||
687 | wpa_printf(MSG_DEBUG, "%s Enter", __func__); | |
688 | filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; | |
689 | ||
690 | ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, | |
691 | sizeof(struct ieee80211req_set_filter)); | |
692 | if (ret) | |
693 | return ret; | |
694 | ||
695 | drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, | |
696 | madwifi_raw_receive, drv, 1); | |
697 | if (drv->sock_raw == NULL) | |
698 | return -1; | |
699 | #endif /* CONFIG_WPS */ | |
700 | return ret; | |
701 | } | |
702 | ||
703 | #ifdef CONFIG_WPS | |
704 | static int | |
705 | madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) | |
706 | { | |
707 | struct madwifi_driver_data *drv = priv; | |
708 | u8 buf[256]; | |
709 | struct ieee80211req_getset_appiebuf *beac_ie; | |
710 | ||
711 | wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, | |
712 | (unsigned long) len); | |
713 | ||
714 | beac_ie = (struct ieee80211req_getset_appiebuf *) buf; | |
715 | beac_ie->app_frmtype = frametype; | |
716 | beac_ie->app_buflen = len; | |
717 | memcpy(&(beac_ie->app_buf[0]), ie, len); | |
718 | ||
719 | return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, | |
720 | sizeof(struct ieee80211req_getset_appiebuf) + len); | |
721 | } | |
722 | ||
723 | static int | |
724 | madwifi_set_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, | |
725 | size_t len) | |
726 | { | |
727 | return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); | |
728 | } | |
729 | ||
730 | static int | |
731 | madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, | |
732 | size_t len) | |
733 | { | |
734 | return madwifi_set_wps_ie(priv, ie, len, | |
735 | IEEE80211_APPIE_FRAME_PROBE_RESP); | |
736 | } | |
737 | #else /* CONFIG_WPS */ | |
738 | #define madwifi_set_wps_beacon_ie NULL | |
739 | #define madwifi_set_wps_probe_resp_ie NULL | |
740 | #endif /* CONFIG_WPS */ | |
741 | ||
742 | static int | |
743 | madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) | |
744 | { | |
745 | struct hostapd_data *hapd = drv->hapd; | |
746 | struct ieee80211req_wpaie ie; | |
747 | int ielen = 0, res; | |
748 | u8 *iebuf = NULL; | |
749 | ||
750 | /* | |
751 | * Fetch negotiated WPA/RSN parameters from the system. | |
752 | */ | |
753 | memset(&ie, 0, sizeof(ie)); | |
754 | memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); | |
755 | if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { | |
756 | /* | |
757 | * See ATH_WPS_IE comment in the beginning of the file for a | |
758 | * possible cause for the failure.. | |
759 | */ | |
760 | wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", | |
761 | __func__, strerror(errno)); | |
762 | goto no_ie; | |
763 | } | |
764 | wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", | |
765 | ie.wpa_ie, IEEE80211_MAX_OPT_IE); | |
766 | wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", | |
767 | ie.rsn_ie, IEEE80211_MAX_OPT_IE); | |
768 | iebuf = ie.wpa_ie; | |
769 | /* madwifi seems to return some random data if WPA/RSN IE is not set. | |
770 | * Assume the IE was not included if the IE type is unknown. */ | |
771 | if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) | |
772 | iebuf[1] = 0; | |
773 | if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { | |
774 | /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not | |
775 | * set. This is needed for WPA2. */ | |
776 | iebuf = ie.rsn_ie; | |
777 | if (iebuf[0] != WLAN_EID_RSN) | |
778 | iebuf[1] = 0; | |
779 | } | |
780 | ||
781 | ielen = iebuf[1]; | |
782 | if (ielen == 0) | |
783 | iebuf = NULL; | |
784 | else | |
785 | ielen += 2; | |
786 | ||
787 | no_ie: | |
788 | res = hostapd_notif_assoc(hapd, addr, iebuf, ielen); | |
789 | ||
790 | if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { | |
791 | /* Cached accounting data is not valid anymore. */ | |
792 | memset(drv->acct_mac, 0, ETH_ALEN); | |
793 | memset(&drv->acct_data, 0, sizeof(drv->acct_data)); | |
794 | } | |
795 | ||
796 | return res; | |
797 | } | |
798 | ||
799 | static void | |
800 | madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, | |
801 | char *custom, char *end) | |
802 | { | |
803 | wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); | |
804 | ||
805 | if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { | |
806 | char *pos; | |
807 | u8 addr[ETH_ALEN]; | |
808 | pos = strstr(custom, "addr="); | |
809 | if (pos == NULL) { | |
810 | wpa_printf(MSG_DEBUG, | |
811 | "MLME-MICHAELMICFAILURE.indication " | |
812 | "without sender address ignored"); | |
813 | return; | |
814 | } | |
815 | pos += 5; | |
816 | if (hwaddr_aton(pos, addr) == 0) { | |
817 | hostapd_michael_mic_failure(drv->hapd, addr); | |
818 | } else { | |
819 | wpa_printf(MSG_DEBUG, | |
820 | "MLME-MICHAELMICFAILURE.indication " | |
821 | "with invalid MAC address"); | |
822 | } | |
823 | } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { | |
824 | char *key, *value; | |
825 | u32 val; | |
826 | key = custom; | |
827 | while ((key = strchr(key, '\n')) != NULL) { | |
828 | key++; | |
829 | value = strchr(key, '='); | |
830 | if (value == NULL) | |
831 | continue; | |
832 | *value++ = '\0'; | |
833 | val = strtoul(value, NULL, 10); | |
834 | if (strcmp(key, "mac") == 0) | |
835 | hwaddr_aton(value, drv->acct_mac); | |
836 | else if (strcmp(key, "rx_packets") == 0) | |
837 | drv->acct_data.rx_packets = val; | |
838 | else if (strcmp(key, "tx_packets") == 0) | |
839 | drv->acct_data.tx_packets = val; | |
840 | else if (strcmp(key, "rx_bytes") == 0) | |
841 | drv->acct_data.rx_bytes = val; | |
842 | else if (strcmp(key, "tx_bytes") == 0) | |
843 | drv->acct_data.tx_bytes = val; | |
844 | key = value; | |
845 | } | |
846 | #ifdef CONFIG_WPS | |
847 | } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { | |
848 | /* Some atheros kernels send push button as a wireless event */ | |
849 | /* PROBLEM! this event is received for ALL BSSs ... | |
850 | * so all are enabled for WPS... ugh. | |
851 | */ | |
852 | hostapd_wps_button_pushed(drv->hapd); | |
853 | } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { | |
854 | /* | |
855 | * Atheros driver uses a hack to pass Probe Request frames as a | |
856 | * binary data in the custom wireless event. The old way (using | |
857 | * packet sniffing) didn't work when bridging. | |
858 | * Format: "Manage.prob_req <frame len>" | zero padding | frame | |
859 | */ | |
860 | #define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ | |
861 | int len = atoi(custom + 16); | |
862 | if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { | |
863 | wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " | |
864 | "length %d", len); | |
865 | return; | |
866 | } | |
867 | madwifi_raw_receive(drv, NULL, | |
868 | (u8 *) custom + WPS_FRAM_TAG_SIZE, len); | |
869 | #endif /* CONFIG_WPS */ | |
870 | } | |
871 | } | |
872 | ||
873 | static void | |
874 | madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, | |
875 | char *data, int len) | |
876 | { | |
877 | struct iw_event iwe_buf, *iwe = &iwe_buf; | |
878 | char *pos, *end, *custom, *buf; | |
879 | ||
880 | pos = data; | |
881 | end = data + len; | |
882 | ||
883 | while (pos + IW_EV_LCP_LEN <= end) { | |
884 | /* Event data may be unaligned, so make a local, aligned copy | |
885 | * before processing. */ | |
886 | memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); | |
887 | wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", | |
888 | iwe->cmd, iwe->len); | |
889 | if (iwe->len <= IW_EV_LCP_LEN) | |
890 | return; | |
891 | ||
892 | custom = pos + IW_EV_POINT_LEN; | |
893 | if (drv->we_version > 18 && | |
894 | (iwe->cmd == IWEVMICHAELMICFAILURE || | |
895 | iwe->cmd == IWEVASSOCREQIE || | |
896 | iwe->cmd == IWEVCUSTOM)) { | |
897 | /* WE-19 removed the pointer from struct iw_point */ | |
898 | char *dpos = (char *) &iwe_buf.u.data.length; | |
899 | int dlen = dpos - (char *) &iwe_buf; | |
900 | memcpy(dpos, pos + IW_EV_LCP_LEN, | |
901 | sizeof(struct iw_event) - dlen); | |
902 | } else { | |
903 | memcpy(&iwe_buf, pos, sizeof(struct iw_event)); | |
904 | custom += IW_EV_POINT_OFF; | |
905 | } | |
906 | ||
907 | switch (iwe->cmd) { | |
908 | case IWEVEXPIRED: | |
909 | hostapd_notif_disassoc(drv->hapd, | |
910 | (u8 *) iwe->u.addr.sa_data); | |
911 | break; | |
912 | case IWEVREGISTERED: | |
913 | madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); | |
914 | break; | |
915 | case IWEVASSOCREQIE: | |
916 | /* Driver hack.. Use IWEVASSOCREQIE to bypass | |
917 | * IWEVCUSTOM size limitations. Need to handle this | |
918 | * just like IWEVCUSTOM. | |
919 | */ | |
920 | case IWEVCUSTOM: | |
921 | if (custom + iwe->u.data.length > end) | |
922 | return; | |
923 | buf = malloc(iwe->u.data.length + 1); | |
924 | if (buf == NULL) | |
925 | return; /* XXX */ | |
926 | memcpy(buf, custom, iwe->u.data.length); | |
927 | buf[iwe->u.data.length] = '\0'; | |
928 | madwifi_wireless_event_wireless_custom( | |
929 | drv, buf, buf + iwe->u.data.length); | |
930 | free(buf); | |
931 | break; | |
932 | } | |
933 | ||
934 | pos += iwe->len; | |
935 | } | |
936 | } | |
937 | ||
938 | ||
939 | static void | |
940 | madwifi_wireless_event_rtm_newlink(struct madwifi_driver_data *drv, | |
941 | struct nlmsghdr *h, int len) | |
942 | { | |
943 | struct ifinfomsg *ifi; | |
944 | int attrlen, nlmsg_len, rta_len; | |
945 | struct rtattr * attr; | |
946 | ||
947 | if (len < (int) sizeof(*ifi)) | |
948 | return; | |
949 | ||
950 | ifi = NLMSG_DATA(h); | |
951 | ||
952 | if (ifi->ifi_index != drv->ifindex) | |
953 | return; | |
954 | ||
955 | nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
956 | ||
957 | attrlen = h->nlmsg_len - nlmsg_len; | |
958 | if (attrlen < 0) | |
959 | return; | |
960 | ||
961 | attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); | |
962 | ||
963 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
964 | while (RTA_OK(attr, attrlen)) { | |
965 | if (attr->rta_type == IFLA_WIRELESS) { | |
966 | madwifi_wireless_event_wireless( | |
967 | drv, ((char *) attr) + rta_len, | |
968 | attr->rta_len - rta_len); | |
969 | } | |
970 | attr = RTA_NEXT(attr, attrlen); | |
971 | } | |
972 | } | |
973 | ||
974 | ||
975 | static void | |
976 | madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) | |
977 | { | |
978 | char buf[256]; | |
979 | int left; | |
980 | struct sockaddr_nl from; | |
981 | socklen_t fromlen; | |
982 | struct nlmsghdr *h; | |
983 | struct madwifi_driver_data *drv = eloop_ctx; | |
984 | ||
985 | fromlen = sizeof(from); | |
986 | left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, | |
987 | (struct sockaddr *) &from, &fromlen); | |
988 | if (left < 0) { | |
989 | if (errno != EINTR && errno != EAGAIN) | |
990 | perror("recvfrom(netlink)"); | |
991 | return; | |
992 | } | |
993 | ||
994 | h = (struct nlmsghdr *) buf; | |
995 | while (left >= (int) sizeof(*h)) { | |
996 | int len, plen; | |
997 | ||
998 | len = h->nlmsg_len; | |
999 | plen = len - sizeof(*h); | |
1000 | if (len > left || plen < 0) { | |
1001 | printf("Malformed netlink message: " | |
1002 | "len=%d left=%d plen=%d\n", | |
1003 | len, left, plen); | |
1004 | break; | |
1005 | } | |
1006 | ||
1007 | switch (h->nlmsg_type) { | |
1008 | case RTM_NEWLINK: | |
1009 | madwifi_wireless_event_rtm_newlink(drv, h, plen); | |
1010 | break; | |
1011 | } | |
1012 | ||
1013 | len = NLMSG_ALIGN(len); | |
1014 | left -= len; | |
1015 | h = (struct nlmsghdr *) ((char *) h + len); | |
1016 | } | |
1017 | ||
1018 | if (left > 0) { | |
1019 | printf("%d extra bytes in the end of netlink message\n", left); | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | ||
1024 | static int | |
1025 | madwifi_get_we_version(struct madwifi_driver_data *drv) | |
1026 | { | |
1027 | struct iw_range *range; | |
1028 | struct iwreq iwr; | |
1029 | int minlen; | |
1030 | size_t buflen; | |
1031 | ||
1032 | drv->we_version = 0; | |
1033 | ||
1034 | /* | |
1035 | * Use larger buffer than struct iw_range in order to allow the | |
1036 | * structure to grow in the future. | |
1037 | */ | |
1038 | buflen = sizeof(struct iw_range) + 500; | |
1039 | range = os_zalloc(buflen); | |
1040 | if (range == NULL) | |
1041 | return -1; | |
1042 | ||
1043 | memset(&iwr, 0, sizeof(iwr)); | |
1044 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
1045 | iwr.u.data.pointer = (caddr_t) range; | |
1046 | iwr.u.data.length = buflen; | |
1047 | ||
1048 | minlen = ((char *) &range->enc_capa) - (char *) range + | |
1049 | sizeof(range->enc_capa); | |
1050 | ||
1051 | if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { | |
1052 | perror("ioctl[SIOCGIWRANGE]"); | |
1053 | free(range); | |
1054 | return -1; | |
1055 | } else if (iwr.u.data.length >= minlen && | |
1056 | range->we_version_compiled >= 18) { | |
1057 | wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " | |
1058 | "WE(source)=%d enc_capa=0x%x", | |
1059 | range->we_version_compiled, | |
1060 | range->we_version_source, | |
1061 | range->enc_capa); | |
1062 | drv->we_version = range->we_version_compiled; | |
1063 | } | |
1064 | ||
1065 | free(range); | |
1066 | return 0; | |
1067 | } | |
1068 | ||
1069 | ||
1070 | static int | |
a9a2cb5a | 1071 | madwifi_wireless_event_init(struct madwifi_driver_data *drv) |
6d7fb691 | 1072 | { |
6d7fb691 JM |
1073 | int s; |
1074 | struct sockaddr_nl local; | |
1075 | ||
1076 | madwifi_get_we_version(drv); | |
1077 | ||
1078 | drv->wext_sock = -1; | |
1079 | ||
1080 | s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
1081 | if (s < 0) { | |
1082 | perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); | |
1083 | return -1; | |
1084 | } | |
1085 | ||
1086 | memset(&local, 0, sizeof(local)); | |
1087 | local.nl_family = AF_NETLINK; | |
1088 | local.nl_groups = RTMGRP_LINK; | |
1089 | if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { | |
1090 | perror("bind(netlink)"); | |
1091 | close(s); | |
1092 | return -1; | |
1093 | } | |
1094 | ||
1095 | eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); | |
1096 | drv->wext_sock = s; | |
1097 | ||
1098 | return 0; | |
1099 | } | |
1100 | ||
1101 | ||
1102 | static void | |
a9a2cb5a | 1103 | madwifi_wireless_event_deinit(struct madwifi_driver_data *drv) |
6d7fb691 | 1104 | { |
a9a2cb5a JM |
1105 | if (drv->wext_sock < 0) |
1106 | return; | |
1107 | eloop_unregister_read_sock(drv->wext_sock); | |
1108 | close(drv->wext_sock); | |
6d7fb691 JM |
1109 | } |
1110 | ||
1111 | ||
1112 | static int | |
1113 | madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, | |
1114 | int encrypt, const u8 *own_addr) | |
1115 | { | |
1116 | struct madwifi_driver_data *drv = priv; | |
1117 | unsigned char buf[3000]; | |
1118 | unsigned char *bp = buf; | |
1119 | struct l2_ethhdr *eth; | |
1120 | size_t len; | |
1121 | int status; | |
1122 | ||
1123 | /* | |
1124 | * Prepend the Ethernet header. If the caller left us | |
1125 | * space at the front we could just insert it but since | |
1126 | * we don't know we copy to a local buffer. Given the frequency | |
1127 | * and size of frames this probably doesn't matter. | |
1128 | */ | |
1129 | len = data_len + sizeof(struct l2_ethhdr); | |
1130 | if (len > sizeof(buf)) { | |
1131 | bp = malloc(len); | |
1132 | if (bp == NULL) { | |
1133 | printf("EAPOL frame discarded, cannot malloc temp " | |
1134 | "buffer of size %lu!\n", (unsigned long) len); | |
1135 | return -1; | |
1136 | } | |
1137 | } | |
1138 | eth = (struct l2_ethhdr *) bp; | |
1139 | memcpy(eth->h_dest, addr, ETH_ALEN); | |
1140 | memcpy(eth->h_source, own_addr, ETH_ALEN); | |
1141 | eth->h_proto = host_to_be16(ETH_P_EAPOL); | |
1142 | memcpy(eth+1, data, data_len); | |
1143 | ||
1144 | wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); | |
1145 | ||
1146 | status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); | |
1147 | ||
1148 | if (bp != buf) | |
1149 | free(bp); | |
1150 | return status; | |
1151 | } | |
1152 | ||
1153 | static void | |
1154 | handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) | |
1155 | { | |
1156 | struct madwifi_driver_data *drv = ctx; | |
1157 | hostapd_eapol_receive(drv->hapd, src_addr, | |
1158 | buf + sizeof(struct l2_ethhdr), | |
1159 | len - sizeof(struct l2_ethhdr)); | |
1160 | } | |
1161 | ||
1162 | static void * | |
92f475b4 | 1163 | madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) |
6d7fb691 JM |
1164 | { |
1165 | struct madwifi_driver_data *drv; | |
1166 | struct ifreq ifr; | |
1167 | struct iwreq iwr; | |
1168 | ||
1169 | drv = os_zalloc(sizeof(struct madwifi_driver_data)); | |
1170 | if (drv == NULL) { | |
1171 | printf("Could not allocate memory for madwifi driver data\n"); | |
1172 | return NULL; | |
1173 | } | |
1174 | ||
1175 | drv->hapd = hapd; | |
1176 | drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); | |
1177 | if (drv->ioctl_sock < 0) { | |
1178 | perror("socket[PF_INET,SOCK_DGRAM]"); | |
1179 | goto bad; | |
1180 | } | |
92f475b4 | 1181 | memcpy(drv->iface, params->ifname, sizeof(drv->iface)); |
6d7fb691 JM |
1182 | |
1183 | memset(&ifr, 0, sizeof(ifr)); | |
1184 | os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); | |
1185 | if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { | |
1186 | perror("ioctl(SIOCGIFINDEX)"); | |
1187 | goto bad; | |
1188 | } | |
1189 | drv->ifindex = ifr.ifr_ifindex; | |
1190 | ||
1191 | drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, | |
1192 | handle_read, drv, 1); | |
1193 | if (drv->sock_xmit == NULL) | |
1194 | goto bad; | |
412036f5 | 1195 | if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) |
6d7fb691 | 1196 | goto bad; |
92f475b4 | 1197 | if (params->bridge[0]) { |
6d7fb691 | 1198 | wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", |
92f475b4 JM |
1199 | params->bridge[0]); |
1200 | drv->sock_recv = l2_packet_init(params->bridge[0], NULL, | |
6d7fb691 JM |
1201 | ETH_P_EAPOL, handle_read, drv, |
1202 | 1); | |
1203 | if (drv->sock_recv == NULL) | |
1204 | goto bad; | |
1205 | } else | |
1206 | drv->sock_recv = drv->sock_xmit; | |
1207 | ||
1208 | memset(&iwr, 0, sizeof(iwr)); | |
1209 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
1210 | ||
1211 | iwr.u.mode = IW_MODE_MASTER; | |
1212 | ||
1213 | if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { | |
1214 | perror("ioctl[SIOCSIWMODE]"); | |
1215 | printf("Could not set interface to master mode!\n"); | |
1216 | goto bad; | |
1217 | } | |
1218 | ||
1219 | madwifi_set_iface_flags(drv, 0); /* mark down during setup */ | |
1220 | madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ | |
1221 | ||
1222 | madwifi_receive_probe_req(drv); | |
1223 | ||
a9a2cb5a JM |
1224 | if (madwifi_wireless_event_init(drv)) |
1225 | goto bad; | |
1226 | ||
6d7fb691 JM |
1227 | return drv; |
1228 | bad: | |
1229 | if (drv->sock_xmit != NULL) | |
1230 | l2_packet_deinit(drv->sock_xmit); | |
1231 | if (drv->ioctl_sock >= 0) | |
1232 | close(drv->ioctl_sock); | |
1233 | if (drv != NULL) | |
1234 | free(drv); | |
1235 | return NULL; | |
1236 | } | |
1237 | ||
1238 | ||
1239 | static void | |
1240 | madwifi_deinit(void *priv) | |
1241 | { | |
1242 | struct madwifi_driver_data *drv = priv; | |
1243 | ||
a9a2cb5a | 1244 | madwifi_wireless_event_deinit(drv); |
6d7fb691 JM |
1245 | (void) madwifi_set_iface_flags(drv, 0); |
1246 | if (drv->ioctl_sock >= 0) | |
1247 | close(drv->ioctl_sock); | |
1248 | if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) | |
1249 | l2_packet_deinit(drv->sock_recv); | |
1250 | if (drv->sock_xmit != NULL) | |
1251 | l2_packet_deinit(drv->sock_xmit); | |
1252 | if (drv->sock_raw) | |
1253 | l2_packet_deinit(drv->sock_raw); | |
1254 | free(drv); | |
1255 | } | |
1256 | ||
1257 | static int | |
1258 | madwifi_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) | |
1259 | { | |
1260 | struct madwifi_driver_data *drv = priv; | |
1261 | struct iwreq iwr; | |
1262 | ||
1263 | memset(&iwr, 0, sizeof(iwr)); | |
1264 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
1265 | iwr.u.essid.flags = 1; /* SSID active */ | |
1266 | iwr.u.essid.pointer = (caddr_t) buf; | |
1267 | iwr.u.essid.length = len + 1; | |
1268 | ||
1269 | if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { | |
1270 | perror("ioctl[SIOCSIWESSID]"); | |
1271 | printf("len=%d\n", len); | |
1272 | return -1; | |
1273 | } | |
1274 | return 0; | |
1275 | } | |
1276 | ||
1277 | static int | |
1278 | madwifi_get_ssid(const char *ifname, void *priv, u8 *buf, int len) | |
1279 | { | |
1280 | struct madwifi_driver_data *drv = priv; | |
1281 | struct iwreq iwr; | |
1282 | int ret = 0; | |
1283 | ||
1284 | memset(&iwr, 0, sizeof(iwr)); | |
1285 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
1286 | iwr.u.essid.pointer = (caddr_t) buf; | |
1287 | iwr.u.essid.length = len; | |
1288 | ||
1289 | if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { | |
1290 | perror("ioctl[SIOCGIWESSID]"); | |
1291 | ret = -1; | |
1292 | } else | |
1293 | ret = iwr.u.essid.length; | |
1294 | ||
1295 | return ret; | |
1296 | } | |
1297 | ||
1298 | static int | |
1299 | madwifi_set_countermeasures(void *priv, int enabled) | |
1300 | { | |
1301 | struct madwifi_driver_data *drv = priv; | |
1302 | wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); | |
1303 | return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); | |
1304 | } | |
1305 | ||
1306 | static int | |
1307 | madwifi_commit(void *priv) | |
1308 | { | |
1309 | return madwifi_set_iface_flags(priv, 1); | |
1310 | } | |
1311 | ||
c5121837 | 1312 | const struct wpa_driver_ops wpa_driver_atheros_ops = { |
6d7fb691 | 1313 | .name = "atheros", |
c5121837 | 1314 | .hapd_init = madwifi_init, |
6d7fb691 JM |
1315 | .deinit = madwifi_deinit, |
1316 | .set_ieee8021x = madwifi_set_ieee8021x, | |
1317 | .set_privacy = madwifi_set_privacy, | |
642187d6 | 1318 | .set_key = madwifi_set_key, |
6d7fb691 JM |
1319 | .get_seqnum = madwifi_get_seqnum, |
1320 | .flush = madwifi_flush, | |
1321 | .set_generic_elem = madwifi_set_opt_ie, | |
6d7fb691 JM |
1322 | .sta_set_flags = madwifi_sta_set_flags, |
1323 | .read_sta_data = madwifi_read_sta_driver_data, | |
c5121837 | 1324 | .hapd_send_eapol = madwifi_send_eapol, |
6d7fb691 JM |
1325 | .sta_disassoc = madwifi_sta_disassoc, |
1326 | .sta_deauth = madwifi_sta_deauth, | |
c5121837 JM |
1327 | .hapd_set_ssid = madwifi_set_ssid, |
1328 | .hapd_get_ssid = madwifi_get_ssid, | |
6d7fb691 JM |
1329 | .set_countermeasures = madwifi_set_countermeasures, |
1330 | .sta_clear_stats = madwifi_sta_clear_stats, | |
1331 | .commit = madwifi_commit, | |
1332 | .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, | |
1333 | .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, | |
1334 | }; |