]>
Commit | Line | Data |
---|---|---|
3f5285e8 JM |
1 | /* |
2 | * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 | |
3 | * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | */ | |
14 | ||
15 | #include "includes.h" | |
16 | #include <sys/ioctl.h> | |
17 | #include <net/if_arp.h> | |
18 | #include <netlink/genl/genl.h> | |
19 | #include <netlink/genl/family.h> | |
20 | #include <netlink/genl/ctrl.h> | |
7e45830a | 21 | #include "nl80211_copy.h" |
1c873584 JM |
22 | #ifdef CONFIG_CLIENT_MLME |
23 | #include <netpacket/packet.h> | |
24 | #include <linux/if_ether.h> | |
25 | #include "radiotap.h" | |
26 | #include "radiotap_iter.h" | |
27 | #endif /* CONFIG_CLIENT_MLME */ | |
3f5285e8 JM |
28 | |
29 | #include "wireless_copy.h" | |
30 | #include "common.h" | |
31 | #include "driver.h" | |
32 | #include "eloop.h" | |
33 | #include "ieee802_11_defs.h" | |
34 | ||
35 | #ifndef IFF_LOWER_UP | |
36 | #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ | |
37 | #endif | |
38 | #ifndef IFF_DORMANT | |
39 | #define IFF_DORMANT 0x20000 /* driver signals dormant */ | |
40 | #endif | |
41 | ||
42 | #ifndef IF_OPER_DORMANT | |
43 | #define IF_OPER_DORMANT 5 | |
44 | #endif | |
45 | #ifndef IF_OPER_UP | |
46 | #define IF_OPER_UP 6 | |
47 | #endif | |
48 | ||
49 | ||
50 | struct wpa_driver_nl80211_data { | |
51 | void *ctx; | |
97865538 | 52 | int wext_event_sock; |
3f5285e8 JM |
53 | int ioctl_sock; |
54 | char ifname[IFNAMSIZ + 1]; | |
55 | int ifindex; | |
7524cfb1 | 56 | int if_removed; |
3f5285e8 JM |
57 | u8 *assoc_req_ies; |
58 | size_t assoc_req_ies_len; | |
59 | u8 *assoc_resp_ies; | |
60 | size_t assoc_resp_ies_len; | |
61 | struct wpa_driver_capa capa; | |
62 | int has_capability; | |
63 | int we_version_compiled; | |
64 | ||
65 | /* for set_auth_alg fallback */ | |
66 | int use_crypt; | |
67 | int auth_alg_fallback; | |
68 | ||
69 | int operstate; | |
70 | ||
71 | char mlmedev[IFNAMSIZ + 1]; | |
72 | ||
73 | int scan_complete_events; | |
74 | ||
75 | struct nl_handle *nl_handle; | |
76 | struct nl_cache *nl_cache; | |
77 | struct nl_cb *nl_cb; | |
78 | struct genl_family *nl80211; | |
1c873584 JM |
79 | |
80 | #ifdef CONFIG_CLIENT_MLME | |
81 | int monitor_sock; /* socket for monitor */ | |
82 | int monitor_ifidx; | |
83 | #endif /* CONFIG_CLIENT_MLME */ | |
3f5285e8 JM |
84 | }; |
85 | ||
86 | ||
87 | static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, | |
88 | void *timeout_ctx); | |
89 | static int wpa_driver_nl80211_set_mode(void *priv, int mode); | |
90 | static int wpa_driver_nl80211_flush_pmkid(void *priv); | |
91 | static int wpa_driver_nl80211_get_range(void *priv); | |
362f781e | 92 | static int |
7524cfb1 JM |
93 | wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); |
94 | ||
3f5285e8 | 95 | |
6241fcb1 JM |
96 | /* nl80211 code */ |
97 | static int ack_handler(struct nl_msg *msg, void *arg) | |
98 | { | |
99 | int *err = arg; | |
100 | *err = 0; | |
101 | return NL_STOP; | |
102 | } | |
103 | ||
104 | static int finish_handler(struct nl_msg *msg, void *arg) | |
105 | { | |
8e8df255 JM |
106 | int *ret = arg; |
107 | *ret = 0; | |
6241fcb1 JM |
108 | return NL_SKIP; |
109 | } | |
110 | ||
111 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, | |
112 | void *arg) | |
113 | { | |
114 | int *ret = arg; | |
115 | *ret = err->error; | |
116 | return NL_SKIP; | |
117 | } | |
118 | ||
119 | static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, | |
120 | struct nl_msg *msg, | |
121 | int (*valid_handler)(struct nl_msg *, void *), | |
122 | void *valid_data) | |
123 | { | |
124 | struct nl_cb *cb; | |
125 | int err = -ENOMEM; | |
126 | ||
127 | cb = nl_cb_clone(drv->nl_cb); | |
128 | if (!cb) | |
129 | goto out; | |
130 | ||
131 | err = nl_send_auto_complete(drv->nl_handle, msg); | |
132 | if (err < 0) | |
133 | goto out; | |
134 | ||
135 | err = 1; | |
136 | ||
137 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); | |
8e8df255 | 138 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); |
6241fcb1 JM |
139 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); |
140 | ||
141 | if (valid_handler) | |
142 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, | |
143 | valid_handler, valid_data); | |
144 | ||
145 | while (err > 0) | |
146 | nl_recvmsgs(drv->nl_handle, cb); | |
147 | out: | |
148 | nl_cb_put(cb); | |
149 | nlmsg_free(msg); | |
150 | return err; | |
151 | } | |
152 | ||
153 | ||
97865538 JM |
154 | struct family_data { |
155 | const char *group; | |
156 | int id; | |
157 | }; | |
158 | ||
159 | ||
160 | static int family_handler(struct nl_msg *msg, void *arg) | |
161 | { | |
162 | struct family_data *res = arg; | |
163 | struct nlattr *tb[CTRL_ATTR_MAX + 1]; | |
164 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
165 | struct nlattr *mcgrp; | |
166 | int i; | |
167 | ||
168 | nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
169 | genlmsg_attrlen(gnlh, 0), NULL); | |
170 | if (!tb[CTRL_ATTR_MCAST_GROUPS]) | |
171 | return NL_SKIP; | |
172 | ||
173 | nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { | |
174 | struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; | |
175 | nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), | |
176 | nla_len(mcgrp), NULL); | |
177 | if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || | |
178 | !tb2[CTRL_ATTR_MCAST_GRP_ID] || | |
179 | os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), | |
180 | res->group, | |
181 | nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) | |
182 | continue; | |
183 | res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); | |
184 | break; | |
185 | }; | |
186 | ||
187 | return NL_SKIP; | |
188 | } | |
189 | ||
190 | ||
191 | static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, | |
192 | const char *family, const char *group) | |
193 | { | |
194 | struct nl_msg *msg; | |
195 | int ret = -1; | |
196 | struct family_data res = { group, -ENOENT }; | |
197 | ||
198 | msg = nlmsg_alloc(); | |
199 | if (!msg) | |
200 | return -ENOMEM; | |
201 | genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), | |
202 | 0, 0, CTRL_CMD_GETFAMILY, 0); | |
203 | NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); | |
204 | ||
205 | ret = send_and_recv_msgs(drv, msg, family_handler, &res); | |
206 | msg = NULL; | |
207 | if (ret == 0) | |
208 | ret = res.id; | |
209 | ||
210 | nla_put_failure: | |
211 | nlmsg_free(msg); | |
212 | return ret; | |
213 | } | |
214 | ||
215 | ||
3f5285e8 JM |
216 | static int wpa_driver_nl80211_send_oper_ifla( |
217 | struct wpa_driver_nl80211_data *drv, | |
218 | int linkmode, int operstate) | |
219 | { | |
220 | struct { | |
221 | struct nlmsghdr hdr; | |
222 | struct ifinfomsg ifinfo; | |
223 | char opts[16]; | |
224 | } req; | |
225 | struct rtattr *rta; | |
226 | static int nl_seq; | |
227 | ssize_t ret; | |
228 | ||
229 | os_memset(&req, 0, sizeof(req)); | |
230 | ||
231 | req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
232 | req.hdr.nlmsg_type = RTM_SETLINK; | |
233 | req.hdr.nlmsg_flags = NLM_F_REQUEST; | |
234 | req.hdr.nlmsg_seq = ++nl_seq; | |
235 | req.hdr.nlmsg_pid = 0; | |
236 | ||
237 | req.ifinfo.ifi_family = AF_UNSPEC; | |
238 | req.ifinfo.ifi_type = 0; | |
239 | req.ifinfo.ifi_index = drv->ifindex; | |
240 | req.ifinfo.ifi_flags = 0; | |
241 | req.ifinfo.ifi_change = 0; | |
242 | ||
243 | if (linkmode != -1) { | |
244 | rta = (struct rtattr *) | |
245 | ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); | |
246 | rta->rta_type = IFLA_LINKMODE; | |
247 | rta->rta_len = RTA_LENGTH(sizeof(char)); | |
248 | *((char *) RTA_DATA(rta)) = linkmode; | |
249 | req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + | |
250 | RTA_LENGTH(sizeof(char)); | |
251 | } | |
252 | if (operstate != -1) { | |
253 | rta = (struct rtattr *) | |
254 | ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); | |
255 | rta->rta_type = IFLA_OPERSTATE; | |
256 | rta->rta_len = RTA_LENGTH(sizeof(char)); | |
257 | *((char *) RTA_DATA(rta)) = operstate; | |
258 | req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + | |
259 | RTA_LENGTH(sizeof(char)); | |
260 | } | |
261 | ||
262 | wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", | |
263 | linkmode, operstate); | |
264 | ||
97865538 | 265 | ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); |
3f5285e8 JM |
266 | if (ret < 0) { |
267 | wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " | |
268 | "%s (assume operstate is not supported)", | |
269 | strerror(errno)); | |
270 | } | |
271 | ||
272 | return ret < 0 ? -1 : 0; | |
273 | } | |
274 | ||
275 | ||
276 | static int wpa_driver_nl80211_set_auth_param( | |
277 | struct wpa_driver_nl80211_data *drv, int idx, u32 value) | |
278 | { | |
279 | struct iwreq iwr; | |
280 | int ret = 0; | |
281 | ||
282 | os_memset(&iwr, 0, sizeof(iwr)); | |
283 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
284 | iwr.u.param.flags = idx & IW_AUTH_INDEX; | |
285 | iwr.u.param.value = value; | |
286 | ||
287 | if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { | |
288 | if (errno != EOPNOTSUPP) { | |
289 | wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " | |
290 | "value 0x%x) failed: %s)", | |
291 | idx, value, strerror(errno)); | |
292 | } | |
293 | ret = errno == EOPNOTSUPP ? -2 : -1; | |
294 | } | |
295 | ||
296 | return ret; | |
297 | } | |
298 | ||
299 | ||
300 | static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) | |
301 | { | |
302 | struct wpa_driver_nl80211_data *drv = priv; | |
303 | struct iwreq iwr; | |
304 | int ret = 0; | |
305 | ||
306 | os_memset(&iwr, 0, sizeof(iwr)); | |
307 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
308 | ||
309 | if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { | |
310 | perror("ioctl[SIOCGIWAP]"); | |
311 | ret = -1; | |
312 | } | |
313 | os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); | |
314 | ||
315 | return ret; | |
316 | } | |
317 | ||
318 | ||
319 | static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) | |
320 | { | |
321 | struct wpa_driver_nl80211_data *drv = priv; | |
322 | struct iwreq iwr; | |
323 | int ret = 0; | |
324 | ||
325 | os_memset(&iwr, 0, sizeof(iwr)); | |
326 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
327 | iwr.u.ap_addr.sa_family = ARPHRD_ETHER; | |
328 | if (bssid) | |
329 | os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); | |
330 | else | |
331 | os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); | |
332 | ||
333 | if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { | |
334 | perror("ioctl[SIOCSIWAP]"); | |
335 | ret = -1; | |
336 | } | |
337 | ||
338 | return ret; | |
339 | } | |
340 | ||
341 | ||
342 | static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) | |
343 | { | |
344 | struct wpa_driver_nl80211_data *drv = priv; | |
345 | struct iwreq iwr; | |
346 | int ret = 0; | |
347 | ||
348 | os_memset(&iwr, 0, sizeof(iwr)); | |
349 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
350 | iwr.u.essid.pointer = (caddr_t) ssid; | |
351 | iwr.u.essid.length = 32; | |
352 | ||
353 | if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { | |
354 | perror("ioctl[SIOCGIWESSID]"); | |
355 | ret = -1; | |
356 | } else { | |
357 | ret = iwr.u.essid.length; | |
358 | if (ret > 32) | |
359 | ret = 32; | |
360 | /* Some drivers include nul termination in the SSID, so let's | |
361 | * remove it here before further processing. WE-21 changes this | |
362 | * to explicitly require the length _not_ to include nul | |
363 | * termination. */ | |
364 | if (ret > 0 && ssid[ret - 1] == '\0' && | |
365 | drv->we_version_compiled < 21) | |
366 | ret--; | |
367 | } | |
368 | ||
369 | return ret; | |
370 | } | |
371 | ||
372 | ||
373 | static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, | |
374 | size_t ssid_len) | |
375 | { | |
376 | struct wpa_driver_nl80211_data *drv = priv; | |
377 | struct iwreq iwr; | |
378 | int ret = 0; | |
379 | char buf[33]; | |
380 | ||
381 | if (ssid_len > 32) | |
382 | return -1; | |
383 | ||
384 | os_memset(&iwr, 0, sizeof(iwr)); | |
385 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
386 | /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ | |
387 | iwr.u.essid.flags = (ssid_len != 0); | |
388 | os_memset(buf, 0, sizeof(buf)); | |
389 | os_memcpy(buf, ssid, ssid_len); | |
390 | iwr.u.essid.pointer = (caddr_t) buf; | |
391 | if (drv->we_version_compiled < 21) { | |
392 | /* For historic reasons, set SSID length to include one extra | |
393 | * character, C string nul termination, even though SSID is | |
394 | * really an octet string that should not be presented as a C | |
395 | * string. Some Linux drivers decrement the length by one and | |
396 | * can thus end up missing the last octet of the SSID if the | |
397 | * length is not incremented here. WE-21 changes this to | |
398 | * explicitly require the length _not_ to include nul | |
399 | * termination. */ | |
400 | if (ssid_len) | |
401 | ssid_len++; | |
402 | } | |
403 | iwr.u.essid.length = ssid_len; | |
404 | ||
405 | if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { | |
406 | perror("ioctl[SIOCSIWESSID]"); | |
407 | ret = -1; | |
408 | } | |
409 | ||
410 | return ret; | |
411 | } | |
412 | ||
413 | ||
414 | static int wpa_driver_nl80211_set_freq(void *priv, int freq) | |
415 | { | |
416 | struct wpa_driver_nl80211_data *drv = priv; | |
417 | struct iwreq iwr; | |
418 | int ret = 0; | |
419 | ||
420 | os_memset(&iwr, 0, sizeof(iwr)); | |
421 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
422 | iwr.u.freq.m = freq * 100000; | |
423 | iwr.u.freq.e = 1; | |
424 | ||
425 | if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { | |
426 | perror("ioctl[SIOCSIWFREQ]"); | |
427 | ret = -1; | |
428 | } | |
429 | ||
430 | return ret; | |
431 | } | |
432 | ||
433 | ||
434 | static void | |
435 | wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) | |
436 | { | |
437 | union wpa_event_data data; | |
438 | ||
439 | wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", | |
440 | custom); | |
441 | ||
442 | os_memset(&data, 0, sizeof(data)); | |
443 | /* Host AP driver */ | |
444 | if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { | |
445 | data.michael_mic_failure.unicast = | |
446 | os_strstr(custom, " unicast ") != NULL; | |
447 | /* TODO: parse parameters(?) */ | |
448 | wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); | |
449 | } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { | |
450 | char *spos; | |
451 | int bytes; | |
452 | ||
453 | spos = custom + 17; | |
454 | ||
455 | bytes = strspn(spos, "0123456789abcdefABCDEF"); | |
456 | if (!bytes || (bytes & 1)) | |
457 | return; | |
458 | bytes /= 2; | |
459 | ||
460 | data.assoc_info.req_ies = os_malloc(bytes); | |
461 | if (data.assoc_info.req_ies == NULL) | |
462 | return; | |
463 | ||
464 | data.assoc_info.req_ies_len = bytes; | |
465 | hexstr2bin(spos, data.assoc_info.req_ies, bytes); | |
466 | ||
467 | spos += bytes * 2; | |
468 | ||
469 | data.assoc_info.resp_ies = NULL; | |
470 | data.assoc_info.resp_ies_len = 0; | |
471 | ||
472 | if (os_strncmp(spos, " RespIEs=", 9) == 0) { | |
473 | spos += 9; | |
474 | ||
475 | bytes = strspn(spos, "0123456789abcdefABCDEF"); | |
476 | if (!bytes || (bytes & 1)) | |
477 | goto done; | |
478 | bytes /= 2; | |
479 | ||
480 | data.assoc_info.resp_ies = os_malloc(bytes); | |
481 | if (data.assoc_info.resp_ies == NULL) | |
482 | goto done; | |
483 | ||
484 | data.assoc_info.resp_ies_len = bytes; | |
485 | hexstr2bin(spos, data.assoc_info.resp_ies, bytes); | |
486 | } | |
487 | ||
488 | wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); | |
489 | ||
490 | done: | |
491 | os_free(data.assoc_info.resp_ies); | |
492 | os_free(data.assoc_info.req_ies); | |
493 | #ifdef CONFIG_PEERKEY | |
494 | } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { | |
495 | if (hwaddr_aton(custom + 17, data.stkstart.peer)) { | |
496 | wpa_printf(MSG_DEBUG, "WEXT: unrecognized " | |
497 | "STKSTART.request '%s'", custom + 17); | |
498 | return; | |
499 | } | |
500 | wpa_supplicant_event(ctx, EVENT_STKSTART, &data); | |
501 | #endif /* CONFIG_PEERKEY */ | |
502 | } | |
503 | } | |
504 | ||
505 | ||
506 | static int wpa_driver_nl80211_event_wireless_michaelmicfailure( | |
507 | void *ctx, const char *ev, size_t len) | |
508 | { | |
509 | const struct iw_michaelmicfailure *mic; | |
510 | union wpa_event_data data; | |
511 | ||
512 | if (len < sizeof(*mic)) | |
513 | return -1; | |
514 | ||
515 | mic = (const struct iw_michaelmicfailure *) ev; | |
516 | ||
517 | wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " | |
518 | "flags=0x%x src_addr=" MACSTR, mic->flags, | |
519 | MAC2STR(mic->src_addr.sa_data)); | |
520 | ||
521 | os_memset(&data, 0, sizeof(data)); | |
522 | data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); | |
523 | wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | ||
529 | static int wpa_driver_nl80211_event_wireless_pmkidcand( | |
530 | struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) | |
531 | { | |
532 | const struct iw_pmkid_cand *cand; | |
533 | union wpa_event_data data; | |
534 | const u8 *addr; | |
535 | ||
536 | if (len < sizeof(*cand)) | |
537 | return -1; | |
538 | ||
539 | cand = (const struct iw_pmkid_cand *) ev; | |
540 | addr = (const u8 *) cand->bssid.sa_data; | |
541 | ||
542 | wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " | |
543 | "flags=0x%x index=%d bssid=" MACSTR, cand->flags, | |
544 | cand->index, MAC2STR(addr)); | |
545 | ||
546 | os_memset(&data, 0, sizeof(data)); | |
547 | os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); | |
548 | data.pmkid_candidate.index = cand->index; | |
549 | data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; | |
550 | wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); | |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
555 | ||
556 | static int wpa_driver_nl80211_event_wireless_assocreqie( | |
557 | struct wpa_driver_nl80211_data *drv, const char *ev, int len) | |
558 | { | |
559 | if (len < 0) | |
560 | return -1; | |
561 | ||
562 | wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, | |
563 | len); | |
564 | os_free(drv->assoc_req_ies); | |
565 | drv->assoc_req_ies = os_malloc(len); | |
566 | if (drv->assoc_req_ies == NULL) { | |
567 | drv->assoc_req_ies_len = 0; | |
568 | return -1; | |
569 | } | |
570 | os_memcpy(drv->assoc_req_ies, ev, len); | |
571 | drv->assoc_req_ies_len = len; | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
576 | ||
577 | static int wpa_driver_nl80211_event_wireless_assocrespie( | |
578 | struct wpa_driver_nl80211_data *drv, const char *ev, int len) | |
579 | { | |
580 | if (len < 0) | |
581 | return -1; | |
582 | ||
583 | wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, | |
584 | len); | |
585 | os_free(drv->assoc_resp_ies); | |
586 | drv->assoc_resp_ies = os_malloc(len); | |
587 | if (drv->assoc_resp_ies == NULL) { | |
588 | drv->assoc_resp_ies_len = 0; | |
589 | return -1; | |
590 | } | |
591 | os_memcpy(drv->assoc_resp_ies, ev, len); | |
592 | drv->assoc_resp_ies_len = len; | |
593 | ||
594 | return 0; | |
595 | } | |
596 | ||
597 | ||
598 | static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) | |
599 | { | |
600 | union wpa_event_data data; | |
601 | ||
602 | if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) | |
603 | return; | |
604 | ||
605 | os_memset(&data, 0, sizeof(data)); | |
606 | if (drv->assoc_req_ies) { | |
607 | data.assoc_info.req_ies = drv->assoc_req_ies; | |
608 | drv->assoc_req_ies = NULL; | |
609 | data.assoc_info.req_ies_len = drv->assoc_req_ies_len; | |
610 | } | |
611 | if (drv->assoc_resp_ies) { | |
612 | data.assoc_info.resp_ies = drv->assoc_resp_ies; | |
613 | drv->assoc_resp_ies = NULL; | |
614 | data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; | |
615 | } | |
616 | ||
617 | wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); | |
618 | ||
619 | os_free(data.assoc_info.req_ies); | |
620 | os_free(data.assoc_info.resp_ies); | |
621 | } | |
622 | ||
623 | ||
624 | static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, | |
625 | void *ctx, char *data, int len) | |
626 | { | |
627 | struct iw_event iwe_buf, *iwe = &iwe_buf; | |
628 | char *pos, *end, *custom, *buf; | |
629 | ||
630 | pos = data; | |
631 | end = data + len; | |
632 | ||
633 | while (pos + IW_EV_LCP_LEN <= end) { | |
634 | /* Event data may be unaligned, so make a local, aligned copy | |
635 | * before processing. */ | |
636 | os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); | |
637 | wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", | |
638 | iwe->cmd, iwe->len); | |
639 | if (iwe->len <= IW_EV_LCP_LEN) | |
640 | return; | |
641 | ||
642 | custom = pos + IW_EV_POINT_LEN; | |
643 | if (drv->we_version_compiled > 18 && | |
644 | (iwe->cmd == IWEVMICHAELMICFAILURE || | |
645 | iwe->cmd == IWEVCUSTOM || | |
646 | iwe->cmd == IWEVASSOCREQIE || | |
647 | iwe->cmd == IWEVASSOCRESPIE || | |
648 | iwe->cmd == IWEVPMKIDCAND)) { | |
649 | /* WE-19 removed the pointer from struct iw_point */ | |
650 | char *dpos = (char *) &iwe_buf.u.data.length; | |
651 | int dlen = dpos - (char *) &iwe_buf; | |
652 | os_memcpy(dpos, pos + IW_EV_LCP_LEN, | |
653 | sizeof(struct iw_event) - dlen); | |
654 | } else { | |
655 | os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); | |
656 | custom += IW_EV_POINT_OFF; | |
657 | } | |
658 | ||
659 | switch (iwe->cmd) { | |
660 | case SIOCGIWAP: | |
661 | wpa_printf(MSG_DEBUG, "Wireless event: new AP: " | |
662 | MACSTR, | |
663 | MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); | |
664 | if (is_zero_ether_addr( | |
665 | (const u8 *) iwe->u.ap_addr.sa_data) || | |
666 | os_memcmp(iwe->u.ap_addr.sa_data, | |
667 | "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == | |
668 | 0) { | |
669 | os_free(drv->assoc_req_ies); | |
670 | drv->assoc_req_ies = NULL; | |
671 | os_free(drv->assoc_resp_ies); | |
672 | drv->assoc_resp_ies = NULL; | |
673 | wpa_supplicant_event(ctx, EVENT_DISASSOC, | |
674 | NULL); | |
675 | ||
676 | } else { | |
677 | wpa_driver_nl80211_event_assoc_ies(drv); | |
678 | wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); | |
679 | } | |
680 | break; | |
681 | case IWEVMICHAELMICFAILURE: | |
682 | wpa_driver_nl80211_event_wireless_michaelmicfailure( | |
683 | ctx, custom, iwe->u.data.length); | |
684 | break; | |
685 | case IWEVCUSTOM: | |
686 | if (custom + iwe->u.data.length > end) | |
687 | return; | |
688 | buf = os_malloc(iwe->u.data.length + 1); | |
689 | if (buf == NULL) | |
690 | return; | |
691 | os_memcpy(buf, custom, iwe->u.data.length); | |
692 | buf[iwe->u.data.length] = '\0'; | |
693 | wpa_driver_nl80211_event_wireless_custom(ctx, buf); | |
694 | os_free(buf); | |
695 | break; | |
3f5285e8 JM |
696 | case IWEVASSOCREQIE: |
697 | wpa_driver_nl80211_event_wireless_assocreqie( | |
698 | drv, custom, iwe->u.data.length); | |
699 | break; | |
700 | case IWEVASSOCRESPIE: | |
701 | wpa_driver_nl80211_event_wireless_assocrespie( | |
702 | drv, custom, iwe->u.data.length); | |
703 | break; | |
704 | case IWEVPMKIDCAND: | |
705 | wpa_driver_nl80211_event_wireless_pmkidcand( | |
706 | drv, custom, iwe->u.data.length); | |
707 | break; | |
708 | } | |
709 | ||
710 | pos += iwe->len; | |
711 | } | |
712 | } | |
713 | ||
714 | ||
7524cfb1 JM |
715 | static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, |
716 | void *ctx, char *buf, size_t len, | |
717 | int del) | |
3f5285e8 JM |
718 | { |
719 | union wpa_event_data event; | |
720 | ||
721 | os_memset(&event, 0, sizeof(event)); | |
722 | if (len > sizeof(event.interface_status.ifname)) | |
723 | len = sizeof(event.interface_status.ifname) - 1; | |
724 | os_memcpy(event.interface_status.ifname, buf, len); | |
725 | event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : | |
726 | EVENT_INTERFACE_ADDED; | |
727 | ||
728 | wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", | |
729 | del ? "DEL" : "NEW", | |
730 | event.interface_status.ifname, | |
731 | del ? "removed" : "added"); | |
732 | ||
7524cfb1 JM |
733 | if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { |
734 | if (del) | |
735 | drv->if_removed = 1; | |
736 | else | |
737 | drv->if_removed = 0; | |
738 | } | |
739 | ||
3f5285e8 JM |
740 | wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); |
741 | } | |
742 | ||
743 | ||
7524cfb1 JM |
744 | static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, |
745 | struct nlmsghdr *h) | |
746 | { | |
747 | struct ifinfomsg *ifi; | |
748 | int attrlen, _nlmsg_len, rta_len; | |
749 | struct rtattr *attr; | |
750 | ||
751 | ifi = NLMSG_DATA(h); | |
752 | ||
753 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
754 | ||
755 | attrlen = h->nlmsg_len - _nlmsg_len; | |
756 | if (attrlen < 0) | |
757 | return 0; | |
758 | ||
759 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
760 | ||
761 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
762 | while (RTA_OK(attr, attrlen)) { | |
763 | if (attr->rta_type == IFLA_IFNAME) { | |
764 | if (os_strcmp(((char *) attr) + rta_len, drv->ifname) | |
765 | == 0) | |
766 | return 1; | |
767 | else | |
768 | break; | |
769 | } | |
770 | attr = RTA_NEXT(attr, attrlen); | |
771 | } | |
772 | ||
773 | return 0; | |
774 | } | |
775 | ||
776 | ||
777 | static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, | |
778 | int ifindex, struct nlmsghdr *h) | |
779 | { | |
780 | if (drv->ifindex == ifindex) | |
781 | return 1; | |
782 | ||
783 | if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { | |
784 | drv->ifindex = if_nametoindex(drv->ifname); | |
785 | wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " | |
786 | "interface"); | |
787 | wpa_driver_nl80211_finish_drv_init(drv); | |
788 | return 1; | |
789 | } | |
790 | ||
791 | return 0; | |
792 | } | |
793 | ||
794 | ||
3f5285e8 JM |
795 | static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, |
796 | void *ctx, struct nlmsghdr *h, | |
797 | size_t len) | |
798 | { | |
799 | struct ifinfomsg *ifi; | |
800 | int attrlen, _nlmsg_len, rta_len; | |
801 | struct rtattr * attr; | |
802 | ||
803 | if (len < sizeof(*ifi)) | |
804 | return; | |
805 | ||
806 | ifi = NLMSG_DATA(h); | |
807 | ||
7524cfb1 | 808 | if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { |
3f5285e8 JM |
809 | wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", |
810 | ifi->ifi_index); | |
811 | return; | |
812 | } | |
813 | ||
814 | wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " | |
815 | "(%s%s%s%s)", | |
816 | drv->operstate, ifi->ifi_flags, | |
817 | (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", | |
818 | (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", | |
819 | (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", | |
820 | (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); | |
821 | /* | |
822 | * Some drivers send the association event before the operup event--in | |
823 | * this case, lifting operstate in wpa_driver_nl80211_set_operstate() | |
824 | * fails. This will hit us when wpa_supplicant does not need to do | |
825 | * IEEE 802.1X authentication | |
826 | */ | |
827 | if (drv->operstate == 1 && | |
828 | (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && | |
829 | !(ifi->ifi_flags & IFF_RUNNING)) | |
830 | wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); | |
831 | ||
832 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
833 | ||
834 | attrlen = h->nlmsg_len - _nlmsg_len; | |
835 | if (attrlen < 0) | |
836 | return; | |
837 | ||
838 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
839 | ||
840 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
841 | while (RTA_OK(attr, attrlen)) { | |
842 | if (attr->rta_type == IFLA_WIRELESS) { | |
843 | wpa_driver_nl80211_event_wireless( | |
844 | drv, ctx, ((char *) attr) + rta_len, | |
845 | attr->rta_len - rta_len); | |
846 | } else if (attr->rta_type == IFLA_IFNAME) { | |
7524cfb1 JM |
847 | wpa_driver_nl80211_event_link( |
848 | drv, ctx, | |
849 | ((char *) attr) + rta_len, | |
850 | attr->rta_len - rta_len, 0); | |
3f5285e8 JM |
851 | } |
852 | attr = RTA_NEXT(attr, attrlen); | |
853 | } | |
854 | } | |
855 | ||
856 | ||
857 | static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, | |
858 | void *ctx, struct nlmsghdr *h, | |
859 | size_t len) | |
860 | { | |
861 | struct ifinfomsg *ifi; | |
862 | int attrlen, _nlmsg_len, rta_len; | |
863 | struct rtattr * attr; | |
864 | ||
865 | if (len < sizeof(*ifi)) | |
866 | return; | |
867 | ||
868 | ifi = NLMSG_DATA(h); | |
869 | ||
870 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
871 | ||
872 | attrlen = h->nlmsg_len - _nlmsg_len; | |
873 | if (attrlen < 0) | |
874 | return; | |
875 | ||
876 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
877 | ||
878 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
879 | while (RTA_OK(attr, attrlen)) { | |
880 | if (attr->rta_type == IFLA_IFNAME) { | |
7524cfb1 JM |
881 | wpa_driver_nl80211_event_link( |
882 | drv, ctx, | |
883 | ((char *) attr) + rta_len, | |
884 | attr->rta_len - rta_len, 1); | |
3f5285e8 JM |
885 | } |
886 | attr = RTA_NEXT(attr, attrlen); | |
887 | } | |
888 | } | |
889 | ||
890 | ||
97865538 JM |
891 | static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, |
892 | void *sock_ctx) | |
3f5285e8 JM |
893 | { |
894 | char buf[8192]; | |
895 | int left; | |
896 | struct sockaddr_nl from; | |
897 | socklen_t fromlen; | |
898 | struct nlmsghdr *h; | |
899 | int max_events = 10; | |
900 | ||
901 | try_again: | |
902 | fromlen = sizeof(from); | |
903 | left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, | |
904 | (struct sockaddr *) &from, &fromlen); | |
905 | if (left < 0) { | |
906 | if (errno != EINTR && errno != EAGAIN) | |
907 | perror("recvfrom(netlink)"); | |
908 | return; | |
909 | } | |
910 | ||
911 | h = (struct nlmsghdr *) buf; | |
912 | while (left >= (int) sizeof(*h)) { | |
913 | int len, plen; | |
914 | ||
915 | len = h->nlmsg_len; | |
916 | plen = len - sizeof(*h); | |
917 | if (len > left || plen < 0) { | |
918 | wpa_printf(MSG_DEBUG, "Malformed netlink message: " | |
919 | "len=%d left=%d plen=%d", | |
920 | len, left, plen); | |
921 | break; | |
922 | } | |
923 | ||
924 | switch (h->nlmsg_type) { | |
925 | case RTM_NEWLINK: | |
926 | wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, | |
927 | h, plen); | |
928 | break; | |
929 | case RTM_DELLINK: | |
930 | wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, | |
931 | h, plen); | |
932 | break; | |
933 | } | |
934 | ||
935 | len = NLMSG_ALIGN(len); | |
936 | left -= len; | |
937 | h = (struct nlmsghdr *) ((char *) h + len); | |
938 | } | |
939 | ||
940 | if (left > 0) { | |
941 | wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " | |
942 | "message", left); | |
943 | } | |
944 | ||
945 | if (--max_events > 0) { | |
946 | /* | |
947 | * Try to receive all events in one eloop call in order to | |
948 | * limit race condition on cases where AssocInfo event, Assoc | |
949 | * event, and EAPOL frames are received more or less at the | |
950 | * same time. We want to process the event messages first | |
951 | * before starting EAPOL processing. | |
952 | */ | |
953 | goto try_again; | |
954 | } | |
955 | } | |
956 | ||
957 | ||
97865538 JM |
958 | static int no_seq_check(struct nl_msg *msg, void *arg) |
959 | { | |
960 | return NL_OK; | |
961 | } | |
962 | ||
963 | ||
964 | static int process_event(struct nl_msg *msg, void *arg) | |
965 | { | |
966 | struct wpa_driver_nl80211_data *drv = arg; | |
967 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
968 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; | |
969 | ||
970 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
971 | genlmsg_attrlen(gnlh, 0), NULL); | |
972 | ||
973 | if (tb[NL80211_ATTR_IFINDEX]) { | |
974 | int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); | |
975 | if (ifindex != drv->ifindex) { | |
976 | wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" | |
977 | " for foreign interface (ifindex %d)", | |
978 | gnlh->cmd, ifindex); | |
979 | return NL_SKIP; | |
980 | } | |
981 | } | |
982 | ||
983 | switch (gnlh->cmd) { | |
984 | case NL80211_CMD_NEW_SCAN_RESULTS: | |
985 | wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); | |
986 | drv->scan_complete_events = 1; | |
987 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, | |
988 | drv->ctx); | |
989 | wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); | |
990 | break; | |
991 | case NL80211_CMD_SCAN_ABORTED: | |
992 | wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); | |
993 | /* | |
994 | * Need to indicate that scan results are available in order | |
995 | * not to make wpa_supplicant stop its scanning. | |
996 | */ | |
997 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, | |
998 | drv->ctx); | |
999 | wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); | |
1000 | break; | |
1001 | default: | |
1002 | wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", | |
1003 | gnlh->cmd); | |
1004 | break; | |
1005 | } | |
1006 | ||
1007 | return NL_SKIP; | |
1008 | } | |
1009 | ||
1010 | ||
1011 | static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, | |
1012 | void *sock_ctx) | |
1013 | { | |
1014 | struct nl_cb *cb; | |
1015 | struct wpa_driver_nl80211_data *drv = eloop_ctx; | |
1016 | ||
1017 | wpa_printf(MSG_DEBUG, "nl80211: Event message available"); | |
1018 | ||
1019 | cb = nl_cb_clone(drv->nl_cb); | |
1020 | if (!cb) | |
1021 | return; | |
1022 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); | |
1023 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); | |
1024 | nl_recvmsgs(drv->nl_handle, cb); | |
1025 | nl_cb_put(cb); | |
1026 | } | |
1027 | ||
1028 | ||
3f5285e8 JM |
1029 | static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, |
1030 | const char *ifname, int *flags) | |
1031 | { | |
1032 | struct ifreq ifr; | |
1033 | ||
1034 | os_memset(&ifr, 0, sizeof(ifr)); | |
1035 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
1036 | if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { | |
1037 | perror("ioctl[SIOCGIFFLAGS]"); | |
1038 | return -1; | |
1039 | } | |
1040 | *flags = ifr.ifr_flags & 0xffff; | |
1041 | return 0; | |
1042 | } | |
1043 | ||
1044 | ||
1045 | /** | |
1046 | * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) | |
1047 | * @drv: driver_nl80211 private data | |
1048 | * @flags: Pointer to returned flags value | |
1049 | * Returns: 0 on success, -1 on failure | |
1050 | */ | |
1051 | static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, | |
1052 | int *flags) | |
1053 | { | |
1054 | return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); | |
1055 | } | |
1056 | ||
1057 | ||
1058 | static int wpa_driver_nl80211_set_ifflags_ifname( | |
1059 | struct wpa_driver_nl80211_data *drv, | |
1060 | const char *ifname, int flags) | |
1061 | { | |
1062 | struct ifreq ifr; | |
1063 | ||
1064 | os_memset(&ifr, 0, sizeof(ifr)); | |
1065 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
1066 | ifr.ifr_flags = flags & 0xffff; | |
1067 | if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { | |
1068 | perror("SIOCSIFFLAGS"); | |
1069 | return -1; | |
1070 | } | |
1071 | return 0; | |
1072 | } | |
1073 | ||
1074 | ||
1075 | /** | |
1076 | * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) | |
1077 | * @drv: driver_nl80211 private data | |
1078 | * @flags: New value for flags | |
1079 | * Returns: 0 on success, -1 on failure | |
1080 | */ | |
1081 | static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, | |
1082 | int flags) | |
1083 | { | |
1084 | return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); | |
1085 | } | |
1086 | ||
1087 | ||
6d158490 LR |
1088 | /** |
1089 | * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain | |
1090 | * @priv: driver_nl80211 private data | |
1091 | * @alpha2_arg: country to which to switch to | |
1092 | * Returns: 0 on success, -1 on failure | |
1093 | * | |
1094 | * This asks nl80211 to set the regulatory domain for given | |
1095 | * country ISO / IEC alpha2. | |
1096 | */ | |
1097 | static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) | |
1098 | { | |
1099 | struct wpa_driver_nl80211_data *drv = priv; | |
1100 | char alpha2[3]; | |
1101 | struct nl_msg *msg; | |
1102 | ||
1103 | msg = nlmsg_alloc(); | |
1104 | if (!msg) | |
1105 | goto nla_put_failure; | |
1106 | ||
1107 | alpha2[0] = alpha2_arg[0]; | |
1108 | alpha2[1] = alpha2_arg[1]; | |
1109 | alpha2[2] = '\0'; | |
1110 | ||
1111 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1112 | 0, NL80211_CMD_REQ_SET_REG, 0); | |
1113 | ||
1114 | NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); | |
1115 | if (send_and_recv_msgs(drv, msg, NULL, NULL)) | |
1116 | return -EINVAL; | |
1117 | return 0; | |
1118 | nla_put_failure: | |
1119 | return -EINVAL; | |
1120 | } | |
1121 | ||
1122 | ||
1c873584 JM |
1123 | #ifdef CONFIG_CLIENT_MLME |
1124 | ||
1125 | static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, | |
1126 | int drop_unencrypted, int userspace_mlme) | |
1127 | { | |
1128 | #ifdef NL80211_CMD_SET_VIF | |
1129 | struct nl_msg *msg; | |
1130 | int ret = -1; | |
1131 | ||
1132 | msg = nlmsg_alloc(); | |
1133 | if (!msg) | |
6241fcb1 | 1134 | return -ENOMEM; |
1c873584 JM |
1135 | |
1136 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1137 | NL80211_CMD_SET_VIF, 0); | |
1138 | ||
1139 | if (drop_unencrypted >= 0) | |
1140 | NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, | |
1141 | drop_unencrypted); | |
1142 | if (userspace_mlme >= 0) | |
1143 | NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, | |
1144 | userspace_mlme); | |
1145 | ||
1146 | ret = 0; | |
1147 | ||
1148 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1149 | ||
6241fcb1 JM |
1150 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1151 | return ret; | |
1c873584 JM |
1152 | |
1153 | nla_put_failure: | |
6241fcb1 | 1154 | return -ENOBUFS; |
1c873584 JM |
1155 | #else /* NL80211_CMD_SET_VIF */ |
1156 | return -1; | |
1157 | #endif /* NL80211_CMD_SET_VIF */ | |
1158 | } | |
1159 | ||
1160 | ||
1161 | static int wpa_driver_nl80211_set_userspace_mlme( | |
1162 | struct wpa_driver_nl80211_data *drv, int enabled) | |
1163 | { | |
1164 | return nl80211_set_vif(drv, -1, enabled); | |
1165 | } | |
1166 | ||
1167 | ||
1168 | static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, | |
1169 | int ifidx) | |
1170 | { | |
1171 | struct nl_msg *msg; | |
1172 | ||
1173 | msg = nlmsg_alloc(); | |
1174 | if (!msg) | |
1175 | goto nla_put_failure; | |
1176 | ||
1177 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1178 | 0, NL80211_CMD_DEL_INTERFACE, 0); | |
1179 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); | |
6241fcb1 JM |
1180 | if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) |
1181 | return; | |
1182 | nla_put_failure: | |
1183 | wpa_printf(MSG_ERROR, "nl80211: Failed to remove interface."); | |
1c873584 JM |
1184 | } |
1185 | ||
1186 | ||
1187 | static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, | |
1188 | const char *ifname, enum nl80211_iftype iftype) | |
1189 | { | |
1190 | struct nl_msg *msg, *flags = NULL; | |
1191 | int ifidx, err; | |
6241fcb1 | 1192 | int ret = -ENOBUFS; |
1c873584 JM |
1193 | |
1194 | msg = nlmsg_alloc(); | |
1195 | if (!msg) | |
1196 | return -1; | |
1197 | ||
1198 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1199 | 0, NL80211_CMD_NEW_INTERFACE, 0); | |
1200 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); | |
1201 | NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); | |
1202 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); | |
1203 | ||
1204 | if (iftype == NL80211_IFTYPE_MONITOR) { | |
1205 | flags = nlmsg_alloc(); | |
1206 | if (!flags) | |
1207 | goto nla_put_failure; | |
1208 | ||
1209 | NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); | |
1210 | ||
1211 | err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); | |
1212 | ||
1213 | nlmsg_free(flags); | |
1214 | ||
1215 | if (err) | |
1216 | goto nla_put_failure; | |
1217 | } | |
1218 | ||
6241fcb1 JM |
1219 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1220 | if (ret) { | |
1221 | nla_put_failure: | |
1222 | wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", | |
1223 | ret); | |
1224 | return ret; | |
1c873584 JM |
1225 | } |
1226 | ||
1c873584 JM |
1227 | ifidx = if_nametoindex(ifname); |
1228 | if (ifidx <= 0) | |
1229 | return -1; | |
1230 | ||
1231 | return ifidx; | |
1232 | } | |
1233 | ||
1234 | ||
1235 | static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) | |
1236 | { | |
1237 | struct wpa_driver_nl80211_data *drv = eloop_ctx; | |
1238 | int len; | |
1239 | unsigned char buf[3000]; | |
1240 | struct ieee80211_radiotap_iterator iter; | |
1241 | int ret; | |
1242 | int injected = 0, failed = 0, rxflags = 0; | |
1243 | struct ieee80211_rx_status rx_status; | |
1244 | ||
1245 | len = recv(sock, buf, sizeof(buf), 0); | |
1246 | if (len < 0) { | |
1247 | perror("recv"); | |
1248 | return; | |
1249 | } | |
1250 | ||
1251 | if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { | |
1252 | wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " | |
1253 | "frame"); | |
1254 | return; | |
1255 | } | |
1256 | ||
1257 | os_memset(&rx_status, 0, sizeof(rx_status)); | |
1258 | ||
1259 | while (1) { | |
1260 | ret = ieee80211_radiotap_iterator_next(&iter); | |
1261 | if (ret == -ENOENT) | |
1262 | break; | |
1263 | if (ret) { | |
1264 | wpa_printf(MSG_DEBUG, "nl80211: received invalid " | |
1265 | "radiotap frame (%d)", ret); | |
1266 | return; | |
1267 | } | |
1268 | switch (iter.this_arg_index) { | |
1269 | case IEEE80211_RADIOTAP_FLAGS: | |
1270 | if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) | |
1271 | len -= 4; | |
1272 | break; | |
1273 | case IEEE80211_RADIOTAP_RX_FLAGS: | |
1274 | rxflags = 1; | |
1275 | break; | |
1276 | case IEEE80211_RADIOTAP_TX_FLAGS: | |
1277 | injected = 1; | |
1278 | failed = le_to_host16((*(u16 *) iter.this_arg)) & | |
1279 | IEEE80211_RADIOTAP_F_TX_FAIL; | |
1280 | break; | |
1281 | case IEEE80211_RADIOTAP_DATA_RETRIES: | |
1282 | break; | |
1283 | case IEEE80211_RADIOTAP_CHANNEL: | |
1284 | /* TODO convert from freq/flags to channel number | |
1285 | * rx_status.channel = XXX; | |
1286 | */ | |
1287 | break; | |
1288 | case IEEE80211_RADIOTAP_RATE: | |
1289 | break; | |
1290 | case IEEE80211_RADIOTAP_DB_ANTSIGNAL: | |
1291 | rx_status.ssi = *iter.this_arg; | |
1292 | break; | |
1293 | } | |
1294 | } | |
1295 | ||
1296 | if (rxflags && injected) | |
1297 | return; | |
1298 | ||
1299 | if (!injected) { | |
1300 | wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, | |
1301 | len - iter.max_length, &rx_status); | |
1302 | } else if (failed) { | |
1303 | /* TX failure callback */ | |
1304 | } else { | |
1305 | /* TX success (ACK) callback */ | |
1306 | } | |
1307 | } | |
1308 | ||
1309 | ||
1310 | static int wpa_driver_nl80211_create_monitor_interface( | |
1311 | struct wpa_driver_nl80211_data *drv) | |
1312 | { | |
1313 | char buf[IFNAMSIZ]; | |
1314 | struct sockaddr_ll ll; | |
1315 | int optval, flags; | |
1316 | socklen_t optlen; | |
1317 | ||
1318 | os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); | |
1319 | buf[IFNAMSIZ - 1] = '\0'; | |
1320 | ||
1321 | drv->monitor_ifidx = | |
1322 | nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); | |
1323 | ||
1324 | if (drv->monitor_ifidx < 0) | |
1325 | return -1; | |
1326 | ||
1327 | if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || | |
1328 | wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != | |
1329 | 0) { | |
1330 | wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " | |
1331 | "UP", buf); | |
1332 | goto error; | |
1333 | } | |
1334 | ||
1335 | os_memset(&ll, 0, sizeof(ll)); | |
1336 | ll.sll_family = AF_PACKET; | |
1337 | ll.sll_ifindex = drv->monitor_ifidx; | |
1338 | drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | |
1339 | if (drv->monitor_sock < 0) { | |
1340 | perror("socket[PF_PACKET,SOCK_RAW]"); | |
1341 | goto error; | |
1342 | } | |
1343 | ||
1344 | if (bind(drv->monitor_sock, (struct sockaddr *) &ll, | |
1345 | sizeof(ll)) < 0) { | |
1346 | perror("monitor socket bind"); | |
1347 | goto error; | |
1348 | } | |
1349 | ||
1350 | optlen = sizeof(optval); | |
1351 | optval = 20; | |
1352 | if (setsockopt | |
1353 | (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { | |
1354 | perror("Failed to set socket priority"); | |
1355 | goto error; | |
1356 | } | |
1357 | ||
1358 | if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, | |
1359 | drv, NULL)) { | |
1360 | wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " | |
1361 | "read socket"); | |
1362 | goto error; | |
1363 | } | |
1364 | ||
1365 | return 0; | |
1366 | ||
1367 | error: | |
1368 | nl80211_remove_iface(drv, drv->monitor_ifidx); | |
1369 | return -1; | |
1370 | } | |
1371 | ||
1372 | #endif /* CONFIG_CLIENT_MLME */ | |
1373 | ||
1374 | ||
80bc75f1 JM |
1375 | struct wiphy_info_data { |
1376 | int max_scan_ssids; | |
1377 | }; | |
1378 | ||
1379 | ||
1380 | static int wiphy_info_handler(struct nl_msg *msg, void *arg) | |
1381 | { | |
1382 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; | |
1383 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
1384 | struct wiphy_info_data *info = arg; | |
1385 | ||
1386 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
1387 | genlmsg_attrlen(gnlh, 0), NULL); | |
1388 | ||
1389 | if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) | |
1390 | info->max_scan_ssids = | |
1391 | nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); | |
1392 | ||
1393 | return NL_SKIP; | |
1394 | } | |
1395 | ||
1396 | ||
1397 | static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, | |
1398 | struct wiphy_info_data *info) | |
1399 | { | |
1400 | struct nl_msg *msg; | |
1401 | ||
1402 | os_memset(info, 0, sizeof(*info)); | |
1403 | msg = nlmsg_alloc(); | |
1404 | if (!msg) | |
1405 | return -1; | |
1406 | ||
1407 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1408 | 0, NL80211_CMD_GET_WIPHY, 0); | |
1409 | ||
1410 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1411 | ||
1412 | if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) | |
1413 | return 0; | |
1414 | msg = NULL; | |
1415 | nla_put_failure: | |
1416 | nlmsg_free(msg); | |
1417 | return -1; | |
1418 | } | |
1419 | ||
1420 | ||
1421 | static void wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) | |
1422 | { | |
1423 | struct wiphy_info_data info; | |
1424 | if (wpa_driver_nl80211_get_info(drv, &info)) | |
1425 | return; | |
1426 | drv->has_capability = 1; | |
1427 | drv->capa.max_scan_ssids = info.max_scan_ssids; | |
1428 | } | |
1429 | ||
1430 | ||
3f5285e8 | 1431 | /** |
7e5ba1b9 | 1432 | * wpa_driver_nl80211_init - Initialize nl80211 driver interface |
3f5285e8 JM |
1433 | * @ctx: context to be used when calling wpa_supplicant functions, |
1434 | * e.g., wpa_supplicant_event() | |
1435 | * @ifname: interface name, e.g., wlan0 | |
1436 | * Returns: Pointer to private data, %NULL on failure | |
1437 | */ | |
7e5ba1b9 | 1438 | static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) |
3f5285e8 | 1439 | { |
97865538 | 1440 | int s, ret; |
3f5285e8 JM |
1441 | struct sockaddr_nl local; |
1442 | struct wpa_driver_nl80211_data *drv; | |
1443 | ||
1444 | drv = os_zalloc(sizeof(*drv)); | |
1445 | if (drv == NULL) | |
1446 | return NULL; | |
1447 | drv->ctx = ctx; | |
1448 | os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); | |
1449 | ||
1450 | drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); | |
1451 | if (drv->nl_cb == NULL) { | |
1452 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " | |
1453 | "callbacks"); | |
1454 | goto err1; | |
1455 | } | |
1456 | ||
1457 | drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); | |
1458 | if (drv->nl_handle == NULL) { | |
1459 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " | |
1460 | "callbacks"); | |
1461 | goto err2; | |
1462 | } | |
1463 | ||
1464 | if (genl_connect(drv->nl_handle)) { | |
1465 | wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " | |
1466 | "netlink"); | |
1467 | goto err3; | |
1468 | } | |
1469 | ||
1470 | drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); | |
1471 | if (drv->nl_cache == NULL) { | |
1472 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " | |
1473 | "netlink cache"); | |
1474 | goto err3; | |
1475 | } | |
1476 | ||
1477 | drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); | |
1478 | if (drv->nl80211 == NULL) { | |
1479 | wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " | |
1480 | "found"); | |
1481 | goto err4; | |
1482 | } | |
1483 | ||
97865538 JM |
1484 | ret = nl_get_multicast_id(drv, "nl80211", "scan"); |
1485 | if (ret >= 0) | |
1486 | ret = nl_socket_add_membership(drv->nl_handle, ret); | |
1487 | if (ret < 0) { | |
1488 | wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " | |
1489 | "membership for scan events: %d (%s)", | |
1490 | ret, strerror(-ret)); | |
1491 | goto err4; | |
1492 | } | |
1493 | eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), | |
1494 | wpa_driver_nl80211_event_receive, drv, ctx); | |
1495 | ||
3f5285e8 JM |
1496 | drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); |
1497 | if (drv->ioctl_sock < 0) { | |
1498 | perror("socket(PF_INET,SOCK_DGRAM)"); | |
1499 | goto err5; | |
1500 | } | |
1501 | ||
1502 | s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
1503 | if (s < 0) { | |
1504 | perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); | |
1505 | goto err6; | |
1506 | } | |
1507 | ||
1508 | os_memset(&local, 0, sizeof(local)); | |
1509 | local.nl_family = AF_NETLINK; | |
1510 | local.nl_groups = RTMGRP_LINK; | |
1511 | if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { | |
1512 | perror("bind(netlink)"); | |
1513 | close(s); | |
1514 | goto err6; | |
1515 | } | |
1516 | ||
97865538 | 1517 | eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, |
3f5285e8 | 1518 | ctx); |
97865538 | 1519 | drv->wext_event_sock = s; |
3f5285e8 | 1520 | |
362f781e JM |
1521 | if (wpa_driver_nl80211_finish_drv_init(drv)) |
1522 | goto err7; | |
7524cfb1 JM |
1523 | |
1524 | return drv; | |
1525 | ||
362f781e JM |
1526 | err7: |
1527 | eloop_unregister_read_sock(drv->wext_event_sock); | |
1528 | close(drv->wext_event_sock); | |
7524cfb1 JM |
1529 | err6: |
1530 | close(drv->ioctl_sock); | |
1531 | err5: | |
1532 | genl_family_put(drv->nl80211); | |
1533 | err4: | |
1534 | nl_cache_free(drv->nl_cache); | |
1535 | err3: | |
1536 | nl_handle_destroy(drv->nl_handle); | |
1537 | err2: | |
1538 | nl_cb_put(drv->nl_cb); | |
1539 | err1: | |
1540 | os_free(drv); | |
1541 | return NULL; | |
1542 | } | |
1543 | ||
1544 | ||
362f781e | 1545 | static int |
7524cfb1 JM |
1546 | wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) |
1547 | { | |
1548 | int flags; | |
1549 | ||
362f781e JM |
1550 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) { |
1551 | wpa_printf(MSG_ERROR, "Could not get interface '%s' flags", | |
1552 | drv->ifname); | |
1553 | return -1; | |
1554 | } | |
1555 | if (!(flags & IFF_UP)) { | |
3f5285e8 | 1556 | if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { |
362f781e JM |
1557 | wpa_printf(MSG_ERROR, "Could not set interface '%s' " |
1558 | "UP", drv->ifname); | |
1559 | return -1; | |
3f5285e8 JM |
1560 | } |
1561 | } | |
1562 | ||
1563 | /* | |
1564 | * Make sure that the driver does not have any obsolete PMKID entries. | |
1565 | */ | |
1566 | wpa_driver_nl80211_flush_pmkid(drv); | |
1567 | ||
1568 | if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { | |
1569 | printf("Could not configure driver to use managed mode\n"); | |
1570 | } | |
1571 | ||
1572 | wpa_driver_nl80211_get_range(drv); | |
1573 | ||
1574 | drv->ifindex = if_nametoindex(drv->ifname); | |
1575 | ||
80bc75f1 JM |
1576 | wpa_driver_nl80211_capa(drv); |
1577 | ||
3f5285e8 | 1578 | wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); |
362f781e JM |
1579 | |
1580 | return 0; | |
3f5285e8 JM |
1581 | } |
1582 | ||
1583 | ||
1584 | /** | |
7e5ba1b9 JM |
1585 | * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface |
1586 | * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() | |
3f5285e8 JM |
1587 | * |
1588 | * Shut down driver interface and processing of driver events. Free | |
1589 | * private data buffer if one was allocated in wpa_driver_nl80211_init(). | |
1590 | */ | |
7e5ba1b9 | 1591 | static void wpa_driver_nl80211_deinit(void *priv) |
3f5285e8 JM |
1592 | { |
1593 | struct wpa_driver_nl80211_data *drv = priv; | |
1594 | int flags; | |
1595 | ||
1c873584 JM |
1596 | #ifdef CONFIG_CLIENT_MLME |
1597 | if (drv->monitor_sock >= 0) { | |
1598 | eloop_unregister_read_sock(drv->monitor_sock); | |
1599 | close(drv->monitor_sock); | |
1600 | } | |
1601 | if (drv->monitor_ifidx > 0) | |
1602 | nl80211_remove_iface(drv, drv->monitor_ifidx); | |
1603 | if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) | |
1604 | wpa_driver_nl80211_set_userspace_mlme(drv, 0); | |
1605 | #endif /* CONFIG_CLIENT_MLME */ | |
1606 | ||
3f5285e8 JM |
1607 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); |
1608 | ||
1609 | /* | |
1610 | * Clear possibly configured driver parameters in order to make it | |
1611 | * easier to use the driver after wpa_supplicant has been terminated. | |
1612 | */ | |
1613 | (void) wpa_driver_nl80211_set_bssid(drv, | |
1614 | (u8 *) "\x00\x00\x00\x00\x00\x00"); | |
1615 | ||
1616 | wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); | |
1617 | ||
97865538 | 1618 | eloop_unregister_read_sock(drv->wext_event_sock); |
3f5285e8 JM |
1619 | |
1620 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) | |
1621 | (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); | |
1622 | ||
97865538 | 1623 | close(drv->wext_event_sock); |
3f5285e8 JM |
1624 | close(drv->ioctl_sock); |
1625 | os_free(drv->assoc_req_ies); | |
1626 | os_free(drv->assoc_resp_ies); | |
1627 | ||
97865538 | 1628 | eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); |
3f5285e8 JM |
1629 | genl_family_put(drv->nl80211); |
1630 | nl_cache_free(drv->nl_cache); | |
1631 | nl_handle_destroy(drv->nl_handle); | |
1632 | nl_cb_put(drv->nl_cb); | |
1633 | ||
1634 | os_free(drv); | |
1635 | } | |
1636 | ||
1637 | ||
1638 | /** | |
1639 | * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion | |
1640 | * @eloop_ctx: Unused | |
1641 | * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() | |
1642 | * | |
1643 | * This function can be used as registered timeout when starting a scan to | |
1644 | * generate a scan completed event if the driver does not report this. | |
1645 | */ | |
1646 | static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) | |
1647 | { | |
1648 | wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); | |
1649 | wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); | |
1650 | } | |
1651 | ||
1652 | ||
1653 | /** | |
1654 | * wpa_driver_nl80211_scan - Request the driver to initiate scan | |
1655 | * @priv: Pointer to private wext data from wpa_driver_nl80211_init() | |
6a1063e0 | 1656 | * @params: Scan parameters |
3f5285e8 JM |
1657 | * Returns: 0 on success, -1 on failure |
1658 | */ | |
6a1063e0 JM |
1659 | static int wpa_driver_nl80211_scan(void *priv, |
1660 | struct wpa_driver_scan_params *params) | |
3f5285e8 JM |
1661 | { |
1662 | struct wpa_driver_nl80211_data *drv = priv; | |
3f5285e8 | 1663 | int ret = 0, timeout; |
0e75527f | 1664 | struct nl_msg *msg, *ssids; |
6a1063e0 | 1665 | size_t i; |
3f5285e8 | 1666 | |
0e75527f JM |
1667 | msg = nlmsg_alloc(); |
1668 | ssids = nlmsg_alloc(); | |
1669 | if (!msg || !ssids) { | |
1670 | nlmsg_free(msg); | |
1671 | nlmsg_free(ssids); | |
3f5285e8 JM |
1672 | return -1; |
1673 | } | |
1674 | ||
0e75527f JM |
1675 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, |
1676 | NL80211_CMD_TRIGGER_SCAN, 0); | |
1677 | ||
1678 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
3f5285e8 | 1679 | |
6a1063e0 JM |
1680 | for (i = 0; i < params->num_ssids; i++) { |
1681 | NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, | |
1682 | params->ssids[i].ssid); | |
3f5285e8 | 1683 | } |
6a1063e0 JM |
1684 | if (params->num_ssids) |
1685 | nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); | |
3f5285e8 | 1686 | |
d173df52 JM |
1687 | if (params->extra_ies) { |
1688 | NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, | |
1689 | params->extra_ies); | |
1690 | } | |
1691 | ||
0e75527f JM |
1692 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1693 | msg = NULL; | |
1694 | if (ret) { | |
1695 | wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " | |
1696 | "(%s)", ret, strerror(-ret)); | |
1697 | goto nla_put_failure; | |
3f5285e8 JM |
1698 | } |
1699 | ||
1700 | /* Not all drivers generate "scan completed" wireless event, so try to | |
1701 | * read results after a timeout. */ | |
0e75527f | 1702 | timeout = 10; |
3f5285e8 JM |
1703 | if (drv->scan_complete_events) { |
1704 | /* | |
d173df52 JM |
1705 | * The driver seems to deliver events to notify when scan is |
1706 | * complete, so use longer timeout to avoid race conditions | |
1707 | * with scanning and following association request. | |
3f5285e8 JM |
1708 | */ |
1709 | timeout = 30; | |
1710 | } | |
1711 | wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " | |
1712 | "seconds", ret, timeout); | |
1713 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); | |
0e75527f JM |
1714 | eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, |
1715 | drv, drv->ctx); | |
3f5285e8 | 1716 | |
0e75527f JM |
1717 | nla_put_failure: |
1718 | nlmsg_free(ssids); | |
1719 | nlmsg_free(msg); | |
3f5285e8 JM |
1720 | return ret; |
1721 | } | |
1722 | ||
1723 | ||
b3db1e1c | 1724 | static int bss_info_handler(struct nl_msg *msg, void *arg) |
3f5285e8 | 1725 | { |
b3db1e1c JM |
1726 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; |
1727 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
1728 | struct nlattr *bss[NL80211_BSS_MAX + 1]; | |
1729 | static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { | |
1730 | [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, | |
1731 | [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, | |
1732 | [NL80211_BSS_TSF] = { .type = NLA_U64 }, | |
1733 | [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, | |
1734 | [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, | |
1735 | [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, | |
1736 | [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, | |
1737 | [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, | |
1738 | }; | |
1739 | struct wpa_scan_results *res = arg; | |
3f5285e8 JM |
1740 | struct wpa_scan_res **tmp; |
1741 | struct wpa_scan_res *r; | |
b3db1e1c JM |
1742 | const u8 *ie; |
1743 | size_t ie_len; | |
3f5285e8 | 1744 | |
b3db1e1c JM |
1745 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
1746 | genlmsg_attrlen(gnlh, 0), NULL); | |
1747 | if (!tb[NL80211_ATTR_BSS]) | |
1748 | return NL_SKIP; | |
1749 | if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], | |
1750 | bss_policy)) | |
1751 | return NL_SKIP; | |
1752 | if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { | |
1753 | ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); | |
1754 | ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); | |
1755 | } else { | |
1756 | ie = NULL; | |
1757 | ie_len = 0; | |
1758 | } | |
3f5285e8 | 1759 | |
b3db1e1c | 1760 | r = os_zalloc(sizeof(*r) + ie_len); |
3f5285e8 | 1761 | if (r == NULL) |
b3db1e1c JM |
1762 | return NL_SKIP; |
1763 | if (bss[NL80211_BSS_BSSID]) | |
1764 | os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), | |
1765 | ETH_ALEN); | |
1766 | if (bss[NL80211_BSS_FREQUENCY]) | |
1767 | r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); | |
1768 | if (bss[NL80211_BSS_BEACON_INTERVAL]) | |
1769 | r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); | |
1770 | if (bss[NL80211_BSS_CAPABILITY]) | |
1771 | r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); | |
1772 | if (bss[NL80211_BSS_SIGNAL_UNSPEC]) | |
1773 | r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); | |
1774 | if (bss[NL80211_BSS_SIGNAL_MBM]) | |
1775 | r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); | |
1776 | if (bss[NL80211_BSS_TSF]) | |
1777 | r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); | |
1778 | r->ie_len = ie_len; | |
1779 | if (ie) | |
1780 | os_memcpy(r + 1, ie, ie_len); | |
3f5285e8 JM |
1781 | |
1782 | tmp = os_realloc(res->res, | |
1783 | (res->num + 1) * sizeof(struct wpa_scan_res *)); | |
1784 | if (tmp == NULL) { | |
1785 | os_free(r); | |
b3db1e1c | 1786 | return NL_SKIP; |
3f5285e8 JM |
1787 | } |
1788 | tmp[res->num++] = r; | |
1789 | res->res = tmp; | |
b3db1e1c JM |
1790 | |
1791 | return NL_SKIP; | |
3f5285e8 | 1792 | } |
b3db1e1c | 1793 | |
3f5285e8 JM |
1794 | |
1795 | /** | |
1796 | * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results | |
1797 | * @priv: Pointer to private wext data from wpa_driver_nl80211_init() | |
1798 | * Returns: Scan results on success, -1 on failure | |
1799 | */ | |
7e5ba1b9 JM |
1800 | static struct wpa_scan_results * |
1801 | wpa_driver_nl80211_get_scan_results(void *priv) | |
3f5285e8 JM |
1802 | { |
1803 | struct wpa_driver_nl80211_data *drv = priv; | |
b3db1e1c | 1804 | struct nl_msg *msg; |
3f5285e8 | 1805 | struct wpa_scan_results *res; |
b3db1e1c | 1806 | int ret; |
3f5285e8 JM |
1807 | |
1808 | res = os_zalloc(sizeof(*res)); | |
b3db1e1c JM |
1809 | if (res == NULL) |
1810 | return 0; | |
1811 | msg = nlmsg_alloc(); | |
1812 | if (!msg) | |
1813 | goto nla_put_failure; | |
3f5285e8 | 1814 | |
b3db1e1c JM |
1815 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, |
1816 | NL80211_CMD_GET_SCAN, 0); | |
1817 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
3f5285e8 | 1818 | |
b3db1e1c JM |
1819 | ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); |
1820 | msg = NULL; | |
1821 | if (ret == 0) { | |
1822 | wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", | |
1823 | (unsigned long) res->num); | |
1824 | return res; | |
3f5285e8 | 1825 | } |
b3db1e1c JM |
1826 | wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " |
1827 | "(%s)", ret, strerror(-ret)); | |
1828 | nla_put_failure: | |
1829 | nlmsg_free(msg); | |
1830 | wpa_scan_results_free(res); | |
1831 | return NULL; | |
3f5285e8 JM |
1832 | } |
1833 | ||
1834 | ||
1835 | static int wpa_driver_nl80211_get_range(void *priv) | |
1836 | { | |
1837 | struct wpa_driver_nl80211_data *drv = priv; | |
1838 | struct iw_range *range; | |
1839 | struct iwreq iwr; | |
1840 | int minlen; | |
1841 | size_t buflen; | |
1842 | ||
1843 | /* | |
1844 | * Use larger buffer than struct iw_range in order to allow the | |
1845 | * structure to grow in the future. | |
1846 | */ | |
1847 | buflen = sizeof(struct iw_range) + 500; | |
1848 | range = os_zalloc(buflen); | |
1849 | if (range == NULL) | |
1850 | return -1; | |
1851 | ||
1852 | os_memset(&iwr, 0, sizeof(iwr)); | |
1853 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
1854 | iwr.u.data.pointer = (caddr_t) range; | |
1855 | iwr.u.data.length = buflen; | |
1856 | ||
1857 | minlen = ((char *) &range->enc_capa) - (char *) range + | |
1858 | sizeof(range->enc_capa); | |
1859 | ||
1860 | if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { | |
1861 | perror("ioctl[SIOCGIWRANGE]"); | |
1862 | os_free(range); | |
1863 | return -1; | |
1864 | } else if (iwr.u.data.length >= minlen && | |
1865 | range->we_version_compiled >= 18) { | |
1866 | wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " | |
1867 | "WE(source)=%d enc_capa=0x%x", | |
1868 | range->we_version_compiled, | |
1869 | range->we_version_source, | |
1870 | range->enc_capa); | |
1871 | drv->has_capability = 1; | |
1872 | drv->we_version_compiled = range->we_version_compiled; | |
1873 | if (range->enc_capa & IW_ENC_CAPA_WPA) { | |
1874 | drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | | |
1875 | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; | |
1876 | } | |
1877 | if (range->enc_capa & IW_ENC_CAPA_WPA2) { | |
1878 | drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | | |
1879 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; | |
1880 | } | |
1881 | drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | | |
1882 | WPA_DRIVER_CAPA_ENC_WEP104; | |
1883 | if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) | |
1884 | drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; | |
1885 | if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) | |
1886 | drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; | |
1887 | wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", | |
1888 | drv->capa.key_mgmt, drv->capa.enc); | |
1889 | } else { | |
1890 | wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " | |
1891 | "assuming WPA is not supported"); | |
1892 | } | |
1893 | ||
1894 | os_free(range); | |
1895 | return 0; | |
1896 | } | |
1897 | ||
1898 | ||
1899 | static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) | |
1900 | { | |
1901 | struct wpa_driver_nl80211_data *drv = priv; | |
1902 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
1903 | ||
1904 | return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, | |
1905 | enabled); | |
1906 | } | |
1907 | ||
1908 | ||
1909 | static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg, | |
1910 | const u8 *addr, int key_idx, | |
1911 | int set_tx, const u8 *seq, | |
1912 | size_t seq_len, | |
1913 | const u8 *key, size_t key_len) | |
1914 | { | |
1915 | struct wpa_driver_nl80211_data *drv = priv; | |
6241fcb1 | 1916 | int err; |
3f5285e8 JM |
1917 | struct nl_msg *msg; |
1918 | ||
1919 | wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " | |
1920 | "seq_len=%lu key_len=%lu", | |
1921 | __func__, alg, addr, key_idx, set_tx, | |
1922 | (unsigned long) seq_len, (unsigned long) key_len); | |
1923 | ||
1924 | msg = nlmsg_alloc(); | |
1925 | if (msg == NULL) | |
1926 | return -1; | |
1927 | ||
1928 | if (alg == WPA_ALG_NONE) { | |
1929 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1930 | NL80211_CMD_DEL_KEY, 0); | |
1931 | } else { | |
1932 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1933 | NL80211_CMD_NEW_KEY, 0); | |
1934 | NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); | |
1935 | switch (alg) { | |
1936 | case WPA_ALG_WEP: | |
1937 | if (key_len == 5) | |
1938 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, | |
1939 | 0x000FAC01); | |
1940 | else | |
1941 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, | |
1942 | 0x000FAC05); | |
1943 | break; | |
1944 | case WPA_ALG_TKIP: | |
1945 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); | |
1946 | break; | |
1947 | case WPA_ALG_CCMP: | |
1948 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); | |
1949 | break; | |
1950 | default: | |
1951 | nlmsg_free(msg); | |
1952 | return -1; | |
1953 | } | |
1954 | } | |
1955 | ||
1956 | if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) | |
1957 | { | |
1958 | wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); | |
1959 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | |
1960 | } | |
1961 | NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); | |
1962 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1963 | ||
6241fcb1 JM |
1964 | err = send_and_recv_msgs(drv, msg, NULL, NULL); |
1965 | if (err) { | |
3f5285e8 | 1966 | wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); |
3f5285e8 JM |
1967 | return -1; |
1968 | } | |
1969 | ||
1970 | if (set_tx && alg != WPA_ALG_NONE) { | |
3f5285e8 JM |
1971 | msg = nlmsg_alloc(); |
1972 | if (msg == NULL) | |
1973 | return -1; | |
1974 | ||
1975 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1976 | 0, NL80211_CMD_SET_KEY, 0); | |
1977 | NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); | |
1978 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1979 | NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); | |
1980 | ||
6241fcb1 JM |
1981 | err = send_and_recv_msgs(drv, msg, NULL, NULL); |
1982 | if (err) { | |
3f5285e8 JM |
1983 | wpa_printf(MSG_DEBUG, "nl80211: set default key " |
1984 | "failed; err=%d", err); | |
3f5285e8 JM |
1985 | return -1; |
1986 | } | |
1987 | } | |
1988 | ||
6241fcb1 | 1989 | return 0; |
3f5285e8 JM |
1990 | |
1991 | nla_put_failure: | |
6241fcb1 | 1992 | return -ENOBUFS; |
3f5285e8 JM |
1993 | } |
1994 | ||
1995 | ||
1996 | static int wpa_driver_nl80211_set_countermeasures(void *priv, | |
1997 | int enabled) | |
1998 | { | |
1999 | struct wpa_driver_nl80211_data *drv = priv; | |
2000 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
2001 | return wpa_driver_nl80211_set_auth_param(drv, | |
2002 | IW_AUTH_TKIP_COUNTERMEASURES, | |
2003 | enabled); | |
2004 | } | |
2005 | ||
2006 | ||
2007 | static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, | |
2008 | int enabled) | |
2009 | { | |
2010 | struct wpa_driver_nl80211_data *drv = priv; | |
2011 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
2012 | drv->use_crypt = enabled; | |
2013 | return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, | |
2014 | enabled); | |
2015 | } | |
2016 | ||
2017 | ||
2018 | static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, | |
2019 | const u8 *addr, int cmd, int reason_code) | |
2020 | { | |
2021 | struct iwreq iwr; | |
2022 | struct iw_mlme mlme; | |
2023 | int ret = 0; | |
2024 | ||
2025 | os_memset(&iwr, 0, sizeof(iwr)); | |
2026 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
2027 | os_memset(&mlme, 0, sizeof(mlme)); | |
2028 | mlme.cmd = cmd; | |
2029 | mlme.reason_code = reason_code; | |
2030 | mlme.addr.sa_family = ARPHRD_ETHER; | |
2031 | os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); | |
2032 | iwr.u.data.pointer = (caddr_t) &mlme; | |
2033 | iwr.u.data.length = sizeof(mlme); | |
2034 | ||
2035 | if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { | |
2036 | perror("ioctl[SIOCSIWMLME]"); | |
2037 | ret = -1; | |
2038 | } | |
2039 | ||
2040 | return ret; | |
2041 | } | |
2042 | ||
2043 | ||
2044 | static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, | |
2045 | int reason_code) | |
2046 | { | |
2047 | struct wpa_driver_nl80211_data *drv = priv; | |
2048 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
2049 | return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); | |
2050 | } | |
2051 | ||
2052 | ||
2053 | static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, | |
2054 | int reason_code) | |
2055 | { | |
2056 | struct wpa_driver_nl80211_data *drv = priv; | |
2057 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
2058 | return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, | |
2059 | reason_code); | |
2060 | } | |
2061 | ||
2062 | ||
2063 | static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, | |
2064 | size_t ie_len) | |
2065 | { | |
2066 | struct wpa_driver_nl80211_data *drv = priv; | |
2067 | struct iwreq iwr; | |
2068 | int ret = 0; | |
2069 | ||
2070 | os_memset(&iwr, 0, sizeof(iwr)); | |
2071 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
2072 | iwr.u.data.pointer = (caddr_t) ie; | |
2073 | iwr.u.data.length = ie_len; | |
2074 | ||
2075 | if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { | |
2076 | perror("ioctl[SIOCSIWGENIE]"); | |
2077 | ret = -1; | |
2078 | } | |
2079 | ||
2080 | return ret; | |
2081 | } | |
2082 | ||
2083 | ||
2084 | static int wpa_driver_nl80211_cipher2wext(int cipher) | |
2085 | { | |
2086 | switch (cipher) { | |
2087 | case CIPHER_NONE: | |
2088 | return IW_AUTH_CIPHER_NONE; | |
2089 | case CIPHER_WEP40: | |
2090 | return IW_AUTH_CIPHER_WEP40; | |
2091 | case CIPHER_TKIP: | |
2092 | return IW_AUTH_CIPHER_TKIP; | |
2093 | case CIPHER_CCMP: | |
2094 | return IW_AUTH_CIPHER_CCMP; | |
2095 | case CIPHER_WEP104: | |
2096 | return IW_AUTH_CIPHER_WEP104; | |
2097 | default: | |
2098 | return 0; | |
2099 | } | |
2100 | } | |
2101 | ||
2102 | ||
2103 | static int wpa_driver_nl80211_keymgmt2wext(int keymgmt) | |
2104 | { | |
2105 | switch (keymgmt) { | |
2106 | case KEY_MGMT_802_1X: | |
2107 | case KEY_MGMT_802_1X_NO_WPA: | |
2108 | return IW_AUTH_KEY_MGMT_802_1X; | |
2109 | case KEY_MGMT_PSK: | |
2110 | return IW_AUTH_KEY_MGMT_PSK; | |
2111 | default: | |
2112 | return 0; | |
2113 | } | |
2114 | } | |
2115 | ||
2116 | ||
2117 | static int | |
2118 | wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, | |
2119 | struct wpa_driver_associate_params *params) | |
2120 | { | |
2121 | struct iwreq iwr; | |
2122 | int ret = 0; | |
2123 | ||
2124 | wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " | |
2125 | "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); | |
2126 | ||
2127 | os_memset(&iwr, 0, sizeof(iwr)); | |
2128 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
2129 | /* Just changing mode, not actual keys */ | |
2130 | iwr.u.encoding.flags = 0; | |
2131 | iwr.u.encoding.pointer = (caddr_t) NULL; | |
2132 | iwr.u.encoding.length = 0; | |
2133 | ||
2134 | /* | |
2135 | * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two | |
2136 | * different things. Here they are used to indicate Open System vs. | |
2137 | * Shared Key authentication algorithm. However, some drivers may use | |
2138 | * them to select between open/restricted WEP encrypted (open = allow | |
2139 | * both unencrypted and encrypted frames; restricted = only allow | |
2140 | * encrypted frames). | |
2141 | */ | |
2142 | ||
2143 | if (!drv->use_crypt) { | |
2144 | iwr.u.encoding.flags |= IW_ENCODE_DISABLED; | |
2145 | } else { | |
2146 | if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) | |
2147 | iwr.u.encoding.flags |= IW_ENCODE_OPEN; | |
2148 | if (params->auth_alg & AUTH_ALG_SHARED_KEY) | |
2149 | iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; | |
2150 | } | |
2151 | ||
2152 | if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { | |
2153 | perror("ioctl[SIOCSIWENCODE]"); | |
2154 | ret = -1; | |
2155 | } | |
2156 | ||
2157 | return ret; | |
2158 | } | |
2159 | ||
2160 | ||
2161 | static int wpa_driver_nl80211_associate( | |
2162 | void *priv, struct wpa_driver_associate_params *params) | |
2163 | { | |
2164 | struct wpa_driver_nl80211_data *drv = priv; | |
2165 | int ret = 0; | |
2166 | int allow_unencrypted_eapol; | |
2167 | int value; | |
2168 | ||
2169 | wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); | |
2170 | ||
2171 | /* | |
2172 | * If the driver did not support SIOCSIWAUTH, fallback to | |
2173 | * SIOCSIWENCODE here. | |
2174 | */ | |
2175 | if (drv->auth_alg_fallback && | |
2176 | wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) | |
2177 | ret = -1; | |
2178 | ||
2179 | if (!params->bssid && | |
2180 | wpa_driver_nl80211_set_bssid(drv, NULL) < 0) | |
2181 | ret = -1; | |
2182 | ||
3f5285e8 JM |
2183 | /* TODO: should consider getting wpa version and cipher/key_mgmt suites |
2184 | * from configuration, not from here, where only the selected suite is | |
2185 | * available */ | |
2186 | if (wpa_driver_nl80211_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) | |
2187 | < 0) | |
2188 | ret = -1; | |
2189 | if (params->wpa_ie == NULL || params->wpa_ie_len == 0) | |
2190 | value = IW_AUTH_WPA_VERSION_DISABLED; | |
2191 | else if (params->wpa_ie[0] == WLAN_EID_RSN) | |
2192 | value = IW_AUTH_WPA_VERSION_WPA2; | |
2193 | else | |
2194 | value = IW_AUTH_WPA_VERSION_WPA; | |
2195 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2196 | IW_AUTH_WPA_VERSION, value) < 0) | |
2197 | ret = -1; | |
2198 | value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); | |
2199 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2200 | IW_AUTH_CIPHER_PAIRWISE, value) < 0) | |
2201 | ret = -1; | |
2202 | value = wpa_driver_nl80211_cipher2wext(params->group_suite); | |
2203 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2204 | IW_AUTH_CIPHER_GROUP, value) < 0) | |
2205 | ret = -1; | |
2206 | value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); | |
2207 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2208 | IW_AUTH_KEY_MGMT, value) < 0) | |
2209 | ret = -1; | |
2210 | value = params->key_mgmt_suite != KEY_MGMT_NONE || | |
2211 | params->pairwise_suite != CIPHER_NONE || | |
2212 | params->group_suite != CIPHER_NONE || | |
2213 | params->wpa_ie_len; | |
2214 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2215 | IW_AUTH_PRIVACY_INVOKED, value) < 0) | |
2216 | ret = -1; | |
2217 | ||
2218 | /* Allow unencrypted EAPOL messages even if pairwise keys are set when | |
2219 | * not using WPA. IEEE 802.1X specifies that these frames are not | |
2220 | * encrypted, but WPA encrypts them when pairwise keys are in use. */ | |
2221 | if (params->key_mgmt_suite == KEY_MGMT_802_1X || | |
2222 | params->key_mgmt_suite == KEY_MGMT_PSK) | |
2223 | allow_unencrypted_eapol = 0; | |
2224 | else | |
2225 | allow_unencrypted_eapol = 1; | |
2226 | ||
2227 | if (wpa_driver_nl80211_set_auth_param(drv, | |
2228 | IW_AUTH_RX_UNENCRYPTED_EAPOL, | |
2229 | allow_unencrypted_eapol) < 0) | |
2230 | ret = -1; | |
2231 | if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) | |
2232 | ret = -1; | |
2233 | if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) | |
2234 | ret = -1; | |
2235 | if (params->bssid && | |
2236 | wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) | |
2237 | ret = -1; | |
2238 | ||
2239 | return ret; | |
2240 | } | |
2241 | ||
2242 | ||
2243 | static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) | |
2244 | { | |
2245 | struct wpa_driver_nl80211_data *drv = priv; | |
2246 | int algs = 0, res; | |
2247 | ||
2248 | if (auth_alg & AUTH_ALG_OPEN_SYSTEM) | |
2249 | algs |= IW_AUTH_ALG_OPEN_SYSTEM; | |
2250 | if (auth_alg & AUTH_ALG_SHARED_KEY) | |
2251 | algs |= IW_AUTH_ALG_SHARED_KEY; | |
2252 | if (auth_alg & AUTH_ALG_LEAP) | |
2253 | algs |= IW_AUTH_ALG_LEAP; | |
2254 | if (algs == 0) { | |
2255 | /* at least one algorithm should be set */ | |
2256 | algs = IW_AUTH_ALG_OPEN_SYSTEM; | |
2257 | } | |
2258 | ||
2259 | res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, | |
2260 | algs); | |
2261 | drv->auth_alg_fallback = res == -2; | |
2262 | return res; | |
2263 | } | |
2264 | ||
2265 | ||
2266 | /** | |
2267 | * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE | |
2268 | * @priv: Pointer to private wext data from wpa_driver_nl80211_init() | |
2269 | * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS | |
2270 | * Returns: 0 on success, -1 on failure | |
2271 | */ | |
2272 | static int wpa_driver_nl80211_set_mode(void *priv, int mode) | |
2273 | { | |
2274 | struct wpa_driver_nl80211_data *drv = priv; | |
2275 | int ret = -1, flags; | |
2276 | struct nl_msg *msg; | |
2277 | ||
2278 | msg = nlmsg_alloc(); | |
2279 | if (!msg) | |
2280 | return -1; | |
2281 | ||
2282 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
2283 | 0, NL80211_CMD_SET_INTERFACE, 0); | |
2284 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
0a4e6cbf JM |
2285 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, |
2286 | mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); | |
3f5285e8 | 2287 | |
6241fcb1 JM |
2288 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
2289 | if (!ret) | |
2290 | return 0; | |
2291 | else | |
3f5285e8 JM |
2292 | goto try_again; |
2293 | ||
3f5285e8 | 2294 | nla_put_failure: |
6241fcb1 | 2295 | wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode"); |
3f5285e8 JM |
2296 | return -1; |
2297 | ||
2298 | try_again: | |
2299 | /* mac80211 doesn't allow mode changes while the device is up, so | |
2300 | * take the device down, try to set the mode again, and bring the | |
2301 | * device back up. | |
2302 | */ | |
2303 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { | |
2304 | (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); | |
2305 | ||
2306 | /* Try to set the mode again while the interface is down */ | |
6241fcb1 JM |
2307 | msg = nlmsg_alloc(); |
2308 | if (!msg) | |
2309 | return -1; | |
2310 | ||
2311 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
2312 | 0, NL80211_CMD_SET_INTERFACE, 0); | |
2313 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
2314 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, | |
2315 | mode ? NL80211_IFTYPE_ADHOC : | |
2316 | NL80211_IFTYPE_STATION); | |
2317 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); | |
2318 | if (ret) { | |
3f5285e8 JM |
2319 | wpa_printf(MSG_ERROR, "Failed to set interface %s " |
2320 | "mode", drv->ifname); | |
6241fcb1 | 2321 | } |
3f5285e8 JM |
2322 | |
2323 | /* Ignore return value of get_ifflags to ensure that the device | |
2324 | * is always up like it was before this function was called. | |
2325 | */ | |
2326 | (void) wpa_driver_nl80211_get_ifflags(drv, &flags); | |
2327 | (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); | |
2328 | } | |
2329 | ||
3f5285e8 JM |
2330 | return ret; |
2331 | } | |
2332 | ||
2333 | ||
2334 | static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, | |
2335 | u32 cmd, const u8 *bssid, const u8 *pmkid) | |
2336 | { | |
2337 | struct iwreq iwr; | |
2338 | struct iw_pmksa pmksa; | |
2339 | int ret = 0; | |
2340 | ||
2341 | os_memset(&iwr, 0, sizeof(iwr)); | |
2342 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
2343 | os_memset(&pmksa, 0, sizeof(pmksa)); | |
2344 | pmksa.cmd = cmd; | |
2345 | pmksa.bssid.sa_family = ARPHRD_ETHER; | |
2346 | if (bssid) | |
2347 | os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); | |
2348 | if (pmkid) | |
2349 | os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); | |
2350 | iwr.u.data.pointer = (caddr_t) &pmksa; | |
2351 | iwr.u.data.length = sizeof(pmksa); | |
2352 | ||
2353 | if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { | |
2354 | if (errno != EOPNOTSUPP) | |
2355 | perror("ioctl[SIOCSIWPMKSA]"); | |
2356 | ret = -1; | |
2357 | } | |
2358 | ||
2359 | return ret; | |
2360 | } | |
2361 | ||
2362 | ||
2363 | static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, | |
2364 | const u8 *pmkid) | |
2365 | { | |
2366 | struct wpa_driver_nl80211_data *drv = priv; | |
2367 | return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); | |
2368 | } | |
2369 | ||
2370 | ||
2371 | static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, | |
2372 | const u8 *pmkid) | |
2373 | { | |
2374 | struct wpa_driver_nl80211_data *drv = priv; | |
2375 | return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); | |
2376 | } | |
2377 | ||
2378 | ||
2379 | static int wpa_driver_nl80211_flush_pmkid(void *priv) | |
2380 | { | |
2381 | struct wpa_driver_nl80211_data *drv = priv; | |
2382 | return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); | |
2383 | } | |
2384 | ||
2385 | ||
2386 | static int wpa_driver_nl80211_get_capa(void *priv, | |
2387 | struct wpa_driver_capa *capa) | |
2388 | { | |
2389 | struct wpa_driver_nl80211_data *drv = priv; | |
2390 | if (!drv->has_capability) | |
2391 | return -1; | |
2392 | os_memcpy(capa, &drv->capa, sizeof(*capa)); | |
2393 | return 0; | |
2394 | } | |
2395 | ||
2396 | ||
2397 | static int wpa_driver_nl80211_set_operstate(void *priv, int state) | |
2398 | { | |
2399 | struct wpa_driver_nl80211_data *drv = priv; | |
2400 | ||
2401 | wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", | |
2402 | __func__, drv->operstate, state, state ? "UP" : "DORMANT"); | |
2403 | drv->operstate = state; | |
2404 | return wpa_driver_nl80211_send_oper_ifla( | |
2405 | drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); | |
2406 | } | |
2407 | ||
2408 | ||
1c873584 JM |
2409 | #ifdef CONFIG_CLIENT_MLME |
2410 | static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) | |
2411 | { | |
2412 | if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { | |
2413 | wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " | |
2414 | "MLME"); | |
2415 | return -1; | |
2416 | } | |
2417 | if (wpa_driver_nl80211_create_monitor_interface(drv)) { | |
2418 | wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " | |
2419 | "interface"); | |
2420 | return -1; | |
2421 | } | |
2422 | return 0; | |
2423 | } | |
2424 | #endif /* CONFIG_CLIENT_MLME */ | |
2425 | ||
2426 | ||
2427 | static int wpa_driver_nl80211_set_param(void *priv, const char *param) | |
2428 | { | |
2429 | #ifdef CONFIG_CLIENT_MLME | |
2430 | struct wpa_driver_nl80211_data *drv = priv; | |
2431 | ||
2432 | if (param == NULL) | |
2433 | return 0; | |
2434 | ||
2435 | wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); | |
2436 | ||
2437 | if (os_strstr(param, "use_mlme=1")) { | |
2438 | wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); | |
2439 | drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; | |
2440 | ||
2441 | if (wpa_driver_nl80211_open_mlme(drv)) | |
2442 | return -1; | |
2443 | } | |
2444 | #endif /* CONFIG_CLIENT_MLME */ | |
2445 | ||
2446 | return 0; | |
2447 | } | |
2448 | ||
2449 | ||
2450 | #ifdef CONFIG_CLIENT_MLME | |
2451 | ||
1c873584 JM |
2452 | struct phy_info_arg { |
2453 | u16 *num_modes; | |
2454 | struct wpa_hw_modes *modes; | |
1c873584 JM |
2455 | }; |
2456 | ||
2457 | ||
2458 | static int phy_info_handler(struct nl_msg *msg, void *arg) | |
2459 | { | |
2460 | struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; | |
2461 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
2462 | struct phy_info_arg *phy_info = arg; | |
2463 | ||
2464 | struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; | |
2465 | ||
2466 | struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; | |
2467 | static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] | |
2468 | = { | |
2469 | [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, | |
2470 | [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, | |
2471 | [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, | |
2472 | [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, | |
2473 | [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, | |
2474 | }; | |
2475 | ||
2476 | struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; | |
2477 | static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { | |
2478 | [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, | |
2479 | [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = | |
2480 | { .type = NLA_FLAG }, | |
2481 | }; | |
2482 | ||
2483 | struct nlattr *nl_band; | |
2484 | struct nlattr *nl_freq; | |
2485 | struct nlattr *nl_rate; | |
2486 | int rem_band, rem_freq, rem_rate; | |
2487 | struct wpa_hw_modes *mode; | |
2488 | int idx, mode_is_set; | |
2489 | ||
2490 | nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
2491 | genlmsg_attrlen(gnlh, 0), NULL); | |
2492 | ||
2493 | if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) | |
2494 | return NL_SKIP; | |
2495 | ||
2496 | nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], | |
2497 | rem_band) { | |
2498 | mode = os_realloc(phy_info->modes, | |
2499 | (*phy_info->num_modes + 1) * sizeof(*mode)); | |
2500 | if (!mode) | |
2501 | return NL_SKIP; | |
2502 | phy_info->modes = mode; | |
2503 | ||
2504 | mode_is_set = 0; | |
2505 | ||
2506 | mode = &phy_info->modes[*(phy_info->num_modes)]; | |
2507 | os_memset(mode, 0, sizeof(*mode)); | |
2508 | *(phy_info->num_modes) += 1; | |
2509 | ||
2510 | nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), | |
2511 | nla_len(nl_band), NULL); | |
2512 | ||
2513 | nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], | |
2514 | rem_freq) { | |
2515 | nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, | |
2516 | nla_data(nl_freq), nla_len(nl_freq), | |
2517 | freq_policy); | |
2518 | if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) | |
2519 | continue; | |
2520 | mode->num_channels++; | |
2521 | } | |
2522 | ||
2523 | mode->channels = os_zalloc(mode->num_channels * | |
2524 | sizeof(struct wpa_channel_data)); | |
2525 | if (!mode->channels) | |
2526 | return NL_SKIP; | |
2527 | ||
2528 | idx = 0; | |
2529 | ||
2530 | nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], | |
2531 | rem_freq) { | |
2532 | nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, | |
2533 | nla_data(nl_freq), nla_len(nl_freq), | |
2534 | freq_policy); | |
2535 | if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) | |
2536 | continue; | |
2537 | ||
2538 | mode->channels[idx].freq = nla_get_u32( | |
2539 | tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); | |
2540 | mode->channels[idx].flag |= WPA_CHAN_W_SCAN | | |
2541 | WPA_CHAN_W_ACTIVE_SCAN | | |
2542 | WPA_CHAN_W_IBSS; | |
2543 | ||
2544 | if (!mode_is_set) { | |
2545 | /* crude heuristic */ | |
2546 | if (mode->channels[idx].freq < 4000) | |
2547 | mode->mode = WPA_MODE_IEEE80211B; | |
2548 | else | |
2549 | mode->mode = WPA_MODE_IEEE80211A; | |
2550 | mode_is_set = 1; | |
2551 | } | |
2552 | ||
2553 | /* crude heuristic */ | |
2554 | if (mode->channels[idx].freq < 4000) { | |
2555 | if (mode->channels[idx].freq == 2848) | |
2556 | mode->channels[idx].chan = 14; | |
2557 | else | |
2558 | mode->channels[idx].chan = | |
2559 | (mode->channels[idx].freq - | |
2560 | 2407) / 5; | |
2561 | } else | |
2562 | mode->channels[idx].chan = | |
2563 | mode->channels[idx].freq / 5 - 1000; | |
2564 | ||
2565 | if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) | |
2566 | mode->channels[idx].flag &= ~WPA_CHAN_W_SCAN; | |
2567 | if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) | |
2568 | mode->channels[idx].flag &= | |
2569 | ~WPA_CHAN_W_ACTIVE_SCAN; | |
2570 | if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) | |
2571 | mode->channels[idx].flag &= ~WPA_CHAN_W_IBSS; | |
2572 | idx++; | |
2573 | } | |
2574 | ||
2575 | nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], | |
2576 | rem_rate) { | |
2577 | nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, | |
2578 | nla_data(nl_rate), nla_len(nl_rate), | |
2579 | rate_policy); | |
2580 | if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) | |
2581 | continue; | |
2582 | mode->num_rates++; | |
2583 | } | |
2584 | ||
2585 | mode->rates = os_zalloc(mode->num_rates * | |
2586 | sizeof(struct wpa_rate_data)); | |
2587 | if (!mode->rates) | |
2588 | return NL_SKIP; | |
2589 | ||
2590 | idx = 0; | |
2591 | ||
2592 | nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], | |
2593 | rem_rate) { | |
2594 | nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, | |
2595 | nla_data(nl_rate), nla_len(nl_rate), | |
2596 | rate_policy); | |
2597 | if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) | |
2598 | continue; | |
2599 | mode->rates[idx].rate = nla_get_u32( | |
2600 | tb_rate[NL80211_BITRATE_ATTR_RATE]); | |
2601 | ||
2602 | /* crude heuristic */ | |
2603 | if (mode->mode == WPA_MODE_IEEE80211B && | |
2604 | mode->rates[idx].rate > 200) | |
2605 | mode->mode = WPA_MODE_IEEE80211G; | |
2606 | ||
2607 | if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) | |
2608 | mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; | |
2609 | ||
2610 | idx++; | |
2611 | } | |
2612 | } | |
2613 | ||
1c873584 JM |
2614 | return NL_SKIP; |
2615 | } | |
2616 | ||
2617 | ||
2618 | static struct wpa_hw_modes * | |
2619 | wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) | |
2620 | { | |
2621 | struct wpa_driver_nl80211_data *drv = priv; | |
2622 | struct nl_msg *msg; | |
1c873584 JM |
2623 | struct phy_info_arg result = { |
2624 | .num_modes = num_modes, | |
2625 | .modes = NULL, | |
1c873584 JM |
2626 | }; |
2627 | ||
2628 | *num_modes = 0; | |
2629 | *flags = 0; | |
2630 | ||
2631 | msg = nlmsg_alloc(); | |
2632 | if (!msg) | |
2633 | return NULL; | |
2634 | ||
2635 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
2636 | 0, NL80211_CMD_GET_WIPHY, 0); | |
2637 | ||
2638 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
2639 | ||
6241fcb1 JM |
2640 | if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) |
2641 | return result.modes; | |
2642 | nla_put_failure: | |
2643 | return NULL; | |
1c873584 JM |
2644 | } |
2645 | ||
2646 | ||
2647 | static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, | |
2648 | int chan, int freq) | |
2649 | { | |
2650 | return wpa_driver_nl80211_set_freq(priv, freq); | |
2651 | } | |
2652 | ||
2653 | ||
2654 | static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, | |
2655 | size_t data_len) | |
2656 | { | |
2657 | struct wpa_driver_nl80211_data *drv = priv; | |
2658 | __u8 rtap_hdr[] = { | |
2659 | 0x00, 0x00, /* radiotap version */ | |
2660 | 0x0e, 0x00, /* radiotap length */ | |
2661 | 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ | |
2662 | 0x0c, /* F_WEP | F_FRAG (encrypt/fragment if required) */ | |
2663 | 0x00, /* padding */ | |
2664 | 0x00, 0x00, /* RX and TX flags to indicate that */ | |
2665 | 0x00, 0x00, /* this is the injected frame directly */ | |
2666 | }; | |
2667 | struct iovec iov[2] = { | |
2668 | { | |
2669 | .iov_base = &rtap_hdr, | |
2670 | .iov_len = sizeof(rtap_hdr), | |
2671 | }, | |
2672 | { | |
2673 | .iov_base = (void *) data, | |
2674 | .iov_len = data_len, | |
2675 | } | |
2676 | }; | |
2677 | struct msghdr msg = { | |
2678 | .msg_name = NULL, | |
2679 | .msg_namelen = 0, | |
2680 | .msg_iov = iov, | |
2681 | .msg_iovlen = 2, | |
2682 | .msg_control = NULL, | |
2683 | .msg_controllen = 0, | |
2684 | .msg_flags = 0, | |
2685 | }; | |
2686 | ||
2687 | if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { | |
2688 | perror("send[MLME]"); | |
2689 | return -1; | |
2690 | } | |
2691 | ||
2692 | return 0; | |
2693 | } | |
2694 | ||
3c83f19c JM |
2695 | |
2696 | static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, | |
2697 | const u8 *supp_rates, | |
2698 | size_t supp_rates_len) | |
2699 | { | |
2700 | struct wpa_driver_nl80211_data *drv = priv; | |
2701 | struct nl_msg *msg; | |
2702 | int ret = -1; | |
2703 | ||
2704 | msg = nlmsg_alloc(); | |
2705 | if (!msg) | |
6241fcb1 | 2706 | return -ENOMEM; |
3c83f19c JM |
2707 | |
2708 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
2709 | 0, NL80211_CMD_NEW_STATION, 0); | |
2710 | ||
2711 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
2712 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | |
2713 | /* TODO: Get proper Association ID and listen interval */ | |
2714 | NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); | |
2715 | NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, | |
2716 | supp_rates); | |
2717 | NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); | |
2718 | ||
6241fcb1 | 2719 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
3c83f19c JM |
2720 | /* ignore EEXIST, this happens if a STA associates while associated */ |
2721 | if (ret == -EEXIST || ret >= 0) | |
2722 | ret = 0; | |
2723 | ||
6241fcb1 | 2724 | nla_put_failure: |
3c83f19c JM |
2725 | return ret; |
2726 | } | |
2727 | ||
2728 | ||
2729 | static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) | |
2730 | { | |
2731 | struct wpa_driver_nl80211_data *drv = priv; | |
2732 | struct nl_msg *msg; | |
2733 | int ret = -1; | |
2734 | ||
2735 | msg = nlmsg_alloc(); | |
2736 | if (!msg) | |
6241fcb1 | 2737 | return -ENOMEM; |
3c83f19c JM |
2738 | |
2739 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
2740 | 0, NL80211_CMD_DEL_STATION, 0); | |
2741 | ||
2742 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
2743 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | |
2744 | ||
2745 | ret = 0; | |
2746 | ||
6241fcb1 | 2747 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
3c83f19c | 2748 | return ret; |
6241fcb1 JM |
2749 | |
2750 | nla_put_failure: | |
2751 | return -ENOBUFS; | |
3c83f19c JM |
2752 | } |
2753 | ||
1c873584 JM |
2754 | #endif /* CONFIG_CLIENT_MLME */ |
2755 | ||
2756 | ||
3f5285e8 JM |
2757 | const struct wpa_driver_ops wpa_driver_nl80211_ops = { |
2758 | .name = "nl80211", | |
2759 | .desc = "Linux nl80211/cfg80211", | |
2760 | .get_bssid = wpa_driver_nl80211_get_bssid, | |
2761 | .get_ssid = wpa_driver_nl80211_get_ssid, | |
2762 | .set_wpa = wpa_driver_nl80211_set_wpa, | |
2763 | .set_key = wpa_driver_nl80211_set_key, | |
2764 | .set_countermeasures = wpa_driver_nl80211_set_countermeasures, | |
2765 | .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, | |
6a1063e0 | 2766 | .scan2 = wpa_driver_nl80211_scan, |
3f5285e8 JM |
2767 | .get_scan_results2 = wpa_driver_nl80211_get_scan_results, |
2768 | .deauthenticate = wpa_driver_nl80211_deauthenticate, | |
2769 | .disassociate = wpa_driver_nl80211_disassociate, | |
ec5f180a | 2770 | .set_mode = wpa_driver_nl80211_set_mode, |
3f5285e8 JM |
2771 | .associate = wpa_driver_nl80211_associate, |
2772 | .set_auth_alg = wpa_driver_nl80211_set_auth_alg, | |
2773 | .init = wpa_driver_nl80211_init, | |
2774 | .deinit = wpa_driver_nl80211_deinit, | |
1c873584 | 2775 | .set_param = wpa_driver_nl80211_set_param, |
3f5285e8 JM |
2776 | .add_pmkid = wpa_driver_nl80211_add_pmkid, |
2777 | .remove_pmkid = wpa_driver_nl80211_remove_pmkid, | |
2778 | .flush_pmkid = wpa_driver_nl80211_flush_pmkid, | |
2779 | .get_capa = wpa_driver_nl80211_get_capa, | |
2780 | .set_operstate = wpa_driver_nl80211_set_operstate, | |
6d158490 | 2781 | .set_country = wpa_driver_nl80211_set_country, |
1c873584 JM |
2782 | #ifdef CONFIG_CLIENT_MLME |
2783 | .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, | |
2784 | .set_channel = wpa_driver_nl80211_set_channel, | |
2785 | .set_ssid = wpa_driver_nl80211_set_ssid, | |
2786 | .set_bssid = wpa_driver_nl80211_set_bssid, | |
2787 | .send_mlme = wpa_driver_nl80211_send_mlme, | |
3c83f19c JM |
2788 | .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, |
2789 | .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, | |
1c873584 | 2790 | #endif /* CONFIG_CLIENT_MLME */ |
3f5285e8 | 2791 | }; |