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