Root/package/iwinfo/src/iwinfo_nl80211.c

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

Archive Download this file



interactive