xref: /netbsd-src/usr.sbin/wiconfig/wiconfig.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
1 /*	$NetBSD: wiconfig.c,v 1.14 2001/05/16 10:49:06 tsubai Exp $	*/
2 /*
3  * Copyright (c) 1997, 1998, 1999
4  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by Bill Paul.
17  * 4. Neither the name of the author nor the names of any co-contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *	From: Id: wicontrol.c,v 1.6 1999/05/22 16:12:49 wpaul Exp $
34  */
35 
36 #include <sys/types.h>
37 #include <sys/cdefs.h>
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 
43 #include <net/if.h>
44 #ifdef __FreeBSD__
45 #include <net/if_var.h>
46 #include <net/ethernet.h>
47 
48 #include <machine/if_wavelan_ieee.h>
49 #else
50 #include <netinet/in.h>
51 #include <netinet/if_ether.h>
52 #ifdef __NetBSD__
53 #include <dev/ic/wi_ieee.h>
54 #else
55 #include <dev/pcmcia/if_wavelan_ieee.h>
56 #endif
57 #endif
58 
59 #include <stdio.h>
60 #include <string.h>
61 #include <ctype.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <errno.h>
65 #include <err.h>
66 
67 #if !defined(lint)
68 static const char copyright[] = "@(#) Copyright (c) 1997, 1998, 1999\
69 	Bill Paul. All rights reserved.";
70 static const char rcsid[] =
71 	"@(#) $Id: wiconfig.c,v 1.14 2001/05/16 10:49:06 tsubai Exp $";
72 #endif
73 
74 struct wi_table {
75 	int wi_type;
76 	int wi_code;
77 #define	WI_NONE			0x00
78 #define	WI_STRING		0x01
79 #define	WI_BOOL			0x02
80 #define	WI_WORDS		0x03
81 #define	WI_HEXBYTES		0x04
82 #define	WI_KEYSTRUCT		0x05
83 	char *wi_label;			/* label used to print info */
84 	int wi_opt;			/* option character to set this */
85 	char *wi_desc;
86 	char *wi_optval;
87 };
88 
89 static void wi_getval		__P((char *, struct wi_req *));
90 static void wi_setval		__P((char *, struct wi_req *));
91 static void wi_printstr		__P((struct wi_req *));
92 static void wi_setstr		__P((char *, int, char *));
93 static void wi_setbytes		__P((char *, int, char *, int));
94 static void wi_setword		__P((char *, int, int));
95 static void wi_sethex		__P((char *, int, char *));
96 static void wi_printwords	__P((struct wi_req *));
97 static void wi_printbool	__P((struct wi_req *));
98 static void wi_printhex		__P((struct wi_req *));
99 static void wi_dumpinfo		__P((char *));
100 static void wi_setkeys		__P((char *, char *, int));
101 static void wi_printkeys	__P((struct wi_req *));
102 static void wi_dumpstats	__P((char *));
103 static void usage		__P((void));
104 static struct wi_table *
105 	wi_optlookup __P((struct wi_table *, int));
106 static int  wi_hex2int(char c);
107 static void wi_str2key		__P((char *, struct wi_key *));
108 int main __P((int argc, char **argv));
109 
110 static void wi_getval(iface, wreq)
111 	char			*iface;
112 	struct wi_req		*wreq;
113 {
114 	struct ifreq		ifr;
115 	int			s;
116 
117 	bzero((char *)&ifr, sizeof(ifr));
118 
119 	strcpy(ifr.ifr_name, iface);
120 	ifr.ifr_data = (caddr_t)wreq;
121 
122 	s = socket(AF_INET, SOCK_DGRAM, 0);
123 
124 	if (s == -1)
125 		err(1, "socket");
126 
127 	if (ioctl(s, SIOCGWAVELAN, &ifr) == -1)
128 		err(1, "SIOCGWAVELAN");
129 
130 	close(s);
131 
132 	return;
133 }
134 
135 static void wi_setval(iface, wreq)
136 	char			*iface;
137 	struct wi_req		*wreq;
138 {
139 	struct ifreq		ifr;
140 	int			s;
141 
142 	bzero((char *)&ifr, sizeof(ifr));
143 
144 	strcpy(ifr.ifr_name, iface);
145 	ifr.ifr_data = (caddr_t)wreq;
146 
147 	s = socket(AF_INET, SOCK_DGRAM, 0);
148 
149 	if (s == -1)
150 		err(1, "socket");
151 
152 	if (ioctl(s, SIOCSWAVELAN, &ifr) == -1)
153 		err(1, "SIOCSWAVELAN");
154 
155 	close(s);
156 
157 	return;
158 }
159 
160 void wi_printstr(wreq)
161 	struct wi_req		*wreq;
162 {
163 	char			*ptr;
164 	int			i;
165 
166 	if (wreq->wi_type == WI_RID_SERIALNO) {
167 		ptr = (char *)&wreq->wi_val;
168 		for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
169 			if (ptr[i] == '\0')
170 				ptr[i] = ' ';
171 		}
172 	} else {
173 		int len = le16toh(wreq->wi_val[0]);
174 
175 		ptr = (char *)&wreq->wi_val[1];
176 		for (i = 0; i < len; i++) {
177 			if (ptr[i] == '\0')
178 				ptr[i] = ' ';
179 		}
180 	}
181 
182 	ptr[i] = '\0';
183 	printf("[ %s ]", ptr);
184 
185 	return;
186 }
187 
188 void wi_setstr(iface, code, str)
189 	char			*iface;
190 	int			code;
191 	char			*str;
192 {
193 	struct wi_req		wreq;
194 
195 	bzero((char *)&wreq, sizeof(wreq));
196 
197 	if (strlen(str) > 30)
198 		errx(1, "string too long");
199 
200 	wreq.wi_type = code;
201 	wreq.wi_len = 18;
202 	wreq.wi_val[0] = htole16(strlen(str));
203 	bcopy(str, (char *)&wreq.wi_val[1], strlen(str));
204 
205 	wi_setval(iface, &wreq);
206 
207 	return;
208 }
209 
210 void wi_setbytes(iface, code, bytes, len)
211 	char			*iface;
212 	int			code;
213 	char			*bytes;
214 	int			len;
215 {
216 	struct wi_req		wreq;
217 
218 	bzero((char *)&wreq, sizeof(wreq));
219 
220 	wreq.wi_type = code;
221 	wreq.wi_len = (len / 2) + 1;
222 	bcopy(bytes, (char *)&wreq.wi_val[0], len);
223 
224 	wi_setval(iface, &wreq);
225 
226 	return;
227 }
228 
229 void wi_setword(iface, code, word)
230 	char			*iface;
231 	int			code;
232 	int			word;
233 {
234 	struct wi_req		wreq;
235 
236 	bzero((char *)&wreq, sizeof(wreq));
237 
238 	wreq.wi_type = code;
239 	wreq.wi_len = 2;
240 	wreq.wi_val[0] = htole16(word);
241 
242 	wi_setval(iface, &wreq);
243 
244 	return;
245 }
246 
247 void wi_sethex(iface, code, str)
248 	char			*iface;
249 	int			code;
250 	char			*str;
251 {
252 	struct ether_addr	*addr;
253 
254 	addr = ether_aton(str);
255 	if (addr == NULL)
256 		errx(1, "badly formatted address");
257 
258 	wi_setbytes(iface, code, (char *)addr, ETHER_ADDR_LEN);
259 
260 	return;
261 }
262 
263 static int
264 wi_hex2int(char c)
265 {
266         if (c >= '0' && c <= '9')
267                 return (c - '0');
268 	if (c >= 'A' && c <= 'F')
269 	        return (c - 'A' + 10);
270 	if (c >= 'a' && c <= 'f')
271                 return (c - 'a' + 10);
272 
273 	return (0);
274 }
275 
276 static void wi_str2key(s, k)
277         char                    *s;
278         struct wi_key           *k;
279 {
280         int                     n, i;
281         char                    *p;
282 
283         /* Is this a hex string? */
284         if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
285                 /* Yes, convert to int. */
286                 n = 0;
287                 p = (char *)&k->wi_keydat[0];
288                 for (i = 2; i < strlen(s); i+= 2) {
289                         *p++ = (wi_hex2int(s[i]) << 4) + wi_hex2int(s[i + 1]);
290                         n++;
291                 }
292                 k->wi_keylen = htole16(n);
293         } else {
294                 /* No, just copy it in. */
295                 bcopy(s, k->wi_keydat, strlen(s));
296                 k->wi_keylen = htole16(strlen(s));
297         }
298 
299         return;
300 }
301 
302 static void wi_setkeys(iface, key, idx)
303         char                    *iface;
304         char                    *key;
305         int                     idx;
306 {
307         struct wi_req           wreq;
308         struct wi_ltv_keys      *keys;
309         struct wi_key           *k;
310 
311         bzero((char *)&wreq, sizeof(wreq));
312         wreq.wi_len = WI_MAX_DATALEN;
313         wreq.wi_type = WI_RID_WEP_AVAIL;
314 
315         wi_getval(iface, &wreq);
316         if (le16toh(wreq.wi_val[0]) == 0)
317                 err(1, "no WEP option available on this card");
318 
319         bzero((char *)&wreq, sizeof(wreq));
320         wreq.wi_len = WI_MAX_DATALEN;
321         wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
322 
323         wi_getval(iface, &wreq);
324         keys = (struct wi_ltv_keys *)&wreq;
325 
326         if (key[0] == '0' && (key[1] == 'x' || key[1] == 'X')) {
327 	        if (strlen(key) > 30)
328 		        err(1, "encryption key must be no "
329 			    "more than 28 hex digits long");
330 	} else {
331 	        if (strlen(key) > 14)
332 		        err(1, "encryption key must be no "
333 			    "more than 14 characters long");
334 	}
335 
336         if (idx > 3)
337                 err(1, "only 4 encryption keys available");
338 
339         k = &keys->wi_keys[idx];
340         wi_str2key(key, k);
341 
342         wreq.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
343         wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
344         wi_setval(iface, &wreq);
345 
346         return;
347 }
348 
349 static void wi_printkeys(wreq)
350         struct wi_req           *wreq;
351 {
352         int                     i, j, bn;
353         struct wi_key           *k;
354         struct wi_ltv_keys      *keys;
355         char                    *ptr;
356 
357 	keys = (struct wi_ltv_keys *)wreq;
358 
359 	for (i = 0, bn = 0; i < 4; i++, bn = 0) {
360                 k = &keys->wi_keys[i];
361                 ptr = (char *)k->wi_keydat;
362                 for (j = 0; j < le16toh(k->wi_keylen); j++) {
363 		        if (!isprint((unsigned char) ptr[j])) {
364 			        bn = 1;
365 				break;
366 			}
367 		}
368 
369 		if (bn)	{
370 		        printf("[ 0x");
371 		        for (j = 0; j < le16toh(k->wi_keylen); j++)
372 			      printf("%02x", ((unsigned char *) ptr)[j]);
373 			printf(" ]");
374 		} else {
375 		        ptr[j] = '\0';
376 			printf("[ %s ]", ptr);
377 		}
378         }
379 
380         return;
381 };
382 
383 void wi_printwords(wreq)
384 	struct wi_req		*wreq;
385 {
386 	int			i;
387 
388 	printf("[ ");
389 	for (i = 0; i < wreq->wi_len - 1; i++)
390 		printf("%d ", le16toh(wreq->wi_val[i]));
391 	printf("]");
392 
393 	return;
394 }
395 
396 void wi_printbool(wreq)
397 	struct wi_req		*wreq;
398 {
399 	if (le16toh(wreq->wi_val[0]))
400 		printf("[ On ]");
401 	else
402 		printf("[ Off ]");
403 
404 	return;
405 }
406 
407 void wi_printhex(wreq)
408 	struct wi_req		*wreq;
409 {
410 	int			i;
411 	unsigned char		*c;
412 
413 	c = (unsigned char *)&wreq->wi_val;
414 
415 	printf("[ ");
416 	for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
417 		printf("%02x", c[i]);
418 		if (i < ((wreq->wi_len - 1) * 2) - 1)
419 			printf(":");
420 	}
421 
422 	printf(" ]");
423 	return;
424 }
425 
426 static struct wi_table wi_table[] = {
427 	{ WI_RID_SERIALNO, WI_STRING, "NIC serial number:\t\t\t" },
428 	{ WI_RID_NODENAME, WI_STRING, "Station name:\t\t\t\t",
429 	    's', "station name" },
430 	{ WI_RID_OWN_SSID, WI_STRING, "SSID for IBSS creation:\t\t\t",
431 	    'q', "own SSID" },
432 	{ WI_RID_CURRENT_SSID, WI_STRING, "Current netname (SSID):\t\t\t" },
433 	{ WI_RID_DESIRED_SSID, WI_STRING, "Desired netname (SSID):\t\t\t",
434 	    'n', "network name" },
435 	{ WI_RID_CURRENT_BSSID, WI_HEXBYTES, "Current BSSID:\t\t\t\t" },
436 	{ WI_RID_CHANNEL_LIST, WI_WORDS, "Channel list:\t\t\t\t" },
437 	{ WI_RID_OWN_CHNL, WI_WORDS, "IBSS channel:\t\t\t\t",
438 	    'f', "frequency" },
439 	{ WI_RID_CURRENT_CHAN, WI_WORDS, "Current channel:\t\t\t" },
440 	{ WI_RID_COMMS_QUALITY, WI_WORDS, "Comms quality/signal/noise:\t\t" },
441 	{ WI_RID_PROMISC, WI_BOOL, "Promiscuous mode:\t\t\t" },
442 	{ WI_RID_PORTTYPE, WI_WORDS, "Port type (1=BSS, 3=ad-hoc):\t\t",
443 	    'p', "port type" },
444 	{ WI_RID_MAC_NODE, WI_HEXBYTES, "MAC address:\t\t\t\t",
445 	    'm', "MAC address" },
446 	{ WI_RID_TX_RATE, WI_WORDS, "TX rate (selection):\t\t\t",
447 	    't', "TX rate" },
448 	{ WI_RID_CUR_TX_RATE, WI_WORDS, "TX rate (actual speed):\t\t\t"},
449 	{ WI_RID_MAX_DATALEN, WI_WORDS, "Maximum data length:\t\t\t",
450 	    'd', "maximum data length" },
451 	{ WI_RID_RTS_THRESH, WI_WORDS, "RTS/CTS handshake threshold:\t\t",
452 	    'r', "RTS threshold" },
453 	{ WI_RID_CREATE_IBSS, WI_BOOL, "Create IBSS:\t\t\t\t",
454 	    'c', "create ibss" },
455 	{ WI_RID_MICROWAVE_OVEN, WI_WORDS, "Microwave oven robustness:\t\t",
456 	  'M', "microwave oven robustness enabled" },
457 	{ WI_RID_ROAMING_MODE, WI_WORDS, "Roaming mode(1:firm,3:disable):\t\t",
458 	  'R', "roaming mode" },
459 	{ WI_RID_SYSTEM_SCALE, WI_WORDS, "Access point density:\t\t\t",
460 	    'a', "system scale" },
461 	{ WI_RID_PM_ENABLED, WI_WORDS, "Power Mgmt (1=on, 0=off):\t\t",
462 	    'P', "power management enabled" },
463 	{ WI_RID_MAX_SLEEP, WI_WORDS, "Max sleep time (msec):\t\t\t",
464 	    'S', "max sleep duration" },
465 	{ 0, WI_NONE }
466 };
467 
468 static struct wi_table wi_crypt_table[] = {
469 	{ WI_RID_ENCRYPTION, WI_BOOL, "WEP encryption:\t\t\t\t",
470 	    'e', "encryption" },
471 	{ WI_RID_AUTH_CNTL, WI_WORDS, "Authentication type \n(1=OpenSys, 2=Shared Key):\t\t",
472 	    'A', "authentication type" },
473         { WI_RID_TX_CRYPT_KEY, WI_WORDS, "TX encryption key:\t\t\t" },
474         { WI_RID_DEFLT_CRYPT_KEYS, WI_KEYSTRUCT, "Encryption keys:\t\t\t" },
475 	{ 0, WI_NONE }
476 };
477 
478 static struct wi_table *wi_tables[] = {
479 	wi_table,
480 	wi_crypt_table,
481 	NULL
482 };
483 
484 static struct wi_table *
485 wi_optlookup(table, opt)
486 	struct wi_table *table;
487 	int opt;
488 {
489 	struct wi_table *wt;
490 
491 	for (wt = table; wt->wi_type != 0; wt++)
492 		if (wt->wi_opt == opt)
493 			return (wt);
494 	return (NULL);
495 }
496 
497 static void wi_dumpinfo(iface)
498 	char			*iface;
499 {
500 	struct wi_req		wreq;
501 	int			i, has_wep;
502 	struct wi_table		*w;
503 
504 	bzero((char *)&wreq, sizeof(wreq));
505 
506 	wreq.wi_len = WI_MAX_DATALEN;
507 	wreq.wi_type = WI_RID_WEP_AVAIL;
508 
509 	wi_getval(iface, &wreq);
510 	has_wep = le16toh(wreq.wi_val[0]);
511 
512 	w = wi_table;
513 
514 	for (i = 0; w[i].wi_code != WI_NONE; i++) {
515 		bzero((char *)&wreq, sizeof(wreq));
516 
517 		wreq.wi_len = WI_MAX_DATALEN;
518 		wreq.wi_type = w[i].wi_type;
519 
520 		wi_getval(iface, &wreq);
521 		printf("%s", w[i].wi_label);
522 		switch (w[i].wi_code) {
523 		case WI_STRING:
524 			wi_printstr(&wreq);
525 			break;
526 		case WI_WORDS:
527 			wi_printwords(&wreq);
528 			break;
529 		case WI_BOOL:
530 			wi_printbool(&wreq);
531 			break;
532 		case WI_HEXBYTES:
533 			wi_printhex(&wreq);
534 			break;
535 		default:
536 			break;
537 		}
538 		printf("\n");
539 	}
540 
541 	if (has_wep) {
542 		w = wi_crypt_table;
543 		for (i = 0; w[i].wi_code != WI_NONE; i++) {
544 			bzero((char *)&wreq, sizeof(wreq));
545 
546 			wreq.wi_len = WI_MAX_DATALEN;
547 			wreq.wi_type = w[i].wi_type;
548 
549 			wi_getval(iface, &wreq);
550 			printf("%s", w[i].wi_label);
551 			switch (w[i].wi_code) {
552 			case WI_STRING:
553 				wi_printstr(&wreq);
554 				break;
555 			case WI_WORDS:
556 				if (wreq.wi_type == WI_RID_TX_CRYPT_KEY)
557 					wreq.wi_val[0] =
558 					  htole16(le16toh(wreq.wi_val[0]) + 1);
559 				wi_printwords(&wreq);
560 				break;
561 			case WI_BOOL:
562 				wi_printbool(&wreq);
563 				break;
564 			case WI_HEXBYTES:
565 				wi_printhex(&wreq);
566 				break;
567 			case WI_KEYSTRUCT:
568 				wi_printkeys(&wreq);
569 				break;
570 			default:
571 				break;
572 			}
573 			printf("\n");
574 		}
575 	}
576 
577 	return;
578 }
579 
580 static void wi_dumpstats(iface)
581 	char			*iface;
582 {
583 	struct wi_req		wreq;
584 	struct wi_counters	*c;
585 
586 	bzero((char *)&wreq, sizeof(wreq));
587 	wreq.wi_len = WI_MAX_DATALEN;
588 	wreq.wi_type = WI_RID_IFACE_STATS;
589 
590 	wi_getval(iface, &wreq);
591 
592 	c = (struct wi_counters *)&wreq.wi_val;
593 
594 	/* XXX native byte order */
595 	printf("Transmitted unicast frames:\t\t%d\n",
596 	    c->wi_tx_unicast_frames);
597 	printf("Transmitted multicast frames:\t\t%d\n",
598 	    c->wi_tx_multicast_frames);
599 	printf("Transmitted fragments:\t\t\t%d\n",
600 	    c->wi_tx_fragments);
601 	printf("Transmitted unicast octets:\t\t%d\n",
602 	    c->wi_tx_unicast_octets);
603 	printf("Transmitted multicast octets:\t\t%d\n",
604 	    c->wi_tx_multicast_octets);
605 	printf("Single transmit retries:\t\t%d\n",
606 	    c->wi_tx_single_retries);
607 	printf("Multiple transmit retries:\t\t%d\n",
608 	    c->wi_tx_multi_retries);
609 	printf("Transmit retry limit exceeded:\t\t%d\n",
610 	    c->wi_tx_retry_limit);
611 	printf("Transmit discards:\t\t\t%d\n",
612 	    c->wi_tx_discards);
613 	printf("Transmit discards due to wrong SA:\t%d\n",
614 	    c->wi_tx_discards_wrong_sa);
615 	printf("Received unicast frames:\t\t%d\n",
616 	    c->wi_rx_unicast_frames);
617 	printf("Received multicast frames:\t\t%d\n",
618 	    c->wi_rx_multicast_frames);
619 	printf("Received fragments:\t\t\t%d\n",
620 	    c->wi_rx_fragments);
621 	printf("Received unicast octets:\t\t%d\n",
622 	    c->wi_rx_unicast_octets);
623 	printf("Received multicast octets:\t\t%d\n",
624 	    c->wi_rx_multicast_octets);
625 	printf("Receive FCS errors:\t\t\t%d\n",
626 	    c->wi_rx_fcs_errors);
627 	printf("Receive discards due to no buffer:\t%d\n",
628 	    c->wi_rx_discards_nobuf);
629 	printf("Can't decrypt WEP frame:\t\t%d\n",
630 	    c->wi_rx_WEP_cant_decrypt);
631 	printf("Received message fragments:\t\t%d\n",
632 	    c->wi_rx_msg_in_msg_frags);
633 	printf("Received message bad fragments:\t\t%d\n",
634 	    c->wi_rx_msg_in_bad_msg_frags);
635 
636 	return;
637 }
638 
639 static void
640 usage()
641 {
642 
643 	fprintf(stderr,
644 	    "usage: %s interface "
645 	    "[-o] [-t tx rate] [-n network name] [-s station name]\n"
646 	    "       [-e 0|1] [-k key [-v 1|2|3|4]] [-T 1|2|3|4]\n"
647 	    "       [-c 0|1] [-q SSID] [-p port type] [-a access point density]\n"
648 	    "       [-m MAC address] [-d max data length] [-r RTS threshold]\n"
649 	    "       [-f frequency] [-M 0|1] [-P 0|1] [-S max sleep duration]\n"
650 	    "       [-A 0|1 ] [-R 1|3]\n"
651 	    ,
652 	    getprogname());
653 	exit(1);
654 }
655 
656 int main(argc, argv)
657 	int			argc;
658 	char			*argv[];
659 {
660 	struct wi_table *wt, **table;
661 	char *iface, *key, *keyv[4], *tx_crypt_key;
662 	int ch, dumpinfo, dumpstats, modifier, oldind;
663 
664 #define	SET_OPERAND(opr, desc) do {				\
665 	if ((opr) == NULL)					\
666 		(opr) = optarg;					\
667 	else							\
668 		warnx("%s is already specified to %s",		\
669 		    desc, (opr));				\
670 } while (0)
671 
672 	dumpinfo = 1;
673 	dumpstats = 0;
674 	iface = key = keyv[0] = keyv[1] = keyv[2] = keyv[3] =
675 	    tx_crypt_key = NULL;
676 
677 	if (argc > 1 && argv[1][0] != '-') {
678 		iface = argv[1];
679 		optind++;
680 	}
681 
682 	while ((ch = getopt(argc, argv,
683 	    "a:c:d:e:f:hi:k:m:n:op:q:r:s:t:A:M:S:P:R:T:")) != -1) {
684 		if (ch != 'i')
685 			dumpinfo = 0;
686 		/*
687 		 * Lookup generic options and remeber operand if found.
688 		 */
689 		for (table = wi_tables; *table != NULL; table++)
690 			if ((wt = wi_optlookup(*table, ch)) != NULL) {
691 				SET_OPERAND(wt->wi_optval, wt->wi_desc);
692 				break;
693 			}
694 		if (wt == NULL)
695 			/*
696 			 * Handle special options.
697 			 */
698 			switch (ch) {
699 			case 'o':
700 				dumpstats = 1;
701 				break;
702 			case 'i':
703 				SET_OPERAND(iface, "interface");
704 				break;
705 			case 'k':
706 				key = optarg;
707 				oldind = optind;
708 				opterr = 0;
709 				ch = getopt(argc, argv, "v:");
710 				opterr = 1;
711 				switch (ch) {
712 				case 'v':
713 					modifier = atoi(optarg) - 1;
714 					break;
715 				default:
716 					modifier = 0;
717 					optind = oldind;
718 					break;
719 				}
720 				keyv[modifier] = key;
721 				break;
722 			case 'T':
723 				SET_OPERAND(tx_crypt_key, "TX encryption key");
724 				break;
725 			case 'h':
726 			default:
727 				usage();
728 				break;
729 			}
730 	}
731 
732 	if (iface == NULL)
733 		usage();
734 
735 	for (table = wi_tables; *table != NULL; table++)
736 		for (wt = *table; wt->wi_code != WI_NONE; wt++)
737 			if (wt->wi_optval != NULL) {
738 				switch (wt->wi_code) {
739 				case WI_BOOL:
740 				case WI_WORDS:
741 					wi_setword(iface, wt->wi_type,
742 					    atoi(wt->wi_optval));
743 					break;
744 				case WI_STRING:
745 					wi_setstr(iface, wt->wi_type,
746 					    wt->wi_optval);
747 					break;
748 				case WI_HEXBYTES:
749 					wi_sethex(iface, wt->wi_type,
750 					    wt->wi_optval);
751 					break;
752 				}
753 			}
754 
755 	if (tx_crypt_key != NULL)
756 		wi_setword(iface, WI_RID_TX_CRYPT_KEY, atoi(tx_crypt_key) - 1);
757 
758 	for (modifier = 0; modifier < sizeof(keyv) / sizeof(keyv[0]);
759 	    modifier++)
760 		if (keyv[modifier] != NULL)
761 			wi_setkeys(iface, keyv[modifier], modifier);
762 
763 	if (dumpstats)
764 		wi_dumpstats(iface);
765 	if (dumpinfo)
766 		wi_dumpinfo(iface);
767 
768 	exit(0);
769 }
770