1 1.46 rillig /* $NetBSD: wiconfig.c,v 1.46 2025/06/15 14:49:32 rillig Exp $ */ 2 1.1 sommerfe /* 3 1.1 sommerfe * Copyright (c) 1997, 1998, 1999 4 1.1 sommerfe * Bill Paul <wpaul (at) ctr.columbia.edu>. All rights reserved. 5 1.1 sommerfe * 6 1.1 sommerfe * Redistribution and use in source and binary forms, with or without 7 1.1 sommerfe * modification, are permitted provided that the following conditions 8 1.1 sommerfe * are met: 9 1.1 sommerfe * 1. Redistributions of source code must retain the above copyright 10 1.1 sommerfe * notice, this list of conditions and the following disclaimer. 11 1.1 sommerfe * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 sommerfe * notice, this list of conditions and the following disclaimer in the 13 1.1 sommerfe * documentation and/or other materials provided with the distribution. 14 1.1 sommerfe * 3. All advertising materials mentioning features or use of this software 15 1.1 sommerfe * must display the following acknowledgement: 16 1.1 sommerfe * This product includes software developed by Bill Paul. 17 1.1 sommerfe * 4. Neither the name of the author nor the names of any co-contributors 18 1.1 sommerfe * may be used to endorse or promote products derived from this software 19 1.1 sommerfe * without specific prior written permission. 20 1.1 sommerfe * 21 1.1 sommerfe * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 22 1.1 sommerfe * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 1.1 sommerfe * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 1.1 sommerfe * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 25 1.1 sommerfe * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 1.1 sommerfe * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 1.1 sommerfe * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.1 sommerfe * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 sommerfe * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 sommerfe * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 1.1 sommerfe * THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 sommerfe * 33 1.8 enami * From: Id: wicontrol.c,v 1.6 1999/05/22 16:12:49 wpaul Exp $ 34 1.1 sommerfe */ 35 1.1 sommerfe 36 1.1 sommerfe #include <sys/types.h> 37 1.1 sommerfe #include <sys/cdefs.h> 38 1.1 sommerfe #include <sys/param.h> 39 1.1 sommerfe #include <sys/socket.h> 40 1.1 sommerfe #include <sys/ioctl.h> 41 1.1 sommerfe 42 1.1 sommerfe #include <net/if.h> 43 1.2 explorer #ifdef __FreeBSD__ 44 1.2 explorer #include <net/if_var.h> 45 1.2 explorer #include <net/ethernet.h> 46 1.2 explorer 47 1.2 explorer #include <machine/if_wavelan_ieee.h> 48 1.2 explorer #else 49 1.2 explorer #include <netinet/in.h> 50 1.2 explorer #include <netinet/if_ether.h> 51 1.2 explorer #ifdef __NetBSD__ 52 1.31 dyoung #include <net80211/ieee80211.h> 53 1.31 dyoung #include <net80211/ieee80211_ioctl.h> 54 1.12 ichiro #include <dev/ic/wi_ieee.h> 55 1.2 explorer #else 56 1.2 explorer #include <dev/pcmcia/if_wavelan_ieee.h> 57 1.2 explorer #endif 58 1.2 explorer #endif 59 1.1 sommerfe 60 1.1 sommerfe #include <stdio.h> 61 1.1 sommerfe #include <string.h> 62 1.2 explorer #include <ctype.h> 63 1.1 sommerfe #include <stdlib.h> 64 1.1 sommerfe #include <unistd.h> 65 1.1 sommerfe #include <errno.h> 66 1.1 sommerfe #include <err.h> 67 1.1 sommerfe 68 1.41 lukem __COPYRIGHT("@(#) Copyright (c) 1997, 1998, 1999\ 69 1.41 lukem Bill Paul. All rights reserved."); 70 1.46 rillig __RCSID("$NetBSD: wiconfig.c,v 1.46 2025/06/15 14:49:32 rillig Exp $"); 71 1.1 sommerfe 72 1.7 enami struct wi_table { 73 1.7 enami int wi_type; 74 1.7 enami int wi_code; 75 1.7 enami #define WI_NONE 0x00 76 1.7 enami #define WI_STRING 0x01 77 1.7 enami #define WI_BOOL 0x02 78 1.7 enami #define WI_WORDS 0x03 79 1.7 enami #define WI_HEXBYTES 0x04 80 1.7 enami #define WI_KEYSTRUCT 0x05 81 1.22 dbj #define WI_BITS 0x06 82 1.29 perry #define WI_VENDOR 0x07 83 1.42 lukem const char *wi_label; /* label used to print info */ 84 1.7 enami int wi_opt; /* option character to set this */ 85 1.42 lukem const char *wi_desc; 86 1.7 enami char *wi_optval; 87 1.7 enami }; 88 1.7 enami 89 1.15 ichiro /* already define in wireg.h XXX */ 90 1.15 ichiro #define WI_APRATE_0 0x00 /* NONE */ 91 1.15 ichiro #define WI_APRATE_1 0x0A /* 1 Mbps */ 92 1.15 ichiro #define WI_APRATE_2 0x14 /* 2 Mbps */ 93 1.15 ichiro #define WI_APRATE_5 0x37 /* 5.5 Mbps */ 94 1.15 ichiro #define WI_APRATE_11 0x6E /* 11 Mbps */ 95 1.15 ichiro 96 1.18 christos #ifdef WI_RID_SCAN_APS 97 1.43 joerg static void wi_apscan(char *); 98 1.43 joerg static int get_if_flags(int, const char *); 99 1.43 joerg static int set_if_flags(int, const char *, int); 100 1.18 christos #endif 101 1.43 joerg static int wi_getval(char *, struct wi_req *); 102 1.43 joerg static void wi_setval(char *, struct wi_req *); 103 1.43 joerg static void wi_printstr(struct wi_req *); 104 1.43 joerg static void wi_setstr(char *, int, char *); 105 1.43 joerg static void wi_setbytes(char *, int, char *, int); 106 1.43 joerg static void wi_setword(char *, int, int); 107 1.43 joerg static void wi_sethex(char *, int, char *); 108 1.43 joerg static void wi_printwords(struct wi_req *); 109 1.43 joerg static void wi_printbool(struct wi_req *); 110 1.43 joerg static void wi_printhex(struct wi_req *); 111 1.43 joerg static void wi_printbits(struct wi_req *); 112 1.43 joerg static void wi_checkwifi(char *); 113 1.43 joerg static void wi_dumpinfo(char *); 114 1.43 joerg static void wi_printkeys(struct wi_req *); 115 1.43 joerg static void wi_printvendor(struct wi_req *); 116 1.43 joerg static void wi_dumpstats(char *); 117 1.43 joerg __dead static void usage(void); 118 1.43 joerg static struct wi_table *wi_optlookup(struct wi_table *, int); 119 1.1 sommerfe 120 1.18 christos #ifdef WI_RID_SCAN_APS 121 1.43 joerg static int 122 1.43 joerg get_if_flags(int s, const char *name) 123 1.17 ichiro { 124 1.17 ichiro struct ifreq ifreq; 125 1.17 ichiro int flags; 126 1.17 ichiro 127 1.17 ichiro strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name)); 128 1.17 ichiro if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifreq) == -1) 129 1.17 ichiro err(1, "SIOCGIFFLAGS"); 130 1.17 ichiro flags = ifreq.ifr_flags; 131 1.17 ichiro 132 1.17 ichiro return flags; 133 1.17 ichiro } 134 1.17 ichiro 135 1.43 joerg static int 136 1.43 joerg set_if_flags(int s, const char *name, int flags) 137 1.17 ichiro { 138 1.17 ichiro struct ifreq ifreq; 139 1.17 ichiro 140 1.17 ichiro ifreq.ifr_flags = flags; 141 1.17 ichiro strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name)); 142 1.17 ichiro if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifreq) == -1) 143 1.17 ichiro err(1, "SIOCSIFFLAGS"); 144 1.17 ichiro 145 1.17 ichiro return 0; 146 1.17 ichiro } 147 1.17 ichiro 148 1.43 joerg static void 149 1.43 joerg wi_apscan(char *iface) 150 1.15 ichiro { 151 1.15 ichiro struct wi_req wreq; 152 1.15 ichiro struct ifreq ifr; 153 1.15 ichiro int s; 154 1.15 ichiro int naps, rate; 155 1.15 ichiro int retries = 10; 156 1.17 ichiro int flags; 157 1.45 riastrad struct wi_apinfo aps[howmany(WI_MAX_DATALEN, 158 1.45 riastrad sizeof(struct wi_apinfo))]; 159 1.15 ichiro int i, j; 160 1.15 ichiro 161 1.15 ichiro if (iface == NULL) 162 1.15 ichiro errx(1, "must specify interface name"); 163 1.17 ichiro 164 1.17 ichiro s = socket(AF_INET, SOCK_DGRAM, 0); 165 1.17 ichiro if (s == -1) 166 1.17 ichiro err(1, "socket"); 167 1.17 ichiro flags = get_if_flags(s, iface); 168 1.17 ichiro if ((flags & IFF_UP) == 0) 169 1.17 ichiro flags = set_if_flags(s, iface, flags | IFF_UP); 170 1.17 ichiro 171 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 172 1.15 ichiro 173 1.15 ichiro wreq.wi_type = WI_RID_SCAN_APS; 174 1.15 ichiro wreq.wi_len = 4; 175 1.15 ichiro /* note chan. 1 is the least significant bit */ 176 1.32 dyoung wreq.wi_val[0] = htole16(0x3fff); /* 1 bit per channel, 1-14 */ 177 1.32 dyoung wreq.wi_val[1] = htole16(0xf); /* tx rate */ 178 1.15 ichiro 179 1.15 ichiro /* write the request */ 180 1.15 ichiro wi_setval(iface, &wreq); 181 1.15 ichiro 182 1.15 ichiro /* now poll for a result */ 183 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 184 1.15 ichiro 185 1.15 ichiro wreq.wi_type = WI_RID_READ_APS; 186 1.15 ichiro wreq.wi_len = WI_MAX_DATALEN; 187 1.15 ichiro 188 1.15 ichiro /* we have to do this ourself as opposed to 189 1.19 ichiro * using getval, because we cannot bail if 190 1.15 ichiro * the ioctl fails 191 1.15 ichiro */ 192 1.46 rillig memset(&ifr, 0, sizeof(ifr)); 193 1.46 rillig strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); 194 1.46 rillig ifr.ifr_data = (caddr_t)&wreq; 195 1.15 ichiro 196 1.15 ichiro printf("scanning ..."); 197 1.17 ichiro fflush(stdout); 198 1.15 ichiro while (ioctl(s, SIOCGWAVELAN, &ifr) == -1) { 199 1.15 ichiro retries--; 200 1.15 ichiro if (retries >= 0) { 201 1.46 rillig printf("."); 202 1.46 rillig fflush(stdout); 203 1.15 ichiro sleep(1); 204 1.15 ichiro } else 205 1.15 ichiro break; 206 1.15 ichiro errno = 0; 207 1.15 ichiro } 208 1.15 ichiro 209 1.15 ichiro if (errno) { 210 1.17 ichiro set_if_flags(s, iface, flags); 211 1.17 ichiro close(s); 212 1.15 ichiro err(1, "ioctl"); 213 1.15 ichiro } 214 1.15 ichiro 215 1.45 riastrad memcpy(&naps, wreq.wi_val, sizeof(int)); 216 1.23 dbj 217 1.23 dbj if (naps > 0) 218 1.23 dbj printf("\nAP Information\n"); 219 1.23 dbj else 220 1.23 dbj printf("\nNo APs available\n"); 221 1.23 dbj 222 1.45 riastrad naps = MIN((unsigned)naps, 223 1.45 riastrad howmany(sizeof(wreq.wi_val) - sizeof(int), sizeof(*aps))); 224 1.45 riastrad memcpy(aps, (const char *)wreq.wi_val + sizeof(int), 225 1.45 riastrad (unsigned)naps * sizeof(*aps)); 226 1.45 riastrad 227 1.45 riastrad for (i = 0; i < naps; i++) { 228 1.45 riastrad const struct wi_apinfo *const w = &aps[i]; 229 1.45 riastrad 230 1.15 ichiro printf("ap[%d]:\n", i); 231 1.15 ichiro if (w->scanreason) { 232 1.42 lukem static const char *scanm[] = { 233 1.15 ichiro "Host initiated", 234 1.15 ichiro "Firmware initiated", 235 1.15 ichiro "Inquiry request from host" 236 1.15 ichiro }; 237 1.15 ichiro printf("\tScanReason:\t\t\t[ %s ]\n", 238 1.46 rillig scanm[w->scanreason - 1]); 239 1.15 ichiro } 240 1.15 ichiro printf("\tnetname (SSID):\t\t\t[ "); 241 1.46 rillig for (j = 0; j < w->namelen; j++) 242 1.46 rillig printf("%c", w->name[j]); 243 1.46 rillig printf(" ]\n"); 244 1.15 ichiro printf("\tBSSID:\t\t\t\t[ %02x:%02x:%02x:%02x:%02x:%02x ]\n", 245 1.46 rillig w->bssid[0] & 0xff, w->bssid[1] & 0xff, 246 1.46 rillig w->bssid[2] & 0xff, w->bssid[3] & 0xff, 247 1.46 rillig w->bssid[4] & 0xff, w->bssid[5] & 0xff); 248 1.15 ichiro printf("\tChannel:\t\t\t[ %d ]\n", w->channel); 249 1.46 rillig printf("" 250 1.46 rillig "\tQuality/Signal/Noise [signal]:\t[ %d / %d / %d ]\n" 251 1.46 rillig "\t [dBm]:\t[ %d / %d / %d ]\n", 252 1.46 rillig w->quality, w->signal, w->noise, 253 1.46 rillig w->quality, w->signal - 149, w->noise - 149); 254 1.46 rillig printf("\tBSS Beacon Interval [msec]:\t[ %d ]\n", w->interval); 255 1.46 rillig printf("\tCapinfo:\t\t\t[ "); 256 1.46 rillig if (w->capinfo & IEEE80211_CAPINFO_ESS) 257 1.46 rillig printf("ESS "); 258 1.46 rillig if (w->capinfo & IEEE80211_CAPINFO_PRIVACY) 259 1.46 rillig printf("WEP "); 260 1.46 rillig printf("]\n"); 261 1.15 ichiro 262 1.15 ichiro switch (w->rate) { 263 1.15 ichiro case WI_APRATE_1: 264 1.15 ichiro rate = 1; 265 1.15 ichiro break; 266 1.15 ichiro case WI_APRATE_2: 267 1.15 ichiro rate = 2; 268 1.15 ichiro break; 269 1.15 ichiro case WI_APRATE_5: 270 1.44 joerg rate = 5; 271 1.15 ichiro break; 272 1.15 ichiro case WI_APRATE_11: 273 1.15 ichiro rate = 11; 274 1.15 ichiro break; 275 1.15 ichiro case WI_APRATE_0: 276 1.15 ichiro default: 277 1.15 ichiro rate = 0; 278 1.15 ichiro break; 279 1.15 ichiro } 280 1.46 rillig if (rate) 281 1.46 rillig printf("\tDataRate [Mbps]:\t\t[ %d ]\n", rate); 282 1.15 ichiro } 283 1.17 ichiro 284 1.17 ichiro set_if_flags(s, iface, flags); 285 1.17 ichiro close(s); 286 1.15 ichiro } 287 1.18 christos #endif 288 1.15 ichiro 289 1.43 joerg static int 290 1.43 joerg wi_getval(char *iface, struct wi_req *wreq) 291 1.1 sommerfe { 292 1.1 sommerfe struct ifreq ifr; 293 1.39 elad int s, error; 294 1.1 sommerfe 295 1.39 elad error = 0; 296 1.46 rillig memset(&ifr, 0, sizeof(ifr)); 297 1.1 sommerfe 298 1.30 itojun strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); 299 1.1 sommerfe ifr.ifr_data = (caddr_t)wreq; 300 1.1 sommerfe 301 1.1 sommerfe s = socket(AF_INET, SOCK_DGRAM, 0); 302 1.1 sommerfe 303 1.1 sommerfe if (s == -1) 304 1.1 sommerfe err(1, "socket"); 305 1.1 sommerfe 306 1.39 elad if (ioctl(s, SIOCGWAVELAN, &ifr) == -1) { 307 1.39 elad warn("SIOCGWAVELAN(wreq %04x)", wreq->wi_type); 308 1.39 elad error = 1; 309 1.39 elad } 310 1.1 sommerfe 311 1.1 sommerfe close(s); 312 1.1 sommerfe 313 1.39 elad return error; 314 1.1 sommerfe } 315 1.1 sommerfe 316 1.43 joerg static void 317 1.43 joerg wi_setval(char *iface, struct wi_req *wreq) 318 1.1 sommerfe { 319 1.1 sommerfe struct ifreq ifr; 320 1.1 sommerfe int s; 321 1.1 sommerfe 322 1.46 rillig memset(&ifr, 0, sizeof(ifr)); 323 1.1 sommerfe 324 1.30 itojun strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); 325 1.1 sommerfe ifr.ifr_data = (caddr_t)wreq; 326 1.1 sommerfe 327 1.1 sommerfe s = socket(AF_INET, SOCK_DGRAM, 0); 328 1.1 sommerfe 329 1.1 sommerfe if (s == -1) 330 1.1 sommerfe err(1, "socket"); 331 1.1 sommerfe 332 1.1 sommerfe if (ioctl(s, SIOCSWAVELAN, &ifr) == -1) 333 1.1 sommerfe err(1, "SIOCSWAVELAN"); 334 1.1 sommerfe 335 1.1 sommerfe close(s); 336 1.1 sommerfe } 337 1.1 sommerfe 338 1.43 joerg static void 339 1.43 joerg wi_printstr(struct wi_req *wreq) 340 1.1 sommerfe { 341 1.1 sommerfe char *ptr; 342 1.1 sommerfe int i; 343 1.1 sommerfe 344 1.1 sommerfe if (wreq->wi_type == WI_RID_SERIALNO) { 345 1.1 sommerfe ptr = (char *)&wreq->wi_val; 346 1.1 sommerfe for (i = 0; i < (wreq->wi_len - 1) * 2; i++) { 347 1.1 sommerfe if (ptr[i] == '\0') 348 1.1 sommerfe ptr[i] = ' '; 349 1.1 sommerfe } 350 1.1 sommerfe } else { 351 1.14 tsubai int len = le16toh(wreq->wi_val[0]); 352 1.14 tsubai 353 1.1 sommerfe ptr = (char *)&wreq->wi_val[1]; 354 1.14 tsubai for (i = 0; i < len; i++) { 355 1.1 sommerfe if (ptr[i] == '\0') 356 1.1 sommerfe ptr[i] = ' '; 357 1.1 sommerfe } 358 1.1 sommerfe } 359 1.1 sommerfe 360 1.1 sommerfe ptr[i] = '\0'; 361 1.1 sommerfe printf("[ %s ]", ptr); 362 1.1 sommerfe } 363 1.1 sommerfe 364 1.43 joerg static void 365 1.43 joerg wi_setstr(char *iface, int code, char *str) 366 1.1 sommerfe { 367 1.1 sommerfe struct wi_req wreq; 368 1.1 sommerfe 369 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 370 1.1 sommerfe 371 1.1 sommerfe if (strlen(str) > 30) 372 1.1 sommerfe errx(1, "string too long"); 373 1.1 sommerfe 374 1.1 sommerfe wreq.wi_type = code; 375 1.1 sommerfe wreq.wi_len = 18; 376 1.14 tsubai wreq.wi_val[0] = htole16(strlen(str)); 377 1.1 sommerfe bcopy(str, (char *)&wreq.wi_val[1], strlen(str)); 378 1.1 sommerfe 379 1.1 sommerfe wi_setval(iface, &wreq); 380 1.1 sommerfe } 381 1.1 sommerfe 382 1.43 joerg static void 383 1.43 joerg wi_setbytes(char *iface, int code, char *bytes, int len) 384 1.1 sommerfe { 385 1.1 sommerfe struct wi_req wreq; 386 1.1 sommerfe 387 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 388 1.1 sommerfe 389 1.1 sommerfe wreq.wi_type = code; 390 1.1 sommerfe wreq.wi_len = (len / 2) + 1; 391 1.1 sommerfe bcopy(bytes, (char *)&wreq.wi_val[0], len); 392 1.1 sommerfe 393 1.1 sommerfe wi_setval(iface, &wreq); 394 1.1 sommerfe } 395 1.1 sommerfe 396 1.43 joerg static void 397 1.43 joerg wi_setword(char *iface, int code, int word) 398 1.1 sommerfe { 399 1.1 sommerfe struct wi_req wreq; 400 1.1 sommerfe 401 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 402 1.1 sommerfe 403 1.1 sommerfe wreq.wi_type = code; 404 1.1 sommerfe wreq.wi_len = 2; 405 1.14 tsubai wreq.wi_val[0] = htole16(word); 406 1.1 sommerfe 407 1.1 sommerfe wi_setval(iface, &wreq); 408 1.1 sommerfe } 409 1.1 sommerfe 410 1.43 joerg static void 411 1.43 joerg wi_sethex(char *iface, int code, char *str) 412 1.1 sommerfe { 413 1.1 sommerfe struct ether_addr *addr; 414 1.1 sommerfe 415 1.1 sommerfe addr = ether_aton(str); 416 1.1 sommerfe if (addr == NULL) 417 1.1 sommerfe errx(1, "badly formatted address"); 418 1.1 sommerfe 419 1.1 sommerfe wi_setbytes(iface, code, (char *)addr, ETHER_ADDR_LEN); 420 1.1 sommerfe } 421 1.1 sommerfe 422 1.43 joerg static void 423 1.43 joerg wi_printkeys(struct wi_req *wreq) 424 1.2 explorer { 425 1.46 rillig int i, j, bn; 426 1.46 rillig struct wi_key *k; 427 1.46 rillig struct wi_ltv_keys *keys; 428 1.46 rillig char *ptr; 429 1.2 explorer 430 1.2 explorer keys = (struct wi_ltv_keys *)wreq; 431 1.2 explorer 432 1.2 explorer for (i = 0, bn = 0; i < 4; i++, bn = 0) { 433 1.46 rillig k = &keys->wi_keys[i]; 434 1.46 rillig ptr = (char *)k->wi_keydat; 435 1.46 rillig for (j = 0; j < le16toh(k->wi_keylen); j++) { 436 1.46 rillig if (!isprint((unsigned char)ptr[j])) { 437 1.46 rillig bn = 1; 438 1.2 explorer break; 439 1.2 explorer } 440 1.2 explorer } 441 1.2 explorer 442 1.46 rillig if (bn) { 443 1.46 rillig printf("[ 0x"); 444 1.46 rillig for (j = 0; j < le16toh(k->wi_keylen); j++) 445 1.46 rillig printf("%02x", ((unsigned char *)ptr)[j]); 446 1.2 explorer printf(" ]"); 447 1.2 explorer } else { 448 1.46 rillig ptr[j] = '\0'; 449 1.2 explorer printf("[ %s ]", ptr); 450 1.2 explorer } 451 1.46 rillig } 452 1.46 rillig } 453 1.2 explorer 454 1.43 joerg static void 455 1.43 joerg wi_printvendor(struct wi_req *wreq) 456 1.29 perry { 457 1.29 perry /* id 458 1.29 perry * vendor 459 1.29 perry * firmware major 460 1.29 perry * minor 461 1.29 perry */ 462 1.29 perry #define WI_RID_STA_IDENTITY_LUCENT 0x1 463 1.29 perry #define WI_RID_STA_IDENTITY_PRISMII 0x2 464 1.29 perry #define WI_RID_STA_IDENTITY_SAMSUNG 0x3 465 1.29 perry #define WI_RID_STA_IDENTITY_DLINK 0x6 466 1.46 rillig 467 1.29 perry const char *vendor = "Unknown"; 468 1.29 perry 469 1.29 perry if (wreq->wi_len < 4) 470 1.29 perry return; 471 1.29 perry 472 1.32 dyoung switch (le16toh(wreq->wi_val[1])) { 473 1.29 perry case WI_RID_STA_IDENTITY_LUCENT: 474 1.29 perry vendor = "Lucent"; 475 1.29 perry break; 476 1.29 perry case WI_RID_STA_IDENTITY_PRISMII: 477 1.29 perry vendor = "generic PRISM II"; 478 1.29 perry break; 479 1.29 perry case WI_RID_STA_IDENTITY_SAMSUNG: 480 1.29 perry vendor = "Samsung"; 481 1.29 perry break; 482 1.29 perry case WI_RID_STA_IDENTITY_DLINK: 483 1.29 perry vendor = "D-Link"; 484 1.29 perry break; 485 1.29 perry } 486 1.32 dyoung printf("[ %s ID: %d version: %d.%d ]", vendor, le16toh(wreq->wi_val[0]), 487 1.32 dyoung le16toh(wreq->wi_val[2]), le16toh(wreq->wi_val[3])); 488 1.46 rillig } 489 1.29 perry 490 1.43 joerg static void 491 1.43 joerg wi_printwords(struct wi_req *wreq) 492 1.1 sommerfe { 493 1.1 sommerfe int i; 494 1.1 sommerfe 495 1.1 sommerfe printf("[ "); 496 1.1 sommerfe for (i = 0; i < wreq->wi_len - 1; i++) 497 1.14 tsubai printf("%d ", le16toh(wreq->wi_val[i])); 498 1.1 sommerfe printf("]"); 499 1.1 sommerfe } 500 1.1 sommerfe 501 1.43 joerg static void 502 1.43 joerg wi_printbool(struct wi_req *wreq) 503 1.1 sommerfe { 504 1.14 tsubai if (le16toh(wreq->wi_val[0])) 505 1.1 sommerfe printf("[ On ]"); 506 1.1 sommerfe else 507 1.1 sommerfe printf("[ Off ]"); 508 1.1 sommerfe } 509 1.1 sommerfe 510 1.43 joerg static void 511 1.43 joerg wi_printhex(struct wi_req *wreq) 512 1.1 sommerfe { 513 1.1 sommerfe int i; 514 1.1 sommerfe unsigned char *c; 515 1.1 sommerfe 516 1.1 sommerfe c = (unsigned char *)&wreq->wi_val; 517 1.1 sommerfe 518 1.1 sommerfe printf("[ "); 519 1.1 sommerfe for (i = 0; i < (wreq->wi_len - 1) * 2; i++) { 520 1.1 sommerfe printf("%02x", c[i]); 521 1.1 sommerfe if (i < ((wreq->wi_len - 1) * 2) - 1) 522 1.1 sommerfe printf(":"); 523 1.1 sommerfe } 524 1.1 sommerfe printf(" ]"); 525 1.1 sommerfe } 526 1.1 sommerfe 527 1.43 joerg static void 528 1.43 joerg wi_printbits(struct wi_req *wreq) 529 1.22 dbj { 530 1.22 dbj int i; 531 1.22 dbj int bits = le16toh(wreq->wi_val[0]); 532 1.22 dbj 533 1.22 dbj printf("["); 534 1.22 dbj for (i = 0; i < 16; i++) { 535 1.46 rillig if (bits & 0x1) 536 1.46 rillig printf(" %d", i + 1); 537 1.22 dbj bits >>= 1; 538 1.22 dbj } 539 1.22 dbj printf(" ]"); 540 1.22 dbj } 541 1.22 dbj 542 1.1 sommerfe static struct wi_table wi_table[] = { 543 1.42 lukem { WI_RID_SERIALNO, WI_STRING, "NIC serial number:\t\t\t", 0, 0, 0 }, 544 1.7 enami { WI_RID_NODENAME, WI_STRING, "Station name:\t\t\t\t", 545 1.42 lukem 's', "station name", 0, }, 546 1.42 lukem { WI_RID_OWN_SSID, WI_STRING, "SSID for IBSS creation:\t\t\t", 0, 0, 0 }, 547 1.42 lukem { WI_RID_CURRENT_SSID, WI_STRING, "Current netname (SSID):\t\t\t", 0, 0, 0 }, 548 1.42 lukem { WI_RID_DESIRED_SSID, WI_STRING, "Desired netname (SSID):\t\t\t", 0, 0, 0 }, 549 1.42 lukem { WI_RID_CURRENT_BSSID, WI_HEXBYTES, "Current BSSID:\t\t\t\t", 0, 0, 0 }, 550 1.42 lukem { WI_RID_CHANNEL_LIST, WI_BITS, "Channel list:\t\t\t\t", 0, 0, 0 }, 551 1.42 lukem { WI_RID_OWN_CHNL, WI_WORDS, "IBSS channel:\t\t\t\t", 0, 0, 0 }, 552 1.42 lukem { WI_RID_CURRENT_CHAN, WI_WORDS, "Current channel:\t\t\t", 0, 0, 0 }, 553 1.42 lukem { WI_RID_COMMS_QUALITY, WI_WORDS, "Comms quality/signal/noise:\t\t", 0, 0, 0 }, 554 1.42 lukem { WI_RID_PROMISC, WI_BOOL, "Promiscuous mode:\t\t\t", 0, 0, 0 }, 555 1.42 lukem { WI_RID_PORTTYPE, WI_WORDS, "Port type:\t\t\t\t", 0, 0, 0 }, 556 1.7 enami { WI_RID_MAC_NODE, WI_HEXBYTES, "MAC address:\t\t\t\t", 557 1.42 lukem 'm', "MAC address", 0, }, 558 1.42 lukem { WI_RID_TX_RATE, WI_WORDS, "TX rate (selection):\t\t\t", 0, 0, 0 }, 559 1.42 lukem { WI_RID_CUR_TX_RATE, WI_WORDS, "TX rate (actual speed):\t\t\t", 0, 0, 0 }, 560 1.42 lukem { WI_RID_CUR_BEACON_INT, WI_WORDS, "Beacon Interval (current) [msec]:\t", 0, 0, 0 }, 561 1.7 enami { WI_RID_MAX_DATALEN, WI_WORDS, "Maximum data length:\t\t\t", 562 1.42 lukem 'd', "maximum data length", 0 }, 563 1.7 enami { WI_RID_RTS_THRESH, WI_WORDS, "RTS/CTS handshake threshold:\t\t", 564 1.42 lukem 'r', "RTS threshold", 0 }, 565 1.28 dyoung { WI_RID_FRAG_THRESH, WI_WORDS, "fragmentation threshold:\t\t", 566 1.42 lukem 'g', "fragmentation threshold", 0, }, 567 1.42 lukem { WI_RID_DBM_ADJUST, WI_WORDS, "RSSI -> dBm adjustment:\t\t\t", 0, 0, 0 }, 568 1.42 lukem { WI_RID_CREATE_IBSS, WI_BOOL, "Create IBSS:\t\t\t\t", 0, 0, 0 }, 569 1.13 ichiro { WI_RID_MICROWAVE_OVEN, WI_WORDS, "Microwave oven robustness:\t\t", 570 1.42 lukem 'M', "microwave oven robustness enabled", 0 }, 571 1.13 ichiro { WI_RID_ROAMING_MODE, WI_WORDS, "Roaming mode(1:firm,3:disable):\t\t", 572 1.42 lukem 'R', "roaming mode", 0 }, 573 1.7 enami { WI_RID_SYSTEM_SCALE, WI_WORDS, "Access point density:\t\t\t", 574 1.42 lukem 'a', "system scale", 0 }, 575 1.42 lukem { WI_RID_PM_ENABLED, WI_WORDS, "Power Mgmt (1=on, 0=off):\t\t", 0, 0, 0 }, 576 1.42 lukem { WI_RID_MAX_SLEEP, WI_WORDS, "Max sleep time (msec):\t\t\t", 0, 0, 0 }, 577 1.42 lukem { WI_RID_STA_IDENTITY, WI_VENDOR, "Vendor info:\t\t\t\t", 0, 0, 0 }, 578 1.42 lukem { 0, WI_NONE, 0, 0, 0, 0 } 579 1.1 sommerfe }; 580 1.1 sommerfe 581 1.2 explorer static struct wi_table wi_crypt_table[] = { 582 1.42 lukem { WI_RID_ENCRYPTION, WI_BOOL, "WEP encryption:\t\t\t\t", 0, 0, 0 }, 583 1.33 wrstuden { WI_RID_CNFAUTHMODE, WI_WORDS, "Authentication type \n(1=OpenSys, 2=Shared Key):\t\t", 584 1.42 lukem 'A', "authentication type", 0 }, 585 1.46 rillig { WI_RID_TX_CRYPT_KEY, WI_WORDS, "TX encryption key:\t\t\t", 0, 0, 0 }, 586 1.46 rillig { WI_RID_DEFLT_CRYPT_KEYS, WI_KEYSTRUCT, "Encryption keys:\t\t\t", 0, 0, 0 }, 587 1.42 lukem { 0, WI_NONE, 0, 0, 0, 0 } 588 1.7 enami }; 589 1.7 enami 590 1.7 enami static struct wi_table *wi_tables[] = { 591 1.7 enami wi_table, 592 1.7 enami wi_crypt_table, 593 1.7 enami NULL 594 1.2 explorer }; 595 1.2 explorer 596 1.7 enami static struct wi_table * 597 1.43 joerg wi_optlookup(struct wi_table *table, int opt) 598 1.7 enami { 599 1.7 enami struct wi_table *wt; 600 1.7 enami 601 1.7 enami for (wt = table; wt->wi_type != 0; wt++) 602 1.7 enami if (wt->wi_opt == opt) 603 1.46 rillig return wt; 604 1.46 rillig return NULL; 605 1.7 enami } 606 1.7 enami 607 1.43 joerg static void 608 1.43 joerg wi_checkwifi(char *iface) 609 1.40 sborrill { 610 1.40 sborrill struct ifreq ifr; 611 1.40 sborrill struct ieee80211_nwid nwid; 612 1.40 sborrill int s; 613 1.40 sborrill 614 1.46 rillig memset(&ifr, 0, sizeof(ifr)); 615 1.40 sborrill 616 1.40 sborrill strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); 617 1.40 sborrill ifr.ifr_data = (void *)&nwid; 618 1.40 sborrill 619 1.40 sborrill s = socket(AF_INET, SOCK_DGRAM, 0); 620 1.40 sborrill 621 1.40 sborrill if (s == -1) 622 1.40 sborrill err(1, "socket"); 623 1.46 rillig 624 1.40 sborrill /* Choice of ioctl inspired by ifconfig/ieee80211.c */ 625 1.40 sborrill if (ioctl(s, SIOCG80211NWID, &ifr) == -1) 626 1.40 sborrill err(1, "SIOCG80211NWID"); 627 1.40 sborrill 628 1.40 sborrill close(s); 629 1.40 sborrill } 630 1.40 sborrill 631 1.43 joerg static void 632 1.43 joerg wi_dumpinfo(char *iface) 633 1.1 sommerfe { 634 1.1 sommerfe struct wi_req wreq; 635 1.2 explorer int i, has_wep; 636 1.1 sommerfe struct wi_table *w; 637 1.1 sommerfe 638 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 639 1.2 explorer 640 1.2 explorer wreq.wi_len = WI_MAX_DATALEN; 641 1.2 explorer wreq.wi_type = WI_RID_WEP_AVAIL; 642 1.2 explorer 643 1.2 explorer wi_getval(iface, &wreq); 644 1.14 tsubai has_wep = le16toh(wreq.wi_val[0]); 645 1.2 explorer 646 1.1 sommerfe w = wi_table; 647 1.1 sommerfe 648 1.7 enami for (i = 0; w[i].wi_code != WI_NONE; i++) { 649 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 650 1.1 sommerfe 651 1.1 sommerfe wreq.wi_len = WI_MAX_DATALEN; 652 1.7 enami wreq.wi_type = w[i].wi_type; 653 1.1 sommerfe 654 1.7 enami printf("%s", w[i].wi_label); 655 1.39 elad if (wi_getval(iface, &wreq)) { 656 1.39 elad printf("[ Unknown ]\n"); 657 1.39 elad continue; 658 1.39 elad } 659 1.7 enami switch (w[i].wi_code) { 660 1.1 sommerfe case WI_STRING: 661 1.1 sommerfe wi_printstr(&wreq); 662 1.1 sommerfe break; 663 1.1 sommerfe case WI_WORDS: 664 1.1 sommerfe wi_printwords(&wreq); 665 1.1 sommerfe break; 666 1.1 sommerfe case WI_BOOL: 667 1.1 sommerfe wi_printbool(&wreq); 668 1.1 sommerfe break; 669 1.1 sommerfe case WI_HEXBYTES: 670 1.1 sommerfe wi_printhex(&wreq); 671 1.22 dbj break; 672 1.22 dbj case WI_BITS: 673 1.22 dbj wi_printbits(&wreq); 674 1.29 perry break; 675 1.29 perry case WI_VENDOR: 676 1.29 perry wi_printvendor(&wreq); 677 1.1 sommerfe break; 678 1.1 sommerfe default: 679 1.1 sommerfe break; 680 1.46 rillig } 681 1.1 sommerfe printf("\n"); 682 1.1 sommerfe } 683 1.1 sommerfe 684 1.2 explorer if (has_wep) { 685 1.2 explorer w = wi_crypt_table; 686 1.7 enami for (i = 0; w[i].wi_code != WI_NONE; i++) { 687 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 688 1.2 explorer 689 1.2 explorer wreq.wi_len = WI_MAX_DATALEN; 690 1.7 enami wreq.wi_type = w[i].wi_type; 691 1.2 explorer 692 1.2 explorer wi_getval(iface, &wreq); 693 1.7 enami printf("%s", w[i].wi_label); 694 1.7 enami switch (w[i].wi_code) { 695 1.2 explorer case WI_STRING: 696 1.2 explorer wi_printstr(&wreq); 697 1.2 explorer break; 698 1.2 explorer case WI_WORDS: 699 1.2 explorer if (wreq.wi_type == WI_RID_TX_CRYPT_KEY) 700 1.14 tsubai wreq.wi_val[0] = 701 1.14 tsubai htole16(le16toh(wreq.wi_val[0]) + 1); 702 1.2 explorer wi_printwords(&wreq); 703 1.2 explorer break; 704 1.2 explorer case WI_BOOL: 705 1.2 explorer wi_printbool(&wreq); 706 1.2 explorer break; 707 1.2 explorer case WI_HEXBYTES: 708 1.2 explorer wi_printhex(&wreq); 709 1.2 explorer break; 710 1.2 explorer case WI_KEYSTRUCT: 711 1.2 explorer wi_printkeys(&wreq); 712 1.2 explorer break; 713 1.2 explorer default: 714 1.2 explorer break; 715 1.46 rillig } 716 1.2 explorer printf("\n"); 717 1.2 explorer } 718 1.2 explorer } 719 1.1 sommerfe } 720 1.1 sommerfe 721 1.43 joerg static void 722 1.43 joerg wi_dumpstats(char *iface) 723 1.1 sommerfe { 724 1.1 sommerfe struct wi_req wreq; 725 1.1 sommerfe struct wi_counters *c; 726 1.1 sommerfe 727 1.46 rillig memset(&wreq, 0, sizeof(wreq)); 728 1.1 sommerfe wreq.wi_len = WI_MAX_DATALEN; 729 1.1 sommerfe wreq.wi_type = WI_RID_IFACE_STATS; 730 1.1 sommerfe 731 1.1 sommerfe wi_getval(iface, &wreq); 732 1.1 sommerfe 733 1.1 sommerfe c = (struct wi_counters *)&wreq.wi_val; 734 1.1 sommerfe 735 1.14 tsubai /* XXX native byte order */ 736 1.1 sommerfe printf("Transmitted unicast frames:\t\t%d\n", 737 1.1 sommerfe c->wi_tx_unicast_frames); 738 1.1 sommerfe printf("Transmitted multicast frames:\t\t%d\n", 739 1.1 sommerfe c->wi_tx_multicast_frames); 740 1.1 sommerfe printf("Transmitted fragments:\t\t\t%d\n", 741 1.1 sommerfe c->wi_tx_fragments); 742 1.1 sommerfe printf("Transmitted unicast octets:\t\t%d\n", 743 1.1 sommerfe c->wi_tx_unicast_octets); 744 1.1 sommerfe printf("Transmitted multicast octets:\t\t%d\n", 745 1.1 sommerfe c->wi_tx_multicast_octets); 746 1.1 sommerfe printf("Single transmit retries:\t\t%d\n", 747 1.1 sommerfe c->wi_tx_single_retries); 748 1.1 sommerfe printf("Multiple transmit retries:\t\t%d\n", 749 1.1 sommerfe c->wi_tx_multi_retries); 750 1.1 sommerfe printf("Transmit retry limit exceeded:\t\t%d\n", 751 1.1 sommerfe c->wi_tx_retry_limit); 752 1.1 sommerfe printf("Transmit discards:\t\t\t%d\n", 753 1.1 sommerfe c->wi_tx_discards); 754 1.1 sommerfe printf("Transmit discards due to wrong SA:\t%d\n", 755 1.1 sommerfe c->wi_tx_discards_wrong_sa); 756 1.1 sommerfe printf("Received unicast frames:\t\t%d\n", 757 1.1 sommerfe c->wi_rx_unicast_frames); 758 1.1 sommerfe printf("Received multicast frames:\t\t%d\n", 759 1.1 sommerfe c->wi_rx_multicast_frames); 760 1.1 sommerfe printf("Received fragments:\t\t\t%d\n", 761 1.1 sommerfe c->wi_rx_fragments); 762 1.1 sommerfe printf("Received unicast octets:\t\t%d\n", 763 1.1 sommerfe c->wi_rx_unicast_octets); 764 1.1 sommerfe printf("Received multicast octets:\t\t%d\n", 765 1.1 sommerfe c->wi_rx_multicast_octets); 766 1.1 sommerfe printf("Receive FCS errors:\t\t\t%d\n", 767 1.1 sommerfe c->wi_rx_fcs_errors); 768 1.1 sommerfe printf("Receive discards due to no buffer:\t%d\n", 769 1.1 sommerfe c->wi_rx_discards_nobuf); 770 1.1 sommerfe printf("Can't decrypt WEP frame:\t\t%d\n", 771 1.1 sommerfe c->wi_rx_WEP_cant_decrypt); 772 1.1 sommerfe printf("Received message fragments:\t\t%d\n", 773 1.1 sommerfe c->wi_rx_msg_in_msg_frags); 774 1.1 sommerfe printf("Received message bad fragments:\t\t%d\n", 775 1.1 sommerfe c->wi_rx_msg_in_bad_msg_frags); 776 1.1 sommerfe } 777 1.1 sommerfe 778 1.7 enami static void 779 1.43 joerg usage(void) 780 1.1 sommerfe { 781 1.7 enami 782 1.2 explorer fprintf(stderr, 783 1.35 wiz "usage: %s interface [-Dho] [-A 1|2] [-a access point density]\n" 784 1.35 wiz " [-d max data length] [-g fragmentation threshold] [-M 0|1]\n" 785 1.35 wiz " [-m MAC address] [-R 1|3] [-r RTS threshold] [-s station name]\n" 786 1.9 jhawk , 787 1.11 cgd getprogname()); 788 1.1 sommerfe exit(1); 789 1.1 sommerfe } 790 1.1 sommerfe 791 1.43 joerg int 792 1.43 joerg main(int argc, char *argv[]) 793 1.1 sommerfe { 794 1.7 enami struct wi_table *wt, **table; 795 1.28 dyoung char *iface; 796 1.28 dyoung int ch, dumpinfo, dumpstats, apscan; 797 1.7 enami 798 1.7 enami #define SET_OPERAND(opr, desc) do { \ 799 1.7 enami if ((opr) == NULL) \ 800 1.7 enami (opr) = optarg; \ 801 1.7 enami else \ 802 1.7 enami warnx("%s is already specified to %s", \ 803 1.7 enami desc, (opr)); \ 804 1.7 enami } while (0) 805 1.7 enami 806 1.7 enami dumpinfo = 1; 807 1.7 enami dumpstats = 0; 808 1.15 ichiro apscan = 0; 809 1.28 dyoung iface = NULL; 810 1.2 explorer 811 1.2 explorer if (argc > 1 && argv[1][0] != '-') { 812 1.2 explorer iface = argv[1]; 813 1.5 enami optind++; 814 1.2 explorer } 815 1.1 sommerfe 816 1.46 rillig /* LINTED 338 "option should be handled in the switch" */ 817 1.3 itojun while ((ch = getopt(argc, argv, 818 1.33 wrstuden "a:d:g:hi:m:or:s:A:M:R:D")) != -1) { 819 1.7 enami if (ch != 'i') 820 1.7 enami dumpinfo = 0; 821 1.7 enami /* 822 1.35 wiz * Lookup generic options and remember operand if found. 823 1.7 enami */ 824 1.37 lukem wt = NULL; /* XXXGCC -Wuninitialized */ 825 1.7 enami for (table = wi_tables; *table != NULL; table++) 826 1.7 enami if ((wt = wi_optlookup(*table, ch)) != NULL) { 827 1.7 enami SET_OPERAND(wt->wi_optval, wt->wi_desc); 828 1.7 enami break; 829 1.7 enami } 830 1.7 enami if (wt == NULL) 831 1.7 enami /* 832 1.7 enami * Handle special options. 833 1.7 enami */ 834 1.7 enami switch (ch) { 835 1.7 enami case 'o': 836 1.7 enami dumpstats = 1; 837 1.7 enami break; 838 1.7 enami case 'i': 839 1.7 enami SET_OPERAND(iface, "interface"); 840 1.7 enami break; 841 1.15 ichiro case 'D': 842 1.15 ichiro apscan = 1; 843 1.15 ichiro break; 844 1.7 enami case 'h': 845 1.7 enami default: 846 1.7 enami usage(); 847 1.7 enami break; 848 1.7 enami } 849 1.1 sommerfe } 850 1.1 sommerfe 851 1.1 sommerfe if (iface == NULL) 852 1.7 enami usage(); 853 1.2 explorer 854 1.40 sborrill /* Check interface is wireless. Will not return on error */ 855 1.40 sborrill wi_checkwifi(iface); 856 1.46 rillig 857 1.7 enami for (table = wi_tables; *table != NULL; table++) 858 1.7 enami for (wt = *table; wt->wi_code != WI_NONE; wt++) 859 1.7 enami if (wt->wi_optval != NULL) { 860 1.7 enami switch (wt->wi_code) { 861 1.7 enami case WI_BOOL: 862 1.7 enami case WI_WORDS: 863 1.7 enami wi_setword(iface, wt->wi_type, 864 1.7 enami atoi(wt->wi_optval)); 865 1.7 enami break; 866 1.7 enami case WI_STRING: 867 1.7 enami wi_setstr(iface, wt->wi_type, 868 1.7 enami wt->wi_optval); 869 1.7 enami break; 870 1.7 enami case WI_HEXBYTES: 871 1.7 enami wi_sethex(iface, wt->wi_type, 872 1.7 enami wt->wi_optval); 873 1.7 enami break; 874 1.7 enami } 875 1.7 enami } 876 1.7 enami 877 1.7 enami if (dumpstats) 878 1.7 enami wi_dumpstats(iface); 879 1.7 enami if (dumpinfo) 880 1.7 enami wi_dumpinfo(iface); 881 1.18 christos 882 1.15 ichiro if (apscan) 883 1.18 christos #ifdef WI_RID_SCAN_APS 884 1.15 ichiro wi_apscan(iface); 885 1.18 christos #else 886 1.18 christos errx(1, "AP scan mode is not available."); 887 1.18 christos #endif 888 1.1 sommerfe 889 1.1 sommerfe exit(0); 890 1.1 sommerfe } 891