98200bfef7d1ae4e53d1973dfead744bb2594e35
[openwrt/svn-archive/archive.git] / package / network / utils / iwinfo / src / iwinfo_nl80211.c
1 /*
2 * iwinfo - Wireless Information Library - NL80211 Backend
3 *
4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * The iwinfo library is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * The iwinfo library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with the iwinfo library. If not, see http://www.gnu.org/licenses/.
17 *
18 * The signal handling code is derived from the official madwifi tools,
19 * wlanconfig.c in particular. The encryption property handling was
20 * inspired by the hostapd madwifi driver.
21 *
22 * Parts of this code are derived from the Linux iw utility.
23 */
24
25 #include <limits.h>
26 #include "iwinfo_nl80211.h"
27
28 #define min(x, y) ((x) < (y)) ? (x) : (y)
29
30 static struct nl80211_state *nls = NULL;
31
32 static void nl80211_close(void)
33 {
34 if (nls)
35 {
36 if (nls->nlctrl)
37 genl_family_put(nls->nlctrl);
38
39 if (nls->nl80211)
40 genl_family_put(nls->nl80211);
41
42 if (nls->nl_sock)
43 nl_socket_free(nls->nl_sock);
44
45 if (nls->nl_cache)
46 nl_cache_free(nls->nl_cache);
47
48 free(nls);
49 nls = NULL;
50 }
51 }
52
53 static int nl80211_init(void)
54 {
55 int err, fd;
56
57 if (!nls)
58 {
59 nls = malloc(sizeof(struct nl80211_state));
60 if (!nls) {
61 err = -ENOMEM;
62 goto err;
63 }
64
65 memset(nls, 0, sizeof(*nls));
66
67 nls->nl_sock = nl_socket_alloc();
68 if (!nls->nl_sock) {
69 err = -ENOMEM;
70 goto err;
71 }
72
73 if (genl_connect(nls->nl_sock)) {
74 err = -ENOLINK;
75 goto err;
76 }
77
78 fd = nl_socket_get_fd(nls->nl_sock);
79 if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) {
80 err = -EINVAL;
81 goto err;
82 }
83
84 if (genl_ctrl_alloc_cache(nls->nl_sock, &nls->nl_cache)) {
85 err = -ENOMEM;
86 goto err;
87 }
88
89 nls->nl80211 = genl_ctrl_search_by_name(nls->nl_cache, "nl80211");
90 if (!nls->nl80211) {
91 err = -ENOENT;
92 goto err;
93 }
94
95 nls->nlctrl = genl_ctrl_search_by_name(nls->nl_cache, "nlctrl");
96 if (!nls->nlctrl) {
97 err = -ENOENT;
98 goto err;
99 }
100 }
101
102 return 0;
103
104
105 err:
106 nl80211_close();
107 return err;
108 }
109
110 static int nl80211_readint(const char *path)
111 {
112 int fd;
113 int rv = -1;
114 char buffer[16];
115
116 if ((fd = open(path, O_RDONLY)) > -1)
117 {
118 if (read(fd, buffer, sizeof(buffer)) > 0)
119 rv = atoi(buffer);
120
121 close(fd);
122 }
123
124 return rv;
125 }
126
127
128 static int nl80211_msg_error(struct sockaddr_nl *nla,
129 struct nlmsgerr *err, void *arg)
130 {
131 int *ret = arg;
132 *ret = err->error;
133 return NL_STOP;
134 }
135
136 static int nl80211_msg_finish(struct nl_msg *msg, void *arg)
137 {
138 int *ret = arg;
139 *ret = 0;
140 return NL_SKIP;
141 }
142
143 static int nl80211_msg_ack(struct nl_msg *msg, void *arg)
144 {
145 int *ret = arg;
146 *ret = 0;
147 return NL_STOP;
148 }
149
150 static int nl80211_msg_response(struct nl_msg *msg, void *arg)
151 {
152 return NL_SKIP;
153 }
154
155 static void nl80211_free(struct nl80211_msg_conveyor *cv)
156 {
157 if (cv)
158 {
159 if (cv->cb)
160 nl_cb_put(cv->cb);
161
162 if (cv->msg)
163 nlmsg_free(cv->msg);
164
165 cv->cb = NULL;
166 cv->msg = NULL;
167 }
168 }
169
170 static struct nl80211_msg_conveyor * nl80211_new(struct genl_family *family,
171 int cmd, int flags)
172 {
173 static struct nl80211_msg_conveyor cv;
174
175 struct nl_msg *req = NULL;
176 struct nl_cb *cb = NULL;
177
178 req = nlmsg_alloc();
179 if (!req)
180 goto err;
181
182 cb = nl_cb_alloc(NL_CB_DEFAULT);
183 if (!cb)
184 goto err;
185
186 genlmsg_put(req, 0, 0, genl_family_get_id(family), 0, flags, cmd, 0);
187
188 cv.msg = req;
189 cv.cb = cb;
190
191 return &cv;
192
193 err:
194 nla_put_failure:
195 if (cb)
196 nl_cb_put(cb);
197
198 if (req)
199 nlmsg_free(req);
200
201 return NULL;
202 }
203
204 static struct nl80211_msg_conveyor * nl80211_ctl(int cmd, int flags)
205 {
206 if (nl80211_init() < 0)
207 return NULL;
208
209 return nl80211_new(nls->nlctrl, cmd, flags);
210 }
211
212 static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname,
213 int cmd, int flags)
214 {
215 int ifidx = -1, phyidx = -1;
216 struct nl80211_msg_conveyor *cv;
217
218 if (nl80211_init() < 0)
219 return NULL;
220
221 if (!strncmp(ifname, "phy", 3))
222 phyidx = atoi(&ifname[3]);
223 else if (!strncmp(ifname, "radio", 5))
224 phyidx = atoi(&ifname[5]);
225 else if (!strncmp(ifname, "mon.", 4))
226 ifidx = if_nametoindex(&ifname[4]);
227 else
228 ifidx = if_nametoindex(ifname);
229
230 if ((ifidx < 0) && (phyidx < 0))
231 return NULL;
232
233 cv = nl80211_new(nls->nl80211, cmd, flags);
234 if (!cv)
235 return NULL;
236
237 if (ifidx > -1)
238 NLA_PUT_U32(cv->msg, NL80211_ATTR_IFINDEX, ifidx);
239
240 if (phyidx > -1)
241 NLA_PUT_U32(cv->msg, NL80211_ATTR_WIPHY, phyidx);
242
243 return cv;
244
245 nla_put_failure:
246 nl80211_free(cv);
247 return NULL;
248 }
249
250 static struct nl80211_msg_conveyor * nl80211_send(
251 struct nl80211_msg_conveyor *cv,
252 int (*cb_func)(struct nl_msg *, void *), void *cb_arg
253 ) {
254 static struct nl80211_msg_conveyor rcv;
255 int err = 1;
256
257 if (cb_func)
258 nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, cb_func, cb_arg);
259 else
260 nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_msg_response, &rcv);
261
262 if (nl_send_auto_complete(nls->nl_sock, cv->msg) < 0)
263 goto err;
264
265 nl_cb_err(cv->cb, NL_CB_CUSTOM, nl80211_msg_error, &err);
266 nl_cb_set(cv->cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_msg_finish, &err);
267 nl_cb_set(cv->cb, NL_CB_ACK, NL_CB_CUSTOM, nl80211_msg_ack, &err);
268
269 while (err > 0)
270 nl_recvmsgs(nls->nl_sock, cv->cb);
271
272 return &rcv;
273
274 err:
275 nl_cb_put(cv->cb);
276 nlmsg_free(cv->msg);
277
278 return NULL;
279 }
280
281 static struct nlattr ** nl80211_parse(struct nl_msg *msg)
282 {
283 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
284 static struct nlattr *attr[NL80211_ATTR_MAX + 1];
285
286 nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
287 genlmsg_attrlen(gnlh, 0), NULL);
288
289 return attr;
290 }
291
292
293 static int nl80211_subscribe_cb(struct nl_msg *msg, void *arg)
294 {
295 struct nl80211_group_conveyor *cv = arg;
296
297 struct nlattr **attr = nl80211_parse(msg);
298 struct nlattr *mgrpinfo[CTRL_ATTR_MCAST_GRP_MAX + 1];
299 struct nlattr *mgrp;
300 int mgrpidx;
301
302 if (!attr[CTRL_ATTR_MCAST_GROUPS])
303 return NL_SKIP;
304
305 nla_for_each_nested(mgrp, attr[CTRL_ATTR_MCAST_GROUPS], mgrpidx)
306 {
307 nla_parse(mgrpinfo, CTRL_ATTR_MCAST_GRP_MAX,
308 nla_data(mgrp), nla_len(mgrp), NULL);
309
310 if (mgrpinfo[CTRL_ATTR_MCAST_GRP_ID] &&
311 mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME] &&
312 !strncmp(nla_data(mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME]),
313 cv->name, nla_len(mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME])))
314 {
315 cv->id = nla_get_u32(mgrpinfo[CTRL_ATTR_MCAST_GRP_ID]);
316 break;
317 }
318 }
319
320 return NL_SKIP;
321 }
322
323 static int nl80211_subscribe(const char *family, const char *group)
324 {
325 struct nl80211_group_conveyor cv = { .name = group, .id = -ENOENT };
326 struct nl80211_msg_conveyor *req;
327
328 req = nl80211_ctl(CTRL_CMD_GETFAMILY, 0);
329 if (req)
330 {
331 NLA_PUT_STRING(req->msg, CTRL_ATTR_FAMILY_NAME, family);
332 nl80211_send(req, nl80211_subscribe_cb, &cv);
333
334 nla_put_failure:
335 nl80211_free(req);
336 }
337
338 return nl_socket_add_membership(nls->nl_sock, cv.id);
339 }
340
341
342 static int nl80211_wait_cb(struct nl_msg *msg, void *arg)
343 {
344 struct nl80211_event_conveyor *cv = arg;
345 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
346
347 if (gnlh->cmd == cv->wait)
348 cv->recv = gnlh->cmd;
349
350 return NL_SKIP;
351 }
352
353 static int nl80211_wait_seq_check(struct nl_msg *msg, void *arg)
354 {
355 return NL_OK;
356 }
357
358 static int nl80211_wait(const char *family, const char *group, int cmd)
359 {
360 struct nl80211_event_conveyor cv = { .wait = cmd };
361 struct nl_cb *cb;
362
363 if (nl80211_subscribe(family, group))
364 return -ENOENT;
365
366 cb = nl_cb_alloc(NL_CB_DEFAULT);
367
368 if (!cb)
369 return -ENOMEM;
370
371 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl80211_wait_seq_check, NULL);
372 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_wait_cb, &cv );
373
374 while (!cv.recv)
375 nl_recvmsgs(nls->nl_sock, cb);
376
377 nl_cb_put(cb);
378
379 return 0;
380 }
381
382
383 static int nl80211_freq2channel(int freq)
384 {
385 if (freq == 2484)
386 return 14;
387 else if (freq < 2484)
388 return (freq - 2407) / 5;
389 else if (freq >= 4910 && freq <= 4980)
390 return (freq - 4000) / 5;
391 else
392 return (freq - 5000) / 5;
393 }
394
395 static int nl80211_channel2freq(int channel, const char *band)
396 {
397 if (!band || band[0] != 'a')
398 {
399 if (channel == 14)
400 return 2484;
401 else if (channel < 14)
402 return (channel * 5) + 2407;
403 }
404 else
405 {
406 if (channel >= 182 && channel <= 196)
407 return (channel * 5) + 4000;
408 else
409 return (channel * 5) + 5000;
410 }
411
412 return 0;
413 }
414
415 static char * nl80211_getval(const char *ifname, const char *buf, const char *key)
416 {
417 int i, len;
418 char lkey[64] = { 0 };
419 const char *ln = buf;
420 static char lval[256] = { 0 };
421
422 int matched_if = ifname ? 0 : 1;
423
424
425 for( i = 0, len = strlen(buf); i < len; i++ )
426 {
427 if (!lkey[0] && (buf[i] == ' ' || buf[i] == '\t'))
428 {
429 ln++;
430 }
431 else if (!lkey[0] && (buf[i] == '='))
432 {
433 if ((&buf[i] - ln) > 0)
434 memcpy(lkey, ln, min(sizeof(lkey) - 1, &buf[i] - ln));
435 }
436 else if (buf[i] == '\n')
437 {
438 if (lkey[0])
439 {
440 memcpy(lval, ln + strlen(lkey) + 1,
441 min(sizeof(lval) - 1, &buf[i] - ln - strlen(lkey) - 1));
442
443 if ((ifname != NULL) &&
444 (!strcmp(lkey, "interface") || !strcmp(lkey, "bss")) )
445 {
446 matched_if = !strcmp(lval, ifname);
447 }
448 else if (matched_if && !strcmp(lkey, key))
449 {
450 return lval;
451 }
452 }
453
454 ln = &buf[i+1];
455 memset(lkey, 0, sizeof(lkey));
456 memset(lval, 0, sizeof(lval));
457 }
458 }
459
460 return NULL;
461 }
462
463 static int nl80211_ifname2phy_cb(struct nl_msg *msg, void *arg)
464 {
465 char *buf = arg;
466 struct nlattr **attr = nl80211_parse(msg);
467
468 if (attr[NL80211_ATTR_WIPHY_NAME])
469 memcpy(buf, nla_data(attr[NL80211_ATTR_WIPHY_NAME]),
470 nla_len(attr[NL80211_ATTR_WIPHY_NAME]));
471 else
472 buf[0] = 0;
473
474 return NL_SKIP;
475 }
476
477 static char * nl80211_ifname2phy(const char *ifname)
478 {
479 static char phy[32] = { 0 };
480 struct nl80211_msg_conveyor *req;
481
482 memset(phy, 0, sizeof(phy));
483
484 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
485 if (req)
486 {
487 nl80211_send(req, nl80211_ifname2phy_cb, phy);
488 nl80211_free(req);
489 }
490
491 return phy[0] ? phy : NULL;
492 }
493
494 static char * nl80211_phy2ifname(const char *ifname)
495 {
496 int fd, ifidx = -1, cifidx = -1, phyidx = -1;
497 char buffer[64];
498 static char nif[IFNAMSIZ] = { 0 };
499
500 DIR *d;
501 struct dirent *e;
502
503 if (!ifname)
504 return NULL;
505 else if (!strncmp(ifname, "phy", 3))
506 phyidx = atoi(&ifname[3]);
507 else if (!strncmp(ifname, "radio", 5))
508 phyidx = atoi(&ifname[5]);
509
510 memset(nif, 0, sizeof(nif));
511
512 if (phyidx > -1)
513 {
514 if ((d = opendir("/sys/class/net")) != NULL)
515 {
516 while ((e = readdir(d)) != NULL)
517 {
518 snprintf(buffer, sizeof(buffer),
519 "/sys/class/net/%s/phy80211/index", e->d_name);
520
521 if (nl80211_readint(buffer) == phyidx)
522 {
523 snprintf(buffer, sizeof(buffer),
524 "/sys/class/net/%s/ifindex", e->d_name);
525
526 if ((cifidx = nl80211_readint(buffer)) >= 0 &&
527 ((ifidx < 0) || (cifidx < ifidx)))
528 {
529 ifidx = cifidx;
530 strncpy(nif, e->d_name, sizeof(nif));
531 }
532 }
533 }
534
535 closedir(d);
536 }
537 }
538
539 return nif[0] ? nif : NULL;
540 }
541
542 static int nl80211_get_mode_cb(struct nl_msg *msg, void *arg)
543 {
544 int *mode = arg;
545 struct nlattr **tb = nl80211_parse(msg);
546 const int ifmodes[NL80211_IFTYPE_MAX + 1] = {
547 IWINFO_OPMODE_UNKNOWN, /* unspecified */
548 IWINFO_OPMODE_ADHOC, /* IBSS */
549 IWINFO_OPMODE_CLIENT, /* managed */
550 IWINFO_OPMODE_MASTER, /* AP */
551 IWINFO_OPMODE_AP_VLAN, /* AP/VLAN */
552 IWINFO_OPMODE_WDS, /* WDS */
553 IWINFO_OPMODE_MONITOR, /* monitor */
554 IWINFO_OPMODE_MESHPOINT, /* mesh point */
555 IWINFO_OPMODE_P2P_CLIENT, /* P2P-client */
556 IWINFO_OPMODE_P2P_GO, /* P2P-GO */
557 };
558
559 if (tb[NL80211_ATTR_IFTYPE])
560 *mode = ifmodes[nla_get_u32(tb[NL80211_ATTR_IFTYPE])];
561
562 return NL_SKIP;
563 }
564
565
566 static int nl80211_get_mode(const char *ifname, int *buf)
567 {
568 char *res;
569 struct nl80211_msg_conveyor *req;
570
571 res = nl80211_phy2ifname(ifname);
572 req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0);
573 *buf = IWINFO_OPMODE_UNKNOWN;
574
575 if (req)
576 {
577 nl80211_send(req, nl80211_get_mode_cb, buf);
578 nl80211_free(req);
579 }
580
581 return (*buf == IWINFO_OPMODE_UNKNOWN) ? -1 : 0;
582 }
583
584 static char * nl80211_hostapd_info(const char *ifname)
585 {
586 int mode;
587 char *phy;
588 char path[32] = { 0 };
589 static char buf[4096] = { 0 };
590 FILE *conf;
591
592 if (nl80211_get_mode(ifname, &mode))
593 return NULL;
594
595 if ((mode == IWINFO_OPMODE_MASTER || mode == IWINFO_OPMODE_AP_VLAN) &&
596 (phy = nl80211_ifname2phy(ifname)) != NULL)
597 {
598 snprintf(path, sizeof(path), "/var/run/hostapd-%s.conf", phy);
599
600 if ((conf = fopen(path, "r")) != NULL)
601 {
602 fread(buf, sizeof(buf) - 1, 1, conf);
603 fclose(conf);
604
605 return buf;
606 }
607 }
608
609 return NULL;
610 }
611
612 static inline int nl80211_wpactl_recv(int sock, char *buf, int blen)
613 {
614 fd_set rfds;
615 struct timeval tv = { 2, 0 };
616
617 FD_ZERO(&rfds);
618 FD_SET(sock, &rfds);
619
620 memset(buf, 0, blen);
621
622
623 if (select(sock + 1, &rfds, NULL, NULL, &tv) < 0)
624 return -1;
625
626 if (!FD_ISSET(sock, &rfds))
627 return -1;
628
629 return recv(sock, buf, blen, 0);
630 }
631
632 static char * nl80211_wpactl_info(const char *ifname, const char *cmd,
633 const char *event)
634 {
635 int numtry = 0;
636 int sock = -1;
637 char *rv = NULL;
638 size_t remote_length, local_length;
639 static char buffer[10240] = { 0 };
640
641 struct sockaddr_un local = { 0 };
642 struct sockaddr_un remote = { 0 };
643
644
645 sock = socket(PF_UNIX, SOCK_DGRAM, 0);
646 if (sock < 0)
647 return NULL;
648
649 remote.sun_family = AF_UNIX;
650 remote_length = sizeof(remote.sun_family) + sprintf(remote.sun_path,
651 "/var/run/wpa_supplicant-%s/%s", ifname, ifname);
652
653 if (fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC) < 0)
654 goto out;
655
656 if (connect(sock, (struct sockaddr *) &remote, remote_length))
657 {
658 remote_length = sizeof(remote.sun_family) + sprintf(remote.sun_path,
659 "/var/run/wpa_supplicant/%s", ifname);
660
661 if (connect(sock, (struct sockaddr *) &remote, remote_length))
662 goto out;
663 }
664
665 local.sun_family = AF_UNIX;
666 local_length = sizeof(local.sun_family) +
667 sprintf(local.sun_path, "/var/run/iwinfo-%s-%d", ifname, getpid());
668
669 if (bind(sock, (struct sockaddr *) &local, local_length))
670 goto out;
671
672
673 if (event)
674 {
675 send(sock, "ATTACH", 6, 0);
676
677 if (nl80211_wpactl_recv(sock, buffer, sizeof(buffer)-1) <= 0)
678 goto out;
679 }
680
681
682 send(sock, cmd, strlen(cmd), 0);
683
684 /* we might have to scan up to 72 channels / 256ms per channel */
685 /* this makes up to 18.5s hence 10 tries */
686 while( numtry++ < 10 )
687 {
688 char *bracket;
689
690 /* make sure there is a terminating nul byte */
691 if (nl80211_wpactl_recv(sock, buffer, sizeof(buffer)-1) <= 0)
692 {
693 if (event)
694 continue;
695
696 break;
697 }
698
699 if ((!event && buffer[0] != '<') || (event && strstr(buffer, event)))
700 break;
701
702 /* there may be more than max(numtry) BSS-ADDED events */
703 /* ignore them similar to wpa_cli */
704 if (buffer[0] == '<' &&
705 (bracket=strchr(buffer,'>')) != NULL &&
706 strncmp(bracket+1,"CTRL-EVENT-BSS-ADDED",20) == 0)
707 numtry--;
708 }
709
710 rv = buffer;
711
712 out:
713 close(sock);
714
715 if (local.sun_family)
716 unlink(local.sun_path);
717
718 return rv;
719 }
720
721 static char * nl80211_ifadd(const char *ifname)
722 {
723 int phyidx;
724 char *rv = NULL, path[PATH_MAX];
725 static char nif[IFNAMSIZ] = { 0 };
726 struct nl80211_msg_conveyor *req, *res;
727 FILE *sysfs;
728
729 req = nl80211_msg(ifname, NL80211_CMD_NEW_INTERFACE, 0);
730 if (req)
731 {
732 snprintf(nif, sizeof(nif), "tmp.%s", ifname);
733
734 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, nif);
735 NLA_PUT_U32(req->msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
736
737 nl80211_send(req, NULL, NULL);
738
739 snprintf(path, sizeof(path) - 1,
740 "/proc/sys/net/ipv6/conf/%s/disable_ipv6", nif);
741
742 if ((sysfs = fopen(path, "w")) != NULL)
743 {
744 fwrite("0\n", 1, 2, sysfs);
745 fclose(sysfs);
746 }
747
748 rv = nif;
749
750 nla_put_failure:
751 nl80211_free(req);
752 }
753
754 return rv;
755 }
756
757 static void nl80211_ifdel(const char *ifname)
758 {
759 struct nl80211_msg_conveyor *req;
760
761 req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
762 if (req)
763 {
764 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, ifname);
765
766 nl80211_send(req, NULL, NULL);
767
768 nla_put_failure:
769 nl80211_free(req);
770 }
771 }
772
773 static void nl80211_hostapd_hup(const char *ifname)
774 {
775 int fd, pid = 0;
776 char buf[32];
777 char *phy = nl80211_ifname2phy(ifname);
778
779 if (phy)
780 {
781 snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
782 if ((fd = open(buf, O_RDONLY)) > 0)
783 {
784 if (read(fd, buf, sizeof(buf)) > 0)
785 pid = atoi(buf);
786
787 close(fd);
788 }
789
790 if (pid > 0)
791 kill(pid, 1);
792 }
793 }
794
795
796 static int nl80211_probe(const char *ifname)
797 {
798 return !!nl80211_ifname2phy(ifname);
799 }
800
801 struct nl80211_ssid_bssid {
802 unsigned char *ssid;
803 unsigned char bssid[7];
804 };
805
806 static int nl80211_get_ssid_bssid_cb(struct nl_msg *msg, void *arg)
807 {
808 int ielen;
809 unsigned char *ie;
810 struct nl80211_ssid_bssid *sb = arg;
811 struct nlattr **tb = nl80211_parse(msg);
812 struct nlattr *bss[NL80211_BSS_MAX + 1];
813
814 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
815 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
816 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
817 };
818
819 if (!tb[NL80211_ATTR_BSS] ||
820 nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
821 bss_policy) ||
822 !bss[NL80211_BSS_BSSID] ||
823 !bss[NL80211_BSS_STATUS] ||
824 !bss[NL80211_BSS_INFORMATION_ELEMENTS])
825 {
826 return NL_SKIP;
827 }
828
829 switch (nla_get_u32(bss[NL80211_BSS_STATUS]))
830 {
831 case NL80211_BSS_STATUS_ASSOCIATED:
832 case NL80211_BSS_STATUS_AUTHENTICATED:
833 case NL80211_BSS_STATUS_IBSS_JOINED:
834
835 if (sb->ssid)
836 {
837 ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
838 ielen = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
839
840 while (ielen >= 2 && ielen >= ie[1])
841 {
842 if (ie[0] == 0)
843 {
844 memcpy(sb->ssid, ie + 2, min(ie[1], IWINFO_ESSID_MAX_SIZE));
845 return NL_SKIP;
846 }
847
848 ielen -= ie[1] + 2;
849 ie += ie[1] + 2;
850 }
851 }
852 else
853 {
854 sb->bssid[0] = 1;
855 memcpy(sb->bssid + 1, nla_data(bss[NL80211_BSS_BSSID]), 6);
856 return NL_SKIP;
857 }
858
859 default:
860 return NL_SKIP;
861 }
862 }
863
864 static int nl80211_get_ssid(const char *ifname, char *buf)
865 {
866 char *res;
867 struct nl80211_msg_conveyor *req;
868 struct nl80211_ssid_bssid sb;
869
870 /* try to find ssid from scan dump results */
871 res = nl80211_phy2ifname(ifname);
872 req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
873
874 sb.ssid = buf;
875 *buf = 0;
876
877 if (req)
878 {
879 nl80211_send(req, nl80211_get_ssid_bssid_cb, &sb);
880 nl80211_free(req);
881 }
882
883 /* failed, try to find from hostapd info */
884 if ((*buf == 0) &&
885 (res = nl80211_hostapd_info(ifname)) &&
886 (res = nl80211_getval(ifname, res, "ssid")))
887 {
888 memcpy(buf, res, strlen(res));
889 }
890
891 return (*buf == 0) ? -1 : 0;
892 }
893
894 static int nl80211_get_bssid(const char *ifname, char *buf)
895 {
896 char *res;
897 struct nl80211_msg_conveyor *req;
898 struct nl80211_ssid_bssid sb;
899
900 /* try to find bssid from scan dump results */
901 res = nl80211_phy2ifname(ifname);
902 req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
903
904 sb.ssid = NULL;
905 sb.bssid[0] = 0;
906
907 if (req)
908 {
909 nl80211_send(req, nl80211_get_ssid_bssid_cb, &sb);
910 nl80211_free(req);
911 }
912
913 /* failed, try to find mac from hostapd info */
914 if ((sb.bssid[0] == 0) &&
915 (res = nl80211_hostapd_info(ifname)) &&
916 (res = nl80211_getval(ifname, res, "bssid")))
917 {
918 sb.bssid[0] = 1;
919 sb.bssid[1] = strtol(&res[0], NULL, 16);
920 sb.bssid[2] = strtol(&res[3], NULL, 16);
921 sb.bssid[3] = strtol(&res[6], NULL, 16);
922 sb.bssid[4] = strtol(&res[9], NULL, 16);
923 sb.bssid[5] = strtol(&res[12], NULL, 16);
924 sb.bssid[6] = strtol(&res[15], NULL, 16);
925 }
926
927 if (sb.bssid[0])
928 {
929 sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
930 sb.bssid[1], sb.bssid[2], sb.bssid[3],
931 sb.bssid[4], sb.bssid[5], sb.bssid[6]);
932
933 return 0;
934 }
935
936 return -1;
937 }
938
939
940 static int nl80211_get_frequency_scan_cb(struct nl_msg *msg, void *arg)
941 {
942 int *freq = arg;
943 struct nlattr **attr = nl80211_parse(msg);
944 struct nlattr *binfo[NL80211_BSS_MAX + 1];
945
946 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
947 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
948 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
949 };
950
951 if (attr[NL80211_ATTR_BSS] &&
952 !nla_parse_nested(binfo, NL80211_BSS_MAX,
953 attr[NL80211_ATTR_BSS], bss_policy))
954 {
955 if (binfo[NL80211_BSS_STATUS] && binfo[NL80211_BSS_FREQUENCY])
956 *freq = nla_get_u32(binfo[NL80211_BSS_FREQUENCY]);
957 }
958
959 return NL_SKIP;
960 }
961
962 static int nl80211_get_frequency_info_cb(struct nl_msg *msg, void *arg)
963 {
964 int *freq = arg;
965 struct nlattr **tb = nl80211_parse(msg);
966
967 if (tb[NL80211_ATTR_WIPHY_FREQ])
968 *freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
969
970 return NL_SKIP;
971 }
972
973 static int nl80211_get_frequency(const char *ifname, int *buf)
974 {
975 int chn;
976 char *res, *channel;
977 struct nl80211_msg_conveyor *req;
978
979 /* try to find frequency from interface info */
980 res = nl80211_phy2ifname(ifname);
981 req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0);
982 *buf = 0;
983
984 if (req)
985 {
986 nl80211_send(req, nl80211_get_frequency_info_cb, buf);
987 nl80211_free(req);
988 }
989
990 /* failed, try to find frequency from hostapd info */
991 if ((*buf == 0) &&
992 (res = nl80211_hostapd_info(ifname)) &&
993 (channel = nl80211_getval(NULL, res, "channel")))
994 {
995 chn = atoi(channel);
996 *buf = nl80211_channel2freq(chn, nl80211_getval(NULL, res, "hw_mode"));
997 }
998 else
999 {
1000 /* failed, try to find frequency from scan results */
1001 if (*buf == 0)
1002 {
1003 res = nl80211_phy2ifname(ifname);
1004 req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN,
1005 NLM_F_DUMP);
1006
1007 if (req)
1008 {
1009 nl80211_send(req, nl80211_get_frequency_scan_cb, buf);
1010 nl80211_free(req);
1011 }
1012 }
1013 }
1014
1015 return (*buf == 0) ? -1 : 0;
1016 }
1017
1018 static int nl80211_get_channel(const char *ifname, int *buf)
1019 {
1020 if (!nl80211_get_frequency(ifname, buf))
1021 {
1022 *buf = nl80211_freq2channel(*buf);
1023 return 0;
1024 }
1025
1026 return -1;
1027 }
1028
1029
1030 static int nl80211_get_txpower(const char *ifname, int *buf)
1031 {
1032 #if 0
1033 char *res;
1034 char path[PATH_MAX];
1035
1036 res = nl80211_ifname2phy(ifname);
1037 snprintf(path, sizeof(path), "/sys/kernel/debug/ieee80211/%s/power",
1038 res ? res : ifname);
1039
1040 if ((*buf = nl80211_readint(path)) > -1)
1041 return 0;
1042 #endif
1043
1044 return wext_ops.txpower(ifname, buf);
1045 }
1046
1047
1048 static int nl80211_fill_signal_cb(struct nl_msg *msg, void *arg)
1049 {
1050 int8_t dbm;
1051 int16_t mbit;
1052 struct nl80211_rssi_rate *rr = arg;
1053 struct nlattr **attr = nl80211_parse(msg);
1054 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
1055 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
1056
1057 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
1058 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
1059 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
1060 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
1061 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
1062 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
1063 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
1064 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
1065 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
1066 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
1067 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
1068 };
1069
1070 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
1071 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
1072 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
1073 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
1074 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
1075 };
1076
1077 if (attr[NL80211_ATTR_STA_INFO])
1078 {
1079 if (!nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
1080 attr[NL80211_ATTR_STA_INFO], stats_policy))
1081 {
1082 if (sinfo[NL80211_STA_INFO_SIGNAL])
1083 {
1084 dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
1085 rr->rssi = rr->rssi ? (int8_t)((rr->rssi + dbm) / 2) : dbm;
1086 }
1087
1088 if (sinfo[NL80211_STA_INFO_TX_BITRATE])
1089 {
1090 if (!nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
1091 sinfo[NL80211_STA_INFO_TX_BITRATE],
1092 rate_policy))
1093 {
1094 if (rinfo[NL80211_RATE_INFO_BITRATE])
1095 {
1096 mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
1097 rr->rate = rr->rate
1098 ? (int16_t)((rr->rate + mbit) / 2) : mbit;
1099 }
1100 }
1101 }
1102 }
1103 }
1104
1105 return NL_SKIP;
1106 }
1107
1108 static void nl80211_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
1109 {
1110 DIR *d;
1111 struct dirent *de;
1112 struct nl80211_msg_conveyor *req;
1113
1114 r->rssi = 0;
1115 r->rate = 0;
1116
1117 if ((d = opendir("/sys/class/net")) != NULL)
1118 {
1119 while ((de = readdir(d)) != NULL)
1120 {
1121 if (!strncmp(de->d_name, ifname, strlen(ifname)) &&
1122 (!de->d_name[strlen(ifname)] ||
1123 !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
1124 {
1125 req = nl80211_msg(de->d_name, NL80211_CMD_GET_STATION,
1126 NLM_F_DUMP);
1127
1128 if (req)
1129 {
1130 nl80211_send(req, nl80211_fill_signal_cb, r);
1131 nl80211_free(req);
1132 }
1133 }
1134 }
1135
1136 closedir(d);
1137 }
1138 }
1139
1140 static int nl80211_get_bitrate(const char *ifname, int *buf)
1141 {
1142 struct nl80211_rssi_rate rr;
1143
1144 nl80211_fill_signal(ifname, &rr);
1145
1146 if (rr.rate)
1147 {
1148 *buf = (rr.rate * 100);
1149 return 0;
1150 }
1151
1152 return -1;
1153 }
1154
1155 static int nl80211_get_signal(const char *ifname, int *buf)
1156 {
1157 struct nl80211_rssi_rate rr;
1158
1159 nl80211_fill_signal(ifname, &rr);
1160
1161 if (rr.rssi)
1162 {
1163 *buf = rr.rssi;
1164 return 0;
1165 }
1166
1167 return -1;
1168 }
1169
1170 static int nl80211_get_noise_cb(struct nl_msg *msg, void *arg)
1171 {
1172 int8_t *noise = arg;
1173 struct nlattr **tb = nl80211_parse(msg);
1174 struct nlattr *si[NL80211_SURVEY_INFO_MAX + 1];
1175
1176 static struct nla_policy sp[NL80211_SURVEY_INFO_MAX + 1] = {
1177 [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
1178 [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
1179 };
1180
1181 if (!tb[NL80211_ATTR_SURVEY_INFO])
1182 return NL_SKIP;
1183
1184 if (nla_parse_nested(si, NL80211_SURVEY_INFO_MAX,
1185 tb[NL80211_ATTR_SURVEY_INFO], sp))
1186 return NL_SKIP;
1187
1188 if (!si[NL80211_SURVEY_INFO_NOISE])
1189 return NL_SKIP;
1190
1191 if (!*noise || si[NL80211_SURVEY_INFO_IN_USE])
1192 *noise = (int8_t)nla_get_u8(si[NL80211_SURVEY_INFO_NOISE]);
1193
1194 return NL_SKIP;
1195 }
1196
1197
1198 static int nl80211_get_noise(const char *ifname, int *buf)
1199 {
1200 int8_t noise;
1201 struct nl80211_msg_conveyor *req;
1202
1203 req = nl80211_msg(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP);
1204 if (req)
1205 {
1206 noise = 0;
1207
1208 nl80211_send(req, nl80211_get_noise_cb, &noise);
1209 nl80211_free(req);
1210
1211 if (noise)
1212 {
1213 *buf = noise;
1214 return 0;
1215 }
1216 }
1217
1218 return -1;
1219 }
1220
1221 static int nl80211_get_quality(const char *ifname, int *buf)
1222 {
1223 int signal;
1224
1225 if (!nl80211_get_signal(ifname, &signal))
1226 {
1227 /* A positive signal level is usually just a quality
1228 * value, pass through as-is */
1229 if (signal >= 0)
1230 {
1231 *buf = signal;
1232 }
1233
1234 /* The cfg80211 wext compat layer assumes a signal range
1235 * of -110 dBm to -40 dBm, the quality value is derived
1236 * by adding 110 to the signal level */
1237 else
1238 {
1239 if (signal < -110)
1240 signal = -110;
1241 else if (signal > -40)
1242 signal = -40;
1243
1244 *buf = (signal + 110);
1245 }
1246
1247 return 0;
1248 }
1249
1250 return -1;
1251 }
1252
1253 static int nl80211_get_quality_max(const char *ifname, int *buf)
1254 {
1255 /* The cfg80211 wext compat layer assumes a maximum
1256 * quality of 70 */
1257 *buf = 70;
1258
1259 return 0;
1260 }
1261
1262 static int nl80211_get_encryption(const char *ifname, char *buf)
1263 {
1264 int i;
1265 char k[9];
1266 char *val, *res;
1267 struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
1268
1269 /* WPA supplicant */
1270 if ((res = nl80211_wpactl_info(ifname, "STATUS", NULL)) &&
1271 (val = nl80211_getval(NULL, res, "pairwise_cipher")))
1272 {
1273 /* WEP */
1274 if (strstr(val, "WEP"))
1275 {
1276 if (strstr(val, "WEP-40"))
1277 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1278
1279 else if (strstr(val, "WEP-104"))
1280 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1281
1282 c->enabled = 1;
1283 c->group_ciphers = c->pair_ciphers;
1284
1285 c->auth_suites |= IWINFO_KMGMT_NONE;
1286 c->auth_algs |= IWINFO_AUTH_OPEN; /* XXX: assumption */
1287 }
1288
1289 /* WPA */
1290 else
1291 {
1292 if (strstr(val, "TKIP"))
1293 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
1294
1295 else if (strstr(val, "CCMP"))
1296 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
1297
1298 else if (strstr(val, "NONE"))
1299 c->pair_ciphers |= IWINFO_CIPHER_NONE;
1300
1301 else if (strstr(val, "WEP-40"))
1302 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1303
1304 else if (strstr(val, "WEP-104"))
1305 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1306
1307
1308 if ((val = nl80211_getval(NULL, res, "group_cipher")))
1309 {
1310 if (strstr(val, "TKIP"))
1311 c->group_ciphers |= IWINFO_CIPHER_TKIP;
1312
1313 else if (strstr(val, "CCMP"))
1314 c->group_ciphers |= IWINFO_CIPHER_CCMP;
1315
1316 else if (strstr(val, "NONE"))
1317 c->group_ciphers |= IWINFO_CIPHER_NONE;
1318
1319 else if (strstr(val, "WEP-40"))
1320 c->group_ciphers |= IWINFO_CIPHER_WEP40;
1321
1322 else if (strstr(val, "WEP-104"))
1323 c->group_ciphers |= IWINFO_CIPHER_WEP104;
1324 }
1325
1326
1327 if ((val = nl80211_getval(NULL, res, "key_mgmt")))
1328 {
1329 if (strstr(val, "WPA2"))
1330 c->wpa_version = 2;
1331
1332 else if (strstr(val, "WPA"))
1333 c->wpa_version = 1;
1334
1335
1336 if (strstr(val, "PSK"))
1337 c->auth_suites |= IWINFO_KMGMT_PSK;
1338
1339 else if (strstr(val, "EAP") || strstr(val, "802.1X"))
1340 c->auth_suites |= IWINFO_KMGMT_8021x;
1341
1342 else if (strstr(val, "NONE"))
1343 c->auth_suites |= IWINFO_KMGMT_NONE;
1344 }
1345
1346 c->enabled = (c->wpa_version && c->auth_suites) ? 1 : 0;
1347 }
1348
1349 return 0;
1350 }
1351
1352 /* Hostapd */
1353 else if ((res = nl80211_hostapd_info(ifname)))
1354 {
1355 if ((val = nl80211_getval(ifname, res, "wpa")) != NULL)
1356 c->wpa_version = atoi(val);
1357
1358 val = nl80211_getval(ifname, res, "wpa_key_mgmt");
1359
1360 if (!val || strstr(val, "PSK"))
1361 c->auth_suites |= IWINFO_KMGMT_PSK;
1362
1363 if (val && strstr(val, "EAP"))
1364 c->auth_suites |= IWINFO_KMGMT_8021x;
1365
1366 if (val && strstr(val, "NONE"))
1367 c->auth_suites |= IWINFO_KMGMT_NONE;
1368
1369 if ((val = nl80211_getval(ifname, res, "wpa_pairwise")) != NULL)
1370 {
1371 if (strstr(val, "TKIP"))
1372 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
1373
1374 if (strstr(val, "CCMP"))
1375 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
1376
1377 if (strstr(val, "NONE"))
1378 c->pair_ciphers |= IWINFO_CIPHER_NONE;
1379 }
1380
1381 if ((val = nl80211_getval(ifname, res, "auth_algs")) != NULL)
1382 {
1383 switch(atoi(val)) {
1384 case 1:
1385 c->auth_algs |= IWINFO_AUTH_OPEN;
1386 break;
1387
1388 case 2:
1389 c->auth_algs |= IWINFO_AUTH_SHARED;
1390 break;
1391
1392 case 3:
1393 c->auth_algs |= IWINFO_AUTH_OPEN;
1394 c->auth_algs |= IWINFO_AUTH_SHARED;
1395 break;
1396
1397 default:
1398 break;
1399 }
1400
1401 for (i = 0; i < 4; i++)
1402 {
1403 snprintf(k, sizeof(k), "wep_key%d", i);
1404
1405 if ((val = nl80211_getval(ifname, res, k)))
1406 {
1407 if ((strlen(val) == 5) || (strlen(val) == 10))
1408 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1409
1410 else if ((strlen(val) == 13) || (strlen(val) == 26))
1411 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1412 }
1413 }
1414 }
1415
1416 c->group_ciphers = c->pair_ciphers;
1417 c->enabled = (c->wpa_version || c->pair_ciphers) ? 1 : 0;
1418
1419 return 0;
1420 }
1421
1422 return -1;
1423 }
1424
1425 static int nl80211_get_phyname(const char *ifname, char *buf)
1426 {
1427 const char *name;
1428
1429 name = nl80211_ifname2phy(ifname);
1430
1431 if (name)
1432 {
1433 strcpy(buf, name);
1434 return 0;
1435 }
1436 else if ((name = nl80211_phy2ifname(ifname)) != NULL)
1437 {
1438 name = nl80211_ifname2phy(name);
1439
1440 if (name)
1441 {
1442 strcpy(buf, ifname);
1443 return 0;
1444 }
1445 }
1446
1447 return -1;
1448 }
1449
1450
1451 static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
1452 {
1453 struct nl80211_array_buf *arr = arg;
1454 struct iwinfo_assoclist_entry *e = arr->buf;
1455 struct nlattr **attr = nl80211_parse(msg);
1456 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
1457 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
1458
1459 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
1460 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
1461 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
1462 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
1463 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
1464 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
1465 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
1466 };
1467
1468 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
1469 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
1470 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
1471 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
1472 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
1473 };
1474
1475 /* advance to end of array */
1476 e += arr->count;
1477 memset(e, 0, sizeof(*e));
1478
1479 if (attr[NL80211_ATTR_MAC])
1480 memcpy(e->mac, nla_data(attr[NL80211_ATTR_MAC]), 6);
1481
1482 if (attr[NL80211_ATTR_STA_INFO] &&
1483 !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
1484 attr[NL80211_ATTR_STA_INFO], stats_policy))
1485 {
1486 if (sinfo[NL80211_STA_INFO_SIGNAL])
1487 e->signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
1488
1489 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
1490 e->inactive = nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]);
1491
1492 if (sinfo[NL80211_STA_INFO_RX_PACKETS])
1493 e->rx_packets = nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]);
1494
1495 if (sinfo[NL80211_STA_INFO_TX_PACKETS])
1496 e->tx_packets = nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]);
1497
1498 if (sinfo[NL80211_STA_INFO_RX_BITRATE] &&
1499 !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
1500 sinfo[NL80211_STA_INFO_RX_BITRATE], rate_policy))
1501 {
1502 if (rinfo[NL80211_RATE_INFO_BITRATE])
1503 e->rx_rate.rate =
1504 nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
1505
1506 if (rinfo[NL80211_RATE_INFO_MCS])
1507 e->rx_rate.mcs = nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]);
1508
1509 if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
1510 e->rx_rate.is_40mhz = 1;
1511
1512 if (rinfo[NL80211_RATE_INFO_SHORT_GI])
1513 e->rx_rate.is_short_gi = 1;
1514 }
1515
1516 if (sinfo[NL80211_STA_INFO_TX_BITRATE] &&
1517 !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
1518 sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy))
1519 {
1520 if (rinfo[NL80211_RATE_INFO_BITRATE])
1521 e->tx_rate.rate =
1522 nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
1523
1524 if (rinfo[NL80211_RATE_INFO_MCS])
1525 e->tx_rate.mcs = nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]);
1526
1527 if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
1528 e->tx_rate.is_40mhz = 1;
1529
1530 if (rinfo[NL80211_RATE_INFO_SHORT_GI])
1531 e->tx_rate.is_short_gi = 1;
1532 }
1533 }
1534
1535 e->noise = 0; /* filled in by caller */
1536 arr->count++;
1537
1538 return NL_SKIP;
1539 }
1540
1541 static int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
1542 {
1543 DIR *d;
1544 int i, noise = 0;
1545 struct dirent *de;
1546 struct nl80211_msg_conveyor *req;
1547 struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
1548 struct iwinfo_assoclist_entry *e;
1549
1550 if ((d = opendir("/sys/class/net")) != NULL)
1551 {
1552 while ((de = readdir(d)) != NULL)
1553 {
1554 if (!strncmp(de->d_name, ifname, strlen(ifname)) &&
1555 (!de->d_name[strlen(ifname)] ||
1556 !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
1557 {
1558 req = nl80211_msg(de->d_name, NL80211_CMD_GET_STATION,
1559 NLM_F_DUMP);
1560
1561 if (req)
1562 {
1563 nl80211_send(req, nl80211_get_assoclist_cb, &arr);
1564 nl80211_free(req);
1565 }
1566 }
1567 }
1568
1569 closedir(d);
1570
1571 if (!nl80211_get_noise(ifname, &noise))
1572 for (i = 0, e = arr.buf; i < arr.count; i++, e++)
1573 e->noise = noise;
1574
1575 *len = (arr.count * sizeof(struct iwinfo_assoclist_entry));
1576 return 0;
1577 }
1578
1579 return -1;
1580 }
1581
1582 static int nl80211_get_txpwrlist_cb(struct nl_msg *msg, void *arg)
1583 {
1584 int *dbm_max = arg;
1585 int ch_cur, ch_cmp, bands_remain, freqs_remain;
1586
1587 struct nlattr **attr = nl80211_parse(msg);
1588 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1589 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1590 struct nlattr *band, *freq;
1591
1592 static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
1593 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
1594 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
1595 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
1596 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
1597 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
1598 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
1599 };
1600
1601 ch_cur = *dbm_max; /* value int* is initialized with channel by caller */
1602 *dbm_max = -1;
1603
1604 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1605 {
1606 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1607 nla_len(band), NULL);
1608
1609 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1610 {
1611 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1612 nla_data(freq), nla_len(freq), freq_policy);
1613
1614 ch_cmp = nl80211_freq2channel(nla_get_u32(
1615 freqs[NL80211_FREQUENCY_ATTR_FREQ]));
1616
1617 if ((!ch_cur || (ch_cmp == ch_cur)) &&
1618 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
1619 {
1620 *dbm_max = (int)(0.01 * nla_get_u32(
1621 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
1622
1623 break;
1624 }
1625 }
1626 }
1627
1628 return NL_SKIP;
1629 }
1630
1631 static int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
1632 {
1633 int ch_cur;
1634 int dbm_max = -1, dbm_cur, dbm_cnt;
1635 struct nl80211_msg_conveyor *req;
1636 struct iwinfo_txpwrlist_entry entry;
1637
1638 if (nl80211_get_channel(ifname, &ch_cur))
1639 ch_cur = 0;
1640
1641 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1642 if (req)
1643 {
1644 /* initialize the value pointer with channel for callback */
1645 dbm_max = ch_cur;
1646
1647 nl80211_send(req, nl80211_get_txpwrlist_cb, &dbm_max);
1648 nl80211_free(req);
1649 }
1650
1651 if (dbm_max > 0)
1652 {
1653 for (dbm_cur = 0, dbm_cnt = 0;
1654 dbm_cur < dbm_max;
1655 dbm_cur++, dbm_cnt++)
1656 {
1657 entry.dbm = dbm_cur;
1658 entry.mw = iwinfo_dbm2mw(dbm_cur);
1659
1660 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1661 }
1662
1663 entry.dbm = dbm_max;
1664 entry.mw = iwinfo_dbm2mw(dbm_max);
1665
1666 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1667 dbm_cnt++;
1668
1669 *len = dbm_cnt * sizeof(entry);
1670 return 0;
1671 }
1672
1673 return -1;
1674 }
1675
1676 static void nl80211_get_scancrypto(const char *spec,
1677 struct iwinfo_crypto_entry *c)
1678 {
1679 if (strstr(spec, "WPA") || strstr(spec, "WEP"))
1680 {
1681 c->enabled = 1;
1682
1683 if (strstr(spec, "WPA2-") && strstr(spec, "WPA-"))
1684 c->wpa_version = 3;
1685
1686 else if (strstr(spec, "WPA2"))
1687 c->wpa_version = 2;
1688
1689 else if (strstr(spec, "WPA"))
1690 c->wpa_version = 1;
1691
1692 else if (strstr(spec, "WEP"))
1693 c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
1694
1695
1696 if (strstr(spec, "PSK"))
1697 c->auth_suites |= IWINFO_KMGMT_PSK;
1698
1699 if (strstr(spec, "802.1X") || strstr(spec, "EAP"))
1700 c->auth_suites |= IWINFO_KMGMT_8021x;
1701
1702 if (strstr(spec, "WPA-NONE"))
1703 c->auth_suites |= IWINFO_KMGMT_NONE;
1704
1705
1706 if (strstr(spec, "TKIP"))
1707 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
1708
1709 if (strstr(spec, "CCMP"))
1710 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
1711
1712 if (strstr(spec, "WEP-40"))
1713 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1714
1715 if (strstr(spec, "WEP-104"))
1716 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1717
1718 c->group_ciphers = c->pair_ciphers;
1719 }
1720 else
1721 {
1722 c->enabled = 0;
1723 }
1724 }
1725
1726
1727 struct nl80211_scanlist {
1728 struct iwinfo_scanlist_entry *e;
1729 int len;
1730 };
1731
1732
1733 static void nl80211_get_scanlist_ie(struct nlattr **bss,
1734 struct iwinfo_scanlist_entry *e)
1735 {
1736 int ielen = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
1737 unsigned char *ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
1738 static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
1739 int len;
1740
1741 while (ielen >= 2 && ielen >= ie[1])
1742 {
1743 switch (ie[0])
1744 {
1745 case 0: /* SSID */
1746 len = min(ie[1], IWINFO_ESSID_MAX_SIZE);
1747 memcpy(e->ssid, ie + 2, len);
1748 e->ssid[len] = 0;
1749 break;
1750
1751 case 48: /* RSN */
1752 iwinfo_parse_rsn(&e->crypto, ie + 2, ie[1],
1753 IWINFO_CIPHER_CCMP, IWINFO_KMGMT_8021x);
1754 break;
1755
1756 case 221: /* Vendor */
1757 if (ie[1] >= 4 && !memcmp(ie + 2, ms_oui, 3) && ie[5] == 1)
1758 iwinfo_parse_rsn(&e->crypto, ie + 6, ie[1] - 4,
1759 IWINFO_CIPHER_TKIP, IWINFO_KMGMT_PSK);
1760 break;
1761 }
1762
1763 ielen -= ie[1] + 2;
1764 ie += ie[1] + 2;
1765 }
1766 }
1767
1768 static int nl80211_get_scanlist_cb(struct nl_msg *msg, void *arg)
1769 {
1770 int8_t rssi;
1771 uint16_t caps;
1772
1773 struct nl80211_scanlist *sl = arg;
1774 struct nlattr **tb = nl80211_parse(msg);
1775 struct nlattr *bss[NL80211_BSS_MAX + 1];
1776
1777 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1778 [NL80211_BSS_TSF] = { .type = NLA_U64 },
1779 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1780 [NL80211_BSS_BSSID] = { },
1781 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
1782 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
1783 [NL80211_BSS_INFORMATION_ELEMENTS] = { },
1784 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
1785 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
1786 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
1787 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
1788 [NL80211_BSS_BEACON_IES] = { },
1789 };
1790
1791 if (!tb[NL80211_ATTR_BSS] ||
1792 nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
1793 bss_policy) ||
1794 !bss[NL80211_BSS_BSSID])
1795 {
1796 return NL_SKIP;
1797 }
1798
1799 if (bss[NL80211_BSS_CAPABILITY])
1800 caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
1801 else
1802 caps = 0;
1803
1804 memset(sl->e, 0, sizeof(*sl->e));
1805 memcpy(sl->e->mac, nla_data(bss[NL80211_BSS_BSSID]), 6);
1806
1807 if (caps & (1<<1))
1808 sl->e->mode = IWINFO_OPMODE_ADHOC;
1809 else if (caps & (1<<0))
1810 sl->e->mode = IWINFO_OPMODE_MASTER;
1811 else
1812 sl->e->mode = IWINFO_OPMODE_MESHPOINT;
1813
1814 if (caps & (1<<4))
1815 sl->e->crypto.enabled = 1;
1816
1817 if (bss[NL80211_BSS_FREQUENCY])
1818 sl->e->channel = nl80211_freq2channel(nla_get_u32(
1819 bss[NL80211_BSS_FREQUENCY]));
1820
1821 if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
1822 nl80211_get_scanlist_ie(bss, sl->e);
1823
1824 if (bss[NL80211_BSS_SIGNAL_MBM])
1825 {
1826 sl->e->signal =
1827 (uint8_t)((int32_t)nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]) / 100);
1828
1829 rssi = sl->e->signal - 0x100;
1830
1831 if (rssi < -110)
1832 rssi = -110;
1833 else if (rssi > -40)
1834 rssi = -40;
1835
1836 sl->e->quality = (rssi + 110);
1837 sl->e->quality_max = 70;
1838 }
1839
1840 if (sl->e->crypto.enabled && !sl->e->crypto.wpa_version)
1841 {
1842 sl->e->crypto.auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
1843 sl->e->crypto.pair_ciphers = IWINFO_CIPHER_WEP40 | IWINFO_CIPHER_WEP104;
1844 }
1845
1846 sl->e++;
1847 sl->len++;
1848
1849 return NL_SKIP;
1850 }
1851
1852 static int nl80211_get_scanlist_nl(const char *ifname, char *buf, int *len)
1853 {
1854 struct nl80211_msg_conveyor *req;
1855 struct nl80211_scanlist sl = { .e = (struct iwinfo_scanlist_entry *)buf };
1856
1857 req = nl80211_msg(ifname, NL80211_CMD_TRIGGER_SCAN, 0);
1858 if (req)
1859 {
1860 nl80211_send(req, NULL, NULL);
1861 nl80211_free(req);
1862 }
1863
1864 nl80211_wait("nl80211", "scan", NL80211_CMD_NEW_SCAN_RESULTS);
1865
1866 req = nl80211_msg(ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
1867 if (req)
1868 {
1869 nl80211_send(req, nl80211_get_scanlist_cb, &sl);
1870 nl80211_free(req);
1871 }
1872
1873 *len = sl.len * sizeof(struct iwinfo_scanlist_entry);
1874 return *len ? 0 : -1;
1875 }
1876
1877 static int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
1878 {
1879 int freq, rssi, qmax, count, mode;
1880 char *res;
1881 char ssid[128] = { 0 };
1882 char bssid[18] = { 0 };
1883 char cipher[256] = { 0 };
1884
1885 /* Got a radioX pseudo interface, find some interface on it or create one */
1886 if (!strncmp(ifname, "radio", 5))
1887 {
1888 /* Reuse existing interface */
1889 if ((res = nl80211_phy2ifname(ifname)) != NULL)
1890 {
1891 return nl80211_get_scanlist(res, buf, len);
1892 }
1893
1894 /* Need to spawn a temporary iface for scanning */
1895 else if ((res = nl80211_ifadd(ifname)) != NULL)
1896 {
1897 count = nl80211_get_scanlist(res, buf, len);
1898 nl80211_ifdel(res);
1899 return count;
1900 }
1901 }
1902
1903 struct iwinfo_scanlist_entry *e = (struct iwinfo_scanlist_entry *)buf;
1904
1905 /* WPA supplicant */
1906 if ((res = nl80211_wpactl_info(ifname, "SCAN", "CTRL-EVENT-SCAN-RESULTS")))
1907 {
1908 if ((res = nl80211_wpactl_info(ifname, "SCAN_RESULTS", NULL)))
1909 {
1910 nl80211_get_quality_max(ifname, &qmax);
1911
1912 count = -1;
1913
1914 do {
1915 if (res[0] == '<')
1916 {
1917 /* skip log lines */
1918 goto nextline;
1919 }
1920 if (count < 0)
1921 {
1922 /* skip header line */
1923 count++;
1924 goto nextline;
1925 }
1926 if (sscanf(res, "%17s %d %d %255s%*[ \t]%127[^\n]\n",
1927 bssid, &freq, &rssi, cipher, ssid) < 5)
1928 {
1929 /* skip malformed lines */
1930 goto nextline;
1931 }
1932 /* BSSID */
1933 e->mac[0] = strtol(&bssid[0], NULL, 16);
1934 e->mac[1] = strtol(&bssid[3], NULL, 16);
1935 e->mac[2] = strtol(&bssid[6], NULL, 16);
1936 e->mac[3] = strtol(&bssid[9], NULL, 16);
1937 e->mac[4] = strtol(&bssid[12], NULL, 16);
1938 e->mac[5] = strtol(&bssid[15], NULL, 16);
1939
1940 /* SSID */
1941 memcpy(e->ssid, ssid, min(strlen(ssid), sizeof(e->ssid) - 1));
1942
1943 /* Mode (assume master) */
1944 if (strstr(cipher,"[MESH]"))
1945 e->mode = IWINFO_OPMODE_MESHPOINT;
1946 else
1947 e->mode = IWINFO_OPMODE_MASTER;
1948
1949 /* Channel */
1950 e->channel = nl80211_freq2channel(freq);
1951
1952 /* Signal */
1953 e->signal = rssi;
1954
1955 /* Quality */
1956 if (rssi < 0)
1957 {
1958 /* The cfg80211 wext compat layer assumes a signal range
1959 * of -110 dBm to -40 dBm, the quality value is derived
1960 * by adding 110 to the signal level */
1961 if (rssi < -110)
1962 rssi = -110;
1963 else if (rssi > -40)
1964 rssi = -40;
1965
1966 e->quality = (rssi + 110);
1967 }
1968 else
1969 {
1970 e->quality = rssi;
1971 }
1972
1973 /* Max. Quality */
1974 e->quality_max = qmax;
1975
1976 /* Crypto */
1977 nl80211_get_scancrypto(cipher, &e->crypto);
1978
1979 count++;
1980 e++;
1981
1982 memset(ssid, 0, sizeof(ssid));
1983 memset(bssid, 0, sizeof(bssid));
1984 memset(cipher, 0, sizeof(cipher));
1985
1986 nextline:
1987 /* advance to next line */
1988 while( *res && *res++ != '\n' );
1989 }
1990 while( *res );
1991
1992 *len = count * sizeof(struct iwinfo_scanlist_entry);
1993 return 0;
1994 }
1995 }
1996
1997 /* station / ad-hoc / monitor scan */
1998 else if (!nl80211_get_mode(ifname, &mode) &&
1999 (mode == IWINFO_OPMODE_ADHOC ||
2000 mode == IWINFO_OPMODE_MASTER ||
2001 mode == IWINFO_OPMODE_CLIENT ||
2002 mode == IWINFO_OPMODE_MONITOR) &&
2003 iwinfo_ifup(ifname))
2004 {
2005 return nl80211_get_scanlist_nl(ifname, buf, len);
2006 }
2007
2008 /* AP scan */
2009 else
2010 {
2011 /* Got a temp interface, don't create yet another one */
2012 if (!strncmp(ifname, "tmp.", 4))
2013 {
2014 if (!iwinfo_ifup(ifname))
2015 return -1;
2016
2017 nl80211_get_scanlist_nl(ifname, buf, len);
2018 iwinfo_ifdown(ifname);
2019 return 0;
2020 }
2021
2022 /* Spawn a new scan interface */
2023 else
2024 {
2025 if (!(res = nl80211_ifadd(ifname)))
2026 goto out;
2027
2028 iwinfo_ifmac(res);
2029
2030 /* if we can take the new interface up, the driver supports an
2031 * additional interface and there's no need to tear down the ap */
2032 if (iwinfo_ifup(res))
2033 {
2034 nl80211_get_scanlist_nl(res, buf, len);
2035 iwinfo_ifdown(res);
2036 }
2037
2038 /* driver cannot create secondary interface, take down ap
2039 * during scan */
2040 else if (iwinfo_ifdown(ifname) && iwinfo_ifup(res))
2041 {
2042 nl80211_get_scanlist_nl(res, buf, len);
2043 iwinfo_ifdown(res);
2044 iwinfo_ifup(ifname);
2045 nl80211_hostapd_hup(ifname);
2046 }
2047
2048 out:
2049 nl80211_ifdel(res);
2050 return 0;
2051 }
2052 }
2053
2054 return -1;
2055 }
2056
2057 static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
2058 {
2059 int bands_remain, freqs_remain;
2060
2061 struct nl80211_array_buf *arr = arg;
2062 struct iwinfo_freqlist_entry *e = arr->buf;
2063
2064 struct nlattr **attr = nl80211_parse(msg);
2065 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
2066 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
2067 struct nlattr *band, *freq;
2068
2069 static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
2070 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
2071 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
2072 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
2073 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
2074 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
2075 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
2076 };
2077
2078 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
2079 {
2080 nla_parse(bands, NL80211_BAND_ATTR_MAX,
2081 nla_data(band), nla_len(band), NULL);
2082
2083 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
2084 {
2085 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
2086 nla_data(freq), nla_len(freq), NULL);
2087
2088 if (!freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
2089 freqs[NL80211_FREQUENCY_ATTR_DISABLED])
2090 continue;
2091
2092 e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
2093 e->channel = nl80211_freq2channel(e->mhz);
2094
2095 e->restricted = (
2096 freqs[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] ||
2097 freqs[NL80211_FREQUENCY_ATTR_NO_IBSS] ||
2098 freqs[NL80211_FREQUENCY_ATTR_RADAR]
2099 ) ? 1 : 0;
2100
2101 e++;
2102 arr->count++;
2103 }
2104 }
2105
2106 return NL_SKIP;
2107 }
2108
2109 static int nl80211_get_freqlist(const char *ifname, char *buf, int *len)
2110 {
2111 struct nl80211_msg_conveyor *req;
2112 struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
2113
2114 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
2115 if (req)
2116 {
2117 nl80211_send(req, nl80211_get_freqlist_cb, &arr);
2118 nl80211_free(req);
2119 }
2120
2121 if (arr.count > 0)
2122 {
2123 *len = arr.count * sizeof(struct iwinfo_freqlist_entry);
2124 return 0;
2125 }
2126
2127 return -1;
2128 }
2129
2130 static int nl80211_get_country_cb(struct nl_msg *msg, void *arg)
2131 {
2132 char *buf = arg;
2133 struct nlattr **attr = nl80211_parse(msg);
2134
2135 if (attr[NL80211_ATTR_REG_ALPHA2])
2136 memcpy(buf, nla_data(attr[NL80211_ATTR_REG_ALPHA2]), 2);
2137 else
2138 buf[0] = 0;
2139
2140 return NL_SKIP;
2141 }
2142
2143 static int nl80211_get_country(const char *ifname, char *buf)
2144 {
2145 int rv = -1;
2146 struct nl80211_msg_conveyor *req;
2147
2148 req = nl80211_msg(ifname, NL80211_CMD_GET_REG, 0);
2149 if (req)
2150 {
2151 nl80211_send(req, nl80211_get_country_cb, buf);
2152 nl80211_free(req);
2153
2154 if (buf[0])
2155 rv = 0;
2156 }
2157
2158 return rv;
2159 }
2160
2161 static int nl80211_get_countrylist(const char *ifname, char *buf, int *len)
2162 {
2163 int i, count;
2164 struct iwinfo_country_entry *e = (struct iwinfo_country_entry *)buf;
2165 const struct iwinfo_iso3166_label *l;
2166
2167 for (l = IWINFO_ISO3166_NAMES, count = 0; l->iso3166; l++, e++, count++)
2168 {
2169 e->iso3166 = l->iso3166;
2170 e->ccode[0] = (l->iso3166 / 256);
2171 e->ccode[1] = (l->iso3166 % 256);
2172 }
2173
2174 *len = (count * sizeof(struct iwinfo_country_entry));
2175 return 0;
2176 }
2177
2178 static int nl80211_get_hwmodelist_cb(struct nl_msg *msg, void *arg)
2179 {
2180 int *modes = arg;
2181 int bands_remain, freqs_remain;
2182 uint16_t caps = 0;
2183 uint32_t vht_caps = 0;
2184 struct nlattr **attr = nl80211_parse(msg);
2185 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
2186 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
2187 struct nlattr *band, *freq;
2188
2189 *modes = 0;
2190
2191 if (attr[NL80211_ATTR_WIPHY_BANDS])
2192 {
2193 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
2194 {
2195 nla_parse(bands, NL80211_BAND_ATTR_MAX,
2196 nla_data(band), nla_len(band), NULL);
2197
2198 if (bands[NL80211_BAND_ATTR_HT_CAPA])
2199 caps = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]);
2200
2201 /* Treat any nonzero capability as 11n */
2202 if (caps > 0)
2203 *modes |= IWINFO_80211_N;
2204
2205 if (bands[NL80211_BAND_ATTR_VHT_CAPA])
2206 vht_caps = nla_get_u32(bands[NL80211_BAND_ATTR_VHT_CAPA]);
2207
2208 /* Treat any nonzero capability as 11ac */
2209 if (vht_caps > 0)
2210 *modes |= IWINFO_80211_AC;
2211
2212 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS],
2213 freqs_remain)
2214 {
2215 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
2216 nla_data(freq), nla_len(freq), NULL);
2217
2218 if (!freqs[NL80211_FREQUENCY_ATTR_FREQ])
2219 continue;
2220
2221 if (nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]) < 2485)
2222 {
2223 *modes |= IWINFO_80211_B;
2224 *modes |= IWINFO_80211_G;
2225 }
2226 else if (!(*modes & IWINFO_80211_AC))
2227 {
2228 *modes |= IWINFO_80211_A;
2229 }
2230 }
2231 }
2232 }
2233
2234 return NL_SKIP;
2235 }
2236
2237 static int nl80211_get_hwmodelist(const char *ifname, int *buf)
2238 {
2239 struct nl80211_msg_conveyor *req;
2240
2241 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
2242 if (req)
2243 {
2244 nl80211_send(req, nl80211_get_hwmodelist_cb, buf);
2245 nl80211_free(req);
2246 }
2247
2248 return *buf ? 0 : -1;
2249 }
2250
2251 static int nl80211_get_ifcomb_cb(struct nl_msg *msg, void *arg)
2252 {
2253 struct nlattr **attr = nl80211_parse(msg);
2254 struct nlattr *comb;
2255 int *ret = arg;
2256 int comb_rem, limit_rem, mode_rem;
2257
2258 *ret = 0;
2259 if (!attr[NL80211_ATTR_INTERFACE_COMBINATIONS])
2260 return NL_SKIP;
2261
2262 nla_for_each_nested(comb, attr[NL80211_ATTR_INTERFACE_COMBINATIONS], comb_rem)
2263 {
2264 static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
2265 [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
2266 [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
2267 };
2268 struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
2269 static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
2270 [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
2271 [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
2272 };
2273 struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
2274 struct nlattr *limit;
2275
2276 nla_parse_nested(tb_comb, NL80211_BAND_ATTR_MAX, comb, iface_combination_policy);
2277
2278 if (!tb_comb[NL80211_IFACE_COMB_LIMITS])
2279 continue;
2280
2281 nla_for_each_nested(limit, tb_comb[NL80211_IFACE_COMB_LIMITS], limit_rem)
2282 {
2283 struct nlattr *mode;
2284
2285 nla_parse_nested(tb_limit, NUM_NL80211_IFACE_LIMIT, limit, iface_limit_policy);
2286
2287 if (!tb_limit[NL80211_IFACE_LIMIT_TYPES] ||
2288 !tb_limit[NL80211_IFACE_LIMIT_MAX])
2289 continue;
2290
2291 if (nla_get_u32(tb_limit[NL80211_IFACE_LIMIT_MAX]) < 2)
2292 continue;
2293
2294 nla_for_each_nested(mode, tb_limit[NL80211_IFACE_LIMIT_TYPES], mode_rem) {
2295 if (nla_type(mode) == NL80211_IFTYPE_AP)
2296 *ret = 1;
2297 }
2298 }
2299 }
2300
2301 return NL_SKIP;
2302 }
2303
2304 static int nl80211_get_mbssid_support(const char *ifname, int *buf)
2305 {
2306 struct nl80211_msg_conveyor *req;
2307
2308 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
2309 if (!req)
2310 return -1;
2311
2312 nl80211_send(req, nl80211_get_ifcomb_cb, buf);
2313 nl80211_free(req);
2314 return 0;
2315 }
2316
2317 static int nl80211_get_hardware_id(const char *ifname, char *buf)
2318 {
2319 int rv;
2320 char *res;
2321
2322 /* Got a radioX pseudo interface, find some interface on it or create one */
2323 if (!strncmp(ifname, "radio", 5))
2324 {
2325 /* Reuse existing interface */
2326 if ((res = nl80211_phy2ifname(ifname)) != NULL)
2327 {
2328 rv = wext_ops.hardware_id(res, buf);
2329 }
2330
2331 /* Need to spawn a temporary iface for finding IDs */
2332 else if ((res = nl80211_ifadd(ifname)) != NULL)
2333 {
2334 rv = wext_ops.hardware_id(res, buf);
2335 nl80211_ifdel(res);
2336 }
2337 }
2338 else
2339 {
2340 rv = wext_ops.hardware_id(ifname, buf);
2341 }
2342
2343 /* Failed to obtain hardware IDs, search board config */
2344 if (rv)
2345 {
2346 rv = iwinfo_hardware_id_from_mtd((struct iwinfo_hardware_id *)buf);
2347 }
2348
2349 return rv;
2350 }
2351
2352 static const struct iwinfo_hardware_entry *
2353 nl80211_get_hardware_entry(const char *ifname)
2354 {
2355 struct iwinfo_hardware_id id;
2356
2357 if (nl80211_get_hardware_id(ifname, (char *)&id))
2358 return NULL;
2359
2360 return iwinfo_hardware(&id);
2361 }
2362
2363 static int nl80211_get_hardware_name(const char *ifname, char *buf)
2364 {
2365 const struct iwinfo_hardware_entry *hw;
2366
2367 if (!(hw = nl80211_get_hardware_entry(ifname)))
2368 sprintf(buf, "Generic MAC80211");
2369 else
2370 sprintf(buf, "%s %s", hw->vendor_name, hw->device_name);
2371
2372 return 0;
2373 }
2374
2375 static int nl80211_get_txpower_offset(const char *ifname, int *buf)
2376 {
2377 const struct iwinfo_hardware_entry *hw;
2378
2379 if (!(hw = nl80211_get_hardware_entry(ifname)))
2380 return -1;
2381
2382 *buf = hw->txpower_offset;
2383 return 0;
2384 }
2385
2386 static int nl80211_get_frequency_offset(const char *ifname, int *buf)
2387 {
2388 const struct iwinfo_hardware_entry *hw;
2389
2390 if (!(hw = nl80211_get_hardware_entry(ifname)))
2391 return -1;
2392
2393 *buf = hw->frequency_offset;
2394 return 0;
2395 }
2396
2397 const struct iwinfo_ops nl80211_ops = {
2398 .name = "nl80211",
2399 .probe = nl80211_probe,
2400 .channel = nl80211_get_channel,
2401 .frequency = nl80211_get_frequency,
2402 .frequency_offset = nl80211_get_frequency_offset,
2403 .txpower = nl80211_get_txpower,
2404 .txpower_offset = nl80211_get_txpower_offset,
2405 .bitrate = nl80211_get_bitrate,
2406 .signal = nl80211_get_signal,
2407 .noise = nl80211_get_noise,
2408 .quality = nl80211_get_quality,
2409 .quality_max = nl80211_get_quality_max,
2410 .mbssid_support = nl80211_get_mbssid_support,
2411 .hwmodelist = nl80211_get_hwmodelist,
2412 .mode = nl80211_get_mode,
2413 .ssid = nl80211_get_ssid,
2414 .bssid = nl80211_get_bssid,
2415 .country = nl80211_get_country,
2416 .hardware_id = nl80211_get_hardware_id,
2417 .hardware_name = nl80211_get_hardware_name,
2418 .encryption = nl80211_get_encryption,
2419 .phyname = nl80211_get_phyname,
2420 .assoclist = nl80211_get_assoclist,
2421 .txpwrlist = nl80211_get_txpwrlist,
2422 .scanlist = nl80211_get_scanlist,
2423 .freqlist = nl80211_get_freqlist,
2424 .countrylist = nl80211_get_countrylist,
2425 .close = nl80211_close
2426 };