]>
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" |
3f5285e8 | 22 | #include "wireless_copy.h" |
625f587b | 23 | |
3f5285e8 JM |
24 | #include "common.h" |
25 | #include "driver.h" | |
26 | #include "eloop.h" | |
27 | #include "ieee802_11_defs.h" | |
28 | ||
29 | #ifndef IFF_LOWER_UP | |
30 | #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ | |
31 | #endif | |
32 | #ifndef IFF_DORMANT | |
33 | #define IFF_DORMANT 0x20000 /* driver signals dormant */ | |
34 | #endif | |
35 | ||
36 | #ifndef IF_OPER_DORMANT | |
37 | #define IF_OPER_DORMANT 5 | |
38 | #endif | |
39 | #ifndef IF_OPER_UP | |
40 | #define IF_OPER_UP 6 | |
41 | #endif | |
42 | ||
43 | ||
44 | struct wpa_driver_nl80211_data { | |
45 | void *ctx; | |
d8816397 | 46 | int link_event_sock; |
3f5285e8 JM |
47 | int ioctl_sock; |
48 | char ifname[IFNAMSIZ + 1]; | |
49 | int ifindex; | |
7524cfb1 | 50 | int if_removed; |
c2a04078 JM |
51 | struct wpa_driver_capa capa; |
52 | int has_capability; | |
c2a04078 | 53 | |
3f5285e8 JM |
54 | int operstate; |
55 | ||
3f5285e8 JM |
56 | int scan_complete_events; |
57 | ||
58 | struct nl_handle *nl_handle; | |
59 | struct nl_cache *nl_cache; | |
60 | struct nl_cb *nl_cb; | |
61 | struct genl_family *nl80211; | |
1c873584 | 62 | |
c2a04078 JM |
63 | u8 bssid[ETH_ALEN]; |
64 | int associated; | |
fd05d64e JM |
65 | u8 ssid[32]; |
66 | size_t ssid_len; | |
3f5285e8 JM |
67 | }; |
68 | ||
69 | ||
70 | static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, | |
71 | void *timeout_ctx); | |
36b15723 JM |
72 | static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv, |
73 | int mode); | |
362f781e | 74 | static int |
7524cfb1 JM |
75 | wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); |
76 | ||
3f5285e8 | 77 | |
6241fcb1 JM |
78 | /* nl80211 code */ |
79 | static int ack_handler(struct nl_msg *msg, void *arg) | |
80 | { | |
81 | int *err = arg; | |
82 | *err = 0; | |
83 | return NL_STOP; | |
84 | } | |
85 | ||
86 | static int finish_handler(struct nl_msg *msg, void *arg) | |
87 | { | |
8e8df255 JM |
88 | int *ret = arg; |
89 | *ret = 0; | |
6241fcb1 JM |
90 | return NL_SKIP; |
91 | } | |
92 | ||
93 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, | |
94 | void *arg) | |
95 | { | |
96 | int *ret = arg; | |
97 | *ret = err->error; | |
98 | return NL_SKIP; | |
99 | } | |
100 | ||
101 | static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, | |
102 | struct nl_msg *msg, | |
103 | int (*valid_handler)(struct nl_msg *, void *), | |
104 | void *valid_data) | |
105 | { | |
106 | struct nl_cb *cb; | |
107 | int err = -ENOMEM; | |
108 | ||
109 | cb = nl_cb_clone(drv->nl_cb); | |
110 | if (!cb) | |
111 | goto out; | |
112 | ||
113 | err = nl_send_auto_complete(drv->nl_handle, msg); | |
114 | if (err < 0) | |
115 | goto out; | |
116 | ||
117 | err = 1; | |
118 | ||
119 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); | |
8e8df255 | 120 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); |
6241fcb1 JM |
121 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); |
122 | ||
123 | if (valid_handler) | |
124 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, | |
125 | valid_handler, valid_data); | |
126 | ||
127 | while (err > 0) | |
128 | nl_recvmsgs(drv->nl_handle, cb); | |
129 | out: | |
130 | nl_cb_put(cb); | |
131 | nlmsg_free(msg); | |
132 | return err; | |
133 | } | |
134 | ||
135 | ||
97865538 JM |
136 | struct family_data { |
137 | const char *group; | |
138 | int id; | |
139 | }; | |
140 | ||
141 | ||
142 | static int family_handler(struct nl_msg *msg, void *arg) | |
143 | { | |
144 | struct family_data *res = arg; | |
145 | struct nlattr *tb[CTRL_ATTR_MAX + 1]; | |
146 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
147 | struct nlattr *mcgrp; | |
148 | int i; | |
149 | ||
150 | nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
151 | genlmsg_attrlen(gnlh, 0), NULL); | |
152 | if (!tb[CTRL_ATTR_MCAST_GROUPS]) | |
153 | return NL_SKIP; | |
154 | ||
155 | nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { | |
156 | struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; | |
157 | nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), | |
158 | nla_len(mcgrp), NULL); | |
159 | if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || | |
160 | !tb2[CTRL_ATTR_MCAST_GRP_ID] || | |
161 | os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), | |
162 | res->group, | |
163 | nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) | |
164 | continue; | |
165 | res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); | |
166 | break; | |
167 | }; | |
168 | ||
169 | return NL_SKIP; | |
170 | } | |
171 | ||
172 | ||
173 | static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, | |
174 | const char *family, const char *group) | |
175 | { | |
176 | struct nl_msg *msg; | |
177 | int ret = -1; | |
178 | struct family_data res = { group, -ENOENT }; | |
179 | ||
180 | msg = nlmsg_alloc(); | |
181 | if (!msg) | |
182 | return -ENOMEM; | |
183 | genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), | |
184 | 0, 0, CTRL_CMD_GETFAMILY, 0); | |
185 | NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); | |
186 | ||
187 | ret = send_and_recv_msgs(drv, msg, family_handler, &res); | |
188 | msg = NULL; | |
189 | if (ret == 0) | |
190 | ret = res.id; | |
191 | ||
192 | nla_put_failure: | |
193 | nlmsg_free(msg); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | ||
3f5285e8 JM |
198 | static int wpa_driver_nl80211_send_oper_ifla( |
199 | struct wpa_driver_nl80211_data *drv, | |
200 | int linkmode, int operstate) | |
201 | { | |
202 | struct { | |
203 | struct nlmsghdr hdr; | |
204 | struct ifinfomsg ifinfo; | |
205 | char opts[16]; | |
206 | } req; | |
207 | struct rtattr *rta; | |
208 | static int nl_seq; | |
209 | ssize_t ret; | |
210 | ||
211 | os_memset(&req, 0, sizeof(req)); | |
212 | ||
213 | req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
214 | req.hdr.nlmsg_type = RTM_SETLINK; | |
215 | req.hdr.nlmsg_flags = NLM_F_REQUEST; | |
216 | req.hdr.nlmsg_seq = ++nl_seq; | |
217 | req.hdr.nlmsg_pid = 0; | |
218 | ||
219 | req.ifinfo.ifi_family = AF_UNSPEC; | |
220 | req.ifinfo.ifi_type = 0; | |
221 | req.ifinfo.ifi_index = drv->ifindex; | |
222 | req.ifinfo.ifi_flags = 0; | |
223 | req.ifinfo.ifi_change = 0; | |
224 | ||
225 | if (linkmode != -1) { | |
226 | rta = (struct rtattr *) | |
227 | ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); | |
228 | rta->rta_type = IFLA_LINKMODE; | |
229 | rta->rta_len = RTA_LENGTH(sizeof(char)); | |
230 | *((char *) RTA_DATA(rta)) = linkmode; | |
231 | req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + | |
232 | RTA_LENGTH(sizeof(char)); | |
233 | } | |
234 | if (operstate != -1) { | |
235 | rta = (struct rtattr *) | |
236 | ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); | |
237 | rta->rta_type = IFLA_OPERSTATE; | |
238 | rta->rta_len = RTA_LENGTH(sizeof(char)); | |
239 | *((char *) RTA_DATA(rta)) = operstate; | |
240 | req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + | |
241 | RTA_LENGTH(sizeof(char)); | |
242 | } | |
243 | ||
244 | wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", | |
245 | linkmode, operstate); | |
246 | ||
d8816397 | 247 | ret = send(drv->link_event_sock, &req, req.hdr.nlmsg_len, 0); |
3f5285e8 JM |
248 | if (ret < 0) { |
249 | wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " | |
250 | "%s (assume operstate is not supported)", | |
251 | strerror(errno)); | |
252 | } | |
253 | ||
254 | return ret < 0 ? -1 : 0; | |
255 | } | |
256 | ||
257 | ||
258 | static int wpa_driver_nl80211_set_auth_param( | |
259 | struct wpa_driver_nl80211_data *drv, int idx, u32 value) | |
260 | { | |
261 | struct iwreq iwr; | |
262 | int ret = 0; | |
263 | ||
264 | os_memset(&iwr, 0, sizeof(iwr)); | |
265 | os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); | |
266 | iwr.u.param.flags = idx & IW_AUTH_INDEX; | |
267 | iwr.u.param.value = value; | |
268 | ||
269 | if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { | |
270 | if (errno != EOPNOTSUPP) { | |
271 | wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " | |
272 | "value 0x%x) failed: %s)", | |
273 | idx, value, strerror(errno)); | |
274 | } | |
275 | ret = errno == EOPNOTSUPP ? -2 : -1; | |
276 | } | |
277 | ||
278 | return ret; | |
279 | } | |
280 | ||
281 | ||
282 | static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) | |
283 | { | |
284 | struct wpa_driver_nl80211_data *drv = priv; | |
c2a04078 JM |
285 | if (!drv->associated) |
286 | return -1; | |
287 | os_memcpy(bssid, drv->bssid, ETH_ALEN); | |
288 | return 0; | |
3f5285e8 JM |
289 | } |
290 | ||
291 | ||
3f5285e8 JM |
292 | static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) |
293 | { | |
294 | struct wpa_driver_nl80211_data *drv = priv; | |
fd05d64e JM |
295 | if (!drv->associated) |
296 | return -1; | |
297 | os_memcpy(ssid, drv->ssid, drv->ssid_len); | |
298 | return drv->ssid_len; | |
3f5285e8 JM |
299 | } |
300 | ||
301 | ||
7524cfb1 JM |
302 | static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, |
303 | void *ctx, char *buf, size_t len, | |
304 | int del) | |
3f5285e8 JM |
305 | { |
306 | union wpa_event_data event; | |
307 | ||
308 | os_memset(&event, 0, sizeof(event)); | |
309 | if (len > sizeof(event.interface_status.ifname)) | |
310 | len = sizeof(event.interface_status.ifname) - 1; | |
311 | os_memcpy(event.interface_status.ifname, buf, len); | |
312 | event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : | |
313 | EVENT_INTERFACE_ADDED; | |
314 | ||
315 | wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", | |
316 | del ? "DEL" : "NEW", | |
317 | event.interface_status.ifname, | |
318 | del ? "removed" : "added"); | |
319 | ||
7524cfb1 JM |
320 | if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { |
321 | if (del) | |
322 | drv->if_removed = 1; | |
323 | else | |
324 | drv->if_removed = 0; | |
325 | } | |
326 | ||
3f5285e8 JM |
327 | wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); |
328 | } | |
329 | ||
330 | ||
7524cfb1 JM |
331 | static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, |
332 | struct nlmsghdr *h) | |
333 | { | |
334 | struct ifinfomsg *ifi; | |
335 | int attrlen, _nlmsg_len, rta_len; | |
336 | struct rtattr *attr; | |
337 | ||
338 | ifi = NLMSG_DATA(h); | |
339 | ||
340 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
341 | ||
342 | attrlen = h->nlmsg_len - _nlmsg_len; | |
343 | if (attrlen < 0) | |
344 | return 0; | |
345 | ||
346 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
347 | ||
348 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
349 | while (RTA_OK(attr, attrlen)) { | |
350 | if (attr->rta_type == IFLA_IFNAME) { | |
351 | if (os_strcmp(((char *) attr) + rta_len, drv->ifname) | |
352 | == 0) | |
353 | return 1; | |
354 | else | |
355 | break; | |
356 | } | |
357 | attr = RTA_NEXT(attr, attrlen); | |
358 | } | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | ||
364 | static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, | |
365 | int ifindex, struct nlmsghdr *h) | |
366 | { | |
367 | if (drv->ifindex == ifindex) | |
368 | return 1; | |
369 | ||
370 | if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { | |
371 | drv->ifindex = if_nametoindex(drv->ifname); | |
372 | wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " | |
373 | "interface"); | |
374 | wpa_driver_nl80211_finish_drv_init(drv); | |
375 | return 1; | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | ||
3f5285e8 JM |
382 | static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, |
383 | void *ctx, struct nlmsghdr *h, | |
384 | size_t len) | |
385 | { | |
386 | struct ifinfomsg *ifi; | |
387 | int attrlen, _nlmsg_len, rta_len; | |
388 | struct rtattr * attr; | |
389 | ||
390 | if (len < sizeof(*ifi)) | |
391 | return; | |
392 | ||
393 | ifi = NLMSG_DATA(h); | |
394 | ||
7524cfb1 | 395 | if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { |
3f5285e8 JM |
396 | wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", |
397 | ifi->ifi_index); | |
398 | return; | |
399 | } | |
400 | ||
401 | wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " | |
402 | "(%s%s%s%s)", | |
403 | drv->operstate, ifi->ifi_flags, | |
404 | (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", | |
405 | (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", | |
406 | (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", | |
407 | (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); | |
408 | /* | |
409 | * Some drivers send the association event before the operup event--in | |
410 | * this case, lifting operstate in wpa_driver_nl80211_set_operstate() | |
411 | * fails. This will hit us when wpa_supplicant does not need to do | |
412 | * IEEE 802.1X authentication | |
413 | */ | |
414 | if (drv->operstate == 1 && | |
415 | (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && | |
416 | !(ifi->ifi_flags & IFF_RUNNING)) | |
417 | wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); | |
418 | ||
419 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
420 | ||
421 | attrlen = h->nlmsg_len - _nlmsg_len; | |
422 | if (attrlen < 0) | |
423 | return; | |
424 | ||
425 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
426 | ||
427 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
428 | while (RTA_OK(attr, attrlen)) { | |
d8816397 | 429 | if (attr->rta_type == IFLA_IFNAME) { |
7524cfb1 JM |
430 | wpa_driver_nl80211_event_link( |
431 | drv, ctx, | |
432 | ((char *) attr) + rta_len, | |
433 | attr->rta_len - rta_len, 0); | |
3f5285e8 JM |
434 | } |
435 | attr = RTA_NEXT(attr, attrlen); | |
436 | } | |
437 | } | |
438 | ||
439 | ||
440 | static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, | |
441 | void *ctx, struct nlmsghdr *h, | |
442 | size_t len) | |
443 | { | |
444 | struct ifinfomsg *ifi; | |
445 | int attrlen, _nlmsg_len, rta_len; | |
446 | struct rtattr * attr; | |
447 | ||
448 | if (len < sizeof(*ifi)) | |
449 | return; | |
450 | ||
451 | ifi = NLMSG_DATA(h); | |
452 | ||
453 | _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); | |
454 | ||
455 | attrlen = h->nlmsg_len - _nlmsg_len; | |
456 | if (attrlen < 0) | |
457 | return; | |
458 | ||
459 | attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); | |
460 | ||
461 | rta_len = RTA_ALIGN(sizeof(struct rtattr)); | |
462 | while (RTA_OK(attr, attrlen)) { | |
463 | if (attr->rta_type == IFLA_IFNAME) { | |
7524cfb1 JM |
464 | wpa_driver_nl80211_event_link( |
465 | drv, ctx, | |
466 | ((char *) attr) + rta_len, | |
467 | attr->rta_len - rta_len, 1); | |
3f5285e8 JM |
468 | } |
469 | attr = RTA_NEXT(attr, attrlen); | |
470 | } | |
471 | } | |
472 | ||
473 | ||
d8816397 | 474 | static void wpa_driver_nl80211_event_receive_link(int sock, void *eloop_ctx, |
97865538 | 475 | void *sock_ctx) |
3f5285e8 JM |
476 | { |
477 | char buf[8192]; | |
478 | int left; | |
479 | struct sockaddr_nl from; | |
480 | socklen_t fromlen; | |
481 | struct nlmsghdr *h; | |
482 | int max_events = 10; | |
483 | ||
484 | try_again: | |
485 | fromlen = sizeof(from); | |
486 | left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, | |
487 | (struct sockaddr *) &from, &fromlen); | |
488 | if (left < 0) { | |
489 | if (errno != EINTR && errno != EAGAIN) | |
490 | perror("recvfrom(netlink)"); | |
491 | return; | |
492 | } | |
493 | ||
494 | h = (struct nlmsghdr *) buf; | |
495 | while (left >= (int) sizeof(*h)) { | |
496 | int len, plen; | |
497 | ||
498 | len = h->nlmsg_len; | |
499 | plen = len - sizeof(*h); | |
500 | if (len > left || plen < 0) { | |
501 | wpa_printf(MSG_DEBUG, "Malformed netlink message: " | |
502 | "len=%d left=%d plen=%d", | |
503 | len, left, plen); | |
504 | break; | |
505 | } | |
506 | ||
507 | switch (h->nlmsg_type) { | |
508 | case RTM_NEWLINK: | |
509 | wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, | |
510 | h, plen); | |
511 | break; | |
512 | case RTM_DELLINK: | |
513 | wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, | |
514 | h, plen); | |
515 | break; | |
516 | } | |
517 | ||
518 | len = NLMSG_ALIGN(len); | |
519 | left -= len; | |
520 | h = (struct nlmsghdr *) ((char *) h + len); | |
521 | } | |
522 | ||
523 | if (left > 0) { | |
524 | wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " | |
525 | "message", left); | |
526 | } | |
527 | ||
528 | if (--max_events > 0) { | |
529 | /* | |
530 | * Try to receive all events in one eloop call in order to | |
531 | * limit race condition on cases where AssocInfo event, Assoc | |
532 | * event, and EAPOL frames are received more or less at the | |
533 | * same time. We want to process the event messages first | |
534 | * before starting EAPOL processing. | |
535 | */ | |
536 | goto try_again; | |
537 | } | |
538 | } | |
539 | ||
540 | ||
97865538 JM |
541 | static int no_seq_check(struct nl_msg *msg, void *arg) |
542 | { | |
543 | return NL_OK; | |
544 | } | |
545 | ||
546 | ||
c2a04078 JM |
547 | static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, |
548 | const u8 *frame, size_t len) | |
549 | { | |
550 | const struct ieee80211_mgmt *mgmt; | |
551 | union wpa_event_data event; | |
552 | ||
553 | mgmt = (const struct ieee80211_mgmt *) frame; | |
554 | if (len < 24 + sizeof(mgmt->u.auth)) { | |
555 | wpa_printf(MSG_DEBUG, "nl80211: Too short association event " | |
556 | "frame"); | |
557 | return; | |
558 | } | |
559 | ||
560 | os_memset(&event, 0, sizeof(event)); | |
561 | os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); | |
562 | event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); | |
563 | event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); | |
564 | if (len > 24 + sizeof(mgmt->u.auth)) { | |
565 | event.auth.ies = mgmt->u.auth.variable; | |
566 | event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); | |
567 | } | |
568 | ||
569 | wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); | |
570 | } | |
571 | ||
572 | ||
573 | static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, | |
574 | const u8 *frame, size_t len) | |
575 | { | |
576 | const struct ieee80211_mgmt *mgmt; | |
577 | union wpa_event_data event; | |
578 | u16 status; | |
579 | ||
580 | mgmt = (const struct ieee80211_mgmt *) frame; | |
581 | if (len < 24 + sizeof(mgmt->u.assoc_resp)) { | |
582 | wpa_printf(MSG_DEBUG, "nl80211: Too short association event " | |
583 | "frame"); | |
584 | return; | |
585 | } | |
586 | ||
587 | status = le_to_host16(mgmt->u.assoc_resp.status_code); | |
588 | if (status != WLAN_STATUS_SUCCESS) { | |
efa46078 JM |
589 | os_memset(&event, 0, sizeof(event)); |
590 | if (len > 24 + sizeof(mgmt->u.assoc_resp)) { | |
591 | event.assoc_reject.resp_ies = | |
592 | (u8 *) mgmt->u.assoc_resp.variable; | |
593 | event.assoc_reject.resp_ies_len = | |
594 | len - 24 - sizeof(mgmt->u.assoc_resp); | |
595 | } | |
596 | event.assoc_reject.status_code = status; | |
597 | ||
598 | wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); | |
c2a04078 JM |
599 | return; |
600 | } | |
601 | ||
602 | drv->associated = 1; | |
603 | os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); | |
604 | ||
605 | os_memset(&event, 0, sizeof(event)); | |
606 | if (len > 24 + sizeof(mgmt->u.assoc_resp)) { | |
607 | event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; | |
608 | event.assoc_info.resp_ies_len = | |
efa46078 | 609 | len - 24 - sizeof(mgmt->u.assoc_resp); |
c2a04078 JM |
610 | } |
611 | ||
612 | wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); | |
613 | } | |
614 | ||
615 | ||
616 | static void mlme_event(struct wpa_driver_nl80211_data *drv, | |
617 | enum nl80211_commands cmd, struct nlattr *frame) | |
618 | { | |
619 | if (frame == NULL) { | |
620 | wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame " | |
621 | "data", cmd); | |
622 | return; | |
623 | } | |
624 | ||
625 | wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd); | |
626 | wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", | |
627 | nla_data(frame), nla_len(frame)); | |
628 | ||
629 | switch (cmd) { | |
630 | case NL80211_CMD_AUTHENTICATE: | |
631 | mlme_event_auth(drv, nla_data(frame), nla_len(frame)); | |
632 | break; | |
633 | case NL80211_CMD_ASSOCIATE: | |
634 | mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); | |
635 | break; | |
636 | case NL80211_CMD_DEAUTHENTICATE: | |
637 | drv->associated = 0; | |
638 | wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL); | |
639 | break; | |
640 | case NL80211_CMD_DISASSOCIATE: | |
641 | drv->associated = 0; | |
642 | wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); | |
643 | break; | |
644 | default: | |
645 | break; | |
646 | } | |
647 | } | |
648 | ||
649 | ||
97865538 JM |
650 | static int process_event(struct nl_msg *msg, void *arg) |
651 | { | |
652 | struct wpa_driver_nl80211_data *drv = arg; | |
653 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
654 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; | |
655 | ||
656 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
657 | genlmsg_attrlen(gnlh, 0), NULL); | |
658 | ||
659 | if (tb[NL80211_ATTR_IFINDEX]) { | |
660 | int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); | |
661 | if (ifindex != drv->ifindex) { | |
662 | wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" | |
663 | " for foreign interface (ifindex %d)", | |
664 | gnlh->cmd, ifindex); | |
665 | return NL_SKIP; | |
666 | } | |
667 | } | |
668 | ||
669 | switch (gnlh->cmd) { | |
670 | case NL80211_CMD_NEW_SCAN_RESULTS: | |
671 | wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); | |
672 | drv->scan_complete_events = 1; | |
673 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, | |
674 | drv->ctx); | |
675 | wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); | |
676 | break; | |
677 | case NL80211_CMD_SCAN_ABORTED: | |
678 | wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); | |
679 | /* | |
680 | * Need to indicate that scan results are available in order | |
681 | * not to make wpa_supplicant stop its scanning. | |
682 | */ | |
683 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, | |
684 | drv->ctx); | |
685 | wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); | |
686 | break; | |
c2a04078 JM |
687 | case NL80211_CMD_AUTHENTICATE: |
688 | case NL80211_CMD_ASSOCIATE: | |
689 | case NL80211_CMD_DEAUTHENTICATE: | |
690 | case NL80211_CMD_DISASSOCIATE: | |
691 | mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME]); | |
692 | break; | |
97865538 | 693 | default: |
c2a04078 JM |
694 | wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " |
695 | "(cmd=%d)", gnlh->cmd); | |
97865538 JM |
696 | break; |
697 | } | |
698 | ||
699 | return NL_SKIP; | |
700 | } | |
701 | ||
702 | ||
703 | static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, | |
704 | void *sock_ctx) | |
705 | { | |
706 | struct nl_cb *cb; | |
707 | struct wpa_driver_nl80211_data *drv = eloop_ctx; | |
708 | ||
709 | wpa_printf(MSG_DEBUG, "nl80211: Event message available"); | |
710 | ||
711 | cb = nl_cb_clone(drv->nl_cb); | |
712 | if (!cb) | |
713 | return; | |
714 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); | |
715 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); | |
716 | nl_recvmsgs(drv->nl_handle, cb); | |
717 | nl_cb_put(cb); | |
718 | } | |
719 | ||
720 | ||
3f5285e8 JM |
721 | static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, |
722 | const char *ifname, int *flags) | |
723 | { | |
724 | struct ifreq ifr; | |
725 | ||
726 | os_memset(&ifr, 0, sizeof(ifr)); | |
727 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
728 | if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { | |
729 | perror("ioctl[SIOCGIFFLAGS]"); | |
730 | return -1; | |
731 | } | |
732 | *flags = ifr.ifr_flags & 0xffff; | |
733 | return 0; | |
734 | } | |
735 | ||
736 | ||
737 | /** | |
738 | * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) | |
739 | * @drv: driver_nl80211 private data | |
740 | * @flags: Pointer to returned flags value | |
741 | * Returns: 0 on success, -1 on failure | |
742 | */ | |
743 | static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, | |
744 | int *flags) | |
745 | { | |
746 | return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); | |
747 | } | |
748 | ||
749 | ||
750 | static int wpa_driver_nl80211_set_ifflags_ifname( | |
751 | struct wpa_driver_nl80211_data *drv, | |
752 | const char *ifname, int flags) | |
753 | { | |
754 | struct ifreq ifr; | |
755 | ||
756 | os_memset(&ifr, 0, sizeof(ifr)); | |
757 | os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); | |
758 | ifr.ifr_flags = flags & 0xffff; | |
759 | if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { | |
760 | perror("SIOCSIFFLAGS"); | |
761 | return -1; | |
762 | } | |
763 | return 0; | |
764 | } | |
765 | ||
766 | ||
767 | /** | |
768 | * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) | |
769 | * @drv: driver_nl80211 private data | |
770 | * @flags: New value for flags | |
771 | * Returns: 0 on success, -1 on failure | |
772 | */ | |
773 | static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, | |
774 | int flags) | |
775 | { | |
776 | return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); | |
777 | } | |
778 | ||
779 | ||
6d158490 LR |
780 | /** |
781 | * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain | |
782 | * @priv: driver_nl80211 private data | |
783 | * @alpha2_arg: country to which to switch to | |
784 | * Returns: 0 on success, -1 on failure | |
785 | * | |
786 | * This asks nl80211 to set the regulatory domain for given | |
787 | * country ISO / IEC alpha2. | |
788 | */ | |
789 | static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) | |
790 | { | |
791 | struct wpa_driver_nl80211_data *drv = priv; | |
792 | char alpha2[3]; | |
793 | struct nl_msg *msg; | |
794 | ||
795 | msg = nlmsg_alloc(); | |
796 | if (!msg) | |
797 | goto nla_put_failure; | |
798 | ||
799 | alpha2[0] = alpha2_arg[0]; | |
800 | alpha2[1] = alpha2_arg[1]; | |
801 | alpha2[2] = '\0'; | |
802 | ||
803 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
804 | 0, NL80211_CMD_REQ_SET_REG, 0); | |
805 | ||
806 | NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); | |
807 | if (send_and_recv_msgs(drv, msg, NULL, NULL)) | |
808 | return -EINVAL; | |
809 | return 0; | |
810 | nla_put_failure: | |
811 | return -EINVAL; | |
812 | } | |
813 | ||
814 | ||
80bc75f1 JM |
815 | struct wiphy_info_data { |
816 | int max_scan_ssids; | |
1581b38b | 817 | int ap_supported; |
80bc75f1 JM |
818 | }; |
819 | ||
820 | ||
821 | static int wiphy_info_handler(struct nl_msg *msg, void *arg) | |
822 | { | |
823 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; | |
824 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
825 | struct wiphy_info_data *info = arg; | |
826 | ||
827 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), | |
828 | genlmsg_attrlen(gnlh, 0), NULL); | |
829 | ||
830 | if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) | |
831 | info->max_scan_ssids = | |
832 | nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); | |
833 | ||
1581b38b JM |
834 | if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { |
835 | struct nlattr *nl_mode; | |
836 | int i; | |
837 | nla_for_each_nested(nl_mode, | |
838 | tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { | |
839 | if (nl_mode->nla_type == NL80211_IFTYPE_AP) { | |
840 | info->ap_supported = 1; | |
841 | break; | |
842 | } | |
843 | } | |
844 | } | |
845 | ||
80bc75f1 JM |
846 | return NL_SKIP; |
847 | } | |
848 | ||
849 | ||
850 | static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, | |
851 | struct wiphy_info_data *info) | |
852 | { | |
853 | struct nl_msg *msg; | |
854 | ||
855 | os_memset(info, 0, sizeof(*info)); | |
856 | msg = nlmsg_alloc(); | |
857 | if (!msg) | |
858 | return -1; | |
859 | ||
860 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
861 | 0, NL80211_CMD_GET_WIPHY, 0); | |
862 | ||
863 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
864 | ||
865 | if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) | |
866 | return 0; | |
867 | msg = NULL; | |
868 | nla_put_failure: | |
869 | nlmsg_free(msg); | |
870 | return -1; | |
871 | } | |
872 | ||
873 | ||
874 | static void wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) | |
875 | { | |
876 | struct wiphy_info_data info; | |
877 | if (wpa_driver_nl80211_get_info(drv, &info)) | |
878 | return; | |
879 | drv->has_capability = 1; | |
1b2a72e8 JM |
880 | /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ |
881 | drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | | |
882 | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | | |
883 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | | |
884 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; | |
885 | drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | | |
886 | WPA_DRIVER_CAPA_ENC_WEP104 | | |
887 | WPA_DRIVER_CAPA_ENC_TKIP | | |
888 | WPA_DRIVER_CAPA_ENC_CCMP; | |
889 | ||
80bc75f1 | 890 | drv->capa.max_scan_ssids = info.max_scan_ssids; |
1581b38b JM |
891 | if (info.ap_supported) |
892 | drv->capa.flags |= WPA_DRIVER_FLAGS_AP; | |
80bc75f1 JM |
893 | } |
894 | ||
895 | ||
3f5285e8 | 896 | /** |
7e5ba1b9 | 897 | * wpa_driver_nl80211_init - Initialize nl80211 driver interface |
3f5285e8 JM |
898 | * @ctx: context to be used when calling wpa_supplicant functions, |
899 | * e.g., wpa_supplicant_event() | |
900 | * @ifname: interface name, e.g., wlan0 | |
901 | * Returns: Pointer to private data, %NULL on failure | |
902 | */ | |
7e5ba1b9 | 903 | static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) |
3f5285e8 | 904 | { |
97865538 | 905 | int s, ret; |
3f5285e8 JM |
906 | struct sockaddr_nl local; |
907 | struct wpa_driver_nl80211_data *drv; | |
908 | ||
909 | drv = os_zalloc(sizeof(*drv)); | |
910 | if (drv == NULL) | |
911 | return NULL; | |
912 | drv->ctx = ctx; | |
913 | os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); | |
914 | ||
915 | drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); | |
916 | if (drv->nl_cb == NULL) { | |
917 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " | |
918 | "callbacks"); | |
919 | goto err1; | |
920 | } | |
921 | ||
922 | drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); | |
923 | if (drv->nl_handle == NULL) { | |
924 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " | |
925 | "callbacks"); | |
926 | goto err2; | |
927 | } | |
928 | ||
929 | if (genl_connect(drv->nl_handle)) { | |
930 | wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " | |
931 | "netlink"); | |
932 | goto err3; | |
933 | } | |
934 | ||
935 | drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); | |
936 | if (drv->nl_cache == NULL) { | |
937 | wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " | |
938 | "netlink cache"); | |
939 | goto err3; | |
940 | } | |
941 | ||
942 | drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); | |
943 | if (drv->nl80211 == NULL) { | |
944 | wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " | |
945 | "found"); | |
946 | goto err4; | |
947 | } | |
948 | ||
97865538 JM |
949 | ret = nl_get_multicast_id(drv, "nl80211", "scan"); |
950 | if (ret >= 0) | |
951 | ret = nl_socket_add_membership(drv->nl_handle, ret); | |
952 | if (ret < 0) { | |
953 | wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " | |
954 | "membership for scan events: %d (%s)", | |
955 | ret, strerror(-ret)); | |
956 | goto err4; | |
957 | } | |
c2a04078 JM |
958 | |
959 | ret = nl_get_multicast_id(drv, "nl80211", "mlme"); | |
960 | if (ret >= 0) | |
961 | ret = nl_socket_add_membership(drv->nl_handle, ret); | |
c2a04078 JM |
962 | if (ret < 0) { |
963 | wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " | |
964 | "membership for mlme events: %d (%s)", | |
965 | ret, strerror(-ret)); | |
966 | goto err4; | |
967 | } | |
968 | drv->capa.flags |= WPA_DRIVER_FLAGS_SME; | |
c2a04078 | 969 | |
97865538 JM |
970 | eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), |
971 | wpa_driver_nl80211_event_receive, drv, ctx); | |
972 | ||
3f5285e8 JM |
973 | drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); |
974 | if (drv->ioctl_sock < 0) { | |
975 | perror("socket(PF_INET,SOCK_DGRAM)"); | |
976 | goto err5; | |
977 | } | |
978 | ||
979 | s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
980 | if (s < 0) { | |
981 | perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); | |
982 | goto err6; | |
983 | } | |
984 | ||
985 | os_memset(&local, 0, sizeof(local)); | |
986 | local.nl_family = AF_NETLINK; | |
987 | local.nl_groups = RTMGRP_LINK; | |
988 | if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { | |
989 | perror("bind(netlink)"); | |
990 | close(s); | |
991 | goto err6; | |
992 | } | |
993 | ||
d8816397 | 994 | eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_link, drv, |
3f5285e8 | 995 | ctx); |
d8816397 | 996 | drv->link_event_sock = s; |
3f5285e8 | 997 | |
362f781e JM |
998 | if (wpa_driver_nl80211_finish_drv_init(drv)) |
999 | goto err7; | |
7524cfb1 JM |
1000 | |
1001 | return drv; | |
1002 | ||
362f781e | 1003 | err7: |
d8816397 JM |
1004 | eloop_unregister_read_sock(drv->link_event_sock); |
1005 | close(drv->link_event_sock); | |
7524cfb1 JM |
1006 | err6: |
1007 | close(drv->ioctl_sock); | |
1008 | err5: | |
1009 | genl_family_put(drv->nl80211); | |
1010 | err4: | |
1011 | nl_cache_free(drv->nl_cache); | |
1012 | err3: | |
1013 | nl_handle_destroy(drv->nl_handle); | |
1014 | err2: | |
1015 | nl_cb_put(drv->nl_cb); | |
1016 | err1: | |
1017 | os_free(drv); | |
1018 | return NULL; | |
1019 | } | |
1020 | ||
1021 | ||
362f781e | 1022 | static int |
7524cfb1 JM |
1023 | wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) |
1024 | { | |
1025 | int flags; | |
1026 | ||
a87c9d96 JM |
1027 | drv->ifindex = if_nametoindex(drv->ifname); |
1028 | ||
1029 | if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { | |
1030 | wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to " | |
1031 | "use managed mode"); | |
1032 | } | |
1033 | ||
362f781e JM |
1034 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) { |
1035 | wpa_printf(MSG_ERROR, "Could not get interface '%s' flags", | |
1036 | drv->ifname); | |
1037 | return -1; | |
1038 | } | |
1039 | if (!(flags & IFF_UP)) { | |
3f5285e8 | 1040 | if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { |
362f781e JM |
1041 | wpa_printf(MSG_ERROR, "Could not set interface '%s' " |
1042 | "UP", drv->ifname); | |
1043 | return -1; | |
3f5285e8 JM |
1044 | } |
1045 | } | |
1046 | ||
80bc75f1 JM |
1047 | wpa_driver_nl80211_capa(drv); |
1048 | ||
3f5285e8 | 1049 | wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); |
362f781e JM |
1050 | |
1051 | return 0; | |
3f5285e8 JM |
1052 | } |
1053 | ||
1054 | ||
1055 | /** | |
7e5ba1b9 JM |
1056 | * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface |
1057 | * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() | |
3f5285e8 JM |
1058 | * |
1059 | * Shut down driver interface and processing of driver events. Free | |
1060 | * private data buffer if one was allocated in wpa_driver_nl80211_init(). | |
1061 | */ | |
7e5ba1b9 | 1062 | static void wpa_driver_nl80211_deinit(void *priv) |
3f5285e8 JM |
1063 | { |
1064 | struct wpa_driver_nl80211_data *drv = priv; | |
1065 | int flags; | |
1066 | ||
1067 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); | |
1068 | ||
36b15723 | 1069 | wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, 0); |
3f5285e8 JM |
1070 | |
1071 | wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); | |
1072 | ||
d8816397 | 1073 | eloop_unregister_read_sock(drv->link_event_sock); |
3f5285e8 JM |
1074 | |
1075 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) | |
1076 | (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); | |
1581b38b | 1077 | wpa_driver_nl80211_set_mode(drv, 0); |
3f5285e8 | 1078 | |
d8816397 | 1079 | close(drv->link_event_sock); |
3f5285e8 | 1080 | close(drv->ioctl_sock); |
3f5285e8 | 1081 | |
97865538 | 1082 | eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); |
3f5285e8 JM |
1083 | genl_family_put(drv->nl80211); |
1084 | nl_cache_free(drv->nl_cache); | |
1085 | nl_handle_destroy(drv->nl_handle); | |
1086 | nl_cb_put(drv->nl_cb); | |
1087 | ||
1088 | os_free(drv); | |
1089 | } | |
1090 | ||
1091 | ||
1092 | /** | |
1093 | * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion | |
1094 | * @eloop_ctx: Unused | |
1095 | * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() | |
1096 | * | |
1097 | * This function can be used as registered timeout when starting a scan to | |
1098 | * generate a scan completed event if the driver does not report this. | |
1099 | */ | |
1100 | static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) | |
1101 | { | |
1102 | wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); | |
1103 | wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); | |
1104 | } | |
1105 | ||
1106 | ||
1107 | /** | |
1108 | * wpa_driver_nl80211_scan - Request the driver to initiate scan | |
1109 | * @priv: Pointer to private wext data from wpa_driver_nl80211_init() | |
6a1063e0 | 1110 | * @params: Scan parameters |
3f5285e8 JM |
1111 | * Returns: 0 on success, -1 on failure |
1112 | */ | |
6a1063e0 JM |
1113 | static int wpa_driver_nl80211_scan(void *priv, |
1114 | struct wpa_driver_scan_params *params) | |
3f5285e8 JM |
1115 | { |
1116 | struct wpa_driver_nl80211_data *drv = priv; | |
3f5285e8 | 1117 | int ret = 0, timeout; |
d3a98225 | 1118 | struct nl_msg *msg, *ssids, *freqs; |
6a1063e0 | 1119 | size_t i; |
3f5285e8 | 1120 | |
0e75527f JM |
1121 | msg = nlmsg_alloc(); |
1122 | ssids = nlmsg_alloc(); | |
d3a98225 JM |
1123 | freqs = nlmsg_alloc(); |
1124 | if (!msg || !ssids || !freqs) { | |
0e75527f JM |
1125 | nlmsg_free(msg); |
1126 | nlmsg_free(ssids); | |
d3a98225 | 1127 | nlmsg_free(freqs); |
3f5285e8 JM |
1128 | return -1; |
1129 | } | |
1130 | ||
0e75527f JM |
1131 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, |
1132 | NL80211_CMD_TRIGGER_SCAN, 0); | |
1133 | ||
1134 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
3f5285e8 | 1135 | |
6a1063e0 JM |
1136 | for (i = 0; i < params->num_ssids; i++) { |
1137 | NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, | |
1138 | params->ssids[i].ssid); | |
3f5285e8 | 1139 | } |
6a1063e0 JM |
1140 | if (params->num_ssids) |
1141 | nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); | |
3f5285e8 | 1142 | |
d173df52 JM |
1143 | if (params->extra_ies) { |
1144 | NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, | |
1145 | params->extra_ies); | |
1146 | } | |
1147 | ||
d3a98225 JM |
1148 | if (params->freqs) { |
1149 | for (i = 0; params->freqs[i]; i++) | |
1150 | NLA_PUT_U32(freqs, i + 1, params->freqs[i]); | |
1151 | nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); | |
1152 | } | |
1153 | ||
0e75527f JM |
1154 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1155 | msg = NULL; | |
1156 | if (ret) { | |
1157 | wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " | |
1158 | "(%s)", ret, strerror(-ret)); | |
1159 | goto nla_put_failure; | |
3f5285e8 JM |
1160 | } |
1161 | ||
1162 | /* Not all drivers generate "scan completed" wireless event, so try to | |
1163 | * read results after a timeout. */ | |
0e75527f | 1164 | timeout = 10; |
3f5285e8 JM |
1165 | if (drv->scan_complete_events) { |
1166 | /* | |
d173df52 JM |
1167 | * The driver seems to deliver events to notify when scan is |
1168 | * complete, so use longer timeout to avoid race conditions | |
1169 | * with scanning and following association request. | |
3f5285e8 JM |
1170 | */ |
1171 | timeout = 30; | |
1172 | } | |
1173 | wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " | |
1174 | "seconds", ret, timeout); | |
1175 | eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); | |
0e75527f JM |
1176 | eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, |
1177 | drv, drv->ctx); | |
3f5285e8 | 1178 | |
0e75527f JM |
1179 | nla_put_failure: |
1180 | nlmsg_free(ssids); | |
1181 | nlmsg_free(msg); | |
d3a98225 | 1182 | nlmsg_free(freqs); |
3f5285e8 JM |
1183 | return ret; |
1184 | } | |
1185 | ||
1186 | ||
b3db1e1c | 1187 | static int bss_info_handler(struct nl_msg *msg, void *arg) |
3f5285e8 | 1188 | { |
b3db1e1c JM |
1189 | struct nlattr *tb[NL80211_ATTR_MAX + 1]; |
1190 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); | |
1191 | struct nlattr *bss[NL80211_BSS_MAX + 1]; | |
1192 | static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { | |
1193 | [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, | |
1194 | [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, | |
1195 | [NL80211_BSS_TSF] = { .type = NLA_U64 }, | |
1196 | [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, | |
1197 | [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, | |
1198 | [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, | |
1199 | [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, | |
1200 | [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, | |
1201 | }; | |
1202 | struct wpa_scan_results *res = arg; | |
3f5285e8 JM |
1203 | struct wpa_scan_res **tmp; |
1204 | struct wpa_scan_res *r; | |
b3db1e1c JM |
1205 | const u8 *ie; |
1206 | size_t ie_len; | |
3f5285e8 | 1207 | |
b3db1e1c JM |
1208 | nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
1209 | genlmsg_attrlen(gnlh, 0), NULL); | |
1210 | if (!tb[NL80211_ATTR_BSS]) | |
1211 | return NL_SKIP; | |
1212 | if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], | |
1213 | bss_policy)) | |
1214 | return NL_SKIP; | |
1215 | if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { | |
1216 | ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); | |
1217 | ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); | |
1218 | } else { | |
1219 | ie = NULL; | |
1220 | ie_len = 0; | |
1221 | } | |
3f5285e8 | 1222 | |
b3db1e1c | 1223 | r = os_zalloc(sizeof(*r) + ie_len); |
3f5285e8 | 1224 | if (r == NULL) |
b3db1e1c JM |
1225 | return NL_SKIP; |
1226 | if (bss[NL80211_BSS_BSSID]) | |
1227 | os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), | |
1228 | ETH_ALEN); | |
1229 | if (bss[NL80211_BSS_FREQUENCY]) | |
1230 | r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); | |
1231 | if (bss[NL80211_BSS_BEACON_INTERVAL]) | |
1232 | r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); | |
1233 | if (bss[NL80211_BSS_CAPABILITY]) | |
1234 | r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); | |
7c2849d2 JM |
1235 | r->flags |= WPA_SCAN_NOISE_INVALID; |
1236 | if (bss[NL80211_BSS_SIGNAL_MBM]) { | |
b3db1e1c | 1237 | r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); |
7c2849d2 JM |
1238 | r->level /= 100; /* mBm to dBm */ |
1239 | r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; | |
1240 | } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { | |
1241 | r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); | |
1242 | r->flags |= WPA_SCAN_LEVEL_INVALID; | |
1243 | } else | |
1244 | r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; | |
b3db1e1c JM |
1245 | if (bss[NL80211_BSS_TSF]) |
1246 | r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); | |
1247 | r->ie_len = ie_len; | |
1248 | if (ie) | |
1249 | os_memcpy(r + 1, ie, ie_len); | |
3f5285e8 JM |
1250 | |
1251 | tmp = os_realloc(res->res, | |
1252 | (res->num + 1) * sizeof(struct wpa_scan_res *)); | |
1253 | if (tmp == NULL) { | |
1254 | os_free(r); | |
b3db1e1c | 1255 | return NL_SKIP; |
3f5285e8 JM |
1256 | } |
1257 | tmp[res->num++] = r; | |
1258 | res->res = tmp; | |
b3db1e1c JM |
1259 | |
1260 | return NL_SKIP; | |
3f5285e8 | 1261 | } |
b3db1e1c | 1262 | |
3f5285e8 JM |
1263 | |
1264 | /** | |
1265 | * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results | |
1266 | * @priv: Pointer to private wext data from wpa_driver_nl80211_init() | |
1267 | * Returns: Scan results on success, -1 on failure | |
1268 | */ | |
7e5ba1b9 JM |
1269 | static struct wpa_scan_results * |
1270 | wpa_driver_nl80211_get_scan_results(void *priv) | |
3f5285e8 JM |
1271 | { |
1272 | struct wpa_driver_nl80211_data *drv = priv; | |
b3db1e1c | 1273 | struct nl_msg *msg; |
3f5285e8 | 1274 | struct wpa_scan_results *res; |
b3db1e1c | 1275 | int ret; |
3f5285e8 JM |
1276 | |
1277 | res = os_zalloc(sizeof(*res)); | |
b3db1e1c JM |
1278 | if (res == NULL) |
1279 | return 0; | |
1280 | msg = nlmsg_alloc(); | |
1281 | if (!msg) | |
1282 | goto nla_put_failure; | |
3f5285e8 | 1283 | |
b3db1e1c JM |
1284 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, |
1285 | NL80211_CMD_GET_SCAN, 0); | |
1286 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
3f5285e8 | 1287 | |
b3db1e1c JM |
1288 | ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); |
1289 | msg = NULL; | |
1290 | if (ret == 0) { | |
1291 | wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", | |
1292 | (unsigned long) res->num); | |
1293 | return res; | |
3f5285e8 | 1294 | } |
b3db1e1c JM |
1295 | wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " |
1296 | "(%s)", ret, strerror(-ret)); | |
1297 | nla_put_failure: | |
1298 | nlmsg_free(msg); | |
1299 | wpa_scan_results_free(res); | |
1300 | return NULL; | |
3f5285e8 JM |
1301 | } |
1302 | ||
1303 | ||
3f5285e8 JM |
1304 | static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg, |
1305 | const u8 *addr, int key_idx, | |
1306 | int set_tx, const u8 *seq, | |
1307 | size_t seq_len, | |
1308 | const u8 *key, size_t key_len) | |
1309 | { | |
1310 | struct wpa_driver_nl80211_data *drv = priv; | |
6241fcb1 | 1311 | int err; |
3f5285e8 JM |
1312 | struct nl_msg *msg; |
1313 | ||
1314 | wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " | |
1315 | "seq_len=%lu key_len=%lu", | |
1316 | __func__, alg, addr, key_idx, set_tx, | |
1317 | (unsigned long) seq_len, (unsigned long) key_len); | |
1318 | ||
1319 | msg = nlmsg_alloc(); | |
1320 | if (msg == NULL) | |
1321 | return -1; | |
1322 | ||
1323 | if (alg == WPA_ALG_NONE) { | |
1324 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1325 | NL80211_CMD_DEL_KEY, 0); | |
1326 | } else { | |
1327 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1328 | NL80211_CMD_NEW_KEY, 0); | |
1329 | NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); | |
1330 | switch (alg) { | |
1331 | case WPA_ALG_WEP: | |
1332 | if (key_len == 5) | |
1333 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, | |
1334 | 0x000FAC01); | |
1335 | else | |
1336 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, | |
1337 | 0x000FAC05); | |
1338 | break; | |
1339 | case WPA_ALG_TKIP: | |
1340 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); | |
1341 | break; | |
1342 | case WPA_ALG_CCMP: | |
1343 | NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); | |
1344 | break; | |
1345 | default: | |
1346 | nlmsg_free(msg); | |
1347 | return -1; | |
1348 | } | |
1349 | } | |
1350 | ||
1351 | if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) | |
1352 | { | |
1353 | wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); | |
1354 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | |
1355 | } | |
1356 | NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); | |
1357 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1358 | ||
6241fcb1 JM |
1359 | err = send_and_recv_msgs(drv, msg, NULL, NULL); |
1360 | if (err) { | |
3f5285e8 | 1361 | wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); |
3f5285e8 JM |
1362 | return -1; |
1363 | } | |
1364 | ||
1365 | if (set_tx && alg != WPA_ALG_NONE) { | |
3f5285e8 JM |
1366 | msg = nlmsg_alloc(); |
1367 | if (msg == NULL) | |
1368 | return -1; | |
1369 | ||
1370 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1371 | 0, NL80211_CMD_SET_KEY, 0); | |
1372 | NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); | |
1373 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1374 | NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); | |
1375 | ||
6241fcb1 JM |
1376 | err = send_and_recv_msgs(drv, msg, NULL, NULL); |
1377 | if (err) { | |
3f5285e8 JM |
1378 | wpa_printf(MSG_DEBUG, "nl80211: set default key " |
1379 | "failed; err=%d", err); | |
3f5285e8 JM |
1380 | return -1; |
1381 | } | |
1382 | } | |
1383 | ||
6241fcb1 | 1384 | return 0; |
3f5285e8 JM |
1385 | |
1386 | nla_put_failure: | |
6241fcb1 | 1387 | return -ENOBUFS; |
3f5285e8 JM |
1388 | } |
1389 | ||
1390 | ||
c2a04078 JM |
1391 | static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, |
1392 | const u8 *addr, int cmd, u16 reason_code) | |
1393 | { | |
1394 | int ret = -1; | |
1395 | struct nl_msg *msg; | |
1396 | ||
1397 | msg = nlmsg_alloc(); | |
1398 | if (!msg) | |
1399 | return -1; | |
1400 | ||
1401 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0); | |
1402 | ||
1403 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1404 | NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); | |
1405 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); | |
1406 | ||
1407 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); | |
1408 | msg = NULL; | |
1409 | if (ret) { | |
1410 | wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " | |
1411 | "(%s)", ret, strerror(-ret)); | |
1412 | goto nla_put_failure; | |
1413 | } | |
1414 | ret = 0; | |
1415 | ||
1416 | nla_put_failure: | |
1417 | nlmsg_free(msg); | |
1418 | return ret; | |
1419 | } | |
3f5285e8 JM |
1420 | |
1421 | ||
1422 | static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, | |
c2a04078 | 1423 | int reason_code) |
3f5285e8 JM |
1424 | { |
1425 | struct wpa_driver_nl80211_data *drv = priv; | |
c2a04078 | 1426 | wpa_printf(MSG_DEBUG, "%s", __func__); |
c2a04078 JM |
1427 | return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, |
1428 | reason_code); | |
3f5285e8 JM |
1429 | } |
1430 | ||
1431 | ||
1432 | static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, | |
c2a04078 | 1433 | int reason_code) |
3f5285e8 JM |
1434 | { |
1435 | struct wpa_driver_nl80211_data *drv = priv; | |
c2a04078 | 1436 | wpa_printf(MSG_DEBUG, "%s", __func__); |
c2a04078 JM |
1437 | return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, |
1438 | reason_code); | |
3f5285e8 JM |
1439 | } |
1440 | ||
1441 | ||
c2a04078 JM |
1442 | static int wpa_driver_nl80211_authenticate( |
1443 | void *priv, struct wpa_driver_auth_params *params) | |
1444 | { | |
1445 | struct wpa_driver_nl80211_data *drv = priv; | |
1446 | int ret = -1; | |
1447 | struct nl_msg *msg; | |
1448 | enum nl80211_auth_type type; | |
1449 | ||
1450 | drv->associated = 0; | |
1451 | ||
1452 | msg = nlmsg_alloc(); | |
1453 | if (!msg) | |
1454 | return -1; | |
1455 | ||
1456 | wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", | |
1457 | drv->ifindex); | |
1458 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1459 | NL80211_CMD_AUTHENTICATE, 0); | |
1460 | ||
1461 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1462 | if (params->bssid) { | |
1463 | wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, | |
1464 | MAC2STR(params->bssid)); | |
1465 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); | |
1466 | } | |
1467 | if (params->freq) { | |
1468 | wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); | |
1469 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); | |
1470 | } | |
1471 | if (params->ssid) { | |
1472 | wpa_hexdump_ascii(MSG_DEBUG, " * SSID", | |
1473 | params->ssid, params->ssid_len); | |
1474 | NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, | |
1475 | params->ssid); | |
1476 | } | |
1477 | wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); | |
1478 | if (params->ie) | |
1479 | NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); | |
1480 | /* | |
1481 | * TODO: if multiple auth_alg options enabled, try them one by one if | |
1482 | * the AP rejects authentication due to unknown auth alg | |
1483 | */ | |
1484 | if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) | |
1485 | type = NL80211_AUTHTYPE_OPEN_SYSTEM; | |
1486 | else if (params->auth_alg & AUTH_ALG_SHARED_KEY) | |
1487 | type = NL80211_AUTHTYPE_SHARED_KEY; | |
1488 | else if (params->auth_alg & AUTH_ALG_LEAP) | |
1489 | type = NL80211_AUTHTYPE_NETWORK_EAP; | |
1490 | else if (params->auth_alg & AUTH_ALG_FT) | |
1491 | type = NL80211_AUTHTYPE_FT; | |
1492 | else | |
1493 | goto nla_put_failure; | |
1494 | wpa_printf(MSG_DEBUG, " * Auth Type %d", type); | |
1495 | NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); | |
1496 | ||
1497 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); | |
1498 | msg = NULL; | |
1499 | if (ret) { | |
1500 | wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " | |
1501 | "(%s)", ret, strerror(-ret)); | |
1502 | goto nla_put_failure; | |
1503 | } | |
1504 | ret = 0; | |
1505 | wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " | |
1506 | "successfully"); | |
1507 | ||
1508 | nla_put_failure: | |
1509 | nlmsg_free(msg); | |
1510 | return ret; | |
1511 | } | |
1512 | ||
1513 | ||
1581b38b JM |
1514 | #ifdef CONFIG_AP |
1515 | static int wpa_driver_nl80211_set_freq2( | |
1516 | struct wpa_driver_nl80211_data *drv, | |
1517 | struct wpa_driver_associate_params *params) | |
1518 | { | |
1519 | struct nl_msg *msg; | |
1520 | ||
1521 | msg = nlmsg_alloc(); | |
1522 | if (!msg) | |
1523 | return -1; | |
1524 | ||
1525 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1526 | NL80211_CMD_SET_WIPHY, 0); | |
1527 | ||
1528 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1529 | ||
1530 | /* TODO: proper channel configuration */ | |
1531 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, 2437); | |
1532 | ||
1533 | if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) | |
1534 | return 0; | |
1535 | nla_put_failure: | |
1536 | return -1; | |
1537 | } | |
1538 | ||
1539 | ||
1540 | static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, | |
1541 | struct wpa_driver_associate_params *params) | |
1542 | { | |
1543 | if (wpa_driver_nl80211_set_mode(drv, params->mode) || | |
1544 | wpa_driver_nl80211_set_freq2(drv, params)) | |
1545 | return -1; | |
1546 | ||
1547 | /* TODO: setup monitor interface (and add code somewhere to remove this | |
1548 | * when AP mode is stopped; associate with mode != 2 or drv_deinit) */ | |
1549 | /* TODO: setup beacon */ | |
1550 | ||
1551 | return 0; | |
1552 | } | |
1553 | #endif /* CONFIG_AP */ | |
1554 | ||
1555 | ||
c2a04078 JM |
1556 | static int wpa_driver_nl80211_associate( |
1557 | void *priv, struct wpa_driver_associate_params *params) | |
1558 | { | |
1559 | struct wpa_driver_nl80211_data *drv = priv; | |
1560 | int ret = -1; | |
1561 | struct nl_msg *msg; | |
1562 | ||
1581b38b JM |
1563 | #ifdef CONFIG_AP |
1564 | if (params->mode == 2) | |
1565 | return wpa_driver_nl80211_ap(drv, params); | |
1566 | #endif /* CONFIG_AP */ | |
1567 | ||
36b15723 JM |
1568 | wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, |
1569 | params->drop_unencrypted); | |
1570 | ||
c2a04078 JM |
1571 | drv->associated = 0; |
1572 | ||
1573 | msg = nlmsg_alloc(); | |
1574 | if (!msg) | |
1575 | return -1; | |
1576 | ||
1577 | wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", | |
1578 | drv->ifindex); | |
1579 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, | |
1580 | NL80211_CMD_ASSOCIATE, 0); | |
1581 | ||
1582 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1583 | if (params->bssid) { | |
1584 | wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, | |
1585 | MAC2STR(params->bssid)); | |
1586 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); | |
1587 | } | |
1588 | if (params->freq) { | |
1589 | wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); | |
1590 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); | |
1591 | } | |
1592 | if (params->ssid) { | |
1593 | wpa_hexdump_ascii(MSG_DEBUG, " * SSID", | |
1594 | params->ssid, params->ssid_len); | |
1595 | NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, | |
1596 | params->ssid); | |
fd05d64e JM |
1597 | if (params->ssid_len > sizeof(drv->ssid)) |
1598 | goto nla_put_failure; | |
1599 | os_memcpy(drv->ssid, params->ssid, params->ssid_len); | |
1600 | drv->ssid_len = params->ssid_len; | |
c2a04078 JM |
1601 | } |
1602 | wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); | |
1603 | if (params->wpa_ie) | |
1604 | NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, | |
1605 | params->wpa_ie); | |
1606 | ||
1607 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); | |
1608 | msg = NULL; | |
1609 | if (ret) { | |
1610 | wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " | |
1611 | "(%s)", ret, strerror(-ret)); | |
1612 | goto nla_put_failure; | |
1613 | } | |
1614 | ret = 0; | |
1615 | wpa_printf(MSG_DEBUG, "nl80211: Association request send " | |
1616 | "successfully"); | |
1617 | ||
1618 | nla_put_failure: | |
1619 | nlmsg_free(msg); | |
1620 | return ret; | |
1621 | } | |
3f5285e8 JM |
1622 | |
1623 | ||
1624 | /** | |
d61f48ba | 1625 | * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc) |
36b15723 | 1626 | * @drv: Pointer to private driver data from wpa_driver_nl80211_init() |
3f5285e8 JM |
1627 | * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS |
1628 | * Returns: 0 on success, -1 on failure | |
1629 | */ | |
36b15723 JM |
1630 | static int wpa_driver_nl80211_set_mode(struct wpa_driver_nl80211_data *drv, |
1631 | int mode) | |
3f5285e8 | 1632 | { |
3f5285e8 JM |
1633 | int ret = -1, flags; |
1634 | struct nl_msg *msg; | |
1581b38b JM |
1635 | int nlmode; |
1636 | ||
1637 | switch (mode) { | |
1638 | case 0: | |
1639 | nlmode = NL80211_IFTYPE_STATION; | |
1640 | break; | |
1641 | case 1: | |
1642 | nlmode = NL80211_IFTYPE_ADHOC; | |
1643 | break; | |
1644 | case 2: | |
1645 | nlmode = NL80211_IFTYPE_AP; | |
1646 | break; | |
1647 | default: | |
1648 | return -1; | |
1649 | } | |
3f5285e8 JM |
1650 | |
1651 | msg = nlmsg_alloc(); | |
1652 | if (!msg) | |
1653 | return -1; | |
1654 | ||
1655 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1656 | 0, NL80211_CMD_SET_INTERFACE, 0); | |
1657 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1581b38b | 1658 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode); |
3f5285e8 | 1659 | |
6241fcb1 JM |
1660 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1661 | if (!ret) | |
1662 | return 0; | |
1663 | else | |
3f5285e8 JM |
1664 | goto try_again; |
1665 | ||
3f5285e8 | 1666 | nla_put_failure: |
a87c9d96 JM |
1667 | wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)", |
1668 | ret, strerror(-ret)); | |
3f5285e8 JM |
1669 | return -1; |
1670 | ||
1671 | try_again: | |
1672 | /* mac80211 doesn't allow mode changes while the device is up, so | |
1673 | * take the device down, try to set the mode again, and bring the | |
1674 | * device back up. | |
1675 | */ | |
1676 | if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { | |
1677 | (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); | |
1678 | ||
1679 | /* Try to set the mode again while the interface is down */ | |
6241fcb1 JM |
1680 | msg = nlmsg_alloc(); |
1681 | if (!msg) | |
1682 | return -1; | |
1683 | ||
1684 | genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, | |
1685 | 0, NL80211_CMD_SET_INTERFACE, 0); | |
1686 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); | |
1581b38b | 1687 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, nlmode); |
6241fcb1 JM |
1688 | ret = send_and_recv_msgs(drv, msg, NULL, NULL); |
1689 | if (ret) { | |
3f5285e8 | 1690 | wpa_printf(MSG_ERROR, "Failed to set interface %s " |
a87c9d96 JM |
1691 | "mode(try_again): %d (%s)", |
1692 | drv->ifname, ret, strerror(-ret)); | |
6241fcb1 | 1693 | } |
3f5285e8 JM |
1694 | |
1695 | /* Ignore return value of get_ifflags to ensure that the device | |
1696 | * is always up like it was before this function was called. | |
1697 | */ | |
1698 | (void) wpa_driver_nl80211_get_ifflags(drv, &flags); | |
1699 | (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); | |
1700 | } | |
1701 | ||
3f5285e8 JM |
1702 | return ret; |
1703 | } | |
1704 | ||
1705 | ||
3f5285e8 JM |
1706 | static int wpa_driver_nl80211_get_capa(void *priv, |
1707 | struct wpa_driver_capa *capa) | |
1708 | { | |
1709 | struct wpa_driver_nl80211_data *drv = priv; | |
1710 | if (!drv->has_capability) | |
1711 | return -1; | |
1712 | os_memcpy(capa, &drv->capa, sizeof(*capa)); | |
1713 | return 0; | |
1714 | } | |
1715 | ||
1716 | ||
1717 | static int wpa_driver_nl80211_set_operstate(void *priv, int state) | |
1718 | { | |
1719 | struct wpa_driver_nl80211_data *drv = priv; | |
1720 | ||
1721 | wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", | |
1722 | __func__, drv->operstate, state, state ? "UP" : "DORMANT"); | |
1723 | drv->operstate = state; | |
1724 | return wpa_driver_nl80211_send_oper_ifla( | |
1725 | drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); | |
1726 | } | |
1727 | ||
1728 | ||
1729 | const struct wpa_driver_ops wpa_driver_nl80211_ops = { | |
1730 | .name = "nl80211", | |
1731 | .desc = "Linux nl80211/cfg80211", | |
1732 | .get_bssid = wpa_driver_nl80211_get_bssid, | |
1733 | .get_ssid = wpa_driver_nl80211_get_ssid, | |
3f5285e8 | 1734 | .set_key = wpa_driver_nl80211_set_key, |
6a1063e0 | 1735 | .scan2 = wpa_driver_nl80211_scan, |
3f5285e8 JM |
1736 | .get_scan_results2 = wpa_driver_nl80211_get_scan_results, |
1737 | .deauthenticate = wpa_driver_nl80211_deauthenticate, | |
1738 | .disassociate = wpa_driver_nl80211_disassociate, | |
c2a04078 | 1739 | .authenticate = wpa_driver_nl80211_authenticate, |
3f5285e8 | 1740 | .associate = wpa_driver_nl80211_associate, |
3f5285e8 JM |
1741 | .init = wpa_driver_nl80211_init, |
1742 | .deinit = wpa_driver_nl80211_deinit, | |
3f5285e8 JM |
1743 | .get_capa = wpa_driver_nl80211_get_capa, |
1744 | .set_operstate = wpa_driver_nl80211_set_operstate, | |
6d158490 | 1745 | .set_country = wpa_driver_nl80211_set_country, |
3f5285e8 | 1746 | }; |