4 Copyright (C) 2006-2009 Jonathan Zarate
12 #include <sys/ioctl.h>
14 #ifndef WL_BSS_INFO_VERSION
15 #error WL_BSS_INFO_VERSION
17 #if WL_BSS_INFO_VERSION < 108
18 #error WL_BSS_INFO_VERSION < 108
23 static int subunit
= 0;
25 static void check_wl_unit(const char *unitarg
)
27 char ifname
[12], *wlunit
;
28 unit
= 0; subunit
= 0;
30 wlunit
= (unitarg
&& *unitarg
) ? (char *)unitarg
:
31 webcgi_safeget("_wl_unit", nvram_safe_get("wl_unit"));
32 snprintf(ifname
, sizeof(ifname
), "wl%s", wlunit
);
33 get_ifname_unit(ifname
, &unit
, &subunit
);
35 _dprintf("check_wl_unit: unitarg: %s, _wl_unit: %s, ifname: %s, unit: %d, subunit: %d\n",
36 unitarg
, webcgi_safeget("_wl_unit", nvram_safe_get("wl_unit")), ifname
, unit
, subunit
);
39 static void wl_restore(char *wif
, int unit
, int ap
, int radio
, int scan_time
)
42 wl_ioctl(wif
, WLC_SET_AP
, &ap
, sizeof(ap
));
44 if (!radio
) set_radio(1, unit
);
45 eval("wl", "-i", wif
, "up"); // without this the router may reboot
46 #if WL_BSS_INFO_VERSION >= 108
47 // no idea why this voodoo sequence works to wake up wl -- zzz
48 eval("wl", "-i", wif
, "ssid", "");
49 eval("wl", "-i", wif
, "ssid", nvram_safe_get(wl_nvname("ssid", unit
, 0)));
52 #ifdef WLC_SET_SCAN_CHANNEL_TIME
54 // restore original scan channel time
55 wl_ioctl(wif
, WLC_SET_SCAN_CHANNEL_TIME
, &scan_time
, sizeof(scan_time
));
58 set_radio(radio
, unit
);
61 // allow to scan using up to MAX_WLIF_SCAN wireless ifaces
62 #define MAX_WLIF_SCAN 3
74 static int start_scan(int idx
, int unit
, int subunit
, void *param
)
76 scan_list_t
*rp
= param
;
81 #ifdef WLC_SET_SCAN_CHANNEL_TIME
85 if ((idx
>= MAX_WLIF_SCAN
) || (rp
->unit_filter
>= 0 && rp
->unit_filter
!= unit
)) return 0;
87 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
89 memset(&sp
, 0xff, sizeof(sp
)); // most default to -1
91 sp
.bss_type
= DOT11_BSSTYPE_ANY
; // =2
94 if (wl_ioctl(wif
, WLC_GET_AP
, &(rp
->wif
[idx
].ap
), sizeof(rp
->wif
[idx
].ap
)) < 0) {
95 // Unable to get AP mode
98 if (rp
->wif
[idx
].ap
> 0) {
99 wl_ioctl(wif
, WLC_SET_AP
, &zero
, sizeof(zero
));
102 // set scan type based on the ap mode
103 sp
.scan_type
= rp
->wif
[idx
].ap
? DOT11_SCANTYPE_PASSIVE
: -1 /* default */;
105 #ifdef WLC_SET_SCAN_CHANNEL_TIME
106 // extend scan channel time to get more AP probe resp
107 wl_ioctl(wif
, WLC_GET_SCAN_CHANNEL_TIME
, &(rp
->wif
[idx
].scan_time
), sizeof(rp
->wif
[idx
].scan_time
));
108 if (rp
->wif
[idx
].scan_time
< scan_time
) {
109 wl_ioctl(wif
, WLC_SET_SCAN_CHANNEL_TIME
, &scan_time
, sizeof(scan_time
));
113 rp
->wif
[idx
].radio
= get_radio(unit
);
114 if (!(rp
->wif
[idx
].radio
)) set_radio(1, unit
);
118 if (wl_ioctl(wif
, WLC_SCAN
, &sp
, WL_SCAN_PARAMS_FIXED_SIZE
) == 0)
120 if (retry
) usleep(100000);
123 // Unable to start scan
124 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
, rp
->wif
[idx
].scan_time
);
128 static int get_scan_results(int idx
, int unit
, int subunit
, void *param
)
130 scan_list_t
*rp
= param
;
132 if ((idx
>= MAX_WLIF_SCAN
) || (rp
->unit_filter
>= 0 && rp
->unit_filter
!= unit
)) return 0;
137 wl_scan_results_t
*results
;
142 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
144 results
= malloc(WLC_IOCTL_MAXLEN
+ sizeof(*results
));
147 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
, rp
->wif
[idx
].scan_time
);
150 results
->buflen
= WLC_IOCTL_MAXLEN
;
151 results
->version
= WL_BSS_INFO_VERSION
;
153 // Keep trying to obtain scan results for up to 4 secs
154 // Passive scan may require more time, although 1 extra sec is almost always enough.
158 r
= wl_ioctl(wif
, WLC_SCAN_RESULTS
, results
, WLC_IOCTL_MAXLEN
);
163 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
, rp
->wif
[idx
].scan_time
);
167 // Unable to obtain scan results
171 // format for javascript
182 bssi
= &results
->bss_info
[0];
183 for (i
= 0; i
< results
->count
; ++i
) {
185 channel
= CHSPEC_CHANNEL(bssi
->chanspec
);
186 if (CHSPEC_IS40(bssi
->chanspec
))
187 channel
= channel
+ (CHSPEC_SB_LOWER(bssi
->chanspec
) ? -2 : 2);
192 if (nvram_get_int("wlx_scrubssid")) {
193 for (k
= j
- 1; k
>= 0; --k
) {
195 if (!isprint(c
)) c
= '?';
200 memcpy(ssid
, bssi
->SSID
, j
);
203 ssidj
= js_string(ssid
);
205 web_printf("%c['%s','%s',%u,%u,%d,%d,[", rp
->comma
,
206 ether_etoa(bssi
->BSSID
.octet
, mac
), ssidj
? ssidj
: "",
208 bssi
->capability
, bssi
->RSSI
, bssi
->phy_noise
);
212 for (j
= 0; j
< bssi
->rateset
.count
; ++j
) {
213 web_printf("%s%u", j
? "," : "", bssi
->rateset
.rates
[j
]);
216 web_printf("],%d,%d,%d]",
219 bssi
->n_cap
? (CHSPEC_IS40(bssi
->chanspec
) ? 40 : (CHSPEC_IS20(bssi
->chanspec
) ? 20 : 10)) : 0);
221 bssi
= (wl_bss_info_t
*)((uint8
*)bssi
+ bssi
->length
);
228 // returns: ['bssid','ssid',channel,capabilities,rssi,noise,[rates,]], or [null,'error message']
229 void asp_wlscan(int argc
, char **argv
)
233 memset(&rp
, 0, sizeof(rp
));
235 rp
.unit_filter
= (argc
> 0) ? atoi(argv
[0]) : (-1);
237 web_puts("\nwlscandata = [");
241 if (foreach_wif(0, &rp
, start_scan
) == 0) {
242 web_puts("[null,'Unable to start scan.']];\n");
249 if (foreach_wif(0, &rp
, get_scan_results
) == 0) {
250 web_puts("[null,'Unable to obtain scan results.']];\n");
257 void wo_wlradio(char *url
)
264 parse_asp("saved.asp");
265 if (nvram_get_int(wl_nvname("radio", unit
, 0))) {
266 if ((enable
= webcgi_get("enable")) != NULL
) {
269 sprintf(sunit
, "%d", unit
);
270 eval("radio", atoi(enable
) ? "on" : "off", sunit
);
276 static int read_noise(int unit
)
280 // WLC_GET_PHY_NOISE = 135
281 if (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit
, 0)), 135, &v
, sizeof(v
)) == 0) {
284 nvram_set(wl_nvname("tnoise", unit
, 0), s
);
290 static int get_wlnoise(int client
, int unit
)
295 v
= read_noise(unit
);
298 v
= nvram_get_int(wl_nvname("tnoise", unit
, 0));
299 if ((v
>= 0) || (v
< -100)) v
= -99;
304 static int print_wlnoise(int idx
, int unit
, int subunit
, void *param
)
306 web_printf("%c%d", (idx
== 0) ? ' ' : ',', get_wlnoise(wl_client(unit
, 0), unit
));
310 void asp_wlnoise(int argc
, char **argv
)
312 web_puts("\nwlnoise = [");
313 foreach_wif(0, NULL
, print_wlnoise
);
317 void wo_wlmnoise(char *url
)
325 parse_asp("mnoise.asp");
329 int radio
= get_radio(unit
);
331 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
332 if (wl_ioctl(wif
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) return;
335 wl_ioctl(wif
, WLC_SET_AP
, &i
, sizeof(i
));
337 for (i
= 10; i
> 0; --i
) {
342 wl_restore(wif
, unit
, ap
, radio
, 0);
345 static int wl_chanfreq(uint ch
, int band
)
347 if ((band
== WLC_BAND_2G
&& (ch
< 1 || ch
> 14)) || (ch
> 200))
349 else if ((band
== WLC_BAND_2G
) && (ch
== 14))
352 return ch
* 5 + ((band
== WLC_BAND_2G
) ? 4814 : 10000) / 2;
355 static int not_wlclient(int idx
, int unit
, int subunit
, void *param
)
357 return (!wl_client(unit
, subunit
));
360 // returns '1' if all wireless interfaces are in client mode, '0' otherwise
361 void asp_wlclient(int argc
, char **argv
)
363 web_puts(foreach_wif(1, NULL
, not_wlclient
) ? "0" : "1");
366 static int print_wlstats(int idx
, int unit
, int subunit
, void *param
)
369 int rate
, client
, nbw
;
370 int chanspec
, channel
, mhz
, band
, scan
;
373 char retbuf
[WLC_IOCTL_SMLEN
];
375 char *ifname
, *ctrlsb
;
377 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
378 client
= wl_client(unit
, 0);
380 /* Get configured phy type */
381 wl_ioctl(ifname
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
383 if (wl_ioctl(ifname
, WLC_GET_RATE
, &rate
, sizeof(rate
)) < 0)
386 if (wl_ioctl(ifname
, WLC_GET_BAND
, &band
, sizeof(band
)) < 0)
387 band
= nvram_get_int(wl_nvname("nband", unit
, 0));
389 channel
= nvram_get_int(wl_nvname("channel", unit
, 0));
393 if (wl_phytype_n(phytype
)) {
394 if (wl_iovar_getint(ifname
, "chanspec", &chanspec
) != 0) {
395 ctrlsb
= nvram_safe_get(wl_nvname("nctrlsb", unit
, 0));
396 nbw
= nvram_get_int(wl_nvname("nbw", unit
, 0));
399 channel
= CHSPEC_CHANNEL(chanspec
);
400 if (CHSPEC_IS40(chanspec
))
401 channel
= channel
+ (CHSPEC_SB_LOWER(chanspec
) ? -2 : 2);
402 ctrlsb
= CHSPEC_SB_LOWER(chanspec
) ? "lower" : (CHSPEC_SB_UPPER(chanspec
) ? "upper" : "none");
403 nbw
= CHSPEC_IS40(chanspec
) ? 40 : 20;
408 if (wl_ioctl(ifname
, WLC_GET_CHANNEL
, &ch
, sizeof(ch
)) == 0) {
409 scan
= (ch
.scan_channel
> 0);
410 channel
= (scan
) ? ch
.scan_channel
: ch
.hw_channel
;
416 mhz
= (channel
) ? wl_chanfreq(channel
, band
) : 0;
417 if (wl_iovar_getint(ifname
, "chanim_enab", (int*)(void*)&chanim_enab
) != 0)
420 if (wl_iovar_getbuf(ifname
, "chanim_state", &chanspec
, sizeof(chanspec
), retbuf
, WLC_IOCTL_SMLEN
) == 0)
421 interference
= *(int*)retbuf
;
424 memset(&rssi
, 0, sizeof(rssi
));
426 if (wl_ioctl(ifname
, WLC_GET_RSSI
, &rssi
, sizeof(rssi
)) != 0)
430 // [ radio, is_client, channel, freq (mhz), rate, nctrlsb, nbw, rssi, noise, interference ]
431 web_printf("%c{ radio: %d, client: %d, channel: %c%d, mhz: %d, rate: %d, ctrlsb: '%s', nbw: %d, rssi: %d, noise: %d, intf: %d }\n",
432 (idx
== 0) ? ' ' : ',',
433 get_radio(unit
), client
, (scan
? '-' : ' '), channel
, mhz
, rate
, ctrlsb
, nbw
, rssi
.val
, get_wlnoise(client
, unit
), interference
);
438 void asp_wlstats(int argc
, char **argv
)
440 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
442 web_puts("\nwlstats = [");
443 foreach_wif(include_vifs
, NULL
, print_wlstats
); // AB multiSSID
447 static void web_print_wlchan(uint chan
, int band
)
450 if ((mhz
= wl_chanfreq(chan
, band
)) > 0)
451 web_printf(",[%d, %d]", chan
, mhz
);
453 web_printf(",[%d, 0]", chan
);
456 static int _wlchanspecs(char *ifname
, char *country
, int band
, int bw
, int ctrlsb
)
458 chanspec_t c
= 0, *chanspec
;
460 wl_uint32_list_t
*list
;
463 char *buf
= (char *)malloc(WLC_IOCTL_MAXLEN
);
467 strcpy(buf
, "chanspecs");
468 buflen
= strlen(buf
) + 1;
470 c
|= (band
== WLC_BAND_5G
) ? WL_CHANSPEC_BAND_5G
: WL_CHANSPEC_BAND_2G
;
471 c
|= (bw
== 20) ? WL_CHANSPEC_BW_20
: WL_CHANSPEC_BW_40
;
473 chanspec
= (chanspec_t
*)(buf
+ buflen
);
475 buflen
+= (sizeof(chanspec_t
));
476 strncpy(buf
+ buflen
, country
, WLC_CNTRY_BUF_SZ
);
477 buflen
+= WLC_CNTRY_BUF_SZ
;
480 list
= (wl_uint32_list_t
*)(buf
+ buflen
);
481 list
->count
= WL_NUMCHANSPECS
;
482 buflen
+= sizeof(uint32
)*(WL_NUMCHANSPECS
+ 1);
484 if (wl_ioctl(ifname
, WLC_GET_VAR
, buf
, buflen
) < 0) {
490 list
= (wl_uint32_list_t
*)buf
;
491 for (i
= 0; i
< list
->count
; i
++) {
492 c
= (chanspec_t
)list
->element
[i
];
493 /* Skip upper.. (take only one of the lower or upper) */
494 if (bw
== 40 && (CHSPEC_CTL_SB(c
) != ctrlsb
))
496 /* Create the actual control channel number from sideband */
497 int chan
= CHSPEC_CHANNEL(c
);
499 chan
+= ((ctrlsb
== WL_CHANSPEC_CTL_SB_UPPER
) ? 2 : -2);
500 web_print_wlchan(chan
, band
);
508 static void _wlchannels(char *ifname
, char *country
, int band
)
511 wl_channels_in_country_t
*cic
;
513 cic
= (wl_channels_in_country_t
*)malloc(WLC_IOCTL_MAXLEN
);
515 cic
->buflen
= WLC_IOCTL_MAXLEN
;
516 strcpy(cic
->country_abbrev
, country
);
519 if (wl_ioctl(ifname
, WLC_GET_CHANNELS_IN_COUNTRY
, cic
, cic
->buflen
) == 0) {
520 for (i
= 0; i
< cic
->count
; i
++) {
521 web_print_wlchan(cic
->channel
[i
], band
);
528 void asp_wlchannels(int argc
, char **argv
)
530 char buf
[WLC_CNTRY_BUF_SZ
];
531 int band
, phytype
, nphy
;
532 int bw
, ctrlsb
, chanspec
;
535 // args: unit, nphy[1|0], bw, band, ctrlsb
537 check_wl_unit(argc
> 0 ? argv
[0] : NULL
);
539 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
540 wl_ioctl(ifname
, WLC_GET_COUNTRY
, buf
, sizeof(buf
));
541 if (wl_ioctl(ifname
, WLC_GET_BAND
, &band
, sizeof(band
)) != 0)
542 band
= nvram_get_int(wl_nvname("nband", unit
, 0));
543 wl_iovar_getint(ifname
, "chanspec", &chanspec
);
546 nphy
= atoi(argv
[1]);
548 wl_ioctl(ifname
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
549 nphy
= wl_phytype_n(phytype
);
552 bw
= (argc
> 2) ? atoi(argv
[2]) : 0;
553 bw
= bw
? : CHSPEC_IS40(chanspec
) ? 40 : 20;
555 if (argc
> 3) band
= atoi(argv
[3]) ? : band
;
558 if (strcmp(argv
[4], "upper") == 0)
559 ctrlsb
= WL_CHANSPEC_CTL_SB_UPPER
;
561 ctrlsb
= WL_CHANSPEC_CTL_SB_LOWER
;
564 ctrlsb
= CHSPEC_CTL_SB(chanspec
);
566 web_puts("\nwl_channels = [\n[0, 0]");
568 if (!_wlchanspecs(ifname
, buf
, band
, bw
, ctrlsb
) && band
== WLC_BAND_2G
&& bw
== 40)
569 _wlchanspecs(ifname
, buf
, band
, 20, ctrlsb
);
572 _wlchannels(ifname
, buf
, band
);
576 static int print_wlbands(int idx
, int unit
, int subunit
, void *param
)
578 char *phytype
, *phylist
, *ifname
;
580 int list
[WLC_BAND_ALL
];
583 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
584 phytype
= nvram_safe_get(wl_nvname("phytype", unit
, 0));
586 web_printf("%c[", (idx
== 0) ? ' ' : ',');
588 if (phytype
[0] == 'n' ||
593 /* Get band list. Assume both the bands in case of error */
594 if (wl_ioctl(ifname
, WLC_GET_BANDLIST
, list
, sizeof(list
)) < 0) {
595 for (i
= WLC_BAND_5G
; i
<= WLC_BAND_2G
; i
++) {
596 web_printf("%c'%d'", comma
, i
);
603 for (i
= 1; i
<= list
[0]; i
++) {
604 web_printf("%c'%d'", comma
, list
[i
]);
610 /* Get available phy types of the currently selected wireless interface */
611 phylist
= nvram_safe_get(wl_nvname("phytypes", unit
, 0));
612 for (i
= 0; i
< strlen(phylist
); i
++) {
613 web_printf("%c'%d'", comma
, phylist
[i
] == 'a' ? WLC_BAND_5G
: WLC_BAND_2G
);
623 void asp_wlbands(int argc
, char **argv
)
625 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
627 web_puts("\nwl_bands = [");
628 foreach_wif(include_vifs
, NULL
, print_wlbands
); // AB multiSSID
632 static int print_wif(int idx
, int unit
, int subunit
, void *param
)
634 char unit_str
[] = "000000";
638 snprintf(unit_str
, sizeof(unit_str
), "%d.%d", unit
, subunit
);
640 snprintf(unit_str
, sizeof(unit_str
), "%d", unit
);
646 if ((sfd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
)) < 0) {
647 _dprintf("[%s %d]: error opening socket %m\n", __FUNCTION__
, __LINE__
);
651 strcpy(ifr
.ifr_name
, nvram_safe_get(wl_nvname("ifname", unit
, subunit
)));
652 if (ioctl(sfd
, SIOCGIFFLAGS
, &ifr
) == 0)
653 up
= (ifr
.ifr_flags
& IFF_UP
);
656 // [ifname, unitstr, unit, subunit, ssid, hwaddr]
657 ssidj
= js_string(nvram_safe_get(wl_nvname("ssid", unit
, subunit
)));
658 web_printf("%c['%s','%s',%d,%d,'%s','%s',%d]", (idx
== 0) ? ' ' : ',',
659 nvram_safe_get(wl_nvname("ifname", unit
, subunit
)),
660 unit_str
, unit
, subunit
, ssidj
,
661 // assume the slave inteface MAC address is the same as the primary interface
662 // nvram_safe_get(wl_nvname("hwaddr", unit, 0))
663 // // virtual inteface MAC address
664 nvram_safe_get(wl_nvname("hwaddr", unit
, subunit
)), up
// AB multiSSID
671 void asp_wlifaces(int argc
, char **argv
)
673 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
675 web_puts("\nwl_ifaces = [");
676 foreach_wif(include_vifs
, NULL
, print_wif
);
680 void asp_wlcountries(int argc
, char **argv
)
682 char s
[128], *p
, *code
, *country
;
686 web_puts("\nwl_countries = [");
687 if ((f
= popen("wl country list", "r")) != NULL
) {
688 // skip the header line
689 fgets(s
, sizeof(s
), f
);
690 while (fgets(s
, sizeof(s
), f
)) {
692 if ((code
= strsep(&p
, " \t\n")) && p
) {
693 country
= strsep(&p
, "\n");
694 if ((country
&& *country
&& strcmp(code
, country
) != 0) ||
695 // special case EU code since the driver may not have a name for it
696 (strcmp(code
, "EU") == 0)) {
697 if (!country
|| *country
== 0) country
= code
;
698 p
= js_string(country
);
699 web_printf("%c['%s', '%s']", i
++ ? ',' : ' ', code
, p
);