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
)
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 set_radio(radio
, unit
);
55 // allow to scan using up to MAX_WLIF_SCAN wireless ifaces
56 #define MAX_WLIF_SCAN 3
67 static int start_scan(int idx
, int unit
, int subunit
, void *param
)
69 scan_list_t
*rp
= param
;
75 if ((idx
>= MAX_WLIF_SCAN
) || (rp
->unit_filter
>= 0 && rp
->unit_filter
!= unit
)) return 0;
77 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
78 memset(&sp
, 0xff, sizeof(sp
)); // most default to -1
80 sp
.bss_type
= DOT11_BSSTYPE_ANY
; // =2
83 if (wl_ioctl(wif
, WLC_GET_AP
, &(rp
->wif
[idx
].ap
), sizeof(rp
->wif
[idx
].ap
)) < 0) {
84 // Unable to get AP mode
87 if (rp
->wif
[idx
].ap
> 0) {
88 wl_ioctl(wif
, WLC_SET_AP
, &zero
, sizeof(zero
));
91 // set scan type based on the ap mode
92 sp
.scan_type
= rp
->wif
[idx
].ap
? DOT11_SCANTYPE_PASSIVE
: -1 /* default */;
94 rp
->wif
[idx
].radio
= get_radio(unit
);
95 if (!(rp
->wif
[idx
].radio
)) set_radio(1, unit
);
99 if (wl_ioctl(wif
, WLC_SCAN
, &sp
, WL_SCAN_PARAMS_FIXED_SIZE
) == 0)
101 if (retry
) usleep(100000);
104 // Unable to start scan
105 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
);
109 static int get_scan_results(int idx
, int unit
, int subunit
, void *param
)
111 scan_list_t
*rp
= param
;
113 if ((idx
>= MAX_WLIF_SCAN
) || (rp
->unit_filter
>= 0 && rp
->unit_filter
!= unit
)) return 0;
118 wl_scan_results_t
*results
;
123 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
125 results
= malloc(WLC_IOCTL_MAXLEN
+ sizeof(*results
));
128 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
);
131 results
->buflen
= WLC_IOCTL_MAXLEN
;
132 results
->version
= WL_BSS_INFO_VERSION
;
134 // Keep trying to obtain scan results for up to 4 secs
135 // Passive scan may require more time, although 1 extra sec is almost always enough.
139 r
= wl_ioctl(wif
, WLC_SCAN_RESULTS
, results
, WLC_IOCTL_MAXLEN
);
144 wl_restore(wif
, unit
, rp
->wif
[idx
].ap
, rp
->wif
[idx
].radio
);
148 // Unable to obtain scan results
152 // format for javascript
163 bssi
= &results
->bss_info
[0];
164 for (i
= 0; i
< results
->count
; ++i
) {
166 channel
= CHSPEC_CHANNEL(bssi
->chanspec
);
167 if (CHSPEC_IS40(bssi
->chanspec
))
168 channel
= channel
+ (CHSPEC_SB_LOWER(bssi
->chanspec
) ? -2 : 2);
173 if (nvram_get_int("wlx_scrubssid")) {
174 for (k
= j
- 1; k
>= 0; --k
) {
176 if (!isprint(c
)) c
= '?';
181 memcpy(ssid
, bssi
->SSID
, j
);
184 ssidj
= js_string(ssid
);
186 web_printf("%c['%s','%s',%u,%u,%d,%d,[", rp
->comma
,
187 ether_etoa(bssi
->BSSID
.octet
, mac
), ssidj
? ssidj
: "",
189 bssi
->capability
, bssi
->RSSI
, bssi
->phy_noise
);
193 for (j
= 0; j
< bssi
->rateset
.count
; ++j
) {
194 web_printf("%s%u", j
? "," : "", bssi
->rateset
.rates
[j
]);
196 web_printf("],%d,%d]", bssi
->n_cap
, bssi
->nbss_cap
);
198 bssi
= (wl_bss_info_t
*)((uint8
*)bssi
+ bssi
->length
);
205 // returns: ['bssid','ssid',channel,capabilities,rssi,noise,[rates,]], or [null,'error message']
206 void asp_wlscan(int argc
, char **argv
)
210 memset(&rp
, 0, sizeof(rp
));
212 rp
.unit_filter
= (argc
> 0) ? atoi(argv
[0]) : (-1);
214 web_puts("\nwlscandata = [");
218 if (foreach_wif(0, &rp
, start_scan
) == 0) {
219 web_puts("[null,'Unable to start scan.']];\n");
226 if (foreach_wif(0, &rp
, get_scan_results
) == 0) {
227 web_puts("[null,'Unable to obtain scan results.']];\n");
234 void wo_wlradio(char *url
)
241 parse_asp("saved.asp");
242 if (nvram_get_int(wl_nvname("radio", unit
, 0))) {
243 if ((enable
= webcgi_get("enable")) != NULL
) {
246 sprintf(sunit
, "%d", unit
);
247 eval("radio", atoi(enable
) ? "on" : "off", sunit
);
253 static int read_noise(int unit
)
257 // WLC_GET_PHY_NOISE = 135
258 if (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit
, 0)), 135, &v
, sizeof(v
)) == 0) {
261 nvram_set(wl_nvname("tnoise", unit
, 0), s
);
267 static int get_wlnoise(int client
, int unit
)
272 v
= read_noise(unit
);
275 v
= nvram_get_int(wl_nvname("tnoise", unit
, 0));
276 if ((v
>= 0) || (v
< -100)) v
= -99;
281 static int print_wlnoise(int idx
, int unit
, int subunit
, void *param
)
283 web_printf("%c%d", (idx
== 0) ? ' ' : ',', get_wlnoise(wl_client(unit
, 0), unit
));
287 void asp_wlnoise(int argc
, char **argv
)
289 web_puts("\nwlnoise = [");
290 foreach_wif(0, NULL
, print_wlnoise
);
294 void wo_wlmnoise(char *url
)
302 parse_asp("mnoise.asp");
306 int radio
= get_radio(unit
);
308 wif
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
309 if (wl_ioctl(wif
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) return;
312 wl_ioctl(wif
, WLC_SET_AP
, &i
, sizeof(i
));
314 for (i
= 10; i
> 0; --i
) {
319 wl_restore(wif
, unit
, ap
, radio
);
322 static int wl_chanfreq(uint ch
, int band
)
324 if ((band
== WLC_BAND_2G
&& (ch
< 1 || ch
> 14)) || (ch
> 200))
326 else if ((band
== WLC_BAND_2G
) && (ch
== 14))
329 return ch
* 5 + ((band
== WLC_BAND_2G
) ? 4814 : 10000) / 2;
332 static int not_wlclient(int idx
, int unit
, int subunit
, void *param
)
334 return (!wl_client(unit
, subunit
));
337 // returns '1' if all wireless interfaces are in client mode, '0' otherwise
338 void asp_wlclient(int argc
, char **argv
)
340 web_puts(foreach_wif(1, NULL
, not_wlclient
) ? "0" : "1");
343 static int print_wlstats(int idx
, int unit
, int subunit
, void *param
)
346 int rate
, client
, nbw
;
347 int chanspec
, channel
, mhz
, band
, scan
;
350 char retbuf
[WLC_IOCTL_SMLEN
];
352 char *ifname
, *ctrlsb
;
354 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
355 client
= wl_client(unit
, 0);
357 /* Get configured phy type */
358 wl_ioctl(ifname
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
360 if (wl_ioctl(ifname
, WLC_GET_RATE
, &rate
, sizeof(rate
)) < 0)
363 if (wl_ioctl(ifname
, WLC_GET_BAND
, &band
, sizeof(band
)) < 0)
364 band
= nvram_get_int(wl_nvname("nband", unit
, 0));
366 channel
= nvram_get_int(wl_nvname("channel", unit
, 0));
370 if (wl_phytype_n(phytype
)) {
371 if (wl_iovar_getint(ifname
, "chanspec", &chanspec
) != 0) {
372 ctrlsb
= nvram_safe_get(wl_nvname("nctrlsb", unit
, 0));
373 nbw
= nvram_get_int(wl_nvname("nbw", unit
, 0));
376 channel
= CHSPEC_CHANNEL(chanspec
);
377 if (CHSPEC_IS40(chanspec
))
378 channel
= channel
+ (CHSPEC_SB_LOWER(chanspec
) ? -2 : 2);
379 ctrlsb
= CHSPEC_SB_LOWER(chanspec
) ? "lower" : (CHSPEC_SB_UPPER(chanspec
) ? "upper" : "none");
380 nbw
= CHSPEC_IS40(chanspec
) ? 40 : 20;
385 if (wl_ioctl(ifname
, WLC_GET_CHANNEL
, &ch
, sizeof(ch
)) == 0) {
386 scan
= (ch
.scan_channel
> 0);
387 channel
= (scan
) ? ch
.scan_channel
: ch
.hw_channel
;
393 mhz
= (channel
) ? wl_chanfreq(channel
, band
) : 0;
394 if (wl_iovar_getint(ifname
, "chanim_enab", (int*)(void*)&chanim_enab
) != 0)
397 if (wl_iovar_getbuf(ifname
, "chanim_state", &chanspec
, sizeof(chanspec
), retbuf
, WLC_IOCTL_SMLEN
) == 0)
398 interference
= *(int*)retbuf
;
401 memset(&rssi
, 0, sizeof(rssi
));
403 if (wl_ioctl(ifname
, WLC_GET_RSSI
, &rssi
, sizeof(rssi
)) != 0)
407 // [ radio, is_client, channel, freq (mhz), rate, nctrlsb, nbw, rssi, noise, interference ]
408 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",
409 (idx
== 0) ? ' ' : ',',
410 get_radio(unit
), client
, (scan
? '-' : ' '), channel
, mhz
, rate
, ctrlsb
, nbw
, rssi
.val
, get_wlnoise(client
, unit
), interference
);
415 void asp_wlstats(int argc
, char **argv
)
417 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
419 web_puts("\nwlstats = [");
420 foreach_wif(include_vifs
, NULL
, print_wlstats
); // AB multiSSID
424 static void web_print_wlchan(uint chan
, int band
)
427 if ((mhz
= wl_chanfreq(chan
, band
)) > 0)
428 web_printf(",[%d, %d]", chan
, mhz
);
430 web_printf(",[%d, 0]", chan
);
433 static int _wlchanspecs(char *ifname
, char *country
, int band
, int bw
, int ctrlsb
)
435 chanspec_t c
= 0, *chanspec
;
437 wl_uint32_list_t
*list
;
440 char *buf
= (char *)malloc(WLC_IOCTL_MAXLEN
);
444 strcpy(buf
, "chanspecs");
445 buflen
= strlen(buf
) + 1;
447 c
|= (band
== WLC_BAND_5G
) ? WL_CHANSPEC_BAND_5G
: WL_CHANSPEC_BAND_2G
;
448 c
|= (bw
== 20) ? WL_CHANSPEC_BW_20
: WL_CHANSPEC_BW_40
;
450 chanspec
= (chanspec_t
*)(buf
+ buflen
);
452 buflen
+= (sizeof(chanspec_t
));
453 strncpy(buf
+ buflen
, country
, WLC_CNTRY_BUF_SZ
);
454 buflen
+= WLC_CNTRY_BUF_SZ
;
457 list
= (wl_uint32_list_t
*)(buf
+ buflen
);
458 list
->count
= WL_NUMCHANSPECS
;
459 buflen
+= sizeof(uint32
)*(WL_NUMCHANSPECS
+ 1);
461 if (wl_ioctl(ifname
, WLC_GET_VAR
, buf
, buflen
) < 0) {
467 list
= (wl_uint32_list_t
*)buf
;
468 for (i
= 0; i
< list
->count
; i
++) {
469 c
= (chanspec_t
)list
->element
[i
];
470 /* Skip upper.. (take only one of the lower or upper) */
471 if (bw
== 40 && (CHSPEC_CTL_SB(c
) != ctrlsb
))
473 /* Create the actual control channel number from sideband */
474 int chan
= CHSPEC_CHANNEL(c
);
476 chan
+= ((ctrlsb
== WL_CHANSPEC_CTL_SB_UPPER
) ? 2 : -2);
477 web_print_wlchan(chan
, band
);
485 static void _wlchannels(char *ifname
, char *country
, int band
)
488 wl_channels_in_country_t
*cic
;
490 cic
= (wl_channels_in_country_t
*)malloc(WLC_IOCTL_MAXLEN
);
492 cic
->buflen
= WLC_IOCTL_MAXLEN
;
493 strcpy(cic
->country_abbrev
, country
);
496 if (wl_ioctl(ifname
, WLC_GET_CHANNELS_IN_COUNTRY
, cic
, cic
->buflen
) == 0) {
497 for (i
= 0; i
< cic
->count
; i
++) {
498 web_print_wlchan(cic
->channel
[i
], band
);
505 void asp_wlchannels(int argc
, char **argv
)
507 char buf
[WLC_CNTRY_BUF_SZ
];
508 int band
, phytype
, nphy
;
509 int bw
, ctrlsb
, chanspec
;
512 // args: unit, nphy[1|0], bw, band, ctrlsb
514 check_wl_unit(argc
> 0 ? argv
[0] : NULL
);
516 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
517 wl_ioctl(ifname
, WLC_GET_COUNTRY
, buf
, sizeof(buf
));
518 if (wl_ioctl(ifname
, WLC_GET_BAND
, &band
, sizeof(band
)) != 0)
519 band
= nvram_get_int(wl_nvname("nband", unit
, 0));
520 wl_iovar_getint(ifname
, "chanspec", &chanspec
);
523 nphy
= atoi(argv
[1]);
525 wl_ioctl(ifname
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
526 nphy
= wl_phytype_n(phytype
);
529 bw
= (argc
> 2) ? atoi(argv
[2]) : 0;
530 bw
= bw
? : CHSPEC_IS40(chanspec
) ? 40 : 20;
532 if (argc
> 3) band
= atoi(argv
[3]) ? : band
;
535 if (strcmp(argv
[4], "upper") == 0)
536 ctrlsb
= WL_CHANSPEC_CTL_SB_UPPER
;
538 ctrlsb
= WL_CHANSPEC_CTL_SB_LOWER
;
541 ctrlsb
= CHSPEC_CTL_SB(chanspec
);
543 web_puts("\nwl_channels = [\n[0, 0]");
545 if (!_wlchanspecs(ifname
, buf
, band
, bw
, ctrlsb
) && band
== WLC_BAND_2G
&& bw
== 40)
546 _wlchanspecs(ifname
, buf
, band
, 20, ctrlsb
);
549 _wlchannels(ifname
, buf
, band
);
553 static int print_wlbands(int idx
, int unit
, int subunit
, void *param
)
555 char *phytype
, *phylist
, *ifname
;
557 int list
[WLC_BAND_ALL
];
560 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, 0));
561 phytype
= nvram_safe_get(wl_nvname("phytype", unit
, 0));
563 web_printf("%c[", (idx
== 0) ? ' ' : ',');
565 if (phytype
[0] == 'n' ||
570 /* Get band list. Assume both the bands in case of error */
571 if (wl_ioctl(ifname
, WLC_GET_BANDLIST
, list
, sizeof(list
)) < 0) {
572 for (i
= WLC_BAND_5G
; i
<= WLC_BAND_2G
; i
++) {
573 web_printf("%c'%d'", comma
, i
);
580 for (i
= 1; i
<= list
[0]; i
++) {
581 web_printf("%c'%d'", comma
, list
[i
]);
587 /* Get available phy types of the currently selected wireless interface */
588 phylist
= nvram_safe_get(wl_nvname("phytypes", unit
, 0));
589 for (i
= 0; i
< strlen(phylist
); i
++) {
590 web_printf("%c'%d'", comma
, phylist
[i
] == 'a' ? WLC_BAND_5G
: WLC_BAND_2G
);
600 void asp_wlbands(int argc
, char **argv
)
602 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
604 web_puts("\nwl_bands = [");
605 foreach_wif(include_vifs
, NULL
, print_wlbands
); // AB multiSSID
609 static int print_wif(int idx
, int unit
, int subunit
, void *param
)
611 char unit_str
[] = "000000";
615 char cap
[WLC_IOCTL_SMLEN
];
616 char caps
[WLC_IOCTL_SMLEN
];
620 snprintf(unit_str
, sizeof(unit_str
), "%d.%d", unit
, subunit
);
622 snprintf(unit_str
, sizeof(unit_str
), "%d", unit
);
624 // wl_iovar_get(wl_nvname("ifname", unit, 0), "cap", (void *)caps, WLC_IOCTL_SMLEN);
625 // wl_iovar_get("eth1", "cap", (void *)caps, WLC_IOCTL_SMLEN);
627 wl_iovar_get(nvram_safe_get(wl_nvname("ifname", unit
, 0)), "cap", (void *)caps
, WLC_IOCTL_SMLEN
);
628 foreach(cap
, caps
, next
) {
629 if (!strcmp(cap
, "mbss16"))
631 if (!strcmp(cap
, "mbss4"))
641 if ((sfd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
)) < 0) {
642 _dprintf("[%s %d]: error opening socket %m\n", __FUNCTION__
, __LINE__
);
646 strcpy(ifr
.ifr_name
, nvram_safe_get(wl_nvname("ifname", unit
, subunit
)));
647 if (ioctl(sfd
, SIOCGIFFLAGS
, &ifr
) == 0)
648 if (ifr
.ifr_flags
& (IFF_UP
| IFF_RUNNING
))
654 ifname
= nvram_safe_get(wl_nvname("ifname", unit
, subunit
));
655 struct ether_addr bssid
;
657 wl_ioctl(ifname
, WLC_GET_BSSID
, &bssid
, ETHER_ADDR_LEN
);
659 // [ifname, unitstr, unit, subunit, ssid, hwaddr, up, wmode, bssid]
660 ssidj
= js_string(nvram_safe_get(wl_nvname("ssid", unit
, subunit
)));
661 web_printf("%c['%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%02X:%02X:%02X:%02X:%02X:%02X']", (idx
== 0) ? ' ' : ',',
662 nvram_safe_get(wl_nvname("ifname", unit
, subunit
)),
663 unit_str
, unit
, subunit
, ssidj
,
664 // // virtual inteface MAC address
665 nvram_safe_get(wl_nvname("hwaddr", unit
, subunit
)), up
, max_no_vifs
, // AB multiSSID
666 nvram_safe_get(wl_nvname("mode", unit
, subunit
)),
667 bssid
.octet
[0], bssid
.octet
[1], bssid
.octet
[2], bssid
.octet
[3], bssid
.octet
[4], bssid
.octet
[5]
674 void asp_wlifaces(int argc
, char **argv
)
676 int include_vifs
= (argc
> 0) ? atoi(argv
[0]) : 0;
678 web_puts("\nwl_ifaces = [");
679 foreach_wif(include_vifs
, NULL
, print_wif
);
683 void asp_wlcountries(int argc
, char **argv
)
685 char s
[128], *p
, *code
, *country
;
689 web_puts("\nwl_countries = [");
690 if ((f
= popen("wl country list", "r")) != NULL
) {
691 // skip the header line
692 fgets(s
, sizeof(s
), f
);
693 while (fgets(s
, sizeof(s
), f
)) {
695 if ((code
= strsep(&p
, " \t\n")) && p
) {
696 country
= strsep(&p
, "\n");
697 if ((country
&& *country
&& strcmp(code
, country
) != 0) ||
698 // special case EU code since the driver may not have a name for it
699 (strcmp(code
, "EU") == 0)) {
700 if (!country
|| *country
== 0) country
= code
;
701 p
= js_string(country
);
702 web_printf("%c['%s', '%s']", i
++ ? ',' : ' ', code
, p
);