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