aca2be233ee286497fb9295084f540862899703c
[project/luci.git] / contrib / package / iwinfo / src / iwinfo_nl80211.c
1 /*
2  * iwinfo - Wireless Information Library - NL80211 Backend
3  *
4  *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  * The iwinfo library is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * The iwinfo library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with the iwinfo library. If not, see http://www.gnu.org/licenses/.
17  *
18  * The signal handling code is derived from the official madwifi tools,
19  * wlanconfig.c in particular. The encryption property handling was
20  * inspired by the hostapd madwifi driver.
21  *
22  * Parts of this code are derived from the Linux iw utility.
23  */
24
25 #include "iwinfo_nl80211.h"
26 #include "iwinfo_wext.h"
27
28 #define min(x, y) ((x) < (y)) ? (x) : (y)
29
30 extern struct iwinfo_iso3166_label ISO3166_Names[];
31 static struct nl80211_state *nls = NULL;
32
33 static int nl80211_init(void)
34 {
35         int err, fd;
36
37         if( !nls )
38         {
39                 nls = malloc(sizeof(struct nl80211_state));
40                 if( !nls ) {
41                         err = -ENOMEM;
42                         goto err;
43                 }
44
45                 nls->nl_sock = nl_socket_alloc();
46                 if( !nls->nl_sock ) {
47                         err = -ENOMEM;
48                         goto err;
49                 }
50
51                 if( genl_connect(nls->nl_sock)) {
52                         err = -ENOLINK;
53                         goto err;
54                 }
55
56                 fd = nl_socket_get_fd(nls->nl_sock);
57                 if( fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0 )
58                 {
59                         err = -EINVAL;
60                         goto err;
61                 }
62
63                 if( genl_ctrl_alloc_cache(nls->nl_sock, &nls->nl_cache)) {
64                         err = -ENOMEM;
65                         goto err;
66                 }
67
68                 nls->nl80211 = genl_ctrl_search_by_name(nls->nl_cache, "nl80211");
69                 if( !nls->nl80211 )
70                 {
71                         err = -ENOENT;
72                         goto err;
73                 }
74         }
75
76         return 0;
77
78
79 err:
80         nl80211_close();
81         return err;
82 }
83
84 static int nl80211_msg_error(struct sockaddr_nl *nla,
85         struct nlmsgerr *err, void *arg)
86 {
87         int *ret = arg;
88         *ret = err->error;
89         return NL_STOP;
90 }
91
92 static int nl80211_msg_finish(struct nl_msg *msg, void *arg)
93 {
94         int *ret = arg;
95         *ret = 0;
96         return NL_SKIP;
97 }
98
99 static int nl80211_msg_ack(struct nl_msg *msg, void *arg)
100 {
101         int *ret = arg;
102         *ret = 0;
103         return NL_STOP;
104 }
105
106 static int nl80211_msg_response(struct nl_msg *msg, void *arg)
107 {
108         struct nl80211_msg_conveyor *cv = arg;
109
110         nlmsg_get(msg);
111
112         cv->msg = msg;
113         cv->hdr = nlmsg_data(nlmsg_hdr(cv->msg));
114
115         nla_parse(cv->attr, NL80211_ATTR_MAX,
116                 genlmsg_attrdata(cv->hdr, 0),
117                 genlmsg_attrlen(cv->hdr, 0), NULL);
118
119         return NL_SKIP;
120 }
121
122 static void nl80211_free(struct nl80211_msg_conveyor *cv)
123 {
124         if( cv )
125         {
126                 if( cv->cb )
127                         nl_cb_put(cv->cb);
128
129                 if( cv->msg )
130                         nlmsg_free(cv->msg);
131
132                 cv->cb  = NULL;
133                 cv->msg = NULL;
134         }
135 }
136
137 static struct nl80211_msg_conveyor * nl80211_msg(const char *ifname, int cmd, int flags)
138 {
139         static struct nl80211_msg_conveyor cv;
140
141         int ifidx = -1, phyidx = -1;
142         struct nl_msg *req = NULL;
143         struct nl_cb *cb = NULL;
144
145         if( nl80211_init() < 0 )
146                 goto err;
147
148         if( !strncmp(ifname, "phy", 3) )
149                 phyidx = atoi(&ifname[3]);
150         else if( !strncmp(ifname, "radio", 5) )
151                 phyidx = atoi(&ifname[5]);
152         else if( !strncmp(ifname, "mon.", 4) )
153                 ifidx = if_nametoindex(&ifname[4]);
154         else
155                 ifidx = if_nametoindex(ifname);
156
157         if( (ifidx < 0) && (phyidx < 0) )
158                 return NULL;
159
160         req = nlmsg_alloc();
161         if( !req )
162                 goto err;
163
164         cb = nl_cb_alloc(NL_CB_DEFAULT);
165         if( !cb )
166                 goto err;
167
168         genlmsg_put(req, 0, 0, genl_family_get_id(nls->nl80211), 0,
169                 flags, cmd, 0);
170
171         if( ifidx > -1 )
172                 NLA_PUT_U32(req, NL80211_ATTR_IFINDEX, ifidx);
173
174         if( phyidx > -1 )
175                 NLA_PUT_U32(req, NL80211_ATTR_WIPHY, phyidx);
176
177         nlmsg_get(req);
178
179         cv.msg       = req;
180         cv.cb        = cb;
181         cv.custom_cb = 0;
182
183         return &cv;
184
185 err:
186 nla_put_failure:
187         if( cb )
188                 nl_cb_put(cb);
189
190         if( req )
191                 nlmsg_free(req);
192
193         return NULL;
194 }
195
196 static void nl80211_cb(struct nl80211_msg_conveyor *cv,
197         int (*cb)(struct nl_msg *, void *), void *arg)
198 {
199         cv->custom_cb = 1;
200         nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, cb, arg);
201 }
202
203 static struct nl80211_msg_conveyor * nl80211_send(struct nl80211_msg_conveyor *cv)
204 {
205         static struct nl80211_msg_conveyor rcv;
206         int err = 1;
207
208         if( !cv->custom_cb )
209                 nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_msg_response, &rcv);
210
211         if( nl_send_auto_complete(nls->nl_sock, cv->msg) < 0 )
212                 goto err;
213
214         nl_cb_err(cv->cb,               NL_CB_CUSTOM, nl80211_msg_error,  &err);
215         nl_cb_set(cv->cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_msg_finish, &err);
216         nl_cb_set(cv->cb, NL_CB_ACK,    NL_CB_CUSTOM, nl80211_msg_ack,    &err);
217
218         while (err > 0)
219                 nl_recvmsgs(nls->nl_sock, cv->cb);
220
221         return &rcv;
222
223 err:
224         nl_cb_put(cv->cb);
225         nlmsg_free(cv->msg);
226
227         return NULL;
228 }
229
230 static int nl80211_freq2channel(int freq)
231 {
232     if (freq == 2484)
233         return 14;
234
235     if (freq < 2484)
236         return (freq - 2407) / 5;
237
238     return (freq / 5) - 1000;
239 }
240
241 static char * nl80211_getval(const char *ifname, const char *buf, const char *key)
242 {
243         int i, len;
244         char lkey[64] = { 0 };
245         const char *ln = buf;
246         static char lval[256] = { 0 };
247
248         int matched_if = ifname ? 0 : 1;
249
250
251         for( i = 0, len = strlen(buf); i < len; i++ )
252         {
253                 if( !lkey[0] && (buf[i] == ' ' || buf[i] == '\t') )
254                 {
255                         ln++;
256                 }
257                 else if( !lkey[0] && (buf[i] == '=') )
258                 {
259                         if( (&buf[i] - ln) > 0 )
260                                 memcpy(lkey, ln, min(sizeof(lkey) - 1, &buf[i] - ln));
261                 }
262                 else if( buf[i] == '\n' )
263                 {
264                         if( lkey[0] )
265                         {
266                                 memcpy(lval, ln + strlen(lkey) + 1,
267                                         min(sizeof(lval) - 1, &buf[i] - ln - strlen(lkey) - 1));
268
269                                 if( (ifname != NULL ) &&
270                                     (!strcmp(lkey, "interface") || !strcmp(lkey, "bss")) )
271                                 {
272                                         matched_if = !strcmp(lval, ifname);
273                                 }
274                                 else if( matched_if && !strcmp(lkey, key) )
275                                 {
276                                         return lval;
277                                 }
278                         }
279
280                         ln = &buf[i+1];
281                         memset(lkey, 0, sizeof(lkey));
282                         memset(lval, 0, sizeof(lval));
283                 }
284         }
285
286         return NULL;
287 }
288
289 static char * nl80211_ifname2phy(const char *ifname)
290 {
291         static char phy[32] = { 0 };
292         struct nl80211_msg_conveyor *req, *res;
293
294         req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
295         if( req )
296         {
297                 res = nl80211_send(req);
298                 if( res )
299                 {
300                         if( res->attr[NL80211_ATTR_WIPHY_NAME] )
301                         {
302                                 snprintf(phy, sizeof(phy), "%s",
303                                          nla_get_string(res->attr[NL80211_ATTR_WIPHY_NAME]));
304                         }
305                         nl80211_free(res);
306                 }
307                 nl80211_free(req);
308         }
309
310         return phy[0] ? phy : NULL;
311 }
312
313 static char * nl80211_hostapd_info(const char *ifname)
314 {
315         char *phy;
316         char path[32] = { 0 };
317         static char buf[4096] = { 0 };
318         FILE *conf;
319
320         if( (phy = nl80211_ifname2phy(ifname)) != NULL )
321         {
322                 snprintf(path, sizeof(path), "/var/run/hostapd-%s.conf", phy);
323
324                 if( (conf = fopen(path, "r")) != NULL )
325                 {
326                         fread(buf, sizeof(buf) - 1, 1, conf);
327                         fclose(conf);
328
329                         return buf;
330                 }
331         }
332
333         return NULL;
334 }
335
336 static inline int nl80211_wpactl_recv(int sock, char *buf, int blen)
337 {
338         fd_set rfds;
339         struct timeval tv = { 2, 0 };
340
341         FD_ZERO(&rfds);
342         FD_SET(sock, &rfds);
343
344         memset(buf, 0, blen);
345
346
347         if( select(sock + 1, &rfds, NULL, NULL, &tv) < 0 )
348                 return -1;
349
350         if( !FD_ISSET(sock, &rfds) )
351                 return -1;
352
353         return recv(sock, buf, blen, 0);
354 }
355
356 static char * nl80211_wpactl_info(const char *ifname, const char *cmd,
357                                                                    const char *event)
358 {
359         int sock = -1;
360         char *rv = NULL;
361         size_t remote_length, local_length;
362         static char buffer[10240] = { 0 };
363
364         struct sockaddr_un local = { 0 };
365         struct sockaddr_un remote = { 0 };
366
367
368         sock = socket(PF_UNIX, SOCK_DGRAM, 0);
369         if( sock < 0 )
370                 return NULL;
371
372         remote.sun_family = AF_UNIX;
373         remote_length = sizeof(remote.sun_family) + sprintf(remote.sun_path,
374                 "/var/run/wpa_supplicant-%s/%s", ifname, ifname);
375
376         if( fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC) < 0 )
377                 goto out;
378
379         if( connect(sock, (struct sockaddr *) &remote, remote_length) )
380                 goto out;
381
382         local.sun_family = AF_UNIX;
383         local_length = sizeof(local.sun_family) + sprintf(local.sun_path,
384                 "/var/run/iwinfo-%s-%d", ifname, getpid());
385
386         if( bind(sock, (struct sockaddr *) &local, local_length) )
387                 goto out;
388
389
390         send(sock, "ATTACH", 6, 0);
391
392         if( nl80211_wpactl_recv(sock, buffer, sizeof(buffer)) <= 0 )
393                 goto out;
394
395
396         send(sock, cmd, strlen(cmd), 0);
397
398         while( 1 )
399         {
400                 if( nl80211_wpactl_recv(sock, buffer, sizeof(buffer)) <= 0 )
401                 {
402                         if( event )
403                                 continue;
404
405                         break;
406                 }
407
408                 if( (!event && buffer[0] != '<') || strstr(buffer, event) )
409                         break;
410         }
411
412         rv = buffer;
413
414 out:
415         close(sock);
416
417         if( local.sun_family )
418                 unlink(local.sun_path);
419
420         return rv;
421 }
422
423 static inline int nl80211_readint(const char *path)
424 {
425         int fd;
426         int rv = -1;
427         char buffer[16];
428
429         if( (fd = open(path, O_RDONLY)) > -1 )
430         {
431                 if( read(fd, buffer, sizeof(buffer)) > 0 )
432                         rv = atoi(buffer);
433
434                 close(fd);
435         }
436
437         return rv;
438 }
439
440 static char * nl80211_phy2ifname(const char *ifname)
441 {
442         int fd, ifidx = -1, cifidx = -1, phyidx = -1;
443         char buffer[64];
444         static char nif[IFNAMSIZ] = { 0 };
445
446         DIR *d;
447         struct dirent *e;
448
449         if( !ifname )
450                 return NULL;
451         else if( !strncmp(ifname, "phy", 3) )
452                 phyidx = atoi(&ifname[3]);
453         else if( !strncmp(ifname, "radio", 5) )
454                 phyidx = atoi(&ifname[5]);
455
456         if( phyidx > -1 )
457         {
458                 if( (d = opendir("/sys/class/net")) != NULL )
459                 {
460                         while( (e = readdir(d)) != NULL )
461                         {
462                                 snprintf(buffer, sizeof(buffer),
463                                         "/sys/class/net/%s/phy80211/index", e->d_name);
464
465                                 if( nl80211_readint(buffer) == phyidx )
466                                 {
467                                         snprintf(buffer, sizeof(buffer),
468                                                 "/sys/class/net/%s/ifindex", e->d_name);
469
470                                         if( (cifidx = nl80211_readint(buffer)) >= 0 &&
471                                             ((ifidx < 0) || (cifidx < ifidx)) )
472                                         {
473                                                 ifidx = cifidx;
474                                                 strncpy(nif, e->d_name, sizeof(nif));
475                                         }
476                                 }
477                         }
478
479                         closedir(d);
480                 }
481         }
482
483         return nif[0] ? nif : NULL;
484 }
485
486 static char * nl80211_ifadd(const char *ifname)
487 {
488         int phyidx;
489         char *rv = NULL;
490         static char nif[IFNAMSIZ] = { 0 };
491         struct nl80211_msg_conveyor *req, *res;
492
493         req = nl80211_msg(ifname, NL80211_CMD_NEW_INTERFACE, 0);
494         if( req )
495         {
496                 snprintf(nif, sizeof(nif), "tmp.%s", ifname);
497
498                 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, nif);
499                 NLA_PUT_U32(req->msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
500
501                 res = nl80211_send(req);
502                 if( res )
503                 {
504                         rv = nif;
505                         nl80211_free(res);
506                 }
507
508         nla_put_failure:
509                 nl80211_free(req);
510         }
511
512         return rv;
513 }
514
515 static void nl80211_ifdel(const char *ifname)
516 {
517         struct nl80211_msg_conveyor *req;
518
519         req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
520         if( req )
521         {
522                 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, ifname);
523
524                 nl80211_free(nl80211_send(req));
525
526         nla_put_failure:
527                 nl80211_free(req);
528         }
529 }
530
531 static void nl80211_hostapd_hup(const char *ifname)
532 {
533         int fd, pid = 0;
534         char buf[32];
535         char *phy = nl80211_ifname2phy(ifname);
536
537         if( phy )
538         {
539                 snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
540                 if( (fd = open(buf, O_RDONLY)) > 0 )
541                 {
542                         if( read(fd, buf, sizeof(buf)) > 0 )
543                                 pid = atoi(buf);
544
545                         close(fd);
546                 }
547
548                 if( pid > 0 )
549                         kill(pid, 1);
550         }
551 }
552
553
554 int nl80211_probe(const char *ifname)
555 {
556         return !!nl80211_ifname2phy(ifname);
557 }
558
559 void nl80211_close(void)
560 {
561         if( nls )
562         {
563                 if( nls->nl_sock )
564                         nl_socket_free(nls->nl_sock);
565
566                 if( nls->nl_cache )
567                         nl_cache_free(nls->nl_cache);
568
569                 free(nls);
570                 nls = NULL;
571         }
572 }
573
574 int nl80211_get_mode(const char *ifname, char *buf)
575 {
576         return wext_get_mode(ifname, buf);
577 }
578
579 int nl80211_get_ssid(const char *ifname, char *buf)
580 {
581         char *ssid;
582
583         if( !wext_get_ssid(ifname, buf) )
584         {
585                 return 0;
586         }
587         else if( (ssid = nl80211_hostapd_info(ifname)) &&
588                  (ssid = nl80211_getval(ifname, ssid, "ssid")) )
589         {
590                 memcpy(buf, ssid, strlen(ssid));
591                 return 0;
592         }
593
594         return -1;
595 }
596
597 int nl80211_get_bssid(const char *ifname, char *buf)
598 {
599         char *bssid;
600         unsigned char mac[6];
601
602         if( !wext_get_bssid(ifname, buf) )
603         {
604                 return 0;
605         }
606         else if( (bssid = nl80211_hostapd_info(ifname)) &&
607                  (bssid = nl80211_getval(ifname, bssid, "bssid")) )
608         {
609                 mac[0] = strtol(&bssid[0],  NULL, 16);
610                 mac[1] = strtol(&bssid[3],  NULL, 16);
611                 mac[2] = strtol(&bssid[6],  NULL, 16);
612                 mac[3] = strtol(&bssid[9],  NULL, 16);
613                 mac[4] = strtol(&bssid[12], NULL, 16);
614                 mac[5] = strtol(&bssid[15], NULL, 16);
615
616                 sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
617                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
618
619                 return 0;
620         }
621
622         return -1;
623 }
624
625 int nl80211_get_channel(const char *ifname, int *buf)
626 {
627         char *first;
628
629         if( !wext_get_channel(ifname, buf) )
630                 return 0;
631
632         else if( (first = nl80211_phy2ifname(nl80211_ifname2phy(ifname))) != NULL )
633                 return wext_get_channel(first, buf);
634
635         return -1;
636 }
637
638 int nl80211_get_frequency(const char *ifname, int *buf)
639 {
640         char *first;
641
642         if( !wext_get_frequency(ifname, buf) )
643                 return 0;
644
645         else if( (first = nl80211_phy2ifname(nl80211_ifname2phy(ifname))) != NULL )
646                 return wext_get_frequency(first, buf);
647
648         return -1;
649 }
650
651 int nl80211_get_txpower(const char *ifname, int *buf)
652 {
653         return wext_get_txpower(ifname, buf);
654 }
655
656
657 static int nl80211_get_signal_cb(struct nl_msg *msg, void *arg)
658 {
659         int8_t dbm;
660         int16_t mbit;
661         struct nl80211_rssi_rate *rr = arg;
662
663         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
664         struct nlattr *attr[NL80211_ATTR_MAX + 1];
665         struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
666         struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
667
668         static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
669                 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32    },
670                 [NL80211_STA_INFO_RX_BYTES]      = { .type = NLA_U32    },
671                 [NL80211_STA_INFO_TX_BYTES]      = { .type = NLA_U32    },
672                 [NL80211_STA_INFO_RX_PACKETS]    = { .type = NLA_U32    },
673                 [NL80211_STA_INFO_TX_PACKETS]    = { .type = NLA_U32    },
674                 [NL80211_STA_INFO_SIGNAL]        = { .type = NLA_U8     },
675                 [NL80211_STA_INFO_TX_BITRATE]    = { .type = NLA_NESTED },
676                 [NL80211_STA_INFO_LLID]          = { .type = NLA_U16    },
677                 [NL80211_STA_INFO_PLID]          = { .type = NLA_U16    },
678                 [NL80211_STA_INFO_PLINK_STATE]   = { .type = NLA_U8     },
679         };
680
681         static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
682                 [NL80211_RATE_INFO_BITRATE]      = { .type = NLA_U16  },
683                 [NL80211_RATE_INFO_MCS]          = { .type = NLA_U8   },
684                 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
685                 [NL80211_RATE_INFO_SHORT_GI]     = { .type = NLA_FLAG },
686         };
687
688         nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
689                   genlmsg_attrlen(gnlh, 0), NULL);
690
691         if( attr[NL80211_ATTR_STA_INFO] )
692         {
693                 if( !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
694                                 attr[NL80211_ATTR_STA_INFO], stats_policy) )
695                 {
696                         if( sinfo[NL80211_STA_INFO_SIGNAL] )
697                         {
698                                 dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
699                                 rr->rssi = rr->rssi ? (int8_t)((rr->rssi + dbm) / 2) : dbm;
700                         }
701
702                         if( sinfo[NL80211_STA_INFO_TX_BITRATE] )
703                         {
704                                 if( !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
705                                                 sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy) )
706                                 {
707                                         if( rinfo[NL80211_RATE_INFO_BITRATE] )
708                                         {
709                                                 mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
710                                                 rr->rate = rr->rate
711                                                         ? (int16_t)((rr->rate + mbit) / 2) : mbit;
712                                         }
713                                 }
714                         }
715                 }
716         }
717
718         return NL_SKIP;
719 }
720
721 int nl80211_get_bitrate(const char *ifname, int *buf)
722 {
723         struct nl80211_rssi_rate rr;
724         struct nl80211_msg_conveyor *req;
725
726         if( !wext_get_bitrate(ifname, buf) )
727                 return 0;
728
729         req = nl80211_msg(ifname, NL80211_CMD_GET_STATION, NLM_F_DUMP);
730         if( req )
731         {
732                 rr.rssi = 0;
733                 rr.rate = 0;
734
735                 nl80211_cb(req, nl80211_get_signal_cb, &rr);
736                 nl80211_send(req);
737                 nl80211_free(req);
738
739                 if( rr.rate )
740                 {
741                         *buf = (rr.rate * 100);
742                         return 0;
743                 }
744         }
745
746         return -1;
747 }
748
749 int nl80211_get_signal(const char *ifname, int *buf)
750 {
751         struct nl80211_rssi_rate rr;
752         struct nl80211_msg_conveyor *req;
753
754         if( !wext_get_signal(ifname, buf) )
755                 return 0;
756
757         req = nl80211_msg(ifname, NL80211_CMD_GET_STATION, NLM_F_DUMP);
758         if( req )
759         {
760                 rr.rssi = 0;
761                 rr.rate = 0;
762
763                 nl80211_cb(req, nl80211_get_signal_cb, &rr);
764                 nl80211_send(req);
765                 nl80211_free(req);
766
767                 if( rr.rssi )
768                 {
769                         *buf = rr.rssi;
770                         return 0;
771                 }
772         }
773
774         return -1;
775 }
776
777 static int nl80211_get_noise_cb(struct nl_msg *msg, void *arg)
778 {
779         int8_t *noise = arg;
780         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
781         struct nlattr *tb[NL80211_ATTR_MAX + 1];
782         struct nlattr *si[NL80211_SURVEY_INFO_MAX + 1];
783
784         static struct nla_policy sp[NL80211_SURVEY_INFO_MAX + 1] = {
785                 [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
786                 [NL80211_SURVEY_INFO_NOISE]     = { .type = NLA_U8  },
787         };
788
789         nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
790                 genlmsg_attrlen(gnlh, 0), NULL);
791
792         if (!tb[NL80211_ATTR_SURVEY_INFO])
793                 return NL_SKIP;
794
795         if (nla_parse_nested(si, NL80211_SURVEY_INFO_MAX,
796                                                  tb[NL80211_ATTR_SURVEY_INFO], sp))
797                 return NL_SKIP;
798
799         if (!si[NL80211_SURVEY_INFO_NOISE])
800                 return NL_SKIP;
801
802         if (!*noise || si[NL80211_SURVEY_INFO_IN_USE])
803                 *noise = (int8_t)nla_get_u8(si[NL80211_SURVEY_INFO_NOISE]);
804
805         return NL_SKIP;
806 }
807
808
809 int nl80211_get_noise(const char *ifname, int *buf)
810 {
811         int8_t noise;
812         struct nl80211_msg_conveyor *req;
813
814         req = nl80211_msg(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP);
815         if (req)
816         {
817                 noise = 0;
818
819                 nl80211_cb(req, nl80211_get_noise_cb, &noise);
820                 nl80211_send(req);
821                 nl80211_free(req);
822
823                 if (noise)
824                 {
825                         *buf = noise;
826                         return 0;
827                 }
828         }
829
830         return -1;
831 }
832
833 int nl80211_get_quality(const char *ifname, int *buf)
834 {
835         int signal;
836
837         if( wext_get_quality(ifname, buf) )
838         {
839                 *buf = 0;
840
841                 if( !nl80211_get_signal(ifname, &signal) )
842                 {
843                         /* A positive signal level is usually just a quality
844                          * value, pass through as-is */
845                         if( signal >= 0 )
846                         {
847                                 *buf = signal;
848                         }
849
850                         /* The cfg80211 wext compat layer assumes a signal range
851                          * of -110 dBm to -40 dBm, the quality value is derived
852                          * by adding 110 to the signal level */
853                         else
854                         {
855                                 if( signal < -110 )
856                                         signal = -110;
857                                 else if( signal > -40 )
858                                         signal = -40;
859
860                                 *buf = (signal + 110);
861                         }
862                 }
863         }
864
865         return 0;
866 }
867
868 int nl80211_get_quality_max(const char *ifname, int *buf)
869 {
870         if( wext_get_quality_max(ifname, buf) )
871                 /* The cfg80211 wext compat layer assumes a maximum
872                  * quality of 70 */
873                 *buf = 70;
874
875         return 0;
876 }
877
878 int nl80211_get_encryption(const char *ifname, char *buf)
879 {
880         int i;
881         char k[9];
882         char *val, *res;
883         struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
884
885         /* Hostapd */
886         if( (res = nl80211_hostapd_info(ifname)) )
887         {
888                 if( (val = nl80211_getval(ifname, res, "wpa")) != NULL )
889                         c->wpa_version = atoi(val);
890
891                 val = nl80211_getval(ifname, res, "wpa_key_mgmt");
892
893                 if( !val || strstr(val, "PSK") )
894                         c->auth_suites |= IWINFO_KMGMT_PSK;
895
896                 if( val && strstr(val, "EAP") )
897                         c->auth_suites |= IWINFO_KMGMT_8021x;
898
899                 if( val && strstr(val, "NONE") )
900                         c->auth_suites |= IWINFO_KMGMT_NONE;
901
902                 if( (val = nl80211_getval(ifname, res, "wpa_pairwise")) != NULL )
903                 {
904                         if( strstr(val, "TKIP") )
905                                 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
906
907                         if( strstr(val, "CCMP") )
908                                 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
909
910                         if( strstr(val, "NONE") )
911                                 c->pair_ciphers |= IWINFO_CIPHER_NONE;
912                 }
913
914                 if( (val = nl80211_getval(ifname, res, "auth_algs")) != NULL )
915                 {
916                         switch(atoi(val)) {
917                                 case 1:
918                                         c->auth_algs |= IWINFO_AUTH_OPEN;
919                                         break;
920
921                                 case 2:
922                                         c->auth_algs |= IWINFO_AUTH_SHARED;
923                                         break;
924
925                                 case 3:
926                                         c->auth_algs |= IWINFO_AUTH_OPEN;
927                                         c->auth_algs |= IWINFO_AUTH_SHARED;
928                                         break;
929
930                                 default:
931                                         break;
932                         }
933
934                         for( i = 0; i < 4; i++ )
935                         {
936                                 snprintf(k, sizeof(k), "wep_key%d", i);
937
938                                 if( (val = nl80211_getval(ifname, res, k)) )
939                                 {
940                                         if( (strlen(val) == 5) || (strlen(val) == 10) )
941                                                 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
942
943                                         else if( (strlen(val) == 13) || (strlen(val) == 26) )
944                                                 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
945                                 }
946                         }
947                 }
948
949                 c->group_ciphers = c->pair_ciphers;
950                 c->enabled = (c->auth_algs || c->auth_suites) ? 1 : 0;
951
952                 return 0;
953         }
954
955         /* WPA supplicant */
956         else if( (res = nl80211_wpactl_info(ifname, "STATUS", NULL)) &&
957                  (val = nl80211_getval(NULL, res, "pairwise_cipher")) )
958         {
959                 /* WEP */
960                 if( strstr(val, "WEP") )
961                 {
962                         if( strstr(val, "WEP-40") )
963                                 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
964
965                         else if( strstr(val, "WEP-104") )
966                                 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
967
968                         c->enabled       = 1;
969                         c->group_ciphers = c->pair_ciphers;
970
971                         c->auth_suites |= IWINFO_KMGMT_NONE;
972                         c->auth_algs   |= IWINFO_AUTH_OPEN; /* XXX: assumption */
973                 }
974
975                 /* WPA */
976                 else
977                 {
978                         if( strstr(val, "TKIP") )
979                                 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
980
981                         else if( strstr(val, "CCMP") )
982                                 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
983
984                         else if( strstr(val, "NONE") )
985                                 c->pair_ciphers |= IWINFO_CIPHER_NONE;
986
987                         else if( strstr(val, "WEP-40") )
988                                 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
989
990                         else if( strstr(val, "WEP-104") )
991                                 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
992
993
994                         if( (val = nl80211_getval(NULL, res, "group_cipher")) )
995                         {
996                                 if( strstr(val, "TKIP") )
997                                         c->group_ciphers |= IWINFO_CIPHER_TKIP;
998
999                                 else if( strstr(val, "CCMP") )
1000                                         c->group_ciphers |= IWINFO_CIPHER_CCMP;
1001
1002                                 else if( strstr(val, "NONE") )
1003                                         c->group_ciphers |= IWINFO_CIPHER_NONE;
1004
1005                                 else if( strstr(val, "WEP-40") )
1006                                         c->group_ciphers |= IWINFO_CIPHER_WEP40;
1007
1008                                 else if( strstr(val, "WEP-104") )
1009                                         c->group_ciphers |= IWINFO_CIPHER_WEP104;
1010                         }
1011
1012
1013                         if( (val = nl80211_getval(NULL, res, "key_mgmt")) )
1014                         {
1015                                 if( strstr(val, "WPA2") )
1016                                         c->wpa_version = 2;
1017
1018                                 else if( strstr(val, "WPA") )
1019                                         c->wpa_version = 1;
1020
1021
1022                                 if( strstr(val, "PSK") )
1023                                         c->auth_suites |= IWINFO_KMGMT_PSK;
1024
1025                                 else if( strstr(val, "EAP") || strstr(val, "802.1X") )
1026                                         c->auth_suites |= IWINFO_KMGMT_8021x;
1027
1028                                 else if( strstr(val, "NONE") )
1029                                         c->auth_suites |= IWINFO_KMGMT_NONE;
1030                         }
1031
1032                         c->enabled = (c->wpa_version && c->auth_suites) ? 1 : 0;
1033                 }
1034
1035                 return 0;
1036         }
1037
1038         return -1;
1039 }
1040
1041
1042 static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
1043 {
1044         struct nl80211_assoc_count *ac = arg;
1045         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1046         struct nlattr *attr[NL80211_ATTR_MAX + 1];
1047         struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
1048
1049         static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
1050                 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32    },
1051                 [NL80211_STA_INFO_RX_BYTES]      = { .type = NLA_U32    },
1052                 [NL80211_STA_INFO_TX_BYTES]      = { .type = NLA_U32    },
1053                 [NL80211_STA_INFO_RX_PACKETS]    = { .type = NLA_U32    },
1054                 [NL80211_STA_INFO_TX_PACKETS]    = { .type = NLA_U32    },
1055                 [NL80211_STA_INFO_SIGNAL]        = { .type = NLA_U8     },
1056                 [NL80211_STA_INFO_TX_BITRATE]    = { .type = NLA_NESTED },
1057                 [NL80211_STA_INFO_LLID]          = { .type = NLA_U16    },
1058                 [NL80211_STA_INFO_PLID]          = { .type = NLA_U16    },
1059                 [NL80211_STA_INFO_PLINK_STATE]   = { .type = NLA_U8     },
1060         };
1061
1062         nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1063                 genlmsg_attrlen(gnlh, 0), NULL);
1064
1065         if( attr[NL80211_ATTR_MAC] )
1066                 memcpy(ac->entry->mac, nla_data(attr[NL80211_ATTR_MAC]), 6);
1067
1068         if( attr[NL80211_ATTR_STA_INFO] )
1069         {
1070                 if( !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
1071                                 attr[NL80211_ATTR_STA_INFO], stats_policy) )
1072                 {
1073                         if( sinfo[NL80211_STA_INFO_SIGNAL] )
1074                                 ac->entry->signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
1075                 }
1076         }
1077
1078         ac->entry->noise = ac->noise;
1079         ac->entry++;
1080         ac->count++;
1081
1082         return NL_SKIP;
1083 }
1084
1085 int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
1086 {
1087         struct nl80211_assoc_count ac;
1088         struct nl80211_msg_conveyor *req;
1089
1090         nl80211_get_noise(ifname, &ac.noise);
1091
1092         req = nl80211_msg(ifname, NL80211_CMD_GET_STATION, NLM_F_DUMP);
1093         if( req )
1094         {
1095                 ac.count = 0;
1096                 ac.entry = (struct iwinfo_assoclist_entry *)buf;
1097
1098                 nl80211_cb(req, nl80211_get_assoclist_cb, &ac);
1099                 nl80211_send(req);
1100                 nl80211_free(req);
1101
1102                 *len = (ac.count * sizeof(struct iwinfo_assoclist_entry));
1103                 return 0;
1104         }
1105
1106         return -1;
1107 }
1108
1109 int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
1110 {
1111         int ch_cur, ch_cmp, bands_remain, freqs_remain;
1112         int dbm_max = -1, dbm_cur, dbm_cnt;
1113         struct nl80211_msg_conveyor *req, *res;
1114         struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1115         struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1116         struct nlattr *band, *freq;
1117         struct iwinfo_txpwrlist_entry entry;
1118
1119         static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
1120                 [NL80211_FREQUENCY_ATTR_FREQ]         = { .type = NLA_U32  },
1121                 [NL80211_FREQUENCY_ATTR_DISABLED]     = { .type = NLA_FLAG },
1122                 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
1123                 [NL80211_FREQUENCY_ATTR_NO_IBSS]      = { .type = NLA_FLAG },
1124                 [NL80211_FREQUENCY_ATTR_RADAR]        = { .type = NLA_FLAG },
1125                 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32  },
1126         };
1127
1128         if( nl80211_get_channel(ifname, &ch_cur) )
1129                 ch_cur = 0;
1130
1131         req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1132         if( req )
1133         {
1134                 res = nl80211_send(req);
1135                 if( res )
1136                 {
1137                         nla_for_each_nested(band,
1138                                 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1139                         {
1140                                 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1141                                           nla_len(band), NULL);
1142
1143                                 nla_for_each_nested(freq,
1144                                         bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1145                                 {
1146                                         nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1147                                                 nla_data(freq), nla_len(freq), freq_policy);
1148
1149                                         ch_cmp = nl80211_freq2channel(
1150                                                 nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]));
1151
1152                                         if( (!ch_cur || (ch_cmp == ch_cur)) &&
1153                                             freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] )
1154                                         {
1155                                                 dbm_max = (int)(0.01 * nla_get_u32(
1156                                                         freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
1157
1158                                                 break;
1159                                         }
1160                                 }
1161                         }
1162
1163                         nl80211_free(res);
1164                 }
1165                 nl80211_free(req);
1166         }
1167
1168         if( dbm_max > -1 )
1169         {
1170                 for( dbm_cur = 0, dbm_cnt = 0;
1171                      dbm_cur < dbm_max;
1172                      dbm_cur += 2, dbm_cnt++ )
1173                 {
1174                         entry.dbm = dbm_cur;
1175                         entry.mw  = iwinfo_dbm2mw(dbm_cur);
1176
1177                         memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1178                 }
1179
1180                 entry.dbm = dbm_max;
1181                 entry.mw  = iwinfo_dbm2mw(dbm_max);
1182
1183                 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1184                 dbm_cnt++;
1185
1186                 *len = dbm_cnt * sizeof(entry);
1187                 return 0;
1188         }
1189
1190         return -1;
1191 }
1192
1193 static void nl80211_get_scancrypto(const char *spec,
1194         struct iwinfo_crypto_entry *c)
1195 {
1196         if( strstr(spec, "WPA") || strstr(spec, "WEP") )
1197         {
1198                 c->enabled = 1;
1199
1200                 if( strstr(spec, "WPA2-") && strstr(spec, "WPA-") )
1201                         c->wpa_version = 3;
1202
1203                 else if( strstr(spec, "WPA2") )
1204                         c->wpa_version = 2;
1205
1206                 else if( strstr(spec, "WPA") )
1207                         c->wpa_version = 1;
1208
1209                 else if( strstr(spec, "WEP") )
1210                         c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
1211
1212
1213                 if( strstr(spec, "PSK") )
1214                         c->auth_suites |= IWINFO_KMGMT_PSK;
1215
1216                 if( strstr(spec, "802.1X") || strstr(spec, "EAP") )
1217                         c->auth_suites |= IWINFO_KMGMT_8021x;
1218
1219                 if( strstr(spec, "WPA-NONE") )
1220                         c->auth_suites |= IWINFO_KMGMT_NONE;
1221
1222
1223                 if( strstr(spec, "TKIP") )
1224                         c->pair_ciphers |= IWINFO_CIPHER_TKIP;
1225
1226                 if( strstr(spec, "CCMP") )
1227                         c->pair_ciphers |= IWINFO_CIPHER_CCMP;
1228
1229                 if( strstr(spec, "WEP-40") )
1230                         c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1231
1232                 if( strstr(spec, "WEP-104") )
1233                         c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1234
1235                 c->group_ciphers = c->pair_ciphers;
1236         }
1237         else
1238         {
1239                 c->enabled = 0;
1240         }
1241 }
1242
1243 int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
1244 {
1245         int freq, rssi, qmax, count;
1246         char *res;
1247         char ssid[128] = { 0 };
1248         char bssid[18] = { 0 };
1249         char cipher[256] = { 0 };
1250
1251         /* Got a radioX pseudo interface, find some interface on it or create one */
1252         if( !strncmp(ifname, "radio", 5) )
1253         {
1254                 /* Reuse existing interface */
1255                 if( (res = nl80211_phy2ifname(ifname)) != NULL )
1256                 {
1257                         return nl80211_get_scanlist(res, buf, len);
1258                 }
1259
1260                 /* Need to spawn a temporary iface for scanning */
1261                 else if( (res = nl80211_ifadd(ifname)) != NULL )
1262                 {
1263                         count = nl80211_get_scanlist(res, buf, len);
1264                         nl80211_ifdel(res);
1265                         return count;
1266                 }
1267         }
1268
1269         struct iwinfo_scanlist_entry *e = (struct iwinfo_scanlist_entry *)buf;
1270
1271         /* WPA supplicant */
1272         if( (res = nl80211_wpactl_info(ifname, "SCAN", "CTRL-EVENT-SCAN-RESULTS")) )
1273         {
1274                 if( (res = nl80211_wpactl_info(ifname, "SCAN_RESULTS", NULL)) )
1275                 {
1276                         nl80211_get_quality_max(ifname, &qmax);
1277
1278                         /* skip header line */
1279                         while( *res++ != '\n' );
1280
1281                         count = 0;
1282
1283                         while( sscanf(res, "%17s %d %d %255s%*[ \t]%127[^\n]\n",
1284                                       bssid, &freq, &rssi, cipher, ssid) > 0 )
1285                         {
1286                                 /* BSSID */
1287                                 e->mac[0] = strtol(&bssid[0],  NULL, 16);
1288                                 e->mac[1] = strtol(&bssid[3],  NULL, 16);
1289                                 e->mac[2] = strtol(&bssid[6],  NULL, 16);
1290                                 e->mac[3] = strtol(&bssid[9],  NULL, 16);
1291                                 e->mac[4] = strtol(&bssid[12], NULL, 16);
1292                                 e->mac[5] = strtol(&bssid[15], NULL, 16);
1293
1294                                 /* SSID */
1295                                 memcpy(e->ssid, ssid,
1296                                         min(strlen(ssid), sizeof(e->ssid) - 1));
1297
1298                                 /* Mode (assume master) */
1299                                 sprintf((char *)e->mode, "Master");
1300
1301                                 /* Channel */
1302                                 e->channel = nl80211_freq2channel(freq);
1303
1304                                 /* Signal */
1305                                 e->signal = rssi;
1306
1307                                 /* Quality */
1308                                 if( rssi < 0 )
1309                                 {
1310                                         /* The cfg80211 wext compat layer assumes a signal range
1311                                          * of -110 dBm to -40 dBm, the quality value is derived
1312                                          * by adding 110 to the signal level */
1313                                         if( rssi < -110 )
1314                                                 rssi = -110;
1315                                         else if( rssi > -40 )
1316                                                 rssi = -40;
1317
1318                                         e->quality = (rssi + 110);
1319                                 }
1320                                 else
1321                                 {
1322                                         e->quality = rssi;
1323                                 }
1324
1325                                 /* Max. Quality */
1326                                 e->quality_max = qmax;
1327
1328                                 /* Crypto */
1329                                 nl80211_get_scancrypto(cipher, &e->crypto);
1330
1331                                 /* advance to next line */
1332                                 while( *res && *res++ != '\n' );
1333
1334                                 count++;
1335                                 e++;
1336
1337                                 memset(ssid, 0, sizeof(ssid));
1338                                 memset(bssid, 0, sizeof(bssid));
1339                                 memset(cipher, 0, sizeof(cipher));
1340                         }
1341
1342                         *len = count * sizeof(struct iwinfo_scanlist_entry);
1343                         return 0;
1344                 }
1345         }
1346
1347         /* AP scan */
1348         else
1349         {
1350                 /* Got a temp interface, don't create yet another one */
1351                 if( !strncmp(ifname, "tmp.", 4) )
1352                 {
1353                         if( !iwinfo_ifup(ifname) )
1354                                 return -1;
1355
1356                         wext_get_scanlist(ifname, buf, len);
1357                         iwinfo_ifdown(ifname);
1358                         return 0;
1359                 }
1360
1361                 /* Spawn a new scan interface */
1362                 else
1363                 {
1364                         if( !(res = nl80211_ifadd(ifname)) )
1365                                 goto out;
1366
1367                         if( !iwinfo_ifmac(res) )
1368                                 goto out;
1369
1370                         /* if we can take the new interface up, the driver supports an
1371                          * additional interface and there's no need to tear down the ap */
1372                         if( iwinfo_ifup(res) )
1373                         {
1374                                 wext_get_scanlist(res, buf, len);
1375                                 iwinfo_ifdown(res);
1376                         }
1377
1378                         /* driver cannot create secondary interface, take down ap
1379                          * during scan */
1380                         else if( iwinfo_ifdown(ifname) && iwinfo_ifup(res) )
1381                         {
1382                                 wext_get_scanlist(res, buf, len);
1383                                 iwinfo_ifdown(res);
1384                                 iwinfo_ifup(ifname);
1385                                 nl80211_hostapd_hup(ifname);
1386                         }
1387
1388                 out:
1389                         nl80211_ifdel(res);
1390                         return 0;
1391                 }
1392         }
1393
1394         return -1;
1395 }
1396
1397 int nl80211_get_freqlist(const char *ifname, char *buf, int *len)
1398 {
1399         int count = 0, bands_remain, freqs_remain;
1400         struct nl80211_msg_conveyor *req, *res;
1401         struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1402         struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1403         struct nlattr *band, *freq;
1404         struct iwinfo_freqlist_entry *e = (struct iwinfo_freqlist_entry *)buf;
1405
1406         req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1407         if( req )
1408         {
1409                 res = nl80211_send(req);
1410                 if( res )
1411                 {
1412                         nla_for_each_nested(band,
1413                                 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1414                         {
1415                                 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1416                                           nla_len(band), NULL);
1417
1418                                 nla_for_each_nested(freq,
1419                                         bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1420                                 {
1421                                         nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1422                                                 nla_data(freq), nla_len(freq), NULL);
1423
1424                                         if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
1425                                             freqs[NL80211_FREQUENCY_ATTR_DISABLED] )
1426                                                 continue;
1427
1428                                         e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
1429                                         e->channel = nl80211_freq2channel(e->mhz);
1430
1431                                         e->restricted = (
1432                                                 freqs[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] ||
1433                                                 freqs[NL80211_FREQUENCY_ATTR_NO_IBSS]      ||
1434                                                 freqs[NL80211_FREQUENCY_ATTR_RADAR]
1435                                         ) ? 1 : 0;
1436
1437                                         e++;
1438                                         count++;
1439                                 }
1440                         }
1441                         nl80211_free(res);
1442                 }
1443                 nl80211_free(req);
1444         }
1445
1446         if( count > 0 )
1447         {
1448                 *len = count * sizeof(struct iwinfo_freqlist_entry);
1449                 return 0;
1450         }
1451
1452         return -1;
1453 }
1454
1455 int nl80211_get_country(const char *ifname, char *buf)
1456 {
1457         int rv = -1;
1458         struct nl80211_msg_conveyor *req, *res;
1459
1460         req = nl80211_msg(ifname, NL80211_CMD_GET_REG, 0);
1461         if( req )
1462         {
1463                 res = nl80211_send(req);
1464                 if( res )
1465                 {
1466                         if( res->attr[NL80211_ATTR_REG_ALPHA2] )
1467                         {
1468                                 memcpy(buf, nla_data(res->attr[NL80211_ATTR_REG_ALPHA2]), 2);
1469                                 rv = 0;
1470                         }
1471                         nl80211_free(res);
1472                 }
1473                 nl80211_free(req);
1474         }
1475
1476         return rv;
1477 }
1478
1479 int nl80211_get_countrylist(const char *ifname, char *buf, int *len)
1480 {
1481         int i, count;
1482         struct iwinfo_iso3166_label *l;
1483         struct iwinfo_country_entry *e = (struct iwinfo_country_entry *)buf;
1484
1485         for( l = ISO3166_Names, count = 0; l->iso3166; l++, e++, count++ )
1486         {
1487                 e->iso3166 = l->iso3166;
1488                 e->ccode[0] = (l->iso3166 / 256);
1489                 e->ccode[1] = (l->iso3166 % 256);
1490         }
1491
1492         *len = (count * sizeof(struct iwinfo_country_entry));
1493         return 0;
1494 }
1495
1496 int nl80211_get_hwmodelist(const char *ifname, int *buf)
1497 {
1498         int bands_remain, freqs_remain;
1499         struct nl80211_msg_conveyor *req, *res;
1500         struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1501         struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1502         struct nlattr *band, *freq;
1503         uint16_t caps = 0;
1504
1505         req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1506         if( req )
1507         {
1508                 res = nl80211_send(req);
1509                 if( res )
1510                 {
1511                         nla_for_each_nested(band,
1512                                 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1513                         {
1514                                 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1515                                           nla_len(band), NULL);
1516
1517                                 if( bands[NL80211_BAND_ATTR_HT_CAPA] )
1518                                         caps = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]);
1519
1520                                 /* Treat any nonzero capability as 11n */
1521                                 if( caps > 0 )
1522                                         *buf |= IWINFO_80211_N;
1523
1524                                 nla_for_each_nested(freq,
1525                                         bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1526                                 {
1527                                         nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1528                                                 nla_data(freq), nla_len(freq), NULL);
1529
1530                                         if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] )
1531                                                 continue;
1532
1533                                         if( nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]) < 2485 )
1534                                         {
1535                                                 *buf |= IWINFO_80211_B;
1536                                                 *buf |= IWINFO_80211_G;
1537                                         }
1538                                         else
1539                                         {
1540                                                 *buf |= IWINFO_80211_A;
1541                                         }
1542                                 }
1543                         }
1544                         nl80211_free(res);
1545                 }
1546                 nl80211_free(req);
1547         }
1548
1549         return *buf ? 0 : -1;
1550 }
1551
1552 int nl80211_get_mbssid_support(const char *ifname, int *buf)
1553 {
1554         /* Test whether we can create another interface */
1555         char *nif = nl80211_ifadd(ifname);
1556
1557         if( nif )
1558         {
1559                 *buf = (iwinfo_ifmac(nif) && iwinfo_ifup(nif));
1560
1561                 iwinfo_ifdown(nif);
1562                 nl80211_ifdel(nif);
1563
1564                 return 0;
1565         }
1566
1567         return -1;
1568 }