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