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