]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
c5121837 | 2 | * Driver interaction with Linux Host AP driver |
6fc6879b JM |
3 | * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> |
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 | ||
9 | #include "includes.h" | |
10 | #include <sys/ioctl.h> | |
11 | ||
545cfc4b | 12 | #include "linux_wext.h" |
6fc6879b JM |
13 | #include "common.h" |
14 | #include "driver.h" | |
15 | #include "driver_wext.h" | |
16 | #include "eloop.h" | |
17 | #include "driver_hostap.h" | |
18 | ||
19 | ||
c5121837 JM |
20 | #include <net/if_arp.h> |
21 | #include <netpacket/packet.h> | |
22 | ||
23 | #include "priv_netlink.h" | |
a6fc4f3c | 24 | #include "netlink.h" |
34f2f814 | 25 | #include "linux_ioctl.h" |
90973fb2 | 26 | #include "common/ieee802_11_defs.h" |
9b90955e | 27 | #include "common/ieee802_11_common.h" |
c5121837 JM |
28 | |
29 | ||
fd7a5dd1 JM |
30 | /* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X |
31 | * frames that might be longer than normal default MTU and they are not | |
32 | * fragmented */ | |
33 | #define HOSTAPD_MTU 2290 | |
34 | ||
c5121837 JM |
35 | static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; |
36 | ||
37 | struct hostap_driver_data { | |
38 | struct hostapd_data *hapd; | |
39 | ||
40 | char iface[IFNAMSIZ + 1]; | |
41 | int sock; /* raw packet socket for driver access */ | |
42 | int ioctl_sock; /* socket for ioctl() use */ | |
a6fc4f3c | 43 | struct netlink_data *netlink; |
c5121837 JM |
44 | |
45 | int we_version; | |
46 | ||
47 | u8 *generic_ie; | |
48 | size_t generic_ie_len; | |
49 | u8 *wps_ie; | |
50 | size_t wps_ie_len; | |
51 | }; | |
52 | ||
53 | ||
54 | static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, | |
55 | int len); | |
56 | static int hostap_set_iface_flags(void *priv, int dev_up); | |
c5121837 JM |
57 | |
58 | static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, | |
59 | u16 stype) | |
60 | { | |
61 | struct ieee80211_hdr *hdr; | |
62 | u16 fc, ethertype; | |
63 | u8 *pos, *sa; | |
64 | size_t left; | |
f8b1f695 | 65 | union wpa_event_data event; |
c5121837 JM |
66 | |
67 | if (len < sizeof(struct ieee80211_hdr)) | |
68 | return; | |
69 | ||
70 | hdr = (struct ieee80211_hdr *) buf; | |
71 | fc = le_to_host16(hdr->frame_control); | |
72 | ||
73 | if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { | |
74 | printf("Not ToDS data frame (fc=0x%04x)\n", fc); | |
75 | return; | |
76 | } | |
77 | ||
78 | sa = hdr->addr2; | |
f8b1f695 | 79 | os_memset(&event, 0, sizeof(event)); |
9b90955e JB |
80 | event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); |
81 | event.rx_from_unknown.addr = sa; | |
f8b1f695 | 82 | wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); |
c5121837 JM |
83 | |
84 | pos = (u8 *) (hdr + 1); | |
85 | left = len - sizeof(*hdr); | |
86 | ||
87 | if (left < sizeof(rfc1042_header)) { | |
88 | printf("Too short data frame\n"); | |
89 | return; | |
90 | } | |
91 | ||
92 | if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { | |
93 | printf("Data frame with no RFC1042 header\n"); | |
94 | return; | |
95 | } | |
96 | pos += sizeof(rfc1042_header); | |
97 | left -= sizeof(rfc1042_header); | |
98 | ||
99 | if (left < 2) { | |
100 | printf("No ethertype in data frame\n"); | |
101 | return; | |
102 | } | |
103 | ||
104 | ethertype = WPA_GET_BE16(pos); | |
105 | pos += 2; | |
106 | left -= 2; | |
107 | switch (ethertype) { | |
108 | case ETH_P_PAE: | |
baac6490 | 109 | drv_event_eapol_rx(drv->hapd, sa, pos, left); |
c5121837 JM |
110 | break; |
111 | ||
112 | default: | |
113 | printf("Unknown ethertype 0x%04x in data frame\n", ethertype); | |
114 | break; | |
115 | } | |
116 | } | |
117 | ||
118 | ||
119 | static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, | |
120 | size_t len, int ok) | |
121 | { | |
122 | struct ieee80211_hdr *hdr; | |
f8b1f695 JM |
123 | u16 fc; |
124 | union wpa_event_data event; | |
c5121837 JM |
125 | |
126 | hdr = (struct ieee80211_hdr *) buf; | |
127 | fc = le_to_host16(hdr->frame_control); | |
128 | ||
f8b1f695 JM |
129 | os_memset(&event, 0, sizeof(event)); |
130 | event.tx_status.type = WLAN_FC_GET_TYPE(fc); | |
131 | event.tx_status.stype = WLAN_FC_GET_STYPE(fc); | |
132 | event.tx_status.dst = hdr->addr1; | |
133 | event.tx_status.data = buf; | |
134 | event.tx_status.data_len = len; | |
135 | event.tx_status.ack = ok; | |
136 | wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); | |
c5121837 JM |
137 | } |
138 | ||
139 | ||
140 | static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) | |
141 | { | |
142 | struct ieee80211_hdr *hdr; | |
7420e362 | 143 | u16 fc, type, stype; |
c5121837 JM |
144 | size_t data_len = len; |
145 | int ver; | |
f8b1f695 | 146 | union wpa_event_data event; |
c5121837 JM |
147 | |
148 | /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass | |
149 | * these to user space */ | |
150 | if (len < 24) { | |
151 | wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", | |
152 | (unsigned long) len); | |
153 | return; | |
154 | } | |
155 | ||
156 | hdr = (struct ieee80211_hdr *) buf; | |
157 | fc = le_to_host16(hdr->frame_control); | |
158 | type = WLAN_FC_GET_TYPE(fc); | |
159 | stype = WLAN_FC_GET_STYPE(fc); | |
160 | ||
161 | if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { | |
162 | wpa_hexdump(MSG_MSGDUMP, "Received management frame", | |
163 | buf, len); | |
164 | } | |
165 | ||
166 | ver = fc & WLAN_FC_PVER; | |
167 | ||
7420e362 JM |
168 | /* protocol version 2 is reserved for indicating ACKed frame (TX |
169 | * callbacks), and version 1 for indicating failed frame (no ACK, TX | |
170 | * callbacks) */ | |
171 | if (ver == 1 || ver == 2) { | |
c5121837 JM |
172 | handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); |
173 | return; | |
174 | } else if (ver != 0) { | |
175 | printf("unknown protocol version %d\n", ver); | |
176 | return; | |
177 | } | |
178 | ||
179 | switch (type) { | |
180 | case WLAN_FC_TYPE_MGMT: | |
f8b1f695 JM |
181 | os_memset(&event, 0, sizeof(event)); |
182 | event.rx_mgmt.frame = buf; | |
183 | event.rx_mgmt.frame_len = data_len; | |
184 | wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); | |
c5121837 JM |
185 | break; |
186 | case WLAN_FC_TYPE_CTRL: | |
187 | wpa_printf(MSG_DEBUG, "CTRL"); | |
188 | break; | |
189 | case WLAN_FC_TYPE_DATA: | |
190 | wpa_printf(MSG_DEBUG, "DATA"); | |
191 | handle_data(drv, buf, data_len, stype); | |
192 | break; | |
193 | default: | |
194 | wpa_printf(MSG_DEBUG, "unknown frame type %d", type); | |
195 | break; | |
196 | } | |
197 | } | |
198 | ||
199 | ||
200 | static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) | |
201 | { | |
202 | struct hostap_driver_data *drv = eloop_ctx; | |
203 | int len; | |
204 | unsigned char buf[3000]; | |
205 | ||
206 | len = recv(sock, buf, sizeof(buf), 0); | |
207 | if (len < 0) { | |
a193231d | 208 | wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); |
c5121837 JM |
209 | return; |
210 | } | |
211 | ||
212 | handle_frame(drv, buf, len); | |
213 | } | |
214 | ||
215 | ||
412036f5 | 216 | static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) |
c5121837 JM |
217 | { |
218 | struct ifreq ifr; | |
219 | struct sockaddr_ll addr; | |
220 | ||
221 | drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | |
222 | if (drv->sock < 0) { | |
a193231d JM |
223 | wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", |
224 | strerror(errno)); | |
c5121837 JM |
225 | return -1; |
226 | } | |
227 | ||
228 | if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { | |
a193231d | 229 | wpa_printf(MSG_ERROR, "Could not register read socket"); |
c5121837 JM |
230 | return -1; |
231 | } | |
232 | ||
233 | memset(&ifr, 0, sizeof(ifr)); | |
b2f1e545 JM |
234 | if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", |
235 | drv->iface) >= (int) sizeof(ifr.ifr_name)) { | |
236 | wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); | |
237 | return -1; | |
238 | } | |
c5121837 | 239 | if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { |
a193231d JM |
240 | wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", |
241 | strerror(errno)); | |
c5121837 JM |
242 | return -1; |
243 | } | |
244 | ||
245 | if (hostap_set_iface_flags(drv, 1)) { | |
246 | return -1; | |
247 | } | |
248 | ||
249 | memset(&addr, 0, sizeof(addr)); | |
250 | addr.sll_family = AF_PACKET; | |
251 | addr.sll_ifindex = ifr.ifr_ifindex; | |
252 | wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", | |
253 | addr.sll_ifindex); | |
254 | ||
255 | if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
a193231d | 256 | wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); |
c5121837 JM |
257 | return -1; |
258 | } | |
259 | ||
2ac9688e | 260 | return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); |
c5121837 JM |
261 | } |
262 | ||
263 | ||
5d180a77 | 264 | static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack, |
2d3943ce AO |
265 | unsigned int freq, |
266 | const u16 *csa_offs, size_t csa_offs_len) | |
c5121837 JM |
267 | { |
268 | struct hostap_driver_data *drv = priv; | |
269 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; | |
270 | int res; | |
271 | ||
272 | /* Request TX callback */ | |
273 | hdr->frame_control |= host_to_le16(BIT(1)); | |
83421302 | 274 | res = send(drv->sock, msg, len, 0); |
c5121837 JM |
275 | hdr->frame_control &= ~host_to_le16(BIT(1)); |
276 | ||
277 | return res; | |
278 | } | |
279 | ||
280 | ||
281 | static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, | |
4378fc14 FF |
282 | size_t data_len, int encrypt, const u8 *own_addr, |
283 | u32 flags) | |
c5121837 JM |
284 | { |
285 | struct hostap_driver_data *drv = priv; | |
286 | struct ieee80211_hdr *hdr; | |
287 | size_t len; | |
288 | u8 *pos; | |
289 | int res; | |
290 | ||
291 | len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; | |
292 | hdr = os_zalloc(len); | |
293 | if (hdr == NULL) { | |
294 | printf("malloc() failed for hostapd_send_data(len=%lu)\n", | |
295 | (unsigned long) len); | |
296 | return -1; | |
297 | } | |
298 | ||
299 | hdr->frame_control = | |
300 | IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); | |
301 | hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); | |
302 | if (encrypt) | |
303 | hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); | |
304 | memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); | |
305 | memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); | |
306 | memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); | |
307 | ||
308 | pos = (u8 *) (hdr + 1); | |
309 | memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); | |
310 | pos += sizeof(rfc1042_header); | |
311 | *((u16 *) pos) = htons(ETH_P_PAE); | |
312 | pos += 2; | |
313 | memcpy(pos, data, data_len); | |
314 | ||
2d3943ce | 315 | res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0); |
c5121837 | 316 | if (res < 0) { |
9f324b61 JM |
317 | wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " |
318 | "failed: %d (%s)", | |
319 | (unsigned long) len, errno, strerror(errno)); | |
c5121837 | 320 | } |
f6df3f3a | 321 | os_free(hdr); |
c5121837 JM |
322 | |
323 | return res; | |
324 | } | |
325 | ||
326 | ||
3234cba4 | 327 | static int hostap_sta_set_flags(void *priv, const u8 *addr, |
f97e3ce4 JM |
328 | unsigned int total_flags, unsigned int flags_or, |
329 | unsigned int flags_and) | |
c5121837 JM |
330 | { |
331 | struct hostap_driver_data *drv = priv; | |
332 | struct prism2_hostapd_param param; | |
333 | ||
0de39516 JM |
334 | if (flags_or & WPA_STA_AUTHORIZED) |
335 | flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ | |
336 | if (!(flags_and & WPA_STA_AUTHORIZED)) | |
337 | flags_and = ~BIT(5); | |
338 | else | |
339 | flags_and = ~0; | |
c5121837 JM |
340 | memset(¶m, 0, sizeof(param)); |
341 | param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; | |
342 | memcpy(param.sta_addr, addr, ETH_ALEN); | |
343 | param.u.set_flags_sta.flags_or = flags_or; | |
344 | param.u.set_flags_sta.flags_and = flags_and; | |
345 | return hostapd_ioctl(drv, ¶m, sizeof(param)); | |
346 | } | |
347 | ||
348 | ||
349 | static int hostap_set_iface_flags(void *priv, int dev_up) | |
350 | { | |
351 | struct hostap_driver_data *drv = priv; | |
352 | struct ifreq ifr; | |
34f2f814 | 353 | char ifname[IFNAMSIZ]; |
c5121837 | 354 | |
b2f1e545 JM |
355 | if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) { |
356 | wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); | |
357 | return -1; | |
358 | } | |
34f2f814 | 359 | if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) |
c5121837 JM |
360 | return -1; |
361 | ||
c5121837 JM |
362 | if (dev_up) { |
363 | memset(&ifr, 0, sizeof(ifr)); | |
34f2f814 | 364 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); |
c5121837 JM |
365 | ifr.ifr_mtu = HOSTAPD_MTU; |
366 | if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { | |
a193231d JM |
367 | wpa_printf(MSG_INFO, |
368 | "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s", | |
369 | strerror(errno)); | |
c5121837 JM |
370 | } |
371 | } | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | ||
377 | static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, | |
378 | int len) | |
379 | { | |
380 | struct hostap_driver_data *drv = priv; | |
381 | struct iwreq iwr; | |
382 | ||
383 | memset(&iwr, 0, sizeof(iwr)); | |
384 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
385 | iwr.u.data.pointer = (caddr_t) param; | |
386 | iwr.u.data.length = len; | |
387 | ||
388 | if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { | |
a193231d JM |
389 | wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s", |
390 | strerror(errno)); | |
c5121837 JM |
391 | return -1; |
392 | } | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | ||
642187d6 | 398 | static int wpa_driver_hostap_set_key(const char *ifname, void *priv, |
71934751 JM |
399 | enum wpa_alg alg, const u8 *addr, |
400 | int key_idx, int set_tx, | |
401 | const u8 *seq, size_t seq_len, | |
642187d6 | 402 | const u8 *key, size_t key_len) |
c5121837 JM |
403 | { |
404 | struct hostap_driver_data *drv = priv; | |
405 | struct prism2_hostapd_param *param; | |
406 | u8 *buf; | |
407 | size_t blen; | |
408 | int ret = 0; | |
409 | ||
410 | blen = sizeof(*param) + key_len; | |
411 | buf = os_zalloc(blen); | |
412 | if (buf == NULL) | |
413 | return -1; | |
414 | ||
415 | param = (struct prism2_hostapd_param *) buf; | |
416 | param->cmd = PRISM2_SET_ENCRYPTION; | |
417 | if (addr == NULL) | |
418 | memset(param->sta_addr, 0xff, ETH_ALEN); | |
419 | else | |
420 | memcpy(param->sta_addr, addr, ETH_ALEN); | |
421 | switch (alg) { | |
422 | case WPA_ALG_NONE: | |
423 | os_strlcpy((char *) param->u.crypt.alg, "NONE", | |
424 | HOSTAP_CRYPT_ALG_NAME_LEN); | |
425 | break; | |
426 | case WPA_ALG_WEP: | |
427 | os_strlcpy((char *) param->u.crypt.alg, "WEP", | |
428 | HOSTAP_CRYPT_ALG_NAME_LEN); | |
429 | break; | |
430 | case WPA_ALG_TKIP: | |
431 | os_strlcpy((char *) param->u.crypt.alg, "TKIP", | |
432 | HOSTAP_CRYPT_ALG_NAME_LEN); | |
433 | break; | |
434 | case WPA_ALG_CCMP: | |
435 | os_strlcpy((char *) param->u.crypt.alg, "CCMP", | |
436 | HOSTAP_CRYPT_ALG_NAME_LEN); | |
437 | break; | |
438 | default: | |
439 | os_free(buf); | |
440 | return -1; | |
441 | } | |
442 | param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; | |
443 | param->u.crypt.idx = key_idx; | |
444 | param->u.crypt.key_len = key_len; | |
445 | memcpy((u8 *) (param + 1), key, key_len); | |
446 | ||
447 | if (hostapd_ioctl(drv, param, blen)) { | |
448 | printf("Failed to set encryption.\n"); | |
449 | ret = -1; | |
450 | } | |
451 | free(buf); | |
452 | ||
453 | return ret; | |
454 | } | |
455 | ||
456 | ||
457 | static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, | |
458 | int idx, u8 *seq) | |
459 | { | |
460 | struct hostap_driver_data *drv = priv; | |
461 | struct prism2_hostapd_param *param; | |
462 | u8 *buf; | |
463 | size_t blen; | |
464 | int ret = 0; | |
465 | ||
466 | blen = sizeof(*param) + 32; | |
467 | buf = os_zalloc(blen); | |
468 | if (buf == NULL) | |
469 | return -1; | |
470 | ||
471 | param = (struct prism2_hostapd_param *) buf; | |
472 | param->cmd = PRISM2_GET_ENCRYPTION; | |
473 | if (addr == NULL) | |
f6df3f3a | 474 | os_memset(param->sta_addr, 0xff, ETH_ALEN); |
c5121837 | 475 | else |
f6df3f3a | 476 | os_memcpy(param->sta_addr, addr, ETH_ALEN); |
c5121837 JM |
477 | param->u.crypt.idx = idx; |
478 | ||
479 | if (hostapd_ioctl(drv, param, blen)) { | |
480 | printf("Failed to get encryption.\n"); | |
481 | ret = -1; | |
482 | } else { | |
f6df3f3a | 483 | os_memcpy(seq, param->u.crypt.seq, 8); |
c5121837 | 484 | } |
f6df3f3a | 485 | os_free(buf); |
c5121837 JM |
486 | |
487 | return ret; | |
488 | } | |
489 | ||
490 | ||
491 | static int hostap_ioctl_prism2param(void *priv, int param, int value) | |
492 | { | |
493 | struct hostap_driver_data *drv = priv; | |
494 | struct iwreq iwr; | |
495 | int *i; | |
496 | ||
497 | memset(&iwr, 0, sizeof(iwr)); | |
498 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
499 | i = (int *) iwr.u.name; | |
500 | *i++ = param; | |
501 | *i++ = value; | |
502 | ||
503 | if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { | |
a193231d JM |
504 | wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s", |
505 | strerror(errno)); | |
c5121837 JM |
506 | return -1; |
507 | } | |
508 | ||
509 | return 0; | |
510 | } | |
511 | ||
512 | ||
e3bd3912 | 513 | static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) |
c5121837 JM |
514 | { |
515 | struct hostap_driver_data *drv = priv; | |
e3bd3912 | 516 | int enabled = params->enabled; |
c5121837 JM |
517 | |
518 | /* enable kernel driver support for IEEE 802.1X */ | |
519 | if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { | |
520 | printf("Could not setup IEEE 802.1X support in kernel driver." | |
521 | "\n"); | |
522 | return -1; | |
523 | } | |
524 | ||
525 | if (!enabled) | |
526 | return 0; | |
527 | ||
528 | /* use host driver implementation of encryption to allow | |
529 | * individual keys and passing plaintext EAPOL frames */ | |
530 | if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || | |
531 | hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { | |
532 | printf("Could not setup host-based encryption in kernel " | |
533 | "driver.\n"); | |
534 | return -1; | |
535 | } | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | ||
d5dd016a | 541 | static int hostap_set_privacy(void *priv, int enabled) |
c5121837 JM |
542 | { |
543 | struct hostap_drvier_data *drv = priv; | |
544 | ||
545 | return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, | |
546 | enabled); | |
547 | } | |
548 | ||
549 | ||
8709de1a | 550 | static int hostap_set_ssid(void *priv, const u8 *buf, int len) |
c5121837 JM |
551 | { |
552 | struct hostap_driver_data *drv = priv; | |
553 | struct iwreq iwr; | |
554 | ||
555 | memset(&iwr, 0, sizeof(iwr)); | |
556 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
557 | iwr.u.essid.flags = 1; /* SSID active */ | |
558 | iwr.u.essid.pointer = (caddr_t) buf; | |
559 | iwr.u.essid.length = len + 1; | |
560 | ||
561 | if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { | |
a193231d JM |
562 | wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", |
563 | len, strerror(errno)); | |
c5121837 JM |
564 | return -1; |
565 | } | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | ||
571 | static int hostap_flush(void *priv) | |
572 | { | |
573 | struct hostap_driver_data *drv = priv; | |
574 | struct prism2_hostapd_param param; | |
575 | ||
576 | memset(¶m, 0, sizeof(param)); | |
577 | param.cmd = PRISM2_HOSTAPD_FLUSH; | |
578 | return hostapd_ioctl(drv, ¶m, sizeof(param)); | |
579 | } | |
580 | ||
581 | ||
582 | static int hostap_read_sta_data(void *priv, | |
583 | struct hostap_sta_driver_data *data, | |
584 | const u8 *addr) | |
585 | { | |
586 | struct hostap_driver_data *drv = priv; | |
587 | char buf[1024], line[128], *pos; | |
588 | FILE *f; | |
589 | unsigned long val; | |
590 | ||
591 | memset(data, 0, sizeof(*data)); | |
592 | snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, | |
593 | drv->iface, MAC2STR(addr)); | |
594 | ||
595 | f = fopen(buf, "r"); | |
596 | if (!f) | |
597 | return -1; | |
598 | /* Need to read proc file with in one piece, so use large enough | |
599 | * buffer. */ | |
600 | setbuffer(f, buf, sizeof(buf)); | |
601 | ||
602 | while (fgets(line, sizeof(line), f)) { | |
603 | pos = strchr(line, '='); | |
604 | if (!pos) | |
605 | continue; | |
606 | *pos++ = '\0'; | |
607 | val = strtoul(pos, NULL, 10); | |
608 | if (strcmp(line, "rx_packets") == 0) | |
609 | data->rx_packets = val; | |
610 | else if (strcmp(line, "tx_packets") == 0) | |
611 | data->tx_packets = val; | |
612 | else if (strcmp(line, "rx_bytes") == 0) | |
613 | data->rx_bytes = val; | |
614 | else if (strcmp(line, "tx_bytes") == 0) | |
615 | data->tx_bytes = val; | |
616 | } | |
617 | ||
618 | fclose(f); | |
619 | ||
620 | return 0; | |
621 | } | |
622 | ||
623 | ||
62847751 | 624 | static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) |
c5121837 JM |
625 | { |
626 | struct hostap_driver_data *drv = priv; | |
627 | struct prism2_hostapd_param param; | |
628 | int tx_supp_rates = 0; | |
629 | size_t i; | |
630 | ||
631 | #define WLAN_RATE_1M BIT(0) | |
632 | #define WLAN_RATE_2M BIT(1) | |
633 | #define WLAN_RATE_5M5 BIT(2) | |
634 | #define WLAN_RATE_11M BIT(3) | |
635 | ||
636 | for (i = 0; i < params->supp_rates_len; i++) { | |
637 | if ((params->supp_rates[i] & 0x7f) == 2) | |
638 | tx_supp_rates |= WLAN_RATE_1M; | |
639 | if ((params->supp_rates[i] & 0x7f) == 4) | |
640 | tx_supp_rates |= WLAN_RATE_2M; | |
641 | if ((params->supp_rates[i] & 0x7f) == 11) | |
642 | tx_supp_rates |= WLAN_RATE_5M5; | |
643 | if ((params->supp_rates[i] & 0x7f) == 22) | |
644 | tx_supp_rates |= WLAN_RATE_11M; | |
645 | } | |
646 | ||
647 | memset(¶m, 0, sizeof(param)); | |
648 | param.cmd = PRISM2_HOSTAPD_ADD_STA; | |
649 | memcpy(param.sta_addr, params->addr, ETH_ALEN); | |
650 | param.u.add_sta.aid = params->aid; | |
651 | param.u.add_sta.capability = params->capability; | |
652 | param.u.add_sta.tx_supp_rates = tx_supp_rates; | |
653 | return hostapd_ioctl(drv, ¶m, sizeof(param)); | |
654 | } | |
655 | ||
656 | ||
657 | static int hostap_sta_remove(void *priv, const u8 *addr) | |
658 | { | |
659 | struct hostap_driver_data *drv = priv; | |
660 | struct prism2_hostapd_param param; | |
661 | ||
3234cba4 | 662 | hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); |
c5121837 JM |
663 | |
664 | memset(¶m, 0, sizeof(param)); | |
665 | param.cmd = PRISM2_HOSTAPD_REMOVE_STA; | |
666 | memcpy(param.sta_addr, addr, ETH_ALEN); | |
667 | if (hostapd_ioctl(drv, ¶m, sizeof(param))) { | |
668 | printf("Could not remove station from kernel driver.\n"); | |
669 | return -1; | |
670 | } | |
671 | return 0; | |
672 | } | |
673 | ||
674 | ||
675 | static int hostap_get_inact_sec(void *priv, const u8 *addr) | |
676 | { | |
677 | struct hostap_driver_data *drv = priv; | |
678 | struct prism2_hostapd_param param; | |
679 | ||
680 | memset(¶m, 0, sizeof(param)); | |
681 | param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; | |
682 | memcpy(param.sta_addr, addr, ETH_ALEN); | |
683 | if (hostapd_ioctl(drv, ¶m, sizeof(param))) { | |
684 | return -1; | |
685 | } | |
686 | ||
687 | return param.u.get_info_sta.inactive_sec; | |
688 | } | |
689 | ||
690 | ||
691 | static int hostap_sta_clear_stats(void *priv, const u8 *addr) | |
692 | { | |
693 | struct hostap_driver_data *drv = priv; | |
694 | struct prism2_hostapd_param param; | |
695 | ||
696 | memset(¶m, 0, sizeof(param)); | |
697 | param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; | |
698 | memcpy(param.sta_addr, addr, ETH_ALEN); | |
699 | if (hostapd_ioctl(drv, ¶m, sizeof(param))) { | |
700 | return -1; | |
701 | } | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
706 | ||
707 | static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) | |
708 | { | |
709 | struct prism2_hostapd_param *param; | |
710 | int res; | |
711 | size_t blen, elem_len; | |
712 | ||
713 | elem_len = drv->generic_ie_len + drv->wps_ie_len; | |
714 | blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; | |
715 | if (blen < sizeof(*param)) | |
716 | blen = sizeof(*param); | |
717 | ||
718 | param = os_zalloc(blen); | |
719 | if (param == NULL) | |
720 | return -1; | |
721 | ||
722 | param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; | |
723 | param->u.generic_elem.len = elem_len; | |
724 | if (drv->generic_ie) { | |
725 | os_memcpy(param->u.generic_elem.data, drv->generic_ie, | |
726 | drv->generic_ie_len); | |
727 | } | |
728 | if (drv->wps_ie) { | |
729 | os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], | |
730 | drv->wps_ie, drv->wps_ie_len); | |
731 | } | |
732 | wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", | |
733 | param->u.generic_elem.data, elem_len); | |
734 | res = hostapd_ioctl(drv, param, blen); | |
735 | ||
736 | os_free(param); | |
737 | ||
738 | return res; | |
739 | } | |
740 | ||
741 | ||
aa484516 | 742 | static int hostap_set_generic_elem(void *priv, |
c5121837 JM |
743 | const u8 *elem, size_t elem_len) |
744 | { | |
745 | struct hostap_driver_data *drv = priv; | |
746 | ||
747 | os_free(drv->generic_ie); | |
748 | drv->generic_ie = NULL; | |
749 | drv->generic_ie_len = 0; | |
750 | if (elem) { | |
a1f11e34 | 751 | drv->generic_ie = os_memdup(elem, elem_len); |
c5121837 JM |
752 | if (drv->generic_ie == NULL) |
753 | return -1; | |
c5121837 JM |
754 | drv->generic_ie_len = elem_len; |
755 | } | |
756 | ||
757 | return hostapd_ioctl_set_generic_elem(drv); | |
758 | } | |
759 | ||
760 | ||
0ebdf627 | 761 | static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, |
0e2e565a JM |
762 | const struct wpabuf *proberesp, |
763 | const struct wpabuf *assocresp) | |
c5121837 JM |
764 | { |
765 | struct hostap_driver_data *drv = priv; | |
766 | ||
14f79386 JM |
767 | /* |
768 | * Host AP driver supports only one set of extra IEs, so we need to | |
769 | * use the Probe Response IEs also for Beacon frames since they include | |
770 | * more information. | |
771 | */ | |
772 | ||
c5121837 JM |
773 | os_free(drv->wps_ie); |
774 | drv->wps_ie = NULL; | |
775 | drv->wps_ie_len = 0; | |
14f79386 | 776 | if (proberesp) { |
a1f11e34 JB |
777 | drv->wps_ie = os_memdup(wpabuf_head(proberesp), |
778 | wpabuf_len(proberesp)); | |
c5121837 JM |
779 | if (drv->wps_ie == NULL) |
780 | return -1; | |
14f79386 | 781 | drv->wps_ie_len = wpabuf_len(proberesp); |
c5121837 JM |
782 | } |
783 | ||
784 | return hostapd_ioctl_set_generic_elem(drv); | |
785 | } | |
786 | ||
787 | ||
788 | static void | |
789 | hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, | |
790 | char *custom) | |
791 | { | |
792 | wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); | |
793 | ||
794 | if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { | |
795 | char *pos; | |
796 | u8 addr[ETH_ALEN]; | |
797 | pos = strstr(custom, "addr="); | |
798 | if (pos == NULL) { | |
799 | wpa_printf(MSG_DEBUG, | |
800 | "MLME-MICHAELMICFAILURE.indication " | |
801 | "without sender address ignored"); | |
802 | return; | |
803 | } | |
804 | pos += 5; | |
805 | if (hwaddr_aton(pos, addr) == 0) { | |
1cd973d5 JM |
806 | union wpa_event_data data; |
807 | os_memset(&data, 0, sizeof(data)); | |
808 | data.michael_mic_failure.unicast = 1; | |
809 | data.michael_mic_failure.src = addr; | |
810 | wpa_supplicant_event(drv->hapd, | |
811 | EVENT_MICHAEL_MIC_FAILURE, &data); | |
c5121837 JM |
812 | } else { |
813 | wpa_printf(MSG_DEBUG, | |
814 | "MLME-MICHAELMICFAILURE.indication " | |
815 | "with invalid MAC address"); | |
816 | } | |
817 | } | |
818 | } | |
819 | ||
820 | ||
821 | static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, | |
d717126a | 822 | char *data, unsigned int len) |
c5121837 JM |
823 | { |
824 | struct iw_event iwe_buf, *iwe = &iwe_buf; | |
825 | char *pos, *end, *custom, *buf; | |
826 | ||
827 | pos = data; | |
828 | end = data + len; | |
829 | ||
d717126a | 830 | while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { |
c5121837 JM |
831 | /* Event data may be unaligned, so make a local, aligned copy |
832 | * before processing. */ | |
833 | memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); | |
834 | wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", | |
835 | iwe->cmd, iwe->len); | |
d717126a | 836 | if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) |
c5121837 JM |
837 | return; |
838 | ||
839 | custom = pos + IW_EV_POINT_LEN; | |
840 | if (drv->we_version > 18 && | |
841 | (iwe->cmd == IWEVMICHAELMICFAILURE || | |
842 | iwe->cmd == IWEVCUSTOM)) { | |
843 | /* WE-19 removed the pointer from struct iw_point */ | |
844 | char *dpos = (char *) &iwe_buf.u.data.length; | |
845 | int dlen = dpos - (char *) &iwe_buf; | |
846 | memcpy(dpos, pos + IW_EV_LCP_LEN, | |
847 | sizeof(struct iw_event) - dlen); | |
848 | } else { | |
849 | memcpy(&iwe_buf, pos, sizeof(struct iw_event)); | |
850 | custom += IW_EV_POINT_OFF; | |
851 | } | |
852 | ||
853 | switch (iwe->cmd) { | |
854 | case IWEVCUSTOM: | |
d717126a | 855 | if (iwe->u.data.length > end - custom) |
c5121837 JM |
856 | return; |
857 | buf = malloc(iwe->u.data.length + 1); | |
858 | if (buf == NULL) | |
859 | return; | |
860 | memcpy(buf, custom, iwe->u.data.length); | |
861 | buf[iwe->u.data.length] = '\0'; | |
862 | hostapd_wireless_event_wireless_custom(drv, buf); | |
863 | free(buf); | |
864 | break; | |
865 | } | |
866 | ||
867 | pos += iwe->len; | |
868 | } | |
869 | } | |
870 | ||
871 | ||
a6fc4f3c JM |
872 | static void hostapd_wireless_event_rtm_newlink(void *ctx, |
873 | struct ifinfomsg *ifi, | |
874 | u8 *buf, size_t len) | |
c5121837 | 875 | { |
a6fc4f3c JM |
876 | struct hostap_driver_data *drv = ctx; |
877 | int attrlen, rta_len; | |
878 | struct rtattr *attr; | |
c5121837 JM |
879 | |
880 | /* TODO: use ifi->ifi_index to filter out wireless events from other | |
881 | * interfaces */ | |
882 | ||
a6fc4f3c JM |
883 | attrlen = len; |
884 | attr = (struct rtattr *) buf; | |
c5121837 JM |
885 | |
886 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
887 | while (RTA_OK(attr, attrlen)) { | |
888 | if (attr->rta_type == IFLA_WIRELESS) { | |
889 | hostapd_wireless_event_wireless( | |
890 | drv, ((char *) attr) + rta_len, | |
891 | attr->rta_len - rta_len); | |
892 | } | |
893 | attr = RTA_NEXT(attr, attrlen); | |
894 | } | |
895 | } | |
896 | ||
897 | ||
c5121837 JM |
898 | static int hostap_get_we_version(struct hostap_driver_data *drv) |
899 | { | |
900 | struct iw_range *range; | |
901 | struct iwreq iwr; | |
902 | int minlen; | |
903 | size_t buflen; | |
904 | ||
905 | drv->we_version = 0; | |
906 | ||
907 | /* | |
908 | * Use larger buffer than struct iw_range in order to allow the | |
909 | * structure to grow in the future. | |
910 | */ | |
911 | buflen = sizeof(struct iw_range) + 500; | |
912 | range = os_zalloc(buflen); | |
913 | if (range == NULL) | |
914 | return -1; | |
915 | ||
916 | memset(&iwr, 0, sizeof(iwr)); | |
917 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
918 | iwr.u.data.pointer = (caddr_t) range; | |
919 | iwr.u.data.length = buflen; | |
920 | ||
921 | minlen = ((char *) &range->enc_capa) - (char *) range + | |
922 | sizeof(range->enc_capa); | |
923 | ||
924 | if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { | |
a193231d JM |
925 | wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", |
926 | strerror(errno)); | |
927 | os_free(range); | |
c5121837 JM |
928 | return -1; |
929 | } else if (iwr.u.data.length >= minlen && | |
930 | range->we_version_compiled >= 18) { | |
931 | wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " | |
932 | "WE(source)=%d enc_capa=0x%x", | |
933 | range->we_version_compiled, | |
934 | range->we_version_source, | |
935 | range->enc_capa); | |
936 | drv->we_version = range->we_version_compiled; | |
937 | } | |
938 | ||
939 | free(range); | |
940 | return 0; | |
941 | } | |
942 | ||
943 | ||
944 | static int hostap_wireless_event_init(struct hostap_driver_data *drv) | |
945 | { | |
a6fc4f3c | 946 | struct netlink_config *cfg; |
c5121837 JM |
947 | |
948 | hostap_get_we_version(drv); | |
949 | ||
a6fc4f3c JM |
950 | cfg = os_zalloc(sizeof(*cfg)); |
951 | if (cfg == NULL) | |
c5121837 | 952 | return -1; |
a6fc4f3c JM |
953 | cfg->ctx = drv; |
954 | cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; | |
955 | drv->netlink = netlink_init(cfg); | |
956 | if (drv->netlink == NULL) { | |
957 | os_free(cfg); | |
c5121837 JM |
958 | return -1; |
959 | } | |
960 | ||
c5121837 JM |
961 | return 0; |
962 | } | |
963 | ||
964 | ||
92f475b4 JM |
965 | static void * hostap_init(struct hostapd_data *hapd, |
966 | struct wpa_init_params *params) | |
c5121837 JM |
967 | { |
968 | struct hostap_driver_data *drv; | |
969 | ||
970 | drv = os_zalloc(sizeof(struct hostap_driver_data)); | |
971 | if (drv == NULL) { | |
972 | printf("Could not allocate memory for hostapd driver data\n"); | |
973 | return NULL; | |
974 | } | |
975 | ||
976 | drv->hapd = hapd; | |
977 | drv->ioctl_sock = drv->sock = -1; | |
92f475b4 | 978 | memcpy(drv->iface, params->ifname, sizeof(drv->iface)); |
c5121837 JM |
979 | |
980 | drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); | |
981 | if (drv->ioctl_sock < 0) { | |
a193231d JM |
982 | wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", |
983 | strerror(errno)); | |
984 | os_free(drv); | |
c5121837 JM |
985 | return NULL; |
986 | } | |
987 | ||
988 | if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { | |
a193231d JM |
989 | wpa_printf(MSG_ERROR, |
990 | "Could not enable hostapd mode for interface %s", | |
991 | drv->iface); | |
c5121837 | 992 | close(drv->ioctl_sock); |
a193231d | 993 | os_free(drv); |
c5121837 JM |
994 | return NULL; |
995 | } | |
996 | ||
412036f5 JM |
997 | if (hostap_init_sockets(drv, params->own_addr) || |
998 | hostap_wireless_event_init(drv)) { | |
c5121837 | 999 | close(drv->ioctl_sock); |
a193231d | 1000 | os_free(drv); |
c5121837 JM |
1001 | return NULL; |
1002 | } | |
1003 | ||
1004 | return drv; | |
1005 | } | |
1006 | ||
1007 | ||
1008 | static void hostap_driver_deinit(void *priv) | |
1009 | { | |
1010 | struct hostap_driver_data *drv = priv; | |
1011 | ||
a6fc4f3c | 1012 | netlink_deinit(drv->netlink); |
c5121837 JM |
1013 | (void) hostap_set_iface_flags(drv, 0); |
1014 | (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); | |
1015 | (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); | |
1016 | ||
1017 | if (drv->ioctl_sock >= 0) | |
1018 | close(drv->ioctl_sock); | |
1019 | ||
1020 | if (drv->sock >= 0) | |
1021 | close(drv->sock); | |
1022 | ||
1023 | os_free(drv->generic_ie); | |
1024 | os_free(drv->wps_ie); | |
1025 | ||
1026 | free(drv); | |
1027 | } | |
1028 | ||
1029 | ||
731723a5 | 1030 | static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, |
4be17ffb | 1031 | u16 reason) |
c5121837 JM |
1032 | { |
1033 | struct hostap_driver_data *drv = priv; | |
1034 | struct ieee80211_mgmt mgmt; | |
1035 | ||
4d379f12 JM |
1036 | if (is_broadcast_ether_addr(addr)) { |
1037 | /* | |
1038 | * New Prism2.5/3 STA firmware versions seem to have issues | |
1039 | * with this broadcast deauth frame. This gets the firmware in | |
1040 | * odd state where nothing works correctly, so let's skip | |
1041 | * sending this for the hostap driver. | |
1042 | */ | |
1043 | return 0; | |
1044 | } | |
1045 | ||
c5121837 JM |
1046 | memset(&mgmt, 0, sizeof(mgmt)); |
1047 | mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
1048 | WLAN_FC_STYPE_DEAUTH); | |
1049 | memcpy(mgmt.da, addr, ETH_ALEN); | |
731723a5 JM |
1050 | memcpy(mgmt.sa, own_addr, ETH_ALEN); |
1051 | memcpy(mgmt.bssid, own_addr, ETH_ALEN); | |
c5121837 | 1052 | mgmt.u.deauth.reason_code = host_to_le16(reason); |
9f324b61 | 1053 | return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + |
2d3943ce | 1054 | sizeof(mgmt.u.deauth), 0, 0, NULL, 0); |
c5121837 JM |
1055 | } |
1056 | ||
1057 | ||
f11634bf PR |
1058 | static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) |
1059 | { | |
1060 | struct hostap_driver_data *drv = priv; | |
1061 | struct iwreq iwr; | |
1062 | ||
1063 | os_memset(&iwr, 0, sizeof(iwr)); | |
1064 | os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); | |
1065 | iwr.u.freq.m = freq->channel; | |
1066 | iwr.u.freq.e = 0; | |
1067 | ||
1068 | if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { | |
a193231d JM |
1069 | wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", |
1070 | strerror(errno)); | |
f11634bf PR |
1071 | return -1; |
1072 | } | |
1073 | ||
1074 | return 0; | |
1075 | } | |
1076 | ||
1077 | ||
731723a5 | 1078 | static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, |
4be17ffb | 1079 | u16 reason) |
c5121837 JM |
1080 | { |
1081 | struct hostap_driver_data *drv = priv; | |
1082 | struct ieee80211_mgmt mgmt; | |
1083 | ||
1084 | memset(&mgmt, 0, sizeof(mgmt)); | |
1085 | mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
1086 | WLAN_FC_STYPE_DISASSOC); | |
1087 | memcpy(mgmt.da, addr, ETH_ALEN); | |
731723a5 JM |
1088 | memcpy(mgmt.sa, own_addr, ETH_ALEN); |
1089 | memcpy(mgmt.bssid, own_addr, ETH_ALEN); | |
c5121837 | 1090 | mgmt.u.disassoc.reason_code = host_to_le16(reason); |
9f324b61 | 1091 | return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + |
2d3943ce | 1092 | sizeof(mgmt.u.disassoc), 0, 0, NULL, 0); |
c5121837 JM |
1093 | } |
1094 | ||
1095 | ||
1096 | static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, | |
1097 | u16 *num_modes, | |
aa56e36d | 1098 | u16 *flags, u8 *dfs) |
c5121837 JM |
1099 | { |
1100 | struct hostapd_hw_modes *mode; | |
1101 | int i, clen, rlen; | |
1102 | const short chan2freq[14] = { | |
1103 | 2412, 2417, 2422, 2427, 2432, 2437, 2442, | |
1104 | 2447, 2452, 2457, 2462, 2467, 2472, 2484 | |
1105 | }; | |
1106 | ||
1107 | mode = os_zalloc(sizeof(struct hostapd_hw_modes)); | |
1108 | if (mode == NULL) | |
1109 | return NULL; | |
1110 | ||
1111 | *num_modes = 1; | |
1112 | *flags = 0; | |
aa56e36d | 1113 | *dfs = 0; |
c5121837 JM |
1114 | |
1115 | mode->mode = HOSTAPD_MODE_IEEE80211B; | |
1116 | mode->num_channels = 14; | |
1117 | mode->num_rates = 4; | |
1118 | ||
1119 | clen = mode->num_channels * sizeof(struct hostapd_channel_data); | |
fb7842aa | 1120 | rlen = mode->num_rates * sizeof(int); |
c5121837 JM |
1121 | |
1122 | mode->channels = os_zalloc(clen); | |
1123 | mode->rates = os_zalloc(rlen); | |
1124 | if (mode->channels == NULL || mode->rates == NULL) { | |
67470d51 JM |
1125 | os_free(mode->channels); |
1126 | os_free(mode->rates); | |
1127 | os_free(mode); | |
c5121837 JM |
1128 | return NULL; |
1129 | } | |
1130 | ||
1131 | for (i = 0; i < 14; i++) { | |
1132 | mode->channels[i].chan = i + 1; | |
1133 | mode->channels[i].freq = chan2freq[i]; | |
4299ad82 | 1134 | mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20; |
c5121837 JM |
1135 | /* TODO: Get allowed channel list from the driver */ |
1136 | if (i >= 11) | |
1137 | mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; | |
1138 | } | |
1139 | ||
fb7842aa JM |
1140 | mode->rates[0] = 10; |
1141 | mode->rates[1] = 20; | |
1142 | mode->rates[2] = 55; | |
1143 | mode->rates[3] = 110; | |
c5121837 JM |
1144 | |
1145 | return mode; | |
1146 | } | |
1147 | ||
bcf24348 JB |
1148 | |
1149 | static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, | |
1150 | const u8 *addr, int qos) | |
1151 | { | |
1152 | struct ieee80211_hdr hdr; | |
1153 | ||
1154 | os_memset(&hdr, 0, sizeof(hdr)); | |
1155 | ||
1156 | /* | |
1157 | * WLAN_FC_STYPE_NULLFUNC would be more appropriate, | |
1158 | * but it is apparently not retried so TX Exc events | |
1159 | * are not received for it. | |
1160 | * This is the reason the driver overrides the default | |
1161 | * handling. | |
1162 | */ | |
1163 | hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, | |
1164 | WLAN_FC_STYPE_DATA); | |
1165 | ||
1166 | hdr.frame_control |= | |
1167 | host_to_le16(WLAN_FC_FROMDS); | |
1168 | os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); | |
1169 | os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); | |
1170 | os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); | |
1171 | ||
2d3943ce | 1172 | hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0); |
bcf24348 JB |
1173 | } |
1174 | ||
6fc6879b JM |
1175 | |
1176 | const struct wpa_driver_ops wpa_driver_hostap_ops = { | |
1177 | .name = "hostap", | |
1178 | .desc = "Host AP driver (Intersil Prism2/2.5/3)", | |
642187d6 | 1179 | .set_key = wpa_driver_hostap_set_key, |
c5121837 JM |
1180 | .hapd_init = hostap_init, |
1181 | .hapd_deinit = hostap_driver_deinit, | |
1182 | .set_ieee8021x = hostap_set_ieee8021x, | |
1183 | .set_privacy = hostap_set_privacy, | |
c5121837 JM |
1184 | .get_seqnum = hostap_get_seqnum, |
1185 | .flush = hostap_flush, | |
1186 | .set_generic_elem = hostap_set_generic_elem, | |
1187 | .read_sta_data = hostap_read_sta_data, | |
1188 | .hapd_send_eapol = hostap_send_eapol, | |
1189 | .sta_set_flags = hostap_sta_set_flags, | |
1190 | .sta_deauth = hostap_sta_deauth, | |
1191 | .sta_disassoc = hostap_sta_disassoc, | |
1192 | .sta_remove = hostap_sta_remove, | |
1193 | .hapd_set_ssid = hostap_set_ssid, | |
9f324b61 | 1194 | .send_mlme = hostap_send_mlme, |
c5121837 JM |
1195 | .sta_add = hostap_sta_add, |
1196 | .get_inact_sec = hostap_get_inact_sec, | |
1197 | .sta_clear_stats = hostap_sta_clear_stats, | |
1198 | .get_hw_feature_data = hostap_get_hw_feature_data, | |
14f79386 | 1199 | .set_ap_wps_ie = hostap_set_ap_wps_ie, |
f11634bf | 1200 | .set_freq = hostap_set_freq, |
bcf24348 | 1201 | .poll_client = wpa_driver_hostap_poll_client, |
6fc6879b | 1202 | }; |