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