iwinfo: nl80211: use new path lookup function for nl80211_phy_idx_from_uci_path
[project/iwinfo.git] / 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 <glob.h>
27 #include <fnmatch.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30
31 #include "iwinfo_nl80211.h"
32
33 #define min(x, y) ((x) < (y)) ? (x) : (y)
34
35 #define BIT(x) (1ULL<<(x))
36
37 static struct nl80211_state *nls = NULL;
38
39 static void nl80211_close(void)
40 {
41 if (nls)
42 {
43 if (nls->nlctrl)
44 genl_family_put(nls->nlctrl);
45
46 if (nls->nl80211)
47 genl_family_put(nls->nl80211);
48
49 if (nls->nl_sock)
50 nl_socket_free(nls->nl_sock);
51
52 if (nls->nl_cache)
53 nl_cache_free(nls->nl_cache);
54
55 free(nls);
56 nls = NULL;
57 }
58 }
59
60 static int nl80211_init(void)
61 {
62 int err, fd;
63
64 if (!nls)
65 {
66 nls = malloc(sizeof(struct nl80211_state));
67 if (!nls) {
68 err = -ENOMEM;
69 goto err;
70 }
71
72 memset(nls, 0, sizeof(*nls));
73
74 nls->nl_sock = nl_socket_alloc();
75 if (!nls->nl_sock) {
76 err = -ENOMEM;
77 goto err;
78 }
79
80 if (genl_connect(nls->nl_sock)) {
81 err = -ENOLINK;
82 goto err;
83 }
84
85 fd = nl_socket_get_fd(nls->nl_sock);
86 if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) {
87 err = -EINVAL;
88 goto err;
89 }
90
91 if (genl_ctrl_alloc_cache(nls->nl_sock, &nls->nl_cache)) {
92 err = -ENOMEM;
93 goto err;
94 }
95
96 nls->nl80211 = genl_ctrl_search_by_name(nls->nl_cache, "nl80211");
97 if (!nls->nl80211) {
98 err = -ENOENT;
99 goto err;
100 }
101
102 nls->nlctrl = genl_ctrl_search_by_name(nls->nl_cache, "nlctrl");
103 if (!nls->nlctrl) {
104 err = -ENOENT;
105 goto err;
106 }
107 }
108
109 return 0;
110
111
112 err:
113 nl80211_close();
114 return err;
115 }
116
117 static int nl80211_readint(const char *path)
118 {
119 int fd;
120 int rv = -1;
121 char buffer[16];
122
123 if ((fd = open(path, O_RDONLY)) > -1)
124 {
125 if (read(fd, buffer, sizeof(buffer)) > 0)
126 rv = atoi(buffer);
127
128 close(fd);
129 }
130
131 return rv;
132 }
133
134 static int nl80211_readstr(const char *path, char *buffer, int length)
135 {
136 int fd;
137 int rv = -1;
138
139 if ((fd = open(path, O_RDONLY)) > -1)
140 {
141 if ((rv = read(fd, buffer, length - 1)) > 0)
142 {
143 if (buffer[rv - 1] == '\n')
144 rv--;
145
146 buffer[rv] = 0;
147 }
148
149 close(fd);
150 }
151
152 return rv;
153 }
154
155
156 static int nl80211_msg_error(struct sockaddr_nl *nla,
157 struct nlmsgerr *err, void *arg)
158 {
159 int *ret = arg;
160 *ret = err->error;
161 return NL_STOP;
162 }
163
164 static int nl80211_msg_finish(struct nl_msg *msg, void *arg)
165 {
166 int *ret = arg;
167 *ret = 0;
168 return NL_SKIP;
169 }
170
171 static int nl80211_msg_ack(struct nl_msg *msg, void *arg)
172 {
173 int *ret = arg;
174 *ret = 0;
175 return NL_STOP;
176 }
177
178 static int nl80211_msg_response(struct nl_msg *msg, void *arg)
179 {
180 return NL_SKIP;
181 }
182
183 static void nl80211_free(struct nl80211_msg_conveyor *cv)
184 {
185 if (cv)
186 {
187 if (cv->cb)
188 nl_cb_put(cv->cb);
189
190 if (cv->msg)
191 nlmsg_free(cv->msg);
192
193 cv->cb = NULL;
194 cv->msg = NULL;
195 }
196 }
197
198 static struct nl80211_msg_conveyor * nl80211_new(struct genl_family *family,
199 int cmd, int flags)
200 {
201 static struct nl80211_msg_conveyor cv;
202
203 struct nl_msg *req = NULL;
204 struct nl_cb *cb = NULL;
205
206 req = nlmsg_alloc();
207 if (!req)
208 goto err;
209
210 cb = nl_cb_alloc(NL_CB_DEFAULT);
211 if (!cb)
212 goto err;
213
214 genlmsg_put(req, 0, 0, genl_family_get_id(family), 0, flags, cmd, 0);
215
216 cv.msg = req;
217 cv.cb = cb;
218
219 return &cv;
220
221 err:
222 if (req)
223 nlmsg_free(req);
224
225 return NULL;
226 }
227
228 static struct nl80211_msg_conveyor * nl80211_ctl(int cmd, int flags)
229 {
230 if (nl80211_init() < 0)
231 return NULL;
232
233 return nl80211_new(nls->nlctrl, cmd, flags);
234 }
235
236 static const char *nl80211_phy_path_str(const char *phyname)
237 {
238 static char path[PATH_MAX];
239 const char *prefix = "/sys/devices/";
240 int prefix_len = strlen(prefix);
241 int buf_len, offset;
242 struct dirent *e;
243 char buf[128], *link;
244 int phy_id;
245 int seq = 0;
246 DIR *d;
247
248 if (strncmp(phyname, "phy", 3) != 0)
249 return NULL;
250
251 phy_id = atoi(phyname + 3);
252 buf_len = snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/device", phyname);
253 link = realpath(buf, path);
254 if (!link)
255 return NULL;
256
257 if (strncmp(link, prefix, prefix_len) != 0)
258 return NULL;
259
260 link += prefix_len;
261
262 prefix = "platform/";
263 prefix_len = strlen(prefix);
264 if (!strncmp(link, prefix, prefix_len) && strstr(link, "/pci"))
265 link += prefix_len;
266
267 snprintf(buf + buf_len, sizeof(buf) - buf_len, "/ieee80211");
268 d = opendir(buf);
269 if (!d)
270 return link;
271
272 while ((e = readdir(d)) != NULL) {
273 int cur_id;
274
275 if (strncmp(e->d_name, "phy", 3) != 0)
276 continue;
277
278 cur_id = atoi(e->d_name + 3);
279 if (cur_id >= phy_id)
280 continue;
281
282 seq++;
283 }
284
285 closedir(d);
286
287 if (!seq)
288 return link;
289
290 offset = link - path + strlen(link);
291 snprintf(path + offset, sizeof(path) - offset, "+%d", seq);
292
293 return link;
294 }
295
296 static int nl80211_phy_idx_from_uci_path(struct uci_section *s)
297 {
298 char buf[128];
299 struct dirent *e;
300 const char *path, *cur_path;
301 int idx = -1;
302 DIR *d;
303
304 path = uci_lookup_option_string(uci_ctx, s, "path");
305 if (!path)
306 return -1;
307
308 d = opendir("/sys/class/ieee80211");
309 if (!d)
310 return -1;
311
312 while ((e = readdir(d)) != NULL) {
313 cur_path = nl80211_phy_path_str(e->d_name);
314 if (!cur_path)
315 continue;
316
317 if (strcmp(cur_path, path) != 0)
318 continue;
319
320 snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", e->d_name);
321 idx = nl80211_readint(buf);
322
323 if (idx >= 0)
324 break;
325 }
326
327 closedir(d);
328
329 return idx;
330 }
331
332 static int nl80211_phy_idx_from_uci_macaddr(struct uci_section *s)
333 {
334 const char *opt;
335 char buf[128];
336 int i, idx = -1;
337 glob_t gl;
338
339 opt = uci_lookup_option_string(uci_ctx, s, "macaddr");
340 if (!opt)
341 return -1;
342
343 snprintf(buf, sizeof(buf), "/sys/class/ieee80211/*"); /**/
344 if (glob(buf, 0, NULL, &gl))
345 return -1;
346
347 for (i = 0; i < gl.gl_pathc; i++)
348 {
349 snprintf(buf, sizeof(buf), "%s/macaddress", gl.gl_pathv[i]);
350 if (nl80211_readstr(buf, buf, sizeof(buf)) <= 0)
351 continue;
352
353 if (fnmatch(opt, buf, FNM_CASEFOLD))
354 continue;
355
356 snprintf(buf, sizeof(buf), "%s/index", gl.gl_pathv[i]);
357 if ((idx = nl80211_readint(buf)) > -1)
358 break;
359 }
360
361 globfree(&gl);
362
363 return idx;
364 }
365
366 static int nl80211_phy_idx_from_uci_phy(struct uci_section *s)
367 {
368 const char *opt;
369 char buf[128];
370
371 opt = uci_lookup_option_string(uci_ctx, s, "phy");
372 if (!opt)
373 return -1;
374
375 snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", opt);
376 return nl80211_readint(buf);
377 }
378
379 static int nl80211_phy_idx_from_uci(const char *name)
380 {
381 struct uci_section *s;
382 int idx = -1;
383
384 s = iwinfo_uci_get_radio(name, "mac80211");
385 if (!s)
386 goto free;
387
388 idx = nl80211_phy_idx_from_uci_path(s);
389
390 if (idx < 0)
391 idx = nl80211_phy_idx_from_uci_macaddr(s);
392
393 if (idx < 0)
394 idx = nl80211_phy_idx_from_uci_phy(s);
395
396 free:
397 iwinfo_uci_free();
398 return idx;
399 }
400
401 static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname,
402 int cmd, int flags)
403 {
404 int ifidx = -1, phyidx = -1;
405 struct nl80211_msg_conveyor *cv;
406
407 if (ifname == NULL)
408 return NULL;
409
410 if (nl80211_init() < 0)
411 return NULL;
412
413 if (!strncmp(ifname, "phy", 3))
414 phyidx = atoi(&ifname[3]);
415 else if (!strncmp(ifname, "radio", 5))
416 phyidx = nl80211_phy_idx_from_uci(ifname);
417
418 if (!strncmp(ifname, "mon.", 4))
419 ifidx = if_nametoindex(&ifname[4]);
420 else
421 ifidx = if_nametoindex(ifname);
422
423 /* Valid ifidx must be greater than 0 */
424 if ((ifidx <= 0) && (phyidx < 0))
425 return NULL;
426
427 cv = nl80211_new(nls->nl80211, cmd, flags);
428 if (!cv)
429 return NULL;
430
431 if (ifidx > 0)
432 NLA_PUT_U32(cv->msg, NL80211_ATTR_IFINDEX, ifidx);
433 else if (phyidx > -1)
434 NLA_PUT_U32(cv->msg, NL80211_ATTR_WIPHY, phyidx);
435
436 return cv;
437
438 nla_put_failure:
439 nl80211_free(cv);
440 return NULL;
441 }
442
443 static int nl80211_send(struct nl80211_msg_conveyor *cv,
444 int (*cb_func)(struct nl_msg *, void *),
445 void *cb_arg)
446 {
447 static struct nl80211_msg_conveyor rcv;
448 int err;
449
450 if (cb_func)
451 nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, cb_func, cb_arg);
452 else
453 nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_msg_response, &rcv);
454
455 err = nl_send_auto_complete(nls->nl_sock, cv->msg);
456
457 if (err < 0)
458 goto out;
459
460 err = 1;
461
462 nl_cb_err(cv->cb, NL_CB_CUSTOM, nl80211_msg_error, &err);
463 nl_cb_set(cv->cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_msg_finish, &err);
464 nl_cb_set(cv->cb, NL_CB_ACK, NL_CB_CUSTOM, nl80211_msg_ack, &err);
465
466 while (err > 0)
467 nl_recvmsgs(nls->nl_sock, cv->cb);
468
469 out:
470 nl80211_free(cv);
471 return err;
472 }
473
474 static int nl80211_request(const char *ifname, int cmd, int flags,
475 int (*cb_func)(struct nl_msg *, void *),
476 void *cb_arg)
477 {
478 struct nl80211_msg_conveyor *cv;
479
480 cv = nl80211_msg(ifname, cmd, flags);
481
482 if (!cv)
483 return -ENOMEM;
484
485 return nl80211_send(cv, cb_func, cb_arg);
486 }
487
488 static struct nlattr ** nl80211_parse(struct nl_msg *msg)
489 {
490 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
491 static struct nlattr *attr[NL80211_ATTR_MAX + 1];
492
493 nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
494 genlmsg_attrlen(gnlh, 0), NULL);
495
496 return attr;
497 }
498
499 static int nl80211_get_protocol_features_cb(struct nl_msg *msg, void *arg)
500 {
501 uint32_t *features = arg;
502 struct nlattr **attr = nl80211_parse(msg);
503
504 if (attr[NL80211_ATTR_PROTOCOL_FEATURES])
505 *features = nla_get_u32(attr[NL80211_ATTR_PROTOCOL_FEATURES]);
506
507 return NL_SKIP;
508 }
509
510 static int nl80211_get_protocol_features(const char *ifname)
511 {
512 struct nl80211_msg_conveyor *req;
513 uint32_t features = 0;
514
515 req = nl80211_msg(ifname, NL80211_CMD_GET_PROTOCOL_FEATURES, 0);
516 if (req) {
517 nl80211_send(req, nl80211_get_protocol_features_cb, &features);
518 nl80211_free(req);
519 }
520
521 return features;
522 }
523
524 static int nl80211_subscribe_cb(struct nl_msg *msg, void *arg)
525 {
526 struct nl80211_group_conveyor *cv = arg;
527
528 struct nlattr **attr = nl80211_parse(msg);
529 struct nlattr *mgrpinfo[CTRL_ATTR_MCAST_GRP_MAX + 1];
530 struct nlattr *mgrp;
531 int mgrpidx;
532
533 if (!attr[CTRL_ATTR_MCAST_GROUPS])
534 return NL_SKIP;
535
536 nla_for_each_nested(mgrp, attr[CTRL_ATTR_MCAST_GROUPS], mgrpidx)
537 {
538 nla_parse(mgrpinfo, CTRL_ATTR_MCAST_GRP_MAX,
539 nla_data(mgrp), nla_len(mgrp), NULL);
540
541 if (mgrpinfo[CTRL_ATTR_MCAST_GRP_ID] &&
542 mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME] &&
543 !strncmp(nla_data(mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME]),
544 cv->name, nla_len(mgrpinfo[CTRL_ATTR_MCAST_GRP_NAME])))
545 {
546 cv->id = nla_get_u32(mgrpinfo[CTRL_ATTR_MCAST_GRP_ID]);
547 break;
548 }
549 }
550
551 return NL_SKIP;
552 }
553
554 static int nl80211_subscribe(const char *family, const char *group)
555 {
556 struct nl80211_group_conveyor cv = { .name = group, .id = -ENOENT };
557 struct nl80211_msg_conveyor *req;
558 int err;
559
560 req = nl80211_ctl(CTRL_CMD_GETFAMILY, 0);
561 if (req)
562 {
563 NLA_PUT_STRING(req->msg, CTRL_ATTR_FAMILY_NAME, family);
564 err = nl80211_send(req, nl80211_subscribe_cb, &cv);
565
566 if (err)
567 return err;
568
569 return nl_socket_add_membership(nls->nl_sock, cv.id);
570
571 nla_put_failure:
572 nl80211_free(req);
573 }
574
575 return -ENOMEM;
576 }
577
578
579 static int nl80211_wait_cb(struct nl_msg *msg, void *arg)
580 {
581 struct nl80211_event_conveyor *cv = arg;
582 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
583
584 if (cv->wait[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32)))
585 cv->recv = gnlh->cmd;
586
587 return NL_SKIP;
588 }
589
590 static int nl80211_wait_seq_check(struct nl_msg *msg, void *arg)
591 {
592 return NL_OK;
593 }
594
595 static int __nl80211_wait(const char *family, const char *group, ...)
596 {
597 struct nl80211_event_conveyor cv = { };
598 struct nl_cb *cb;
599 int err = 0;
600 int cmd;
601 va_list ap;
602
603 if (nl80211_subscribe(family, group))
604 return -ENOENT;
605
606 cb = nl_cb_alloc(NL_CB_DEFAULT);
607
608 if (!cb)
609 return -ENOMEM;
610
611 nl_cb_err(cb, NL_CB_CUSTOM, nl80211_msg_error, &err);
612 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl80211_wait_seq_check, NULL);
613 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_wait_cb, &cv );
614
615 va_start(ap, group);
616
617 for (cmd = va_arg(ap, int); cmd != 0; cmd = va_arg(ap, int))
618 cv.wait[cmd / 32] |= (1 << (cmd % 32));
619
620 va_end(ap);
621
622 while (!cv.recv && !err)
623 nl_recvmsgs(nls->nl_sock, cb);
624
625 nl_cb_put(cb);
626
627 return err;
628 }
629
630 #define nl80211_wait(family, group, ...) \
631 __nl80211_wait(family, group, __VA_ARGS__, 0)
632
633
634 static int nl80211_freq2channel(int freq)
635 {
636 if (freq == 2484)
637 return 14;
638 else if (freq < 2484)
639 return (freq - 2407) / 5;
640 else if (freq >= 4910 && freq <= 4980)
641 return (freq - 4000) / 5;
642 else if(freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6)
643 return (freq - 56160) / 2160;
644 else
645 return (freq - 5000) / 5;
646 }
647
648 static int nl80211_channel2freq(int channel, const char *band)
649 {
650 if (!band || band[0] != 'a')
651 {
652 if (channel == 14)
653 return 2484;
654 else if (channel < 14)
655 return (channel * 5) + 2407;
656 }
657 else if ( strcmp(band, "ad") == 0)
658 {
659 return 56160 + 2160 * channel;
660 }
661 else
662 {
663 if (channel >= 182 && channel <= 196)
664 return (channel * 5) + 4000;
665 else
666 return (channel * 5) + 5000;
667 }
668
669 return 0;
670 }
671
672 static int nl80211_ifname2phy_cb(struct nl_msg *msg, void *arg)
673 {
674 char *buf = arg;
675 struct nlattr **attr = nl80211_parse(msg);
676
677 if (attr[NL80211_ATTR_WIPHY_NAME])
678 memcpy(buf, nla_data(attr[NL80211_ATTR_WIPHY_NAME]),
679 nla_len(attr[NL80211_ATTR_WIPHY_NAME]));
680 else
681 buf[0] = 0;
682
683 return NL_SKIP;
684 }
685
686 static char * nl80211_ifname2phy(const char *ifname)
687 {
688 static char phy[32] = { 0 };
689
690 memset(phy, 0, sizeof(phy));
691
692 nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
693 nl80211_ifname2phy_cb, phy);
694
695 return phy[0] ? phy : NULL;
696 }
697
698 static char * nl80211_phy2ifname(const char *ifname)
699 {
700 int ifidx = -1, cifidx = -1, phyidx = -1;
701 char buffer[64];
702 static char nif[IFNAMSIZ] = { 0 };
703
704 DIR *d;
705 struct dirent *e;
706
707 /* Only accept phy name of the form phy%d or radio%d */
708 if (!ifname)
709 return NULL;
710 else if (!strncmp(ifname, "phy", 3))
711 phyidx = atoi(&ifname[3]);
712 else if (!strncmp(ifname, "radio", 5))
713 phyidx = nl80211_phy_idx_from_uci(ifname);
714 else
715 return NULL;
716
717 memset(nif, 0, sizeof(nif));
718
719 if (phyidx > -1)
720 {
721 if ((d = opendir("/sys/class/net")) != NULL)
722 {
723 while ((e = readdir(d)) != NULL)
724 {
725 snprintf(buffer, sizeof(buffer),
726 "/sys/class/net/%s/phy80211/index", e->d_name);
727
728 if (nl80211_readint(buffer) == phyidx)
729 {
730 snprintf(buffer, sizeof(buffer),
731 "/sys/class/net/%s/ifindex", e->d_name);
732
733 if ((cifidx = nl80211_readint(buffer)) >= 0 &&
734 ((ifidx < 0) || (cifidx < ifidx)))
735 {
736 ifidx = cifidx;
737 strncpy(nif, e->d_name, sizeof(nif) - 1);
738 }
739 }
740 }
741
742 closedir(d);
743 }
744 }
745
746 return nif[0] ? nif : NULL;
747 }
748
749 static int nl80211_get_mode_cb(struct nl_msg *msg, void *arg)
750 {
751 int *mode = arg;
752 struct nlattr **tb = nl80211_parse(msg);
753 const int ifmodes[NL80211_IFTYPE_MAX + 1] = {
754 IWINFO_OPMODE_UNKNOWN, /* unspecified */
755 IWINFO_OPMODE_ADHOC, /* IBSS */
756 IWINFO_OPMODE_CLIENT, /* managed */
757 IWINFO_OPMODE_MASTER, /* AP */
758 IWINFO_OPMODE_AP_VLAN, /* AP/VLAN */
759 IWINFO_OPMODE_WDS, /* WDS */
760 IWINFO_OPMODE_MONITOR, /* monitor */
761 IWINFO_OPMODE_MESHPOINT, /* mesh point */
762 IWINFO_OPMODE_P2P_CLIENT, /* P2P-client */
763 IWINFO_OPMODE_P2P_GO, /* P2P-GO */
764 };
765
766 if (tb[NL80211_ATTR_IFTYPE])
767 *mode = ifmodes[nla_get_u32(tb[NL80211_ATTR_IFTYPE])];
768
769 return NL_SKIP;
770 }
771
772
773 static int nl80211_get_mode(const char *ifname, int *buf)
774 {
775 char *res;
776
777 *buf = IWINFO_OPMODE_UNKNOWN;
778
779 res = nl80211_phy2ifname(ifname);
780
781 nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
782 nl80211_get_mode_cb, buf);
783
784 return (*buf == IWINFO_OPMODE_UNKNOWN) ? -1 : 0;
785 }
786
787 static int __nl80211_hostapd_query(const char *ifname, ...)
788 {
789 va_list ap, ap_cur;
790 char *phy, *search, *dest, *key, *val, buf[128];
791 int len, mode, found = 0, match = 1;
792 FILE *fp;
793
794 if (nl80211_get_mode(ifname, &mode))
795 return 0;
796
797 if (mode != IWINFO_OPMODE_MASTER && mode != IWINFO_OPMODE_AP_VLAN)
798 return 0;
799
800 phy = nl80211_ifname2phy(ifname);
801
802 if (!phy)
803 return 0;
804
805 snprintf(buf, sizeof(buf), "/var/run/hostapd-%s.conf", phy);
806 fp = fopen(buf, "r");
807
808 if (!fp)
809 return 0;
810
811 va_start(ap, ifname);
812
813 /* clear all destination buffers */
814 va_copy(ap_cur, ap);
815
816 while ((search = va_arg(ap_cur, char *)) != NULL)
817 {
818 dest = va_arg(ap_cur, char *);
819 len = va_arg(ap_cur, int);
820
821 memset(dest, 0, len);
822 }
823
824 va_end(ap_cur);
825
826 /* iterate applicable lines and copy found values into dest buffers */
827 while (fgets(buf, sizeof(buf), fp))
828 {
829 key = strtok(buf, " =\t\n");
830 val = strtok(NULL, "\n");
831
832 if (!key || !val || !*key || *key == '#')
833 continue;
834
835 if (!strcmp(key, "interface") || !strcmp(key, "bss"))
836 match = !strcmp(ifname, val);
837
838 if (!match)
839 continue;
840
841 va_copy(ap_cur, ap);
842
843 while ((search = va_arg(ap_cur, char *)) != NULL)
844 {
845 dest = va_arg(ap_cur, char *);
846 len = va_arg(ap_cur, int);
847
848 if (!strcmp(search, key))
849 {
850 strncpy(dest, val, len - 1);
851 found++;
852 break;
853 }
854 }
855
856 va_end(ap_cur);
857 }
858
859 fclose(fp);
860
861 va_end(ap);
862
863 return found;
864 }
865
866 #define nl80211_hostapd_query(ifname, ...) \
867 __nl80211_hostapd_query(ifname, ##__VA_ARGS__, NULL)
868
869
870 static inline int nl80211_wpactl_recv(int sock, char *buf, int blen)
871 {
872 fd_set rfds;
873 struct timeval tv = { 0, 256000 };
874
875 FD_ZERO(&rfds);
876 FD_SET(sock, &rfds);
877
878 memset(buf, 0, blen);
879
880 if (select(sock + 1, &rfds, NULL, NULL, &tv) < 0)
881 return -1;
882
883 if (!FD_ISSET(sock, &rfds))
884 return -1;
885
886 return recv(sock, buf, blen - 1, 0);
887 }
888
889 static int nl80211_wpactl_connect(const char *ifname, struct sockaddr_un *local)
890 {
891 struct sockaddr_un remote = { 0 };
892 size_t remote_length, local_length;
893
894 int sock = socket(PF_UNIX, SOCK_DGRAM, 0);
895 if (sock < 0)
896 return sock;
897
898 remote.sun_family = AF_UNIX;
899 remote_length = sizeof(remote.sun_family) +
900 sprintf(remote.sun_path, "/var/run/wpa_supplicant-%s/%s",
901 ifname, ifname);
902
903 if (fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC) < 0)
904 {
905 close(sock);
906 return -1;
907 }
908
909 if (connect(sock, (struct sockaddr *)&remote, remote_length))
910 {
911 remote_length = sizeof(remote.sun_family) +
912 sprintf(remote.sun_path, "/var/run/wpa_supplicant/%s", ifname);
913
914 if (connect(sock, (struct sockaddr *)&remote, remote_length))
915 {
916 close(sock);
917 return -1;
918 }
919 }
920
921 local->sun_family = AF_UNIX;
922 local_length = sizeof(local->sun_family) +
923 sprintf(local->sun_path, "/var/run/iwinfo-%s-%d", ifname, getpid());
924
925 if (bind(sock, (struct sockaddr *)local, local_length) < 0)
926 {
927 close(sock);
928 return -1;
929 }
930
931 return sock;
932 }
933
934 static int __nl80211_wpactl_query(const char *ifname, ...)
935 {
936 va_list ap, ap_cur;
937 struct sockaddr_un local = { 0 };
938 int len, mode, found = 0, sock = -1;
939 char *search, *dest, *key, *val, *line, *pos, buf[512];
940
941 if (nl80211_get_mode(ifname, &mode))
942 return 0;
943
944 if (mode != IWINFO_OPMODE_CLIENT &&
945 mode != IWINFO_OPMODE_ADHOC &&
946 mode != IWINFO_OPMODE_MESHPOINT)
947 return 0;
948
949 sock = nl80211_wpactl_connect(ifname, &local);
950
951 if (sock < 0)
952 return 0;
953
954 va_start(ap, ifname);
955
956 /* clear all destination buffers */
957 va_copy(ap_cur, ap);
958
959 while ((search = va_arg(ap_cur, char *)) != NULL)
960 {
961 dest = va_arg(ap_cur, char *);
962 len = va_arg(ap_cur, int);
963
964 memset(dest, 0, len);
965 }
966
967 va_end(ap_cur);
968
969 send(sock, "STATUS", 6, 0);
970
971 while (true)
972 {
973 if (nl80211_wpactl_recv(sock, buf, sizeof(buf)) <= 0)
974 break;
975
976 if (buf[0] == '<')
977 continue;
978
979 for (line = strtok_r(buf, "\n", &pos);
980 line != NULL;
981 line = strtok_r(NULL, "\n", &pos))
982 {
983 key = strtok(line, "=");
984 val = strtok(NULL, "\n");
985
986 if (!key || !val)
987 continue;
988
989 va_copy(ap_cur, ap);
990
991 while ((search = va_arg(ap_cur, char *)) != NULL)
992 {
993 dest = va_arg(ap_cur, char *);
994 len = va_arg(ap_cur, int);
995
996 if (!strcmp(search, key))
997 {
998 strncpy(dest, val, len - 1);
999 found++;
1000 break;
1001 }
1002 }
1003
1004 va_end(ap_cur);
1005 }
1006
1007 break;
1008 }
1009
1010 va_end(ap);
1011
1012 close(sock);
1013 unlink(local.sun_path);
1014
1015 return found;
1016 }
1017
1018 #define nl80211_wpactl_query(ifname, ...) \
1019 __nl80211_wpactl_query(ifname, ##__VA_ARGS__, NULL)
1020
1021
1022 static char * nl80211_ifadd(const char *ifname)
1023 {
1024 char path[PATH_MAX];
1025 static char nif[IFNAMSIZ] = { 0 };
1026 struct nl80211_msg_conveyor *req;
1027 FILE *sysfs;
1028
1029 req = nl80211_msg(ifname, NL80211_CMD_NEW_INTERFACE, 0);
1030 if (req)
1031 {
1032 snprintf(nif, sizeof(nif), "tmp.%s", ifname);
1033
1034 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, nif);
1035 NLA_PUT_U32(req->msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
1036
1037 nl80211_send(req, NULL, NULL);
1038
1039 snprintf(path, sizeof(path) - 1,
1040 "/proc/sys/net/ipv6/conf/%s/disable_ipv6", nif);
1041
1042 if ((sysfs = fopen(path, "w")) != NULL)
1043 {
1044 fwrite("0\n", 1, 2, sysfs);
1045 fclose(sysfs);
1046 }
1047
1048 return nif;
1049
1050 nla_put_failure:
1051 nl80211_free(req);
1052 }
1053
1054 return NULL;
1055 }
1056
1057 static void nl80211_ifdel(const char *ifname)
1058 {
1059 struct nl80211_msg_conveyor *req;
1060 int err;
1061
1062 req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
1063 if (req)
1064 {
1065 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, ifname);
1066
1067 nl80211_send(req, NULL, NULL);
1068 return;
1069
1070 nla_put_failure:
1071 nl80211_free(req);
1072 }
1073 }
1074
1075 static void nl80211_hostapd_hup(const char *ifname)
1076 {
1077 int fd, pid = 0;
1078 char buf[32];
1079 char *phy = nl80211_ifname2phy(ifname);
1080
1081 if (phy)
1082 {
1083 snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
1084 if ((fd = open(buf, O_RDONLY)) >= 0)
1085 {
1086 if (read(fd, buf, sizeof(buf)) > 0)
1087 pid = atoi(buf);
1088
1089 close(fd);
1090 }
1091
1092 if (pid > 0)
1093 kill(pid, 1);
1094 }
1095 }
1096
1097
1098 static int nl80211_probe(const char *ifname)
1099 {
1100 return !!nl80211_ifname2phy(ifname);
1101 }
1102
1103 struct nl80211_ssid_bssid {
1104 unsigned char *ssid;
1105 unsigned char bssid[7];
1106 };
1107
1108 static int nl80211_get_macaddr_cb(struct nl_msg *msg, void *arg)
1109 {
1110 struct nl80211_ssid_bssid *sb = arg;
1111 struct nlattr **tb = nl80211_parse(msg);
1112
1113 if (tb[NL80211_ATTR_MAC]) {
1114 sb->bssid[0] = 1;
1115 memcpy(sb->bssid + 1, nla_data(tb[NL80211_ATTR_MAC]),
1116 sizeof(sb->bssid) - 1);
1117 }
1118
1119 return NL_SKIP;
1120 }
1121
1122 static int nl80211_get_ssid_bssid_cb(struct nl_msg *msg, void *arg)
1123 {
1124 int ielen;
1125 unsigned char *ie;
1126 struct nl80211_ssid_bssid *sb = arg;
1127 struct nlattr **tb = nl80211_parse(msg);
1128 struct nlattr *bss[NL80211_BSS_MAX + 1];
1129
1130 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1131 [NL80211_BSS_INFORMATION_ELEMENTS] = { 0 },
1132 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
1133 };
1134
1135 if (!tb[NL80211_ATTR_BSS] ||
1136 nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
1137 bss_policy) ||
1138 !bss[NL80211_BSS_BSSID] ||
1139 !bss[NL80211_BSS_STATUS] ||
1140 !bss[NL80211_BSS_INFORMATION_ELEMENTS])
1141 {
1142 return NL_SKIP;
1143 }
1144
1145 switch (nla_get_u32(bss[NL80211_BSS_STATUS]))
1146 {
1147 case NL80211_BSS_STATUS_ASSOCIATED:
1148 case NL80211_BSS_STATUS_AUTHENTICATED:
1149 case NL80211_BSS_STATUS_IBSS_JOINED:
1150
1151 if (sb->ssid)
1152 {
1153 ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
1154 ielen = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
1155
1156 while (ielen >= 2 && ielen >= ie[1])
1157 {
1158 if (ie[0] == 0)
1159 {
1160 memcpy(sb->ssid, ie + 2, min(ie[1], IWINFO_ESSID_MAX_SIZE));
1161 return NL_SKIP;
1162 }
1163
1164 ielen -= ie[1] + 2;
1165 ie += ie[1] + 2;
1166 }
1167 }
1168 else
1169 {
1170 sb->bssid[0] = 1;
1171 memcpy(sb->bssid + 1, nla_data(bss[NL80211_BSS_BSSID]), 6);
1172 return NL_SKIP;
1173 }
1174
1175 default:
1176 return NL_SKIP;
1177 }
1178 }
1179
1180 static int nl80211_get_ssid(const char *ifname, char *buf)
1181 {
1182 char *res;
1183 struct nl80211_ssid_bssid sb = { .ssid = (unsigned char *)buf };
1184
1185 /* try to find ssid from scan dump results */
1186 res = nl80211_phy2ifname(ifname);
1187 sb.ssid[0] = 0;
1188
1189 nl80211_request(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
1190 nl80211_get_ssid_bssid_cb, &sb);
1191
1192 /* failed, try to find from hostapd info */
1193 if (sb.ssid[0] == 0)
1194 nl80211_hostapd_query(ifname, "ssid", sb.ssid,
1195 IWINFO_ESSID_MAX_SIZE + 1);
1196
1197 /* failed, try to obtain Mesh ID */
1198 if (sb.ssid[0] == 0)
1199 iwinfo_ubus_query(res ? res : ifname, "mesh_id",
1200 sb.ssid, IWINFO_ESSID_MAX_SIZE + 1);
1201
1202 return (sb.ssid[0] == 0) ? -1 : 0;
1203 }
1204
1205 static int nl80211_get_bssid(const char *ifname, char *buf)
1206 {
1207 char *res, bssid[sizeof("FF:FF:FF:FF:FF:FF\0")];
1208 struct nl80211_ssid_bssid sb = { };
1209
1210 res = nl80211_phy2ifname(ifname);
1211
1212 /* try to obtain mac address via NL80211_CMD_GET_INTERFACE */
1213 nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
1214 nl80211_get_macaddr_cb, &sb);
1215
1216 /* failed, try to find bssid from scan dump results */
1217 if (sb.bssid[0] == 0)
1218 nl80211_request(res ? res : ifname,
1219 NL80211_CMD_GET_SCAN, NLM_F_DUMP,
1220 nl80211_get_ssid_bssid_cb, &sb);
1221
1222 /* failed, try to find mac from hostapd info */
1223 if ((sb.bssid[0] == 0) &&
1224 nl80211_hostapd_query(ifname, "bssid", bssid, sizeof(bssid)))
1225 {
1226 sb.bssid[0] = 1;
1227 sb.bssid[1] = strtol(&bssid[0], NULL, 16);
1228 sb.bssid[2] = strtol(&bssid[3], NULL, 16);
1229 sb.bssid[3] = strtol(&bssid[6], NULL, 16);
1230 sb.bssid[4] = strtol(&bssid[9], NULL, 16);
1231 sb.bssid[5] = strtol(&bssid[12], NULL, 16);
1232 sb.bssid[6] = strtol(&bssid[15], NULL, 16);
1233 }
1234
1235 if (sb.bssid[0])
1236 {
1237 sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
1238 sb.bssid[1], sb.bssid[2], sb.bssid[3],
1239 sb.bssid[4], sb.bssid[5], sb.bssid[6]);
1240
1241 return 0;
1242 }
1243
1244 return -1;
1245 }
1246
1247
1248 static int nl80211_get_frequency_scan_cb(struct nl_msg *msg, void *arg)
1249 {
1250 int *freq = arg;
1251 struct nlattr **attr = nl80211_parse(msg);
1252 struct nlattr *binfo[NL80211_BSS_MAX + 1];
1253
1254 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
1255 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
1256 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
1257 };
1258
1259 if (attr[NL80211_ATTR_BSS] &&
1260 !nla_parse_nested(binfo, NL80211_BSS_MAX,
1261 attr[NL80211_ATTR_BSS], bss_policy))
1262 {
1263 if (binfo[NL80211_BSS_STATUS] && binfo[NL80211_BSS_FREQUENCY])
1264 *freq = nla_get_u32(binfo[NL80211_BSS_FREQUENCY]);
1265 }
1266
1267 return NL_SKIP;
1268 }
1269
1270 static int nl80211_get_frequency_info_cb(struct nl_msg *msg, void *arg)
1271 {
1272 int *freq = arg;
1273 struct nlattr **tb = nl80211_parse(msg);
1274
1275 if (tb[NL80211_ATTR_WIPHY_FREQ])
1276 *freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
1277
1278 return NL_SKIP;
1279 }
1280
1281 static int nl80211_get_frequency(const char *ifname, int *buf)
1282 {
1283 char *res, channel[4], hwmode[3];
1284
1285 /* try to find frequency from interface info */
1286 res = nl80211_phy2ifname(ifname);
1287 *buf = 0;
1288
1289 nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
1290 nl80211_get_frequency_info_cb, buf);
1291
1292 /* failed, try to find frequency from hostapd info */
1293 if ((*buf == 0) &&
1294 nl80211_hostapd_query(ifname, "hw_mode", hwmode, sizeof(hwmode),
1295 "channel", channel, sizeof(channel)) == 2)
1296 {
1297 *buf = nl80211_channel2freq(atoi(channel), hwmode);
1298 }
1299
1300 /* failed, try to find frequency from scan results */
1301 if (*buf == 0)
1302 {
1303 res = nl80211_phy2ifname(ifname);
1304
1305 nl80211_request(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
1306 nl80211_get_frequency_scan_cb, buf);
1307 }
1308
1309 return (*buf == 0) ? -1 : 0;
1310 }
1311
1312 static int nl80211_get_center_freq1_cb(struct nl_msg *msg, void *arg)
1313 {
1314 int *freq = arg;
1315 struct nlattr **tb = nl80211_parse(msg);
1316
1317 if (tb[NL80211_ATTR_CENTER_FREQ1])
1318 *freq = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
1319
1320 return NL_SKIP;
1321 }
1322
1323 static int nl80211_get_center_freq1(const char *ifname, int *buf)
1324 {
1325 char *res;
1326
1327 /* try to find frequency from interface info */
1328 res = nl80211_phy2ifname(ifname);
1329 *buf = 0;
1330
1331 nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
1332 nl80211_get_center_freq1_cb, buf);
1333
1334 return (*buf == 0) ? -1 : 0;
1335 }
1336
1337 static int nl80211_get_center_freq2_cb(struct nl_msg *msg, void *arg)
1338 {
1339 int *freq = arg;
1340 struct nlattr **tb = nl80211_parse(msg);
1341
1342 if (tb[NL80211_ATTR_CENTER_FREQ2])
1343 *freq = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
1344
1345 return NL_SKIP;
1346 }
1347
1348 static int nl80211_get_center_freq2(const char *ifname, int *buf)
1349 {
1350 char *res;
1351
1352 /* try to find frequency from interface info */
1353 res = nl80211_phy2ifname(ifname);
1354 *buf = 0;
1355
1356 nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
1357 nl80211_get_center_freq2_cb, buf);
1358
1359 return (*buf == 0) ? -1 : 0;
1360 }
1361
1362 static int nl80211_get_channel(const char *ifname, int *buf)
1363 {
1364 if (!nl80211_get_frequency(ifname, buf))
1365 {
1366 *buf = nl80211_freq2channel(*buf);
1367 return 0;
1368 }
1369
1370 return -1;
1371 }
1372
1373 static int nl80211_get_center_chan1(const char *ifname, int *buf)
1374 {
1375 if (!nl80211_get_center_freq1(ifname, buf))
1376 {
1377 *buf = nl80211_freq2channel(*buf);
1378 return 0;
1379 }
1380
1381 return -1;
1382 }
1383
1384 static int nl80211_get_center_chan2(const char *ifname, int *buf)
1385 {
1386 if (!nl80211_get_center_freq2(ifname, buf))
1387 {
1388 *buf = nl80211_freq2channel(*buf);
1389 return 0;
1390 }
1391
1392 return -1;
1393 }
1394
1395 static int nl80211_get_txpower_cb(struct nl_msg *msg, void *arg)
1396 {
1397 int *buf = arg;
1398 struct nlattr **tb = nl80211_parse(msg);
1399
1400 if (tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL])
1401 *buf = iwinfo_mbm2dbm(nla_get_u32(tb[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]));
1402
1403 return NL_SKIP;
1404 }
1405
1406 static int nl80211_get_txpower(const char *ifname, int *buf)
1407 {
1408 char *res;
1409
1410 res = nl80211_phy2ifname(ifname);
1411 *buf = 0;
1412
1413 if (nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
1414 nl80211_get_txpower_cb, buf))
1415 return -1;
1416
1417 return 0;
1418 }
1419
1420
1421 static int nl80211_fill_signal_cb(struct nl_msg *msg, void *arg)
1422 {
1423 int8_t dbm;
1424 int16_t mbit;
1425 struct nl80211_rssi_rate *rr = arg;
1426 struct nlattr **attr = nl80211_parse(msg);
1427 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
1428 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
1429
1430 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
1431 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
1432 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
1433 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
1434 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
1435 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
1436 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
1437 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
1438 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
1439 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
1440 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
1441 };
1442
1443 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
1444 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
1445 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
1446 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
1447 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
1448 };
1449
1450 if (attr[NL80211_ATTR_STA_INFO])
1451 {
1452 if (!nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
1453 attr[NL80211_ATTR_STA_INFO], stats_policy))
1454 {
1455 if (sinfo[NL80211_STA_INFO_SIGNAL])
1456 {
1457 dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
1458 rr->rssi = (rr->rssi * rr->rssi_samples + dbm) / (rr->rssi_samples + 1);
1459 rr->rssi_samples++;
1460 }
1461
1462 if (sinfo[NL80211_STA_INFO_TX_BITRATE])
1463 {
1464 if (!nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
1465 sinfo[NL80211_STA_INFO_TX_BITRATE],
1466 rate_policy))
1467 {
1468 if (rinfo[NL80211_RATE_INFO_BITRATE])
1469 {
1470 mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
1471 rr->rate = (rr->rate * rr->rate_samples + mbit) / (rr->rate_samples + 1);
1472 rr->rate_samples++;
1473 }
1474 }
1475 }
1476 }
1477 }
1478
1479 return NL_SKIP;
1480 }
1481
1482 static void nl80211_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
1483 {
1484 DIR *d;
1485 struct dirent *de;
1486
1487 memset(r, 0, sizeof(*r));
1488
1489 if ((d = opendir("/sys/class/net")) != NULL)
1490 {
1491 while ((de = readdir(d)) != NULL)
1492 {
1493 if (!strncmp(de->d_name, ifname, strlen(ifname)) &&
1494 (!de->d_name[strlen(ifname)] ||
1495 !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
1496 {
1497 nl80211_request(de->d_name, NL80211_CMD_GET_STATION,
1498 NLM_F_DUMP, nl80211_fill_signal_cb, r);
1499 }
1500 }
1501
1502 closedir(d);
1503 }
1504 }
1505
1506 static int nl80211_get_bitrate(const char *ifname, int *buf)
1507 {
1508 struct nl80211_rssi_rate rr;
1509
1510 nl80211_fill_signal(ifname, &rr);
1511
1512 if (rr.rate_samples)
1513 {
1514 *buf = (rr.rate * 100);
1515 return 0;
1516 }
1517
1518 return -1;
1519 }
1520
1521 static int nl80211_get_signal(const char *ifname, int *buf)
1522 {
1523 struct nl80211_rssi_rate rr;
1524
1525 nl80211_fill_signal(ifname, &rr);
1526
1527 if (rr.rssi_samples)
1528 {
1529 *buf = rr.rssi;
1530 return 0;
1531 }
1532
1533 return -1;
1534 }
1535
1536 static int nl80211_get_noise_cb(struct nl_msg *msg, void *arg)
1537 {
1538 int8_t *noise = arg;
1539 struct nlattr **tb = nl80211_parse(msg);
1540 struct nlattr *si[NL80211_SURVEY_INFO_MAX + 1];
1541
1542 static struct nla_policy sp[NL80211_SURVEY_INFO_MAX + 1] = {
1543 [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
1544 [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
1545 };
1546
1547 if (!tb[NL80211_ATTR_SURVEY_INFO])
1548 return NL_SKIP;
1549
1550 if (nla_parse_nested(si, NL80211_SURVEY_INFO_MAX,
1551 tb[NL80211_ATTR_SURVEY_INFO], sp))
1552 return NL_SKIP;
1553
1554 if (!si[NL80211_SURVEY_INFO_NOISE])
1555 return NL_SKIP;
1556
1557 if (!*noise || si[NL80211_SURVEY_INFO_IN_USE])
1558 *noise = (int8_t)nla_get_u8(si[NL80211_SURVEY_INFO_NOISE]);
1559
1560 return NL_SKIP;
1561 }
1562
1563
1564 static int nl80211_get_noise(const char *ifname, int *buf)
1565 {
1566 int8_t noise = 0;
1567
1568 if (nl80211_request(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP,
1569 nl80211_get_noise_cb, &noise))
1570 goto out;
1571
1572 *buf = noise;
1573 return 0;
1574
1575 out:
1576 *buf = 0;
1577 return -1;
1578 }
1579
1580 static int nl80211_get_quality(const char *ifname, int *buf)
1581 {
1582 int signal;
1583
1584 if (!nl80211_get_signal(ifname, &signal))
1585 {
1586 /* A positive signal level is usually just a quality
1587 * value, pass through as-is */
1588 if (signal >= 0)
1589 {
1590 *buf = signal;
1591 }
1592
1593 /* The cfg80211 wext compat layer assumes a signal range
1594 * of -110 dBm to -40 dBm, the quality value is derived
1595 * by adding 110 to the signal level */
1596 else
1597 {
1598 if (signal < -110)
1599 signal = -110;
1600 else if (signal > -40)
1601 signal = -40;
1602
1603 *buf = (signal + 110);
1604 }
1605
1606 return 0;
1607 }
1608
1609 return -1;
1610 }
1611
1612 static int nl80211_get_quality_max(const char *ifname, int *buf)
1613 {
1614 /* The cfg80211 wext compat layer assumes a maximum
1615 * quality of 70 */
1616 *buf = 70;
1617
1618 return 0;
1619 }
1620
1621 static int nl80211_check_wepkey(const char *key)
1622 {
1623 if (key && *key)
1624 {
1625 switch (strlen(key))
1626 {
1627 case 5:
1628 case 10:
1629 return IWINFO_CIPHER_WEP40;
1630
1631 case 13:
1632 case 26:
1633 return IWINFO_CIPHER_WEP104;
1634 }
1635 }
1636
1637 return 0;
1638 }
1639
1640 static struct {
1641 const char *match;
1642 int version;
1643 int suite;
1644 } wpa_key_mgmt_strings[] = {
1645 { "IEEE 802.1X/EAP", 0, IWINFO_KMGMT_8021x },
1646 { "EAP-SUITE-B-192", 4, IWINFO_KMGMT_8021x },
1647 { "EAP-SUITE-B", 4, IWINFO_KMGMT_8021x },
1648 { "EAP-SHA256", 0, IWINFO_KMGMT_8021x },
1649 { "PSK-SHA256", 0, IWINFO_KMGMT_PSK },
1650 { "NONE", 0, IWINFO_KMGMT_NONE },
1651 { "None", 0, IWINFO_KMGMT_NONE },
1652 { "PSK", 0, IWINFO_KMGMT_PSK },
1653 { "EAP", 0, IWINFO_KMGMT_8021x },
1654 { "SAE", 4, IWINFO_KMGMT_SAE },
1655 { "OWE", 4, IWINFO_KMGMT_OWE }
1656 };
1657
1658 static void parse_wpa_suites(const char *str, int defversion,
1659 uint8_t *versions, uint8_t *suites)
1660 {
1661 size_t l;
1662 int i, version;
1663 const char *p, *q, *m, *sep = " \t\n,-+/";
1664
1665 for (p = str; *p; )
1666 {
1667 q = p;
1668
1669 for (i = 0; i < ARRAY_SIZE(wpa_key_mgmt_strings); i++)
1670 {
1671 m = wpa_key_mgmt_strings[i].match;
1672 l = strlen(m);
1673
1674 if (!strncmp(q, m, l) && (!q[l] || strchr(sep, q[l])))
1675 {
1676 if (wpa_key_mgmt_strings[i].version != 0)
1677 version = wpa_key_mgmt_strings[i].version;
1678 else
1679 version = defversion;
1680
1681 *versions |= version;
1682 *suites |= wpa_key_mgmt_strings[i].suite;
1683
1684 q += l;
1685 break;
1686 }
1687 }
1688
1689 if (q == p)
1690 q += strcspn(q, sep);
1691
1692 p = q + strspn(q, sep);
1693 }
1694 }
1695
1696 static struct {
1697 const char *match;
1698 int cipher;
1699 } wpa_cipher_strings[] = {
1700 { "WEP-104", IWINFO_CIPHER_WEP104 },
1701 { "WEP-40", IWINFO_CIPHER_WEP40 },
1702 { "NONE", IWINFO_CIPHER_NONE },
1703 { "TKIP", IWINFO_CIPHER_TKIP },
1704 { "CCMP", IWINFO_CIPHER_CCMP },
1705 { "GCMP", IWINFO_CIPHER_GCMP }
1706 };
1707
1708 static void parse_wpa_ciphers(const char *str, uint16_t *ciphers)
1709 {
1710 int i;
1711 size_t l;
1712 const char *m, *p, *q, *sep = " \t\n,-+/";
1713
1714 for (p = str; *p; )
1715 {
1716 q = p;
1717
1718 for (i = 0; i < ARRAY_SIZE(wpa_cipher_strings); i++)
1719 {
1720 m = wpa_cipher_strings[i].match;
1721 l = strlen(m);
1722
1723 if (!strncmp(q, m, l) && (!q[l] || strchr(sep, q[l])))
1724 {
1725 *ciphers |= wpa_cipher_strings[i].cipher;
1726
1727 q += l;
1728 break;
1729 }
1730 }
1731
1732 if (q == p)
1733 q += strcspn(q, sep);
1734
1735 p = q + strspn(q, sep);
1736 }
1737 }
1738
1739 static int nl80211_get_encryption(const char *ifname, char *buf)
1740 {
1741 char *p;
1742 int opmode;
1743 uint8_t wpa_version = 0;
1744 char wpa[2], wpa_key_mgmt[64], wpa_pairwise[16], wpa_groupwise[16];
1745 char auth_algs[2], wep_key0[27], wep_key1[27], wep_key2[27], wep_key3[27];
1746 char mode[16];
1747
1748 struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
1749
1750 /* WPA supplicant */
1751 if (nl80211_wpactl_query(ifname,
1752 "pairwise_cipher", wpa_pairwise, sizeof(wpa_pairwise),
1753 "group_cipher", wpa_groupwise, sizeof(wpa_groupwise),
1754 "key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
1755 "mode", mode, sizeof(mode)))
1756 {
1757 /* WEP or Open */
1758 if (!strcmp(wpa_key_mgmt, "NONE"))
1759 {
1760 parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
1761 parse_wpa_ciphers(wpa_groupwise, &c->group_ciphers);
1762
1763 if (c->pair_ciphers != 0 && c->pair_ciphers != IWINFO_CIPHER_NONE) {
1764 c->enabled = 1;
1765 c->auth_suites = IWINFO_KMGMT_NONE;
1766 c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
1767 }
1768 else {
1769 c->pair_ciphers = 0;
1770 c->group_ciphers = 0;
1771 }
1772 }
1773
1774 /* MESH with SAE */
1775 else if (!strcmp(mode, "mesh") && !strcmp(wpa_key_mgmt, "UNKNOWN"))
1776 {
1777 c->enabled = 1;
1778 c->wpa_version = 4;
1779 c->auth_suites = IWINFO_KMGMT_SAE;
1780 c->pair_ciphers = IWINFO_CIPHER_CCMP;
1781 c->group_ciphers = IWINFO_CIPHER_CCMP;
1782 }
1783
1784 /* WPA */
1785 else
1786 {
1787 parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
1788 parse_wpa_ciphers(wpa_groupwise, &c->group_ciphers);
1789
1790 p = wpa_key_mgmt;
1791
1792 if (!strncmp(p, "WPA2-", 5) || !strncmp(p, "WPA2/", 5))
1793 {
1794 p += 5;
1795 wpa_version = 2;
1796 }
1797 else if (!strncmp(p, "WPA-", 4))
1798 {
1799 p += 4;
1800 wpa_version = 1;
1801 }
1802
1803 parse_wpa_suites(p, wpa_version, &c->wpa_version, &c->auth_suites);
1804
1805 c->enabled = !!(c->wpa_version && c->auth_suites);
1806 }
1807
1808 return 0;
1809 }
1810
1811 /* Hostapd */
1812 else if (nl80211_hostapd_query(ifname,
1813 "wpa", wpa, sizeof(wpa),
1814 "wpa_key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
1815 "wpa_pairwise", wpa_pairwise, sizeof(wpa_pairwise),
1816 "auth_algs", auth_algs, sizeof(auth_algs),
1817 "wep_key0", wep_key0, sizeof(wep_key0),
1818 "wep_key1", wep_key1, sizeof(wep_key1),
1819 "wep_key2", wep_key2, sizeof(wep_key2),
1820 "wep_key3", wep_key3, sizeof(wep_key3)))
1821 {
1822 c->wpa_version = 0;
1823
1824 if (wpa_key_mgmt[0])
1825 {
1826 for (p = strtok(wpa_key_mgmt, " \t"); p != NULL; p = strtok(NULL, " \t"))
1827 {
1828 if (!strncmp(p, "WPA-", 4))
1829 p += 4;
1830
1831 parse_wpa_suites(p, atoi(wpa), &c->wpa_version, &c->auth_suites);
1832 }
1833
1834 c->enabled = c->wpa_version ? 1 : 0;
1835 }
1836
1837 if (wpa_pairwise[0])
1838 parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
1839
1840 if (auth_algs[0])
1841 {
1842 switch (atoi(auth_algs))
1843 {
1844 case 1:
1845 c->auth_algs |= IWINFO_AUTH_OPEN;
1846 break;
1847
1848 case 2:
1849 c->auth_algs |= IWINFO_AUTH_SHARED;
1850 break;
1851
1852 case 3:
1853 c->auth_algs |= IWINFO_AUTH_OPEN;
1854 c->auth_algs |= IWINFO_AUTH_SHARED;
1855 break;
1856 }
1857
1858 c->pair_ciphers |= nl80211_check_wepkey(wep_key0);
1859 c->pair_ciphers |= nl80211_check_wepkey(wep_key1);
1860 c->pair_ciphers |= nl80211_check_wepkey(wep_key2);
1861 c->pair_ciphers |= nl80211_check_wepkey(wep_key3);
1862
1863 c->enabled = (c->auth_algs && c->pair_ciphers) ? 1 : 0;
1864 }
1865
1866 c->group_ciphers = c->pair_ciphers;
1867
1868 return 0;
1869 }
1870
1871 /* Ad-Hoc or Mesh interfaces without wpa_supplicant are open */
1872 else if (!nl80211_get_mode(ifname, &opmode) &&
1873 (opmode == IWINFO_OPMODE_ADHOC ||
1874 opmode == IWINFO_OPMODE_MESHPOINT))
1875 {
1876 c->enabled = 0;
1877
1878 return 0;
1879 }
1880
1881
1882 return -1;
1883 }
1884
1885 static int nl80211_get_phyname(const char *ifname, char *buf)
1886 {
1887 const char *name;
1888
1889 name = nl80211_ifname2phy(ifname);
1890
1891 if (name)
1892 {
1893 strcpy(buf, name);
1894 return 0;
1895 }
1896 else if ((name = nl80211_phy2ifname(ifname)) != NULL)
1897 {
1898 name = nl80211_ifname2phy(name);
1899
1900 if (name)
1901 {
1902 strcpy(buf, ifname);
1903 return 0;
1904 }
1905 }
1906
1907 return -1;
1908 }
1909
1910
1911 static void nl80211_parse_rateinfo(struct nlattr **ri,
1912 struct iwinfo_rate_entry *re)
1913 {
1914 if (ri[NL80211_RATE_INFO_BITRATE32])
1915 re->rate = nla_get_u32(ri[NL80211_RATE_INFO_BITRATE32]) * 100;
1916 else if (ri[NL80211_RATE_INFO_BITRATE])
1917 re->rate = nla_get_u16(ri[NL80211_RATE_INFO_BITRATE]) * 100;
1918
1919 if (ri[NL80211_RATE_INFO_HE_MCS])
1920 {
1921 re->is_he = 1;
1922 re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_HE_MCS]);
1923
1924 if (ri[NL80211_RATE_INFO_HE_NSS])
1925 re->nss = nla_get_u8(ri[NL80211_RATE_INFO_HE_NSS]);
1926 if (ri[NL80211_RATE_INFO_HE_GI])
1927 re->he_gi = nla_get_u8(ri[NL80211_RATE_INFO_HE_GI]);
1928 if (ri[NL80211_RATE_INFO_HE_DCM])
1929 re->he_dcm = nla_get_u8(ri[NL80211_RATE_INFO_HE_DCM]);
1930 }
1931 else if (ri[NL80211_RATE_INFO_VHT_MCS])
1932 {
1933 re->is_vht = 1;
1934 re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_VHT_MCS]);
1935
1936 if (ri[NL80211_RATE_INFO_VHT_NSS])
1937 re->nss = nla_get_u8(ri[NL80211_RATE_INFO_VHT_NSS]);
1938 }
1939 else if (ri[NL80211_RATE_INFO_MCS])
1940 {
1941 re->is_ht = 1;
1942 re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_MCS]);
1943 }
1944
1945 if (ri[NL80211_RATE_INFO_5_MHZ_WIDTH])
1946 re->mhz = 5;
1947 else if (ri[NL80211_RATE_INFO_10_MHZ_WIDTH])
1948 re->mhz = 10;
1949 else if (ri[NL80211_RATE_INFO_40_MHZ_WIDTH])
1950 re->mhz = 40;
1951 else if (ri[NL80211_RATE_INFO_80_MHZ_WIDTH])
1952 re->mhz = 80;
1953 else if (ri[NL80211_RATE_INFO_80P80_MHZ_WIDTH] ||
1954 ri[NL80211_RATE_INFO_160_MHZ_WIDTH])
1955 re->mhz = 160;
1956 else
1957 re->mhz = 20;
1958
1959 if (ri[NL80211_RATE_INFO_SHORT_GI])
1960 re->is_short_gi = 1;
1961
1962 re->is_40mhz = (re->mhz == 40);
1963 }
1964
1965 static int nl80211_get_survey_cb(struct nl_msg *msg, void *arg)
1966 {
1967 struct nl80211_array_buf *arr = arg;
1968 struct iwinfo_survey_entry *e = arr->buf;
1969 struct nlattr **attr = nl80211_parse(msg);
1970 struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
1971 int rc;
1972
1973 static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
1974 [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
1975 [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
1976 [NL80211_SURVEY_INFO_TIME] = { .type = NLA_U64 },
1977 [NL80211_SURVEY_INFO_TIME_BUSY] = { .type = NLA_U64 },
1978 [NL80211_SURVEY_INFO_TIME_EXT_BUSY] = { .type = NLA_U64 },
1979 [NL80211_SURVEY_INFO_TIME_RX] = { .type = NLA_U64 },
1980 [NL80211_SURVEY_INFO_TIME_TX] = { .type = NLA_U64 },
1981 };
1982
1983 rc = nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
1984 attr[NL80211_ATTR_SURVEY_INFO],
1985 survey_policy);
1986 if (rc)
1987 return NL_SKIP;
1988
1989 /* advance to end of array */
1990 e += arr->count;
1991 memset(e, 0, sizeof(*e));
1992
1993 if (sinfo[NL80211_SURVEY_INFO_FREQUENCY])
1994 e->mhz = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
1995
1996 if (sinfo[NL80211_SURVEY_INFO_NOISE])
1997 e->noise = nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
1998
1999 if (sinfo[NL80211_SURVEY_INFO_TIME])
2000 e->active_time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME]);
2001
2002 if (sinfo[NL80211_SURVEY_INFO_TIME_BUSY])
2003 e->busy_time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_BUSY]);
2004
2005 if (sinfo[NL80211_SURVEY_INFO_TIME_EXT_BUSY])
2006 e->busy_time_ext = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_EXT_BUSY]);
2007
2008 if (sinfo[NL80211_SURVEY_INFO_TIME_RX])
2009 e->rxtime = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_RX]);
2010
2011 if (sinfo[NL80211_SURVEY_INFO_TIME_TX])
2012 e->txtime = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_TX]);
2013
2014 arr->count++;
2015 return NL_SKIP;
2016 }
2017
2018
2019 static void plink_state_to_str(char *dst, unsigned state)
2020 {
2021 switch (state) {
2022 case NL80211_PLINK_LISTEN:
2023 strcpy(dst, "LISTEN");
2024 break;
2025 case NL80211_PLINK_OPN_SNT:
2026 strcpy(dst, "OPN_SNT");
2027 break;
2028 case NL80211_PLINK_OPN_RCVD:
2029 strcpy(dst, "OPN_RCVD");
2030 break;
2031 case NL80211_PLINK_CNF_RCVD:
2032 strcpy(dst, "CNF_RCVD");
2033 break;
2034 case NL80211_PLINK_ESTAB:
2035 strcpy(dst, "ESTAB");
2036 break;
2037 case NL80211_PLINK_HOLDING:
2038 strcpy(dst, "HOLDING");
2039 break;
2040 case NL80211_PLINK_BLOCKED:
2041 strcpy(dst, "BLOCKED");
2042 break;
2043 default:
2044 strcpy(dst, "UNKNOWN");
2045 break;
2046 }
2047 }
2048
2049 static void power_mode_to_str(char *dst, struct nlattr *a)
2050 {
2051 enum nl80211_mesh_power_mode pm = nla_get_u32(a);
2052
2053 switch (pm) {
2054 case NL80211_MESH_POWER_ACTIVE:
2055 strcpy(dst, "ACTIVE");
2056 break;
2057 case NL80211_MESH_POWER_LIGHT_SLEEP:
2058 strcpy(dst, "LIGHT SLEEP");
2059 break;
2060 case NL80211_MESH_POWER_DEEP_SLEEP:
2061 strcpy(dst, "DEEP SLEEP");
2062 break;
2063 default:
2064 strcpy(dst, "UNKNOWN");
2065 break;
2066 }
2067 }
2068
2069 static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
2070 {
2071 struct nl80211_array_buf *arr = arg;
2072 struct iwinfo_assoclist_entry *e = arr->buf;
2073 struct nlattr **attr = nl80211_parse(msg);
2074 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
2075 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
2076 struct nl80211_sta_flag_update *sta_flags;
2077
2078 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
2079 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
2080 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
2081 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
2082 [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
2083 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
2084 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
2085 [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
2086 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
2087 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
2088 [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
2089 [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
2090 [NL80211_STA_INFO_CONNECTED_TIME]= { .type = NLA_U32 },
2091 [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
2092 [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
2093 [NL80211_STA_INFO_STA_FLAGS] =
2094 { .minlen = sizeof(struct nl80211_sta_flag_update) },
2095 [NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
2096 /* mesh */
2097 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
2098 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
2099 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
2100 [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32 },
2101 [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32 },
2102 [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32 },
2103 };
2104
2105 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
2106 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
2107 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
2108 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
2109 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
2110 };
2111
2112 /* advance to end of array */
2113 e += arr->count;
2114 memset(e, 0, sizeof(*e));
2115
2116 if (attr[NL80211_ATTR_MAC])
2117 memcpy(e->mac, nla_data(attr[NL80211_ATTR_MAC]), 6);
2118
2119 if (attr[NL80211_ATTR_STA_INFO] &&
2120 !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
2121 attr[NL80211_ATTR_STA_INFO], stats_policy))
2122 {
2123 if (sinfo[NL80211_STA_INFO_SIGNAL])
2124 e->signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
2125
2126 if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
2127 e->signal_avg = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
2128
2129 if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
2130 e->inactive = nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]);
2131
2132 if (sinfo[NL80211_STA_INFO_CONNECTED_TIME])
2133 e->connected_time = nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]);
2134
2135 if (sinfo[NL80211_STA_INFO_RX_PACKETS])
2136 e->rx_packets = nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]);
2137
2138 if (sinfo[NL80211_STA_INFO_TX_PACKETS])
2139 e->tx_packets = nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]);
2140
2141 if (sinfo[NL80211_STA_INFO_RX_BITRATE] &&
2142 !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
2143 sinfo[NL80211_STA_INFO_RX_BITRATE], rate_policy))
2144 nl80211_parse_rateinfo(rinfo, &e->rx_rate);
2145
2146 if (sinfo[NL80211_STA_INFO_TX_BITRATE] &&
2147 !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
2148 sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy))
2149 nl80211_parse_rateinfo(rinfo, &e->tx_rate);
2150
2151 if (sinfo[NL80211_STA_INFO_RX_BYTES])
2152 e->rx_bytes = nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]);
2153
2154 if (sinfo[NL80211_STA_INFO_TX_BYTES])
2155 e->tx_bytes = nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]);
2156
2157 if (sinfo[NL80211_STA_INFO_TX_RETRIES])
2158 e->tx_retries = nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]);
2159
2160 if (sinfo[NL80211_STA_INFO_TX_FAILED])
2161 e->tx_failed = nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]);
2162
2163 if (sinfo[NL80211_STA_INFO_T_OFFSET])
2164 e->t_offset = nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]);
2165
2166 if (sinfo[NL80211_STA_INFO_RX_DROP_MISC])
2167 e->rx_drop_misc = nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC]);
2168
2169 if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
2170 e->thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
2171
2172 /* mesh */
2173 if (sinfo[NL80211_STA_INFO_LLID])
2174 e->llid = nla_get_u16(sinfo[NL80211_STA_INFO_LLID]);
2175
2176 if (sinfo[NL80211_STA_INFO_PLID])
2177 e->plid = nla_get_u16(sinfo[NL80211_STA_INFO_PLID]);
2178
2179 if (sinfo[NL80211_STA_INFO_PLINK_STATE])
2180 plink_state_to_str(e->plink_state,
2181 nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE]));
2182
2183 if (sinfo[NL80211_STA_INFO_LOCAL_PM])
2184 power_mode_to_str(e->local_ps, sinfo[NL80211_STA_INFO_LOCAL_PM]);
2185 if (sinfo[NL80211_STA_INFO_PEER_PM])
2186 power_mode_to_str(e->peer_ps, sinfo[NL80211_STA_INFO_PEER_PM]);
2187 if (sinfo[NL80211_STA_INFO_NONPEER_PM])
2188 power_mode_to_str(e->nonpeer_ps, sinfo[NL80211_STA_INFO_NONPEER_PM]);
2189
2190 /* Station flags */
2191 if (sinfo[NL80211_STA_INFO_STA_FLAGS])
2192 {
2193 sta_flags = (struct nl80211_sta_flag_update *)
2194 nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
2195
2196 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
2197 sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED))
2198 e->is_authorized = 1;
2199
2200 if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
2201 sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
2202 e->is_authenticated = 1;
2203
2204 if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) &&
2205 sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
2206 e->is_preamble_short = 1;
2207
2208 if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME) &&
2209 sta_flags->set & BIT(NL80211_STA_FLAG_WME))
2210 e->is_wme = 1;
2211
2212 if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP) &&
2213 sta_flags->set & BIT(NL80211_STA_FLAG_MFP))
2214 e->is_mfp = 1;
2215
2216 if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER) &&
2217 sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER))
2218 e->is_tdls = 1;
2219 }
2220 }
2221
2222 e->noise = 0; /* filled in by caller */
2223 arr->count++;
2224
2225 return NL_SKIP;
2226 }
2227
2228 static int nl80211_get_survey(const char *ifname, char *buf, int *len)
2229 {
2230 struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
2231 int rc;
2232
2233 rc = nl80211_request(ifname, NL80211_CMD_GET_SURVEY,
2234 NLM_F_DUMP, nl80211_get_survey_cb, &arr);
2235 if (!rc)
2236 *len = (arr.count * sizeof(struct iwinfo_survey_entry));
2237 else
2238 *len = 0;
2239
2240 return 0;
2241 }
2242
2243 static int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
2244 {
2245 DIR *d;
2246 int i, noise = 0;
2247 struct dirent *de;
2248 struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
2249 struct iwinfo_assoclist_entry *e;
2250
2251 if ((d = opendir("/sys/class/net")) != NULL)
2252 {
2253 while ((de = readdir(d)) != NULL)
2254 {
2255 if (!strncmp(de->d_name, ifname, strlen(ifname)) &&
2256 (!de->d_name[strlen(ifname)] ||
2257 !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
2258 {
2259 nl80211_request(de->d_name, NL80211_CMD_GET_STATION,
2260 NLM_F_DUMP, nl80211_get_assoclist_cb, &arr);
2261 }
2262 }
2263
2264 closedir(d);
2265
2266 if (!nl80211_get_noise(ifname, &noise))
2267 for (i = 0, e = arr.buf; i < arr.count; i++, e++)
2268 e->noise = noise;
2269
2270 *len = (arr.count * sizeof(struct iwinfo_assoclist_entry));
2271 return 0;
2272 }
2273
2274 return -1;
2275 }
2276
2277 static int nl80211_get_txpwrlist_cb(struct nl_msg *msg, void *arg)
2278 {
2279 int *dbm_max = arg;
2280 int ch_cur, ch_cmp, bands_remain, freqs_remain;
2281
2282 struct nlattr **attr = nl80211_parse(msg);
2283 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
2284 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
2285 struct nlattr *band, *freq;
2286
2287 static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
2288 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
2289 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
2290 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
2291 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
2292 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
2293 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
2294 };
2295
2296 ch_cur = *dbm_max; /* value int* is initialized with channel by caller */
2297 *dbm_max = -1;
2298
2299 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
2300 {
2301 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
2302 nla_len(band), NULL);
2303
2304 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
2305 {
2306 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
2307 nla_data(freq), nla_len(freq), freq_policy);
2308
2309 ch_cmp = nl80211_freq2channel(nla_get_u32(
2310 freqs[NL80211_FREQUENCY_ATTR_FREQ]));
2311
2312 if ((!ch_cur || (ch_cmp == ch_cur)) &&
2313 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
2314 {
2315 *dbm_max = (int)(0.01 * nla_get_u32(
2316 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
2317
2318 break;
2319 }
2320 }
2321 }
2322
2323 return NL_SKIP;
2324 }
2325
2326 static int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
2327 {
2328 int err, ch_cur;
2329 int dbm_max = -1, dbm_cur, dbm_cnt;
2330 struct nl80211_msg_conveyor *req;
2331 struct iwinfo_txpwrlist_entry entry;
2332
2333 if (nl80211_get_channel(ifname, &ch_cur))
2334 ch_cur = 0;
2335
2336 /* initialize the value pointer with channel for callback */
2337 dbm_max = ch_cur;
2338
2339 err = nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
2340 nl80211_get_txpwrlist_cb, &dbm_max);
2341
2342 if (!err)
2343 {
2344 for (dbm_cur = 0, dbm_cnt = 0;
2345 dbm_cur < dbm_max;
2346 dbm_cur++, dbm_cnt++)
2347 {
2348 entry.dbm = dbm_cur;
2349 entry.mw = iwinfo_dbm2mw(dbm_cur);
2350
2351 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
2352 }
2353
2354 entry.dbm = dbm_max;
2355 entry.mw = iwinfo_dbm2mw(dbm_max);
2356
2357 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
2358 dbm_cnt++;
2359
2360 *len = dbm_cnt * sizeof(entry);
2361 return 0;
2362 }
2363
2364 return -1;
2365 }
2366
2367 static void nl80211_get_scancrypto(char *spec, struct iwinfo_crypto_entry *c)
2368 {
2369 int wpa_version = 0;
2370 char *p, *q, *proto, *suites;
2371
2372 c->enabled = 0;
2373
2374 for (p = strtok_r(spec, "[]", &q); p; p = strtok_r(NULL, "[]", &q)) {
2375 if (!strcmp(p, "WEP")) {
2376 c->enabled = 1;
2377 c->auth_suites = IWINFO_KMGMT_NONE;
2378 c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
2379 c->pair_ciphers = IWINFO_CIPHER_WEP40 | IWINFO_CIPHER_WEP104;
2380 break;
2381 }
2382
2383 proto = strtok(p, "-");
2384 suites = strtok(NULL, "]");
2385
2386 if (!proto || !suites)
2387 continue;
2388
2389 if (!strcmp(proto, "WPA2") || !strcmp(proto, "RSN"))
2390 wpa_version = 2;
2391 else if (!strcmp(proto, "WPA"))
2392 wpa_version = 1;
2393 else
2394 continue;
2395
2396 c->enabled = 1;
2397
2398 parse_wpa_suites(suites, wpa_version, &c->wpa_version, &c->auth_suites);
2399 parse_wpa_ciphers(suites, &c->pair_ciphers);
2400 }
2401 }
2402
2403
2404 struct nl80211_scanlist {
2405 struct iwinfo_scanlist_entry *e;
2406 int len;
2407 };
2408
2409
2410 static void nl80211_get_scanlist_ie(struct nlattr **bss,
2411 struct iwinfo_scanlist_entry *e)
2412 {
2413 int ielen = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
2414 unsigned char *ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
2415 static unsigned char ms_oui[3] = { 0x00, 0x50, 0xf2 };
2416 int len;
2417
2418 while (ielen >= 2 && ielen >= ie[1])
2419 {
2420 switch (ie[0])
2421 {
2422 case 0: /* SSID */
2423 case 114: /* Mesh ID */
2424 if (e->ssid[0] == 0) {
2425 len = min(ie[1], IWINFO_ESSID_MAX_SIZE);
2426 memcpy(e->ssid, ie + 2, len);
2427 e->ssid[len] = 0;
2428 }
2429 break;
2430
2431 case 48: /* RSN */
2432 iwinfo_parse_rsn(&e->crypto, ie + 2, ie[1],
2433 IWINFO_CIPHER_CCMP, IWINFO_KMGMT_8021x);
2434 break;
2435
2436 case 221: /* Vendor */
2437 if (ie[1] >= 4 && !memcmp(ie + 2, ms_oui, 3) && ie[5] == 1)
2438 iwinfo_parse_rsn(&e->crypto, ie + 6, ie[1] - 4,
2439 IWINFO_CIPHER_TKIP, IWINFO_KMGMT_PSK);
2440 break;
2441 case 61: /* HT oeration */
2442 if (ie[1] >= 3) {
2443 e->ht_chan_info.primary_chan = ie[2];
2444 e->ht_chan_info.secondary_chan_off = ie[3] & 0x3;
2445 e->ht_chan_info.chan_width = (ie[4] & 0x4)>>2;
2446 }
2447 break;
2448 case 192: /* VHT operation */
2449 if (ie[1] >= 3) {
2450 e->vht_chan_info.chan_width = ie[2];
2451 e->vht_chan_info.center_chan_1 = ie[3];
2452 e->vht_chan_info.center_chan_2 = ie[4];
2453 }
2454 break;
2455 }
2456
2457 ielen -= ie[1] + 2;
2458 ie += ie[1] + 2;
2459 }
2460 }
2461
2462 static int nl80211_get_scanlist_cb(struct nl_msg *msg, void *arg)
2463 {
2464 int8_t rssi;
2465 uint16_t caps;
2466
2467 struct nl80211_scanlist *sl = arg;
2468 struct nlattr **tb = nl80211_parse(msg);
2469 struct nlattr *bss[NL80211_BSS_MAX + 1];
2470
2471 static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
2472 [NL80211_BSS_TSF] = { .type = NLA_U64 },
2473 [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
2474 [NL80211_BSS_BSSID] = { 0 },
2475 [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
2476 [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
2477 [NL80211_BSS_INFORMATION_ELEMENTS] = { 0 },
2478 [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
2479 [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
2480 [NL80211_BSS_STATUS] = { .type = NLA_U32 },
2481 [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
2482 [NL80211_BSS_BEACON_IES] = { 0 },
2483 };
2484
2485 if (!tb[NL80211_ATTR_BSS] ||
2486 nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
2487 bss_policy) ||
2488 !bss[NL80211_BSS_BSSID])
2489 {
2490 return NL_SKIP;
2491 }
2492
2493 if (bss[NL80211_BSS_CAPABILITY])
2494 caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
2495 else
2496 caps = 0;
2497
2498 memset(sl->e, 0, sizeof(*sl->e));
2499 memcpy(sl->e->mac, nla_data(bss[NL80211_BSS_BSSID]), 6);
2500
2501 if (caps & (1<<1))
2502 sl->e->mode = IWINFO_OPMODE_ADHOC;
2503 else if (caps & (1<<0))
2504 sl->e->mode = IWINFO_OPMODE_MASTER;
2505 else
2506 sl->e->mode = IWINFO_OPMODE_MESHPOINT;
2507
2508 if (caps & (1<<4))
2509 sl->e->crypto.enabled = 1;
2510
2511 if (bss[NL80211_BSS_FREQUENCY])
2512 sl->e->channel = nl80211_freq2channel(nla_get_u32(
2513 bss[NL80211_BSS_FREQUENCY]));
2514
2515 if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
2516 nl80211_get_scanlist_ie(bss, sl->e);
2517
2518 if (bss[NL80211_BSS_SIGNAL_MBM])
2519 {
2520 sl->e->signal =
2521 (uint8_t)((int32_t)nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]) / 100);
2522
2523 rssi = sl->e->signal - 0x100;
2524
2525 if (rssi < -110)
2526 rssi = -110;
2527 else if (rssi > -40)
2528 rssi = -40;
2529
2530 sl->e->quality = (rssi + 110);
2531 sl->e->quality_max = 70;
2532 }
2533
2534 if (sl->e->crypto.enabled && !sl->e->crypto.wpa_version)
2535 {
2536 sl->e->crypto.auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
2537 sl->e->crypto.pair_ciphers = IWINFO_CIPHER_WEP40 | IWINFO_CIPHER_WEP104;
2538 }
2539
2540 sl->e++;
2541 sl->len++;
2542
2543 return NL_SKIP;
2544 }
2545
2546 static int nl80211_get_scanlist_nl(const char *ifname, char *buf, int *len)
2547 {
2548 struct nl80211_scanlist sl = { .e = (struct iwinfo_scanlist_entry *)buf };
2549
2550 if (nl80211_request(ifname, NL80211_CMD_TRIGGER_SCAN, 0, NULL, NULL))
2551 goto out;
2552
2553 if (nl80211_wait("nl80211", "scan",
2554 NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_SCAN_ABORTED))
2555 goto out;
2556
2557 if (nl80211_request(ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
2558 nl80211_get_scanlist_cb, &sl))
2559 goto out;
2560
2561 *len = sl.len * sizeof(struct iwinfo_scanlist_entry);
2562 return 0;
2563
2564 out:
2565 *len = 0;
2566 return -1;
2567 }
2568
2569 static int wpasupp_ssid_decode(const char *in, char *out, int outlen)
2570 {
2571 #define hex(x) \
2572 (((x) >= 'a') ? ((x) - 'a' + 10) : \
2573 (((x) >= 'A') ? ((x) - 'A' + 10) : ((x) - '0')))
2574
2575 int len = 0;
2576
2577 while (*in)
2578 {
2579 if (len + 1 >= outlen)
2580 break;
2581
2582 switch (*in)
2583 {
2584 case '\\':
2585 in++;
2586 switch (*in)
2587 {
2588 case 'n':
2589 out[len++] = '\n'; in++;
2590 break;
2591
2592 case 'r':
2593 out[len++] = '\r'; in++;
2594 break;
2595
2596 case 't':
2597 out[len++] = '\t'; in++;
2598 break;
2599
2600 case 'e':
2601 out[len++] = '\033'; in++;
2602 break;
2603
2604 case 'x':
2605 if (isxdigit(*(in+1)) && isxdigit(*(in+2)))
2606 out[len++] = hex(*(in+1)) * 16 + hex(*(in+2));
2607 in += 3;
2608 break;
2609
2610 default:
2611 out[len++] = *in++;
2612 break;
2613 }
2614 break;
2615
2616 default:
2617 out[len++] = *in++;
2618 break;
2619 }
2620 }
2621
2622 if (outlen > len)
2623 out[len] = '\0';
2624
2625 return len;
2626 }
2627
2628 static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
2629 {
2630 int sock, qmax, rssi, tries, count = -1, ready = 0;
2631 char *pos, *line, *bssid, *freq, *signal, *flags, *ssid, reply[4096];
2632 struct sockaddr_un local = { 0 };
2633 struct iwinfo_scanlist_entry *e = (struct iwinfo_scanlist_entry *)buf;
2634
2635 sock = nl80211_wpactl_connect(ifname, &local);
2636
2637 if (sock < 0)
2638 return sock;
2639
2640 send(sock, "ATTACH", 6, 0);
2641 send(sock, "SCAN", 4, 0);
2642
2643 /*
2644 * wait for scan results:
2645 * nl80211_wpactl_recv() will use a timeout of 256ms and we need to scan
2646 * 72 channels at most. We'll also receive two "OK" messages acknowledging
2647 * the "ATTACH" and "SCAN" commands and the driver might need a bit extra
2648 * time to process the results, so try 72 + 2 + 1 times.
2649 */
2650 for (tries = 0; tries < 75; tries++)
2651 {
2652 if (nl80211_wpactl_recv(sock, reply, sizeof(reply)) <= 0)
2653 continue;
2654
2655 /* got an event notification */
2656 if (reply[0] == '<')
2657 {
2658 /* scan results are ready */
2659 if (strstr(reply, "CTRL-EVENT-SCAN-RESULTS"))
2660 {
2661 /* send "SCAN_RESULTS" command */
2662 ready = (send(sock, "SCAN_RESULTS", 12, 0) == 12);
2663 break;
2664 }
2665
2666 /* is another unrelated event, retry */
2667 tries--;
2668 }
2669
2670 /* scanning already in progress, keep awaiting results */
2671 else if (!strcmp(reply, "FAIL-BUSY\n"))
2672 {
2673 tries--;
2674 }
2675
2676 /* another failure, abort */
2677 else if (!strncmp(reply, "FAIL-", 5))
2678 {
2679 break;
2680 }
2681 }
2682
2683 /* receive and parse scan results if the wait above didn't time out */
2684 while (ready && nl80211_wpactl_recv(sock, reply, sizeof(reply)) > 0)
2685 {
2686 /* received an event notification, receive again */
2687 if (reply[0] == '<')
2688 continue;
2689
2690 nl80211_get_quality_max(ifname, &qmax);
2691
2692 for (line = strtok_r(reply, "\n", &pos);
2693 line != NULL;
2694 line = strtok_r(NULL, "\n", &pos))
2695 {
2696 /* skip header line */
2697 if (count < 0)
2698 {
2699 count++;
2700 continue;
2701 }
2702
2703 bssid = strtok(line, "\t");
2704 freq = strtok(NULL, "\t");
2705 signal = strtok(NULL, "\t");
2706 flags = strtok(NULL, "\t");
2707 ssid = strtok(NULL, "\n");
2708
2709 if (!bssid || !freq || !signal || !flags)
2710 continue;
2711
2712 /* BSSID */
2713 e->mac[0] = strtol(&bssid[0], NULL, 16);
2714 e->mac[1] = strtol(&bssid[3], NULL, 16);
2715 e->mac[2] = strtol(&bssid[6], NULL, 16);
2716 e->mac[3] = strtol(&bssid[9], NULL, 16);
2717 e->mac[4] = strtol(&bssid[12], NULL, 16);
2718 e->mac[5] = strtol(&bssid[15], NULL, 16);
2719
2720 /* SSID */
2721 if (ssid)
2722 wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
2723 else
2724 e->ssid[0] = 0;
2725
2726 /* Mode */
2727 if (strstr(flags, "[MESH]"))
2728 e->mode = IWINFO_OPMODE_MESHPOINT;
2729 else if (strstr(flags, "[IBSS]"))
2730 e->mode = IWINFO_OPMODE_ADHOC;
2731 else
2732 e->mode = IWINFO_OPMODE_MASTER;
2733
2734 /* Channel */
2735 e->channel = nl80211_freq2channel(atoi(freq));
2736
2737 /* Signal */
2738 rssi = atoi(signal);
2739 e->signal = rssi;
2740
2741 /* Quality */
2742 if (rssi < 0)
2743 {
2744 /* The cfg80211 wext compat layer assumes a signal range
2745 * of -110 dBm to -40 dBm, the quality value is derived
2746 * by adding 110 to the signal level */
2747 if (rssi < -110)
2748 rssi = -110;
2749 else if (rssi > -40)
2750 rssi = -40;
2751
2752 e->quality = (rssi + 110);
2753 }
2754 else
2755 {
2756 e->quality = rssi;
2757 }
2758
2759 /* Max. Quality */
2760 e->quality_max = qmax;
2761
2762 /* Crypto */
2763 nl80211_get_scancrypto(flags, &e->crypto);
2764
2765 count++;
2766 e++;
2767 }
2768
2769 *len = count * sizeof(struct iwinfo_scanlist_entry);
2770 break;
2771 }
2772
2773 close(sock);
2774 unlink(local.sun_path);
2775
2776 return (count >= 0) ? 0 : -1;
2777 }
2778
2779 static int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
2780 {
2781 char *res;
2782 int rv, mode;
2783
2784 *len = 0;
2785
2786 /* Got a radioX pseudo interface, find some interface on it or create one */
2787 if (!strncmp(ifname, "radio", 5))
2788 {
2789 /* Reuse existing interface */
2790 if ((res = nl80211_phy2ifname(ifname)) != NULL)
2791 {
2792 return nl80211_get_scanlist(res, buf, len);
2793 }
2794
2795 /* Need to spawn a temporary iface for scanning */
2796 else if ((res = nl80211_ifadd(ifname)) != NULL)
2797 {
2798 rv = nl80211_get_scanlist(res, buf, len);
2799 nl80211_ifdel(res);
2800 return rv;
2801 }
2802 }
2803
2804 /* WPA supplicant */
2805 if (!nl80211_get_scanlist_wpactl(ifname, buf, len))
2806 {
2807 return 0;
2808 }
2809
2810 /* station / ad-hoc / monitor scan */
2811 else if (!nl80211_get_mode(ifname, &mode) &&
2812 (mode == IWINFO_OPMODE_ADHOC ||
2813 mode == IWINFO_OPMODE_MASTER ||
2814 mode == IWINFO_OPMODE_CLIENT ||
2815 mode == IWINFO_OPMODE_MONITOR) &&
2816 iwinfo_ifup(ifname))
2817 {
2818 return nl80211_get_scanlist_nl(ifname, buf, len);
2819 }
2820
2821 /* AP scan */
2822 else
2823 {
2824 /* Got a temp interface, don't create yet another one */
2825 if (!strncmp(ifname, "tmp.", 4))
2826 {
2827 if (!iwinfo_ifup(ifname))
2828 return -1;
2829
2830 rv = nl80211_get_scanlist_nl(ifname, buf, len);
2831 iwinfo_ifdown(ifname);
2832 return rv;
2833 }
2834
2835 /* Spawn a new scan interface */
2836 else
2837 {
2838 if (!(res = nl80211_ifadd(ifname)))
2839 return -1;
2840
2841 iwinfo_ifmac(res);
2842
2843 /* if we can take the new interface up, the driver supports an
2844 * additional interface and there's no need to tear down the ap */
2845 if (iwinfo_ifup(res))
2846 {
2847 rv = nl80211_get_scanlist_nl(res, buf, len);
2848 iwinfo_ifdown(res);
2849 }
2850
2851 /* driver cannot create secondary interface, take down ap
2852 * during scan */
2853 else if (iwinfo_ifdown(ifname) && iwinfo_ifup(res))
2854 {
2855 rv = nl80211_get_scanlist_nl(res, buf, len);
2856 iwinfo_ifdown(res);
2857 iwinfo_ifup(ifname);
2858 nl80211_hostapd_hup(ifname);
2859 }
2860
2861 nl80211_ifdel(res);
2862 return rv;
2863 }
2864 }
2865
2866 return -1;
2867 }
2868
2869 static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
2870 {
2871 int bands_remain, freqs_remain;
2872
2873 struct nl80211_array_buf *arr = arg;
2874 struct iwinfo_freqlist_entry *e;
2875
2876 struct nlattr **attr = nl80211_parse(msg);
2877 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
2878 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
2879 struct nlattr *band, *freq;
2880
2881 e = arr->buf;
2882 e += arr->count;
2883
2884 if (attr[NL80211_ATTR_WIPHY_BANDS]) {
2885 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
2886 {
2887 nla_parse(bands, NL80211_BAND_ATTR_MAX,
2888 nla_data(band), nla_len(band), NULL);
2889
2890 if (bands[NL80211_BAND_ATTR_FREQS]) {
2891 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
2892 {
2893 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
2894 nla_data(freq), nla_len(freq), NULL);
2895
2896 if (!freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
2897 freqs[NL80211_FREQUENCY_ATTR_DISABLED])
2898 continue;
2899
2900 e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
2901 e->channel = nl80211_freq2channel(e->mhz);
2902
2903 e->restricted = (
2904 freqs[NL80211_FREQUENCY_ATTR_NO_IR] &&
2905 !freqs[NL80211_FREQUENCY_ATTR_RADAR]
2906 ) ? 1 : 0;
2907
2908 if (freqs[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
2909 e->flags |= IWINFO_FREQ_NO_HT40MINUS;
2910 if (freqs[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
2911 e->flags |= IWINFO_FREQ_NO_HT40PLUS;
2912 if (freqs[NL80211_FREQUENCY_ATTR_NO_80MHZ])
2913 e->flags |= IWINFO_FREQ_NO_80MHZ;
2914 if (freqs[NL80211_FREQUENCY_ATTR_NO_160MHZ])
2915 e->flags |= IWINFO_FREQ_NO_160MHZ;
2916 if (freqs[NL80211_FREQUENCY_ATTR_NO_20MHZ])
2917 e->flags |= IWINFO_FREQ_NO_20MHZ;
2918 if (freqs[NL80211_FREQUENCY_ATTR_NO_10MHZ])
2919 e->flags |= IWINFO_FREQ_NO_10MHZ;
2920
2921 e++;
2922 arr->count++;
2923 }
2924 }
2925 }
2926 }
2927
2928 return NL_SKIP;
2929 }
2930
2931 static int nl80211_get_freqlist(const char *ifname, char *buf, int *len)
2932 {
2933 struct nl80211_msg_conveyor *cv;
2934 struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
2935 uint32_t features = nl80211_get_protocol_features(ifname);
2936 int flags;
2937
2938 flags = features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP ? NLM_F_DUMP : 0;
2939 cv = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, flags);
2940 if (!cv)
2941 goto out;
2942
2943 NLA_PUT_FLAG(cv->msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
2944 if (nl80211_send(cv, nl80211_get_freqlist_cb, &arr))
2945 goto out;
2946
2947 *len = arr.count * sizeof(struct iwinfo_freqlist_entry);
2948 return 0;
2949
2950 nla_put_failure:
2951 nl80211_free(cv);
2952 out:
2953 *len = 0;
2954 return -1;
2955 }
2956
2957 static int nl80211_get_country_cb(struct nl_msg *msg, void *arg)
2958 {
2959 char *buf = arg;
2960 struct nlattr **attr = nl80211_parse(msg);
2961
2962 if (attr[NL80211_ATTR_REG_ALPHA2])
2963 memcpy(buf, nla_data(attr[NL80211_ATTR_REG_ALPHA2]), 2);
2964 else
2965 buf[0] = 0;
2966
2967 return NL_SKIP;
2968 }
2969
2970 static int nl80211_get_country(const char *ifname, char *buf)
2971 {
2972 if (nl80211_request(ifname, NL80211_CMD_GET_REG, 0,
2973 nl80211_get_country_cb, buf))
2974 return -1;
2975
2976 return 0;
2977 }
2978
2979 static int nl80211_get_countrylist(const char *ifname, char *buf, int *len)
2980 {
2981 int count;
2982 struct iwinfo_country_entry *e = (struct iwinfo_country_entry *)buf;
2983 const struct iwinfo_iso3166_label *l;
2984
2985 for (l = IWINFO_ISO3166_NAMES, count = 0; l->iso3166; l++, e++, count++)
2986 {
2987 e->iso3166 = l->iso3166;
2988 e->ccode[0] = (l->iso3166 / 256);
2989 e->ccode[1] = (l->iso3166 % 256);
2990 e->ccode[2] = 0;
2991 }
2992
2993 *len = (count * sizeof(struct iwinfo_country_entry));
2994 return 0;
2995 }
2996
2997
2998 struct nl80211_modes
2999 {
3000 bool ok;
3001 uint32_t hw;
3002 uint32_t ht;
3003
3004 uint32_t nl_freq;
3005 uint16_t nl_ht;
3006 uint32_t nl_vht;
3007 uint16_t he_phy_cap[6];
3008 };
3009
3010 static int nl80211_eval_modelist(struct nl80211_modes *m)
3011 {
3012 /* Treat any nonzero capability as 11n */
3013 if (m->nl_ht > 0)
3014 {
3015 m->hw |= IWINFO_80211_N;
3016 m->ht |= IWINFO_HTMODE_HT20;
3017
3018 if (m->nl_ht & (1 << 1))
3019 m->ht |= IWINFO_HTMODE_HT40;
3020 }
3021
3022 if (m->he_phy_cap[0] != 0) {
3023 m->hw |= IWINFO_80211_AX;
3024 m->ht |= IWINFO_HTMODE_HE20;
3025
3026 if (m->he_phy_cap[0] & BIT(9))
3027 m->ht |= IWINFO_HTMODE_HE40;
3028 if (m->he_phy_cap[0] & BIT(10))
3029 m->ht |= IWINFO_HTMODE_HE40 | IWINFO_HTMODE_HE80;
3030 if (m->he_phy_cap[0] & BIT(11))
3031 m->ht |= IWINFO_HTMODE_HE160;
3032 if (m->he_phy_cap[0] & BIT(12))
3033 m->ht |= IWINFO_HTMODE_HE160 | IWINFO_HTMODE_HE80_80;
3034 }
3035
3036 if (m->nl_freq < 2485)
3037 {
3038 m->hw |= IWINFO_80211_B;
3039 m->hw |= IWINFO_80211_G;
3040 }
3041 else if (m->nl_vht)
3042 {
3043 /* Treat any nonzero capability as 11ac */
3044 if (m->nl_vht > 0)
3045 {
3046 m->hw |= IWINFO_80211_AC;
3047 m->ht |= IWINFO_HTMODE_VHT20 | IWINFO_HTMODE_VHT40 | IWINFO_HTMODE_VHT80;
3048
3049 switch ((m->nl_vht >> 2) & 3)
3050 {
3051 case 2:
3052 m->ht |= IWINFO_HTMODE_VHT80_80;
3053 /* fall through */
3054
3055 case 1:
3056 m->ht |= IWINFO_HTMODE_VHT160;
3057 }
3058 }
3059 }
3060 else if (m->nl_freq >= 56160)
3061 {
3062 m->hw |= IWINFO_80211_AD;
3063 }
3064 else if (!(m->hw & IWINFO_80211_AC))
3065 {
3066 m->hw |= IWINFO_80211_A;
3067 }
3068 }
3069
3070 static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
3071 {
3072 struct nl80211_modes *m = arg;
3073 int bands_remain, freqs_remain;
3074 uint16_t caps = 0;
3075 uint32_t vht_caps = 0;
3076 struct nlattr **attr = nl80211_parse(msg);
3077 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
3078 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
3079 struct nlattr *band, *freq;
3080
3081 if (attr[NL80211_ATTR_WIPHY_BANDS])
3082 {
3083 nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
3084 {
3085 nla_parse(bands, NL80211_BAND_ATTR_MAX,
3086 nla_data(band), nla_len(band), NULL);
3087
3088 if (bands[NL80211_BAND_ATTR_HT_CAPA])
3089 m->nl_ht = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]);
3090
3091 if (bands[NL80211_BAND_ATTR_VHT_CAPA])
3092 m->nl_vht = nla_get_u32(bands[NL80211_BAND_ATTR_VHT_CAPA]);
3093
3094 if (bands[NL80211_BAND_ATTR_IFTYPE_DATA]) {
3095 struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
3096 struct nlattr *nl_iftype;
3097 int rem_band;
3098 int len;
3099
3100 nla_for_each_nested(nl_iftype, bands[NL80211_BAND_ATTR_IFTYPE_DATA], rem_band) {
3101 nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
3102 nla_data(nl_iftype), nla_len(nl_iftype), NULL);
3103 if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
3104 len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
3105
3106 if (len > sizeof(m->he_phy_cap) - 1)
3107 len = sizeof(m->he_phy_cap) - 1;
3108 memcpy(&((__u8 *)m->he_phy_cap)[1],
3109 nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
3110 len);
3111 }
3112 }
3113 }
3114
3115 if (bands[NL80211_BAND_ATTR_FREQS]) {
3116 nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS],
3117 freqs_remain)
3118 {
3119 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
3120 nla_data(freq), nla_len(freq), NULL);
3121
3122 if (!freqs[NL80211_FREQUENCY_ATTR_FREQ])
3123 continue;
3124
3125 m->nl_freq = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
3126 }
3127 }
3128 }
3129
3130 m->ok = 1;
3131 }
3132
3133 return NL_SKIP;
3134 }
3135
3136 static int nl80211_get_hwmodelist(const char *ifname, int *buf)
3137 {
3138 struct nl80211_msg_conveyor *cv;
3139 struct nl80211_modes m = {};
3140 uint32_t features = nl80211_get_protocol_features(ifname);
3141 int flags;
3142
3143 flags = features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP ? NLM_F_DUMP : 0;
3144 cv = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, flags);
3145 if (!cv)
3146 goto out;
3147
3148 NLA_PUT_FLAG(cv->msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
3149 if (nl80211_send(cv, nl80211_get_modelist_cb, &m))
3150 goto nla_put_failure;
3151
3152 nl80211_eval_modelist(&m);
3153
3154 *buf = m.hw;
3155
3156 return 0;
3157
3158 nla_put_failure:
3159 nl80211_free(cv);
3160 out:
3161 return -1;
3162 }
3163
3164 struct chan_info {
3165 int width;
3166 int mode;
3167 };
3168
3169 static int nl80211_get_htmode_cb(struct nl_msg *msg, void *arg)
3170 {
3171 struct nlattr **tb = nl80211_parse(msg);
3172 struct nlattr *cur;
3173 struct chan_info *chn = arg;
3174
3175 if ((cur = tb[NL80211_ATTR_CHANNEL_WIDTH]))
3176 chn->width = nla_get_u32(cur);
3177
3178 if ((cur = tb[NL80211_ATTR_BSS_HT_OPMODE]))
3179 chn->mode = nla_get_u32(cur);
3180
3181 return NL_SKIP;
3182 }
3183
3184 static int nl80211_get_htmode(const char *ifname, int *buf)
3185 {
3186 struct chan_info chn = { .width = 0, .mode = 0 };
3187 char *res;
3188 int err;
3189
3190 res = nl80211_phy2ifname(ifname);
3191 *buf = 0;
3192
3193 err = nl80211_request(res ? res : ifname,
3194 NL80211_CMD_GET_INTERFACE, 0,
3195 nl80211_get_htmode_cb, &chn);
3196 if (err)
3197 return -1;
3198
3199 switch (chn.width) {
3200 case NL80211_CHAN_WIDTH_20:
3201 if (chn.mode == -1)
3202 *buf = IWINFO_HTMODE_VHT20;
3203 else
3204 *buf = IWINFO_HTMODE_HT20;
3205 break;
3206 case NL80211_CHAN_WIDTH_40:
3207 if (chn.mode == -1)
3208 *buf = IWINFO_HTMODE_VHT40;
3209 else
3210 *buf = IWINFO_HTMODE_HT40;
3211 break;
3212 case NL80211_CHAN_WIDTH_80:
3213 *buf = IWINFO_HTMODE_VHT80;
3214 break;
3215 case NL80211_CHAN_WIDTH_80P80:
3216 *buf = IWINFO_HTMODE_VHT80_80;
3217 break;
3218 case NL80211_CHAN_WIDTH_160:
3219 *buf = IWINFO_HTMODE_VHT160;
3220 break;
3221 case NL80211_CHAN_WIDTH_5:
3222 case NL80211_CHAN_WIDTH_10:
3223 case NL80211_CHAN_WIDTH_20_NOHT:
3224 *buf = IWINFO_HTMODE_NOHT;
3225 break;
3226 default:
3227 return -1;
3228 }
3229
3230 return 0;
3231 }
3232
3233 static int nl80211_get_htmodelist(const char *ifname, int *buf)
3234 {
3235 struct nl80211_msg_conveyor *cv;
3236 struct nl80211_modes m = {};
3237 uint32_t features = nl80211_get_protocol_features(ifname);
3238 int flags;
3239
3240 flags = features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP ? NLM_F_DUMP : 0;
3241 cv = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, flags);
3242 if (!cv)
3243 goto out;
3244
3245 NLA_PUT_FLAG(cv->msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
3246 if (nl80211_send(cv, nl80211_get_modelist_cb, &m))
3247 goto nla_put_failure;
3248
3249 nl80211_eval_modelist(&m);
3250
3251 *buf = m.ht;
3252
3253 return 0;
3254
3255 nla_put_failure:
3256 nl80211_free(cv);
3257 out:
3258 return -1;
3259 }
3260
3261
3262 static int nl80211_get_ifcomb_cb(struct nl_msg *msg, void *arg)
3263 {
3264 struct nlattr **attr = nl80211_parse(msg);
3265 struct nlattr *comb;
3266 int *ret = arg;
3267 int comb_rem, limit_rem, mode_rem;
3268
3269 *ret = 0;
3270 if (!attr[NL80211_ATTR_INTERFACE_COMBINATIONS])
3271 return NL_SKIP;
3272
3273 nla_for_each_nested(comb, attr[NL80211_ATTR_INTERFACE_COMBINATIONS], comb_rem)
3274 {
3275 static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
3276 [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
3277 [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
3278 };
3279 struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB+1];
3280 static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
3281 [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
3282 [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
3283 };
3284 struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT+1];
3285 struct nlattr *limit;
3286
3287 nla_parse_nested(tb_comb, NUM_NL80211_IFACE_COMB, comb, iface_combination_policy);
3288
3289 if (!tb_comb[NL80211_IFACE_COMB_LIMITS])
3290 continue;
3291
3292 nla_for_each_nested(limit, tb_comb[NL80211_IFACE_COMB_LIMITS], limit_rem)
3293 {
3294 struct nlattr *mode;
3295
3296 nla_parse_nested(tb_limit, NUM_NL80211_IFACE_LIMIT, limit, iface_limit_policy);
3297
3298 if (!tb_limit[NL80211_IFACE_LIMIT_TYPES] ||
3299 !tb_limit[NL80211_IFACE_LIMIT_MAX])
3300 continue;
3301
3302 if (nla_get_u32(tb_limit[NL80211_IFACE_LIMIT_MAX]) < 2)
3303 continue;
3304
3305 nla_for_each_nested(mode, tb_limit[NL80211_IFACE_LIMIT_TYPES], mode_rem) {
3306 if (nla_type(mode) == NL80211_IFTYPE_AP)
3307 *ret = 1;
3308 }
3309 }
3310 }
3311
3312 return NL_SKIP;
3313 }
3314
3315 static int nl80211_get_mbssid_support(const char *ifname, int *buf)
3316 {
3317 if (nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
3318 nl80211_get_ifcomb_cb, buf))
3319 return -1;
3320
3321 return 0;
3322 }
3323
3324 static int nl80211_hardware_id_from_fdt(struct iwinfo_hardware_id *id, const char *ifname)
3325 {
3326 char *phy, compat[64], path[PATH_MAX];
3327 int i;
3328
3329 /* Try to determine the phy name from the given interface */
3330 phy = nl80211_ifname2phy(ifname);
3331
3332 snprintf(path, sizeof(path), "/sys/class/%s/%s/device/of_node/compatible",
3333 phy ? "ieee80211" : "net", phy ? phy : ifname);
3334
3335 if (nl80211_readstr(path, compat, sizeof(compat)) <= 0)
3336 return -1;
3337
3338 if (!strcmp(compat, "qca,ar9130-wmac")) {
3339 id->vendor_id = 0x168c;
3340 id->device_id = 0x0029;
3341 id->subsystem_vendor_id = 0x168c;
3342 id->subsystem_device_id = 0x9130;
3343 } else if (!strcmp(compat, "qca,ar9330-wmac")) {
3344 id->vendor_id = 0x168c;
3345 id->device_id = 0x0030;
3346 id->subsystem_vendor_id = 0x168c;
3347 id->subsystem_device_id = 0x9330;
3348 } else if (!strcmp(compat, "qca,ar9340-wmac")) {
3349 id->vendor_id = 0x168c;
3350 id->device_id = 0x0030;
3351 id->subsystem_vendor_id = 0x168c;
3352 id->subsystem_device_id = 0x9340;
3353 } else if (!strcmp(compat, "qca,qca9530-wmac")) {
3354 id->vendor_id = 0x168c;
3355 id->device_id = 0x0033;
3356 id->subsystem_vendor_id = 0x168c;
3357 id->subsystem_device_id = 0x9530;
3358 } else if (!strcmp(compat, "qca,qca9550-wmac")) {
3359 id->vendor_id = 0x168c;
3360 id->device_id = 0x0033;
3361 id->subsystem_vendor_id = 0x168c;
3362 id->subsystem_device_id = 0x9550;
3363 } else if (!strcmp(compat, "qca,qca9560-wmac")) {
3364 id->vendor_id = 0x168c;
3365 id->device_id = 0x0033;
3366 id->subsystem_vendor_id = 0x168c;
3367 id->subsystem_device_id = 0x9560;
3368 } else if (!strcmp(compat, "qcom,ipq4019-wifi")) {
3369 id->vendor_id = 0x168c;
3370 id->device_id = 0x003c;
3371 id->subsystem_vendor_id = 0x168c;
3372 id->subsystem_device_id = 0x4019;
3373 } else if (!strcmp(compat, "mediatek,mt7622-wmac")) {
3374 id->vendor_id = 0x14c3;
3375 id->device_id = 0x7622;
3376 id->subsystem_vendor_id = 0x14c3;
3377 id->subsystem_device_id = 0x7622;
3378 }
3379 return (id->vendor_id && id->device_id) ? 0 : -1;
3380 }
3381
3382
3383 static int nl80211_get_hardware_id(const char *ifname, char *buf)
3384 {
3385 struct iwinfo_hardware_id *id = (struct iwinfo_hardware_id *)buf;
3386 char *phy, num[8], path[PATH_MAX];
3387 int i;
3388
3389 struct { const char *path; uint16_t *dest; } lookup[] = {
3390 { "vendor", &id->vendor_id },
3391 { "device", &id->device_id },
3392 { "subsystem_vendor", &id->subsystem_vendor_id },
3393 { "subsystem_device", &id->subsystem_device_id }
3394 };
3395
3396 memset(id, 0, sizeof(*id));
3397
3398 /* Try to determine the phy name from the given interface */
3399 phy = nl80211_ifname2phy(ifname);
3400
3401 for (i = 0; i < ARRAY_SIZE(lookup); i++)
3402 {
3403 snprintf(path, sizeof(path), "/sys/class/%s/%s/device/%s",
3404 phy ? "ieee80211" : "net",
3405 phy ? phy : ifname, lookup[i].path);
3406
3407 if (nl80211_readstr(path, num, sizeof(num)) > 0)
3408 *lookup[i].dest = strtoul(num, NULL, 16);
3409 }
3410
3411 /* Failed to obtain hardware IDs, try FDT */
3412 if (id->vendor_id == 0 || id->device_id == 0)
3413 if (!nl80211_hardware_id_from_fdt(id, ifname))
3414 return 0;
3415
3416 /* Failed to obtain hardware IDs, search board config */
3417 if (id->vendor_id == 0 || id->device_id == 0)
3418 return iwinfo_hardware_id_from_mtd(id);
3419
3420 return 0;
3421 }
3422
3423 static const struct iwinfo_hardware_entry *
3424 nl80211_get_hardware_entry(const char *ifname)
3425 {
3426 struct iwinfo_hardware_id id;
3427
3428 if (nl80211_get_hardware_id(ifname, (char *)&id))
3429 return NULL;
3430
3431 return iwinfo_hardware(&id);
3432 }
3433
3434 static int nl80211_get_hardware_name(const char *ifname, char *buf)
3435 {
3436 const struct iwinfo_hardware_entry *hw;
3437
3438 if (!(hw = nl80211_get_hardware_entry(ifname)))
3439 sprintf(buf, "Generic MAC80211");
3440 else
3441 sprintf(buf, "%s %s", hw->vendor_name, hw->device_name);
3442
3443 return 0;
3444 }
3445
3446 static int nl80211_get_txpower_offset(const char *ifname, int *buf)
3447 {
3448 const struct iwinfo_hardware_entry *hw;
3449
3450 if (!(hw = nl80211_get_hardware_entry(ifname)))
3451 return -1;
3452
3453 *buf = hw->txpower_offset;
3454 return 0;
3455 }
3456
3457 static int nl80211_get_frequency_offset(const char *ifname, int *buf)
3458 {
3459 const struct iwinfo_hardware_entry *hw;
3460
3461 if (!(hw = nl80211_get_hardware_entry(ifname)))
3462 return -1;
3463
3464 *buf = hw->frequency_offset;
3465 return 0;
3466 }
3467
3468 static int nl80211_lookup_phyname(const char *section, char *buf)
3469 {
3470 int idx;
3471
3472 if ((idx = nl80211_phy_idx_from_uci(section)) < 0)
3473 return -1;
3474
3475 sprintf(buf, "phy%d", idx);
3476 return 0;
3477 }
3478
3479 static int nl80211_phy_path(const char *phyname, const char **path)
3480 {
3481 if (strncmp(phyname, "phy", 3) != 0)
3482 return -1;
3483
3484 if (strchr(phyname, '/'))
3485 return -1;
3486
3487 *path = nl80211_phy_path_str(phyname);
3488 if (!*path)
3489 return -1;
3490
3491 return 0;
3492 }
3493
3494 const struct iwinfo_ops nl80211_ops = {
3495 .name = "nl80211",
3496 .probe = nl80211_probe,
3497 .channel = nl80211_get_channel,
3498 .center_chan1 = nl80211_get_center_chan1,
3499 .center_chan2 = nl80211_get_center_chan2,
3500 .frequency = nl80211_get_frequency,
3501 .frequency_offset = nl80211_get_frequency_offset,
3502 .txpower = nl80211_get_txpower,
3503 .txpower_offset = nl80211_get_txpower_offset,
3504 .bitrate = nl80211_get_bitrate,
3505 .signal = nl80211_get_signal,
3506 .noise = nl80211_get_noise,
3507 .quality = nl80211_get_quality,
3508 .quality_max = nl80211_get_quality_max,
3509 .mbssid_support = nl80211_get_mbssid_support,
3510 .hwmodelist = nl80211_get_hwmodelist,
3511 .htmodelist = nl80211_get_htmodelist,
3512 .htmode = nl80211_get_htmode,
3513 .mode = nl80211_get_mode,
3514 .ssid = nl80211_get_ssid,
3515 .bssid = nl80211_get_bssid,
3516 .country = nl80211_get_country,
3517 .hardware_id = nl80211_get_hardware_id,
3518 .hardware_name = nl80211_get_hardware_name,
3519 .encryption = nl80211_get_encryption,
3520 .phyname = nl80211_get_phyname,
3521 .assoclist = nl80211_get_assoclist,
3522 .txpwrlist = nl80211_get_txpwrlist,
3523 .scanlist = nl80211_get_scanlist,
3524 .freqlist = nl80211_get_freqlist,
3525 .countrylist = nl80211_get_countrylist,
3526 .survey = nl80211_get_survey,
3527 .lookup_phy = nl80211_lookup_phyname,
3528 .phy_path = nl80211_phy_path,
3529 .close = nl80211_close
3530 };