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

Archive Download this file



interactive