libiwinfo: fix nl80211_get_channel() and nl80211_get_frequency()
[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 char * nl80211_wpasupp_info(const char *ifname, const char *cmd)
337 {
338 int sock = -1, len;
339 char *rv = NULL;
340 size_t remote_length, local_length;
341 static char buffer[1024] = { 0 };
342
343 struct timeval tv = { 2, 0 };
344 struct sockaddr_un local = { 0 };
345 struct sockaddr_un remote = { 0 };
346
347 fd_set rfds;
348
349 sock = socket(PF_UNIX, SOCK_DGRAM, 0);
350 if( sock < 0 )
351 return NULL;
352
353 remote.sun_family = AF_UNIX;
354 remote_length = sizeof(remote.sun_family) + sprintf(remote.sun_path,
355 "/var/run/wpa_supplicant-%s/%s", ifname, ifname);
356
357 if( fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC) < 0 )
358 goto out;
359
360 if( connect(sock, (struct sockaddr *) &remote, remote_length) )
361 goto out;
362
363 local.sun_family = AF_UNIX;
364 local_length = sizeof(local.sun_family) + sprintf(local.sun_path,
365 "/var/run/iwinfo-%s-%d", ifname, getpid());
366
367 if( bind(sock, (struct sockaddr *) &local, local_length) )
368 goto out;
369
370 send(sock, cmd, strlen(cmd), 0);
371
372 while( 1 )
373 {
374 FD_ZERO(&rfds);
375 FD_SET(sock, &rfds);
376
377 if( select(sock + 1, &rfds, NULL, NULL, &tv) < 0 )
378 goto out;
379
380 if( !FD_ISSET(sock, &rfds) )
381 break;
382
383 if( (len = recv(sock, buffer, sizeof(buffer), 0)) <= 0 )
384 goto out;
385
386 buffer[len] = 0;
387
388 if( buffer[0] != '<' )
389 break;
390 }
391
392 rv = buffer;
393
394 out:
395 close(sock);
396
397 if( local.sun_family )
398 unlink(local.sun_path);
399
400 return rv;
401 }
402
403 static inline int nl80211_readint(const char *path)
404 {
405 int fd;
406 int rv = -1;
407 char buffer[16];
408
409 if( (fd = open(path, O_RDONLY)) > -1 )
410 {
411 if( read(fd, buffer, sizeof(buffer)) > 0 )
412 rv = atoi(buffer);
413
414 close(fd);
415 }
416
417 return rv;
418 }
419
420 static char * nl80211_phy2ifname(const char *ifname)
421 {
422 int fd, ifidx = -1, cifidx = -1, phyidx = -1;
423 char buffer[64];
424 static char nif[IFNAMSIZ] = { 0 };
425
426 DIR *d;
427 struct dirent *e;
428
429 if( !ifname )
430 return NULL;
431 else if( !strncmp(ifname, "phy", 3) )
432 phyidx = atoi(&ifname[3]);
433 else if( !strncmp(ifname, "radio", 5) )
434 phyidx = atoi(&ifname[5]);
435
436 if( phyidx > -1 )
437 {
438 if( (d = opendir("/sys/class/net")) != NULL )
439 {
440 while( (e = readdir(d)) != NULL )
441 {
442 snprintf(buffer, sizeof(buffer),
443 "/sys/class/net/%s/phy80211/index", e->d_name);
444
445 if( nl80211_readint(buffer) == phyidx )
446 {
447 snprintf(buffer, sizeof(buffer),
448 "/sys/class/net/%s/ifindex", e->d_name);
449
450 if( (cifidx = nl80211_readint(buffer)) >= 0 &&
451 ((ifidx < 0) || (cifidx < ifidx)) )
452 {
453 ifidx = cifidx;
454 strncpy(nif, e->d_name, sizeof(nif));
455 }
456 }
457 }
458
459 closedir(d);
460 }
461 }
462
463 return nif[0] ? nif : NULL;
464 }
465
466 static char * nl80211_ifadd(const char *ifname)
467 {
468 int phyidx;
469 char *rv = NULL;
470 static char nif[IFNAMSIZ] = { 0 };
471 struct nl80211_msg_conveyor *req, *res;
472
473 req = nl80211_msg(ifname, NL80211_CMD_NEW_INTERFACE, 0);
474 if( req )
475 {
476 snprintf(nif, sizeof(nif), "tmp.%s", ifname);
477
478 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, nif);
479 NLA_PUT_U32(req->msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
480
481 res = nl80211_send(req);
482 if( res )
483 {
484 rv = nif;
485 nl80211_free(res);
486 }
487
488 nla_put_failure:
489 nl80211_free(req);
490 }
491
492 return rv;
493 }
494
495 static void nl80211_ifdel(const char *ifname)
496 {
497 struct nl80211_msg_conveyor *req;
498
499 req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
500 if( req )
501 {
502 NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, ifname);
503
504 nl80211_free(nl80211_send(req));
505
506 nla_put_failure:
507 nl80211_free(req);
508 }
509 }
510
511 static void nl80211_hostapd_hup(const char *ifname)
512 {
513 int fd, pid = 0;
514 char buf[32];
515 char *phy = nl80211_ifname2phy(ifname);
516
517 if( phy )
518 {
519 snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
520 if( (fd = open(buf, O_RDONLY)) > 0 )
521 {
522 if( read(fd, buf, sizeof(buf)) > 0 )
523 pid = atoi(buf);
524
525 close(fd);
526 }
527
528 if( pid > 0 )
529 kill(pid, 1);
530 }
531 }
532
533
534 int nl80211_probe(const char *ifname)
535 {
536 return !!nl80211_ifname2phy(ifname);
537 }
538
539 void nl80211_close(void)
540 {
541 if( nls )
542 {
543 if( nls->nl_sock )
544 nl_socket_free(nls->nl_sock);
545
546 if( nls->nl_cache )
547 nl_cache_free(nls->nl_cache);
548
549 free(nls);
550 nls = NULL;
551 }
552 }
553
554 int nl80211_get_mode(const char *ifname, char *buf)
555 {
556 return wext_get_mode(ifname, buf);
557 }
558
559 int nl80211_get_ssid(const char *ifname, char *buf)
560 {
561 char *ssid;
562
563 if( !wext_get_ssid(ifname, buf) )
564 {
565 return 0;
566 }
567 else if( (ssid = nl80211_hostapd_info(ifname)) &&
568 (ssid = nl80211_getval(ifname, ssid, "ssid")) )
569 {
570 memcpy(buf, ssid, strlen(ssid));
571 return 0;
572 }
573
574 return -1;
575 }
576
577 int nl80211_get_bssid(const char *ifname, char *buf)
578 {
579 char *bssid;
580 unsigned char mac[6];
581
582 if( !wext_get_bssid(ifname, buf) )
583 {
584 return 0;
585 }
586 else if( (bssid = nl80211_hostapd_info(ifname)) &&
587 (bssid = nl80211_getval(ifname, bssid, "bssid")) )
588 {
589 mac[0] = strtol(&bssid[0], NULL, 16);
590 mac[1] = strtol(&bssid[3], NULL, 16);
591 mac[2] = strtol(&bssid[6], NULL, 16);
592 mac[3] = strtol(&bssid[9], NULL, 16);
593 mac[4] = strtol(&bssid[12], NULL, 16);
594 mac[5] = strtol(&bssid[15], NULL, 16);
595
596 sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
597 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
598
599 return 0;
600 }
601
602 return -1;
603 }
604
605 int nl80211_get_channel(const char *ifname, int *buf)
606 {
607 char *first;
608
609 if( !wext_get_channel(ifname, buf) )
610 return 0;
611
612 else if( (first = nl80211_phy2ifname(nl80211_ifname2phy(ifname))) != NULL )
613 return wext_get_channel(first, buf);
614
615 return -1;
616 }
617
618 int nl80211_get_frequency(const char *ifname, int *buf)
619 {
620 char *first;
621
622 if( !wext_get_frequency(ifname, buf) )
623 return 0;
624
625 else if( (first = nl80211_phy2ifname(nl80211_ifname2phy(ifname))) != NULL )
626 return wext_get_frequency(first, buf);
627
628 return -1;
629 }
630
631 int nl80211_get_txpower(const char *ifname, int *buf)
632 {
633 return wext_get_txpower(ifname, buf);
634 }
635
636
637 static int nl80211_get_signal_cb(struct nl_msg *msg, void *arg)
638 {
639 int8_t dbm;
640 int16_t mbit;
641 struct nl80211_rssi_rate *rr = arg;
642
643 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
644 struct nlattr *attr[NL80211_ATTR_MAX + 1];
645 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
646 struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
647
648 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
649 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
650 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
651 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
652 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
653 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
654 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
655 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
656 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
657 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
658 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
659 };
660
661 static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
662 [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
663 [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
664 [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
665 [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
666 };
667
668 nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
669 genlmsg_attrlen(gnlh, 0), NULL);
670
671 if( attr[NL80211_ATTR_STA_INFO] )
672 {
673 if( !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
674 attr[NL80211_ATTR_STA_INFO], stats_policy) )
675 {
676 if( sinfo[NL80211_STA_INFO_SIGNAL] )
677 {
678 dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
679 rr->rssi = rr->rssi ? (int8_t)((rr->rssi + dbm) / 2) : dbm;
680 }
681
682 if( sinfo[NL80211_STA_INFO_TX_BITRATE] )
683 {
684 if( !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
685 sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy) )
686 {
687 if( rinfo[NL80211_RATE_INFO_BITRATE] )
688 {
689 mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
690 rr->rate = rr->rate
691 ? (int16_t)((rr->rate + mbit) / 2) : mbit;
692 }
693 }
694 }
695 }
696 }
697
698 return NL_SKIP;
699 }
700
701 int nl80211_get_bitrate(const char *ifname, int *buf)
702 {
703 struct nl80211_rssi_rate rr;
704 struct nl80211_msg_conveyor *req;
705
706 if( !wext_get_bitrate(ifname, buf) )
707 return 0;
708
709 req = nl80211_msg(ifname, NL80211_CMD_GET_STATION, NLM_F_DUMP);
710 if( req )
711 {
712 rr.rssi = 0;
713 rr.rate = 0;
714
715 nl80211_cb(req, nl80211_get_signal_cb, &rr);
716 nl80211_send(req);
717 nl80211_free(req);
718
719 if( rr.rate )
720 {
721 *buf = (rr.rate * 100);
722 return 0;
723 }
724 }
725
726 return -1;
727 }
728
729 int nl80211_get_signal(const char *ifname, int *buf)
730 {
731 struct nl80211_rssi_rate rr;
732 struct nl80211_msg_conveyor *req;
733
734 if( !wext_get_signal(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.rssi )
748 {
749 *buf = rr.rssi;
750 return 0;
751 }
752 }
753
754 return -1;
755 }
756
757 int nl80211_get_noise(const char *ifname, int *buf)
758 {
759 int rv = -1;
760 struct nl80211_msg_conveyor *req, *res;
761 struct nlattr *si[NL80211_SURVEY_INFO_MAX + 1];
762
763 static struct nla_policy sp[NL80211_SURVEY_INFO_MAX + 1] = {
764 [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
765 [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
766 };
767
768 req = nl80211_msg(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP);
769 if( req )
770 {
771 res = nl80211_send(req);
772 if( res )
773 {
774 if( res->attr[NL80211_ATTR_SURVEY_INFO] )
775 {
776 if( !nla_parse_nested(si, NL80211_SURVEY_INFO_MAX,
777 res->attr[NL80211_ATTR_SURVEY_INFO], sp) &&
778 si[NL80211_SURVEY_INFO_NOISE] )
779 {
780 *buf = (int8_t)nla_get_u8(si[NL80211_SURVEY_INFO_NOISE]);
781 rv = 0;
782 }
783 }
784 nl80211_free(res);
785 }
786 nl80211_free(req);
787 }
788
789 return rv;
790 }
791
792 int nl80211_get_quality(const char *ifname, int *buf)
793 {
794 int signal;
795
796 if( wext_get_quality(ifname, buf) )
797 {
798 *buf = 0;
799
800 if( !nl80211_get_signal(ifname, &signal) )
801 {
802 /* A positive signal level is usually just a quality
803 * value, pass through as-is */
804 if( signal >= 0 )
805 {
806 *buf = signal;
807 }
808
809 /* The cfg80211 wext compat layer assumes a signal range
810 * of -110 dBm to -40 dBm, the quality value is derived
811 * by adding 110 to the signal level */
812 else
813 {
814 if( signal < -110 )
815 signal = -110;
816 else if( signal > -40 )
817 signal = -40;
818
819 *buf = (signal + 110);
820 }
821 }
822 }
823
824 return 0;
825 }
826
827 int nl80211_get_quality_max(const char *ifname, int *buf)
828 {
829 if( wext_get_quality_max(ifname, buf) )
830 /* The cfg80211 wext compat layer assumes a maximum
831 * quality of 70 */
832 *buf = 70;
833
834 return 0;
835 }
836
837 int nl80211_get_encryption(const char *ifname, char *buf)
838 {
839 int i;
840 char k[9];
841 char *val, *res;
842 struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
843
844 /* Hostapd */
845 if( (res = nl80211_hostapd_info(ifname)) )
846 {
847 if( (val = nl80211_getval(ifname, res, "auth_algs")) && (val > 0) )
848 {
849 c->auth_suites |= IWINFO_KMGMT_NONE;
850
851 switch(atoi(val)) {
852 case 1:
853 c->auth_algs |= IWINFO_AUTH_OPEN;
854 break;
855
856 case 2:
857 c->auth_algs |= IWINFO_AUTH_SHARED;
858 break;
859
860 case 3:
861 c->auth_algs |= IWINFO_AUTH_OPEN;
862 c->auth_algs |= IWINFO_AUTH_SHARED;
863 break;
864
865 default:
866 break;
867 }
868
869 for( i = 0; i < 4; i++ )
870 {
871 snprintf(k, sizeof(k), "wep_key%d", i);
872
873 if( (val = nl80211_getval(ifname, res, k)) )
874 {
875 if( (strlen(val) == 5) || (strlen(val) == 10) )
876 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
877
878 else if( (strlen(val) == 13) || (strlen(val) == 26) )
879 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
880 }
881 }
882
883 c->group_ciphers = c->pair_ciphers;
884
885 return 0;
886 }
887
888
889 if( (val = nl80211_getval(ifname, res, "wpa")) != NULL )
890 c->wpa_version = atoi(val);
891
892
893 val = nl80211_getval(ifname, res, "wpa_key_mgmt");
894
895 if( !val || strstr(val, "PSK") )
896 c->auth_suites |= IWINFO_KMGMT_PSK;
897
898 if( val && strstr(val, "EAP") )
899 c->auth_suites |= IWINFO_KMGMT_8021x;
900
901 if( val && strstr(val, "NONE") )
902 c->auth_suites |= IWINFO_KMGMT_NONE;
903
904
905 if( (val = nl80211_getval(ifname, res, "wpa_pairwise")) != NULL )
906 {
907 if( strstr(val, "TKIP") )
908 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
909
910 if( strstr(val, "CCMP") )
911 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
912
913 if( strstr(val, "NONE") )
914 c->pair_ciphers |= IWINFO_CIPHER_NONE;
915 }
916
917
918 c->group_ciphers = c->pair_ciphers;
919 c->enabled = (c->auth_algs || c->auth_suites) ? 1 : 0;
920
921 return 0;
922 }
923
924 /* WPA supplicant */
925 else if( (res = nl80211_wpasupp_info(ifname, "STATUS")) &&
926 (val = nl80211_getval(NULL, res, "pairwise_cipher")) )
927 {
928 /* WEP */
929 if( strstr(val, "WEP") )
930 {
931 if( strstr(val, "WEP-40") )
932 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
933
934 else if( strstr(val, "WEP-104") )
935 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
936
937 c->enabled = 1;
938 c->group_ciphers = c->pair_ciphers;
939
940 c->auth_suites |= IWINFO_KMGMT_NONE;
941 c->auth_algs |= IWINFO_AUTH_OPEN; /* XXX: assumption */
942 }
943
944 /* WPA */
945 else
946 {
947 if( strstr(val, "TKIP") )
948 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
949
950 else if( strstr(val, "CCMP") )
951 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
952
953 else if( strstr(val, "NONE") )
954 c->pair_ciphers |= IWINFO_CIPHER_NONE;
955
956 else if( strstr(val, "WEP-40") )
957 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
958
959 else if( strstr(val, "WEP-104") )
960 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
961
962
963 if( (val = nl80211_getval(NULL, res, "group_cipher")) )
964 {
965 if( strstr(val, "TKIP") )
966 c->group_ciphers |= IWINFO_CIPHER_TKIP;
967
968 else if( strstr(val, "CCMP") )
969 c->group_ciphers |= IWINFO_CIPHER_CCMP;
970
971 else if( strstr(val, "NONE") )
972 c->group_ciphers |= IWINFO_CIPHER_NONE;
973
974 else if( strstr(val, "WEP-40") )
975 c->group_ciphers |= IWINFO_CIPHER_WEP40;
976
977 else if( strstr(val, "WEP-104") )
978 c->group_ciphers |= IWINFO_CIPHER_WEP104;
979 }
980
981
982 if( (val = nl80211_getval(NULL, res, "key_mgmt")) )
983 {
984 if( strstr(val, "WPA2") )
985 c->wpa_version = 2;
986
987 else if( strstr(val, "WPA") )
988 c->wpa_version = 1;
989
990
991 if( strstr(val, "PSK") )
992 c->auth_suites |= IWINFO_KMGMT_PSK;
993
994 else if( strstr(val, "EAP") || strstr(val, "802.1X") )
995 c->auth_suites |= IWINFO_KMGMT_8021x;
996
997 else if( strstr(val, "NONE") )
998 c->auth_suites |= IWINFO_KMGMT_NONE;
999 }
1000
1001 c->enabled = (c->wpa_version && c->auth_suites) ? 1 : 0;
1002 }
1003
1004 return 0;
1005 }
1006
1007 return -1;
1008 }
1009
1010
1011 static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
1012 {
1013 struct nl80211_assoc_count *ac = arg;
1014 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1015 struct nlattr *attr[NL80211_ATTR_MAX + 1];
1016 struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
1017
1018 static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
1019 [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
1020 [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
1021 [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
1022 [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
1023 [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
1024 [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
1025 [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
1026 [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
1027 [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
1028 [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
1029 };
1030
1031 nla_parse(attr, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1032 genlmsg_attrlen(gnlh, 0), NULL);
1033
1034 if( attr[NL80211_ATTR_MAC] )
1035 memcpy(ac->entry->mac, nla_data(attr[NL80211_ATTR_MAC]), 6);
1036
1037 if( attr[NL80211_ATTR_STA_INFO] )
1038 {
1039 if( !nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
1040 attr[NL80211_ATTR_STA_INFO], stats_policy) )
1041 {
1042 if( sinfo[NL80211_STA_INFO_SIGNAL] )
1043 ac->entry->signal = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
1044 }
1045 }
1046
1047 ac->entry->noise = ac->noise;
1048 ac->entry++;
1049 ac->count++;
1050
1051 return NL_SKIP;
1052 }
1053
1054 int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
1055 {
1056 struct nl80211_assoc_count ac;
1057 struct nl80211_msg_conveyor *req;
1058
1059 nl80211_get_noise(ifname, &ac.noise);
1060
1061 req = nl80211_msg(ifname, NL80211_CMD_GET_STATION, NLM_F_DUMP);
1062 if( req )
1063 {
1064 ac.count = 0;
1065 ac.entry = (struct iwinfo_assoclist_entry *)buf;
1066
1067 nl80211_cb(req, nl80211_get_assoclist_cb, &ac);
1068 nl80211_send(req);
1069 nl80211_free(req);
1070
1071 *len = (ac.count * sizeof(struct iwinfo_assoclist_entry));
1072 return 0;
1073 }
1074
1075 return -1;
1076 }
1077
1078 int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
1079 {
1080 int ch_cur, ch_cmp, bands_remain, freqs_remain;
1081 int dbm_max = -1, dbm_cur, dbm_cnt;
1082 struct nl80211_msg_conveyor *req, *res;
1083 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1084 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1085 struct nlattr *band, *freq;
1086 struct iwinfo_txpwrlist_entry entry;
1087
1088 static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
1089 [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
1090 [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
1091 [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
1092 [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
1093 [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
1094 [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
1095 };
1096
1097 if( nl80211_get_channel(ifname, &ch_cur) )
1098 ch_cur = 0;
1099
1100 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1101 if( req )
1102 {
1103 res = nl80211_send(req);
1104 if( res )
1105 {
1106 nla_for_each_nested(band,
1107 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1108 {
1109 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1110 nla_len(band), NULL);
1111
1112 nla_for_each_nested(freq,
1113 bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1114 {
1115 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1116 nla_data(freq), nla_len(freq), freq_policy);
1117
1118 ch_cmp = nl80211_freq2channel(
1119 nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]));
1120
1121 if( (!ch_cur || (ch_cmp == ch_cur)) &&
1122 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] )
1123 {
1124 dbm_max = (int)(0.01 * nla_get_u32(
1125 freqs[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
1126
1127 break;
1128 }
1129 }
1130 }
1131
1132 nl80211_free(res);
1133 }
1134 nl80211_free(req);
1135 }
1136
1137 if( dbm_max > -1 )
1138 {
1139 for( dbm_cur = 0, dbm_cnt = 0;
1140 dbm_cur < dbm_max;
1141 dbm_cur += 2, dbm_cnt++ )
1142 {
1143 entry.dbm = dbm_cur;
1144 entry.mw = iwinfo_dbm2mw(dbm_cur);
1145
1146 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1147 }
1148
1149 entry.dbm = dbm_max;
1150 entry.mw = iwinfo_dbm2mw(dbm_max);
1151
1152 memcpy(&buf[dbm_cnt * sizeof(entry)], &entry, sizeof(entry));
1153 dbm_cnt++;
1154
1155 *len = dbm_cnt * sizeof(entry);
1156 return 0;
1157 }
1158
1159 return -1;
1160 }
1161
1162 static void nl80211_get_scancrypto(const char *spec,
1163 struct iwinfo_crypto_entry *c)
1164 {
1165 if( strstr(spec, "OPEN") )
1166 {
1167 c->enabled = 0;
1168 }
1169 else
1170 {
1171 c->enabled = 1;
1172
1173 if( strstr(spec, "WPA2-") && strstr(spec, "WPA-") )
1174 c->wpa_version = 3;
1175
1176 else if( strstr(spec, "WPA2") )
1177 c->wpa_version = 2;
1178
1179 else if( strstr(spec, "WPA") )
1180 c->wpa_version = 1;
1181
1182 else if( strstr(spec, "WEP") )
1183 c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
1184
1185
1186 if( strstr(spec, "PSK") )
1187 c->auth_suites |= IWINFO_KMGMT_PSK;
1188
1189 if( strstr(spec, "802.1X") || strstr(spec, "EAP") )
1190 c->auth_suites |= IWINFO_KMGMT_8021x;
1191
1192 if( strstr(spec, "WPA-NONE") )
1193 c->auth_suites |= IWINFO_KMGMT_NONE;
1194
1195
1196 if( strstr(spec, "TKIP") )
1197 c->pair_ciphers |= IWINFO_CIPHER_TKIP;
1198
1199 if( strstr(spec, "CCMP") )
1200 c->pair_ciphers |= IWINFO_CIPHER_CCMP;
1201
1202 if( strstr(spec, "WEP-40") )
1203 c->pair_ciphers |= IWINFO_CIPHER_WEP40;
1204
1205 if( strstr(spec, "WEP-104") )
1206 c->pair_ciphers |= IWINFO_CIPHER_WEP104;
1207
1208 c->group_ciphers = c->pair_ciphers;
1209 }
1210 }
1211
1212 int nl80211_get_scanlist(const char *ifname, char *buf, int *len)
1213 {
1214 int freq, rssi, qmax, count;
1215 char *res;
1216 char ssid[128] = { 0 };
1217 char bssid[18] = { 0 };
1218 char cipher[256] = { 0 };
1219
1220 /* Got a radioX pseudo interface, find some interface on it or create one */
1221 if( !strncmp(ifname, "radio", 5) )
1222 {
1223 /* Reuse existing interface */
1224 if( (res = nl80211_phy2ifname(ifname)) != NULL )
1225 {
1226 return nl80211_get_scanlist(res, buf, len);
1227 }
1228
1229 /* Need to spawn a temporary iface for scanning */
1230 else if( (res = nl80211_ifadd(ifname)) != NULL )
1231 {
1232 count = nl80211_get_scanlist(res, buf, len);
1233 nl80211_ifdel(res);
1234 return count;
1235 }
1236 }
1237
1238 struct iwinfo_scanlist_entry *e = (struct iwinfo_scanlist_entry *)buf;
1239
1240 /* WPA supplicant */
1241 if( (res = nl80211_wpasupp_info(ifname, "SCAN")) && !strcmp(res, "OK\n") )
1242 {
1243 sleep(2);
1244
1245 if( (res = nl80211_wpasupp_info(ifname, "SCAN_RESULTS")) )
1246 {
1247 nl80211_get_quality_max(ifname, &qmax);
1248
1249 /* skip header line */
1250 while( *res++ != '\n' );
1251
1252 count = 0;
1253
1254 while( sscanf(res, "%17s %d %d %255s %127[^\n]\n",
1255 bssid, &freq, &rssi, cipher, ssid) > 0 )
1256 {
1257 /* BSSID */
1258 e->mac[0] = strtol(&bssid[0], NULL, 16);
1259 e->mac[1] = strtol(&bssid[3], NULL, 16);
1260 e->mac[2] = strtol(&bssid[6], NULL, 16);
1261 e->mac[3] = strtol(&bssid[9], NULL, 16);
1262 e->mac[4] = strtol(&bssid[12], NULL, 16);
1263 e->mac[5] = strtol(&bssid[15], NULL, 16);
1264
1265 /* SSID */
1266 memcpy(e->ssid, ssid,
1267 min(strlen(ssid), sizeof(e->ssid) - 1));
1268
1269 /* Mode (assume master) */
1270 sprintf((char *)e->mode, "Master");
1271
1272 /* Channel */
1273 e->channel = nl80211_freq2channel(freq);
1274
1275 /* Signal */
1276 e->signal = rssi;
1277
1278 /* Quality */
1279 if( rssi < 0 )
1280 {
1281 /* The cfg80211 wext compat layer assumes a signal range
1282 * of -110 dBm to -40 dBm, the quality value is derived
1283 * by adding 110 to the signal level */
1284 if( rssi < -110 )
1285 rssi = -110;
1286 else if( rssi > -40 )
1287 rssi = -40;
1288
1289 e->quality = (rssi + 110);
1290 }
1291 else
1292 {
1293 e->quality = rssi;
1294 }
1295
1296 /* Max. Quality */
1297 e->quality_max = qmax;
1298
1299 /* Crypto */
1300 nl80211_get_scancrypto(cipher, &e->crypto);
1301
1302 /* advance to next line */
1303 while( *res && *res++ != '\n' );
1304
1305 count++;
1306 e++;
1307 }
1308
1309 *len = count * sizeof(struct iwinfo_scanlist_entry);
1310 return 0;
1311 }
1312 }
1313
1314 /* AP scan */
1315 else
1316 {
1317 /* Got a temp interface, don't create yet another one */
1318 if( !strncmp(ifname, "tmp.", 4) )
1319 {
1320 if( !iwinfo_ifup(ifname) )
1321 return -1;
1322
1323 wext_get_scanlist(ifname, buf, len);
1324 iwinfo_ifdown(ifname);
1325 return 0;
1326 }
1327
1328 /* Spawn a new scan interface */
1329 else
1330 {
1331 if( !(res = nl80211_ifadd(ifname)) )
1332 goto out;
1333
1334 if( !iwinfo_ifmac(res) )
1335 goto out;
1336
1337 /* if we can take the new interface up, the driver supports an
1338 * additional interface and there's no need to tear down the ap */
1339 if( iwinfo_ifup(res) )
1340 {
1341 wext_get_scanlist(res, buf, len);
1342 iwinfo_ifdown(res);
1343 }
1344
1345 /* driver cannot create secondary interface, take down ap
1346 * during scan */
1347 else if( iwinfo_ifdown(ifname) && iwinfo_ifup(res) )
1348 {
1349 wext_get_scanlist(res, buf, len);
1350 iwinfo_ifdown(res);
1351 iwinfo_ifup(ifname);
1352 nl80211_hostapd_hup(ifname);
1353 }
1354
1355 out:
1356 nl80211_ifdel(res);
1357 return 0;
1358 }
1359 }
1360
1361 return -1;
1362 }
1363
1364 int nl80211_get_freqlist(const char *ifname, char *buf, int *len)
1365 {
1366 int count = 0, bands_remain, freqs_remain;
1367 struct nl80211_msg_conveyor *req, *res;
1368 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1369 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1370 struct nlattr *band, *freq;
1371 struct iwinfo_freqlist_entry *e = (struct iwinfo_freqlist_entry *)buf;
1372
1373 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1374 if( req )
1375 {
1376 res = nl80211_send(req);
1377 if( res )
1378 {
1379 nla_for_each_nested(band,
1380 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1381 {
1382 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1383 nla_len(band), NULL);
1384
1385 nla_for_each_nested(freq,
1386 bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1387 {
1388 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1389 nla_data(freq), nla_len(freq), NULL);
1390
1391 if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
1392 freqs[NL80211_FREQUENCY_ATTR_DISABLED] )
1393 continue;
1394
1395 e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
1396 e->channel = nl80211_freq2channel(e->mhz);
1397
1398 e->restricted = (
1399 freqs[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] ||
1400 freqs[NL80211_FREQUENCY_ATTR_NO_IBSS] ||
1401 freqs[NL80211_FREQUENCY_ATTR_RADAR]
1402 ) ? 1 : 0;
1403
1404 e++;
1405 count++;
1406 }
1407 }
1408 nl80211_free(res);
1409 }
1410 nl80211_free(req);
1411 }
1412
1413 if( count > 0 )
1414 {
1415 *len = count * sizeof(struct iwinfo_freqlist_entry);
1416 return 0;
1417 }
1418
1419 return -1;
1420 }
1421
1422 int nl80211_get_country(const char *ifname, char *buf)
1423 {
1424 int rv = -1;
1425 struct nl80211_msg_conveyor *req, *res;
1426
1427 req = nl80211_msg(ifname, NL80211_CMD_GET_REG, 0);
1428 if( req )
1429 {
1430 res = nl80211_send(req);
1431 if( res )
1432 {
1433 if( res->attr[NL80211_ATTR_REG_ALPHA2] )
1434 {
1435 memcpy(buf, nla_data(res->attr[NL80211_ATTR_REG_ALPHA2]), 2);
1436 rv = 0;
1437 }
1438 nl80211_free(res);
1439 }
1440 nl80211_free(req);
1441 }
1442
1443 return rv;
1444 }
1445
1446 int nl80211_get_countrylist(const char *ifname, char *buf, int *len)
1447 {
1448 int i, count;
1449 struct iwinfo_iso3166_label *l;
1450 struct iwinfo_country_entry *e = (struct iwinfo_country_entry *)buf;
1451
1452 for( l = ISO3166_Names, count = 0; l->iso3166; l++, e++, count++ )
1453 {
1454 e->iso3166 = l->iso3166;
1455 e->ccode[0] = (l->iso3166 / 256);
1456 e->ccode[1] = (l->iso3166 % 256);
1457 }
1458
1459 *len = (count * sizeof(struct iwinfo_country_entry));
1460 return 0;
1461 }
1462
1463 int nl80211_get_hwmodelist(const char *ifname, int *buf)
1464 {
1465 int bands_remain, freqs_remain;
1466 struct nl80211_msg_conveyor *req, *res;
1467 struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
1468 struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
1469 struct nlattr *band, *freq;
1470 uint16_t caps = 0;
1471
1472 req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
1473 if( req )
1474 {
1475 res = nl80211_send(req);
1476 if( res )
1477 {
1478 nla_for_each_nested(band,
1479 res->attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
1480 {
1481 nla_parse(bands, NL80211_BAND_ATTR_MAX, nla_data(band),
1482 nla_len(band), NULL);
1483
1484 if( bands[NL80211_BAND_ATTR_HT_CAPA] )
1485 caps = nla_get_u16(bands[NL80211_BAND_ATTR_HT_CAPA]);
1486
1487 /* Treat any nonzero capability as 11n */
1488 if( caps > 0 )
1489 *buf |= IWINFO_80211_N;
1490
1491 nla_for_each_nested(freq,
1492 bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
1493 {
1494 nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
1495 nla_data(freq), nla_len(freq), NULL);
1496
1497 if( !freqs[NL80211_FREQUENCY_ATTR_FREQ] )
1498 continue;
1499
1500 if( nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]) < 2485 )
1501 {
1502 *buf |= IWINFO_80211_B;
1503 *buf |= IWINFO_80211_G;
1504 }
1505 else
1506 {
1507 *buf |= IWINFO_80211_A;
1508 }
1509 }
1510 }
1511 nl80211_free(res);
1512 }
1513 nl80211_free(req);
1514 }
1515
1516 return *buf ? 0 : -1;
1517 }
1518
1519 int nl80211_get_mbssid_support(const char *ifname, int *buf)
1520 {
1521 /* Test whether we can create another interface */
1522 char *nif = nl80211_ifadd(ifname);
1523
1524 if( nif )
1525 {
1526 *buf = (iwinfo_ifmac(nif) && iwinfo_ifup(nif));
1527
1528 iwinfo_ifdown(nif);
1529 nl80211_ifdel(nif);
1530
1531 return 0;
1532 }
1533
1534 return -1;
1535 }