Deleted Added
full compact
ifieee80211.c (187845) ifieee80211.c (188038)
1/*
2 * Copyright 2001 The Aerospace Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of The Aerospace Corporation may not be used to endorse or
13 * promote products derived from this software.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
1/*
2 * Copyright 2001 The Aerospace Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of The Aerospace Corporation may not be used to endorse or
13 * promote products derived from this software.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 187845 2009-01-28 19:24:29Z sam $
27 * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 188038 2009-02-03 00:15:19Z delphij $
28 */
29
30/*-
31 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32 * All rights reserved.
33 *
34 * This code is derived from software contributed to The NetBSD Foundation
35 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36 * NASA Ames Research Center.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the NetBSD
49 * Foundation, Inc. and its contributors.
50 * 4. Neither the name of The NetBSD Foundation nor the names of its
51 * contributors may be used to endorse or promote products derived
52 * from this software without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64 * POSSIBILITY OF SUCH DAMAGE.
65 */
66
67#include <sys/param.h>
68#include <sys/ioctl.h>
69#include <sys/socket.h>
70#include <sys/sysctl.h>
71#include <sys/time.h>
72
73#include <net/ethernet.h>
74#include <net/if.h>
75#include <net/if_dl.h>
76#include <net/if_types.h>
77#include <net/if_media.h>
78#include <net/route.h>
79
80#include <net80211/ieee80211_ioctl.h>
81
82#include <assert.h>
83#include <ctype.h>
84#include <err.h>
85#include <errno.h>
86#include <fcntl.h>
87#include <inttypes.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <unistd.h>
92#include <stdarg.h>
93#include <stddef.h> /* NB: for offsetof */
94
95#include "ifconfig.h"
96#include "regdomain.h"
97
98#ifndef IEEE80211_FIXED_RATE_NONE
99#define IEEE80211_FIXED_RATE_NONE 0xff
100#endif
101
102#define REQ_ECM 0x01000000 /* enable if ECM set */
103#define REQ_OUTDOOR 0x02000000 /* enable for outdoor operation */
104#define REQ_FLAGS 0xff000000 /* private flags, don't pass to os */
105
106/* XXX need these publicly defined or similar */
107#ifndef IEEE80211_NODE_AUTH
108#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
109#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
110#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
111#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
112#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
113#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
114#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
115#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
116#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
117#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
118#define IEEE80211_NODE_MIMO_PS 0x1000 /* MIMO power save enabled */
119#define IEEE80211_NODE_MIMO_RTS 0x2000 /* send RTS in MIMO PS */
120#define IEEE80211_NODE_RIFS 0x4000 /* RIFS enabled */
121#endif
122
123#define MAXCHAN 1536 /* max 1.5K channels */
124
125#define MAXCOL 78
126static int col;
127static char spacer;
128
129static void LINE_INIT(char c);
130static void LINE_BREAK(void);
131static void LINE_CHECK(const char *fmt, ...);
132
133static const char *modename[] = {
134 "auto", "11a", "11b", "11g", "fh", "turboA", "turboG",
135 "sturbo", "11na", "11ng"
136};
137
138static void set80211(int s, int type, int val, int len, void *data);
139static int get80211(int s, int type, void *data, int len);
140static int get80211len(int s, int type, void *data, int len, int *plen);
141static int get80211val(int s, int type, int *val);
142static const char *get_string(const char *val, const char *sep,
143 u_int8_t *buf, int *lenp);
144static void print_string(const u_int8_t *buf, int len);
145static void print_regdomain(const struct ieee80211_regdomain *, int);
146static void print_channels(int, const struct ieee80211req_chaninfo *,
147 int allchans, int verbose);
148static void regdomain_makechannels(struct ieee80211_regdomain_req *,
149 const struct ieee80211_devcaps_req *);
150
151static struct ieee80211req_chaninfo *chaninfo;
152static struct ieee80211_regdomain regdomain;
153static int gotregdomain = 0;
154static struct ieee80211_roamparams_req roamparams;
155static int gotroam = 0;
156static struct ieee80211_txparams_req txparams;
157static int gottxparams = 0;
158static struct ieee80211_channel curchan;
159static int gotcurchan = 0;
160static struct ifmediareq *ifmr;
161static int htconf = 0;
162static int gothtconf = 0;
163
164static void
165gethtconf(int s)
166{
167 if (gothtconf)
168 return;
169 if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
170 warn("unable to get HT configuration information");
171 gothtconf = 1;
172}
173
174/*
175 * Collect channel info from the kernel. We use this (mostly)
176 * to handle mapping between frequency and IEEE channel number.
177 */
178static void
179getchaninfo(int s)
180{
181 if (chaninfo != NULL)
182 return;
183 chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
184 if (chaninfo == NULL)
185 errx(1, "no space for channel list");
186 if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
187 IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
188 err(1, "unable to get channel information");
189 ifmr = ifmedia_getstate(s);
190 gethtconf(s);
191}
192
193static struct regdata *
194getregdata(void)
195{
196 static struct regdata *rdp = NULL;
197 if (rdp == NULL) {
198 rdp = lib80211_alloc_regdata();
199 if (rdp == NULL)
200 errx(-1, "missing or corrupted regdomain database");
201 }
202 return rdp;
203}
204
205/*
206 * Given the channel at index i with attributes from,
207 * check if there is a channel with attributes to in
208 * the channel table. With suitable attributes this
209 * allows the caller to look for promotion; e.g. from
210 * 11b > 11g.
211 */
212static int
213canpromote(int i, int from, int to)
214{
215 const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
216 int j;
217
218 if ((fc->ic_flags & from) != from)
219 return i;
220 /* NB: quick check exploiting ordering of chans w/ same frequency */
221 if (i+1 < chaninfo->ic_nchans &&
222 chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
223 (chaninfo->ic_chans[i+1].ic_flags & to) == to)
224 return i+1;
225 /* brute force search in case channel list is not ordered */
226 for (j = 0; j < chaninfo->ic_nchans; j++) {
227 const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
228 if (j != i &&
229 tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
230 return j;
231 }
232 return i;
233}
234
235/*
236 * Handle channel promotion. When a channel is specified with
237 * only a frequency we want to promote it to the ``best'' channel
238 * available. The channel list has separate entries for 11b, 11g,
239 * 11a, and 11n[ga] channels so specifying a frequency w/o any
240 * attributes requires we upgrade, e.g. from 11b -> 11g. This
241 * gets complicated when the channel is specified on the same
242 * command line with a media request that constrains the available
243 * channe list (e.g. mode 11a); we want to honor that to avoid
244 * confusing behaviour.
245 */
246static int
247promote(int i)
248{
249 /*
250 * Query the current mode of the interface in case it's
251 * constrained (e.g. to 11a). We must do this carefully
252 * as there may be a pending ifmedia request in which case
253 * asking the kernel will give us the wrong answer. This
254 * is an unfortunate side-effect of the way ifconfig is
255 * structure for modularity (yech).
256 *
257 * NB: ifmr is actually setup in getchaninfo (above); we
258 * assume it's called coincident with to this call so
259 * we have a ``current setting''; otherwise we must pass
260 * the socket descriptor down to here so we can make
261 * the ifmedia_getstate call ourselves.
262 */
263 int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
264
265 /* when ambiguous promote to ``best'' */
266 /* NB: we abitrarily pick HT40+ over HT40- */
267 if (chanmode != IFM_IEEE80211_11B)
268 i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
269 if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
270 i = canpromote(i, IEEE80211_CHAN_G,
271 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
272 if (htconf & 2) {
273 i = canpromote(i, IEEE80211_CHAN_G,
274 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
275 i = canpromote(i, IEEE80211_CHAN_G,
276 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
277 }
278 }
279 if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
280 i = canpromote(i, IEEE80211_CHAN_A,
281 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
282 if (htconf & 2) {
283 i = canpromote(i, IEEE80211_CHAN_A,
284 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
285 i = canpromote(i, IEEE80211_CHAN_A,
286 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
287 }
288 }
289 return i;
290}
291
292static void
293mapfreq(struct ieee80211_channel *chan, int freq, int flags)
294{
295 int i;
296
297 for (i = 0; i < chaninfo->ic_nchans; i++) {
298 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
299
300 if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
301 if (flags == 0) {
302 /* when ambiguous promote to ``best'' */
303 c = &chaninfo->ic_chans[promote(i)];
304 }
305 *chan = *c;
306 return;
307 }
308 }
309 errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
310}
311
312static void
313mapchan(struct ieee80211_channel *chan, int ieee, int flags)
314{
315 int i;
316
317 for (i = 0; i < chaninfo->ic_nchans; i++) {
318 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
319
320 if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
321 if (flags == 0) {
322 /* when ambiguous promote to ``best'' */
323 c = &chaninfo->ic_chans[promote(i)];
324 }
325 *chan = *c;
326 return;
327 }
328 }
329 errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
330}
331
332static const struct ieee80211_channel *
333getcurchan(int s)
334{
335 if (gotcurchan)
336 return &curchan;
337 if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
338 int val;
339 /* fall back to legacy ioctl */
340 if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
341 err(-1, "cannot figure out current channel");
342 getchaninfo(s);
343 mapchan(&curchan, val, 0);
344 }
345 gotcurchan = 1;
346 return &curchan;
347}
348
349static enum ieee80211_phymode
350chan2mode(const struct ieee80211_channel *c)
351{
352 if (IEEE80211_IS_CHAN_HTA(c))
353 return IEEE80211_MODE_11NA;
354 if (IEEE80211_IS_CHAN_HTG(c))
355 return IEEE80211_MODE_11NG;
356 if (IEEE80211_IS_CHAN_108A(c))
357 return IEEE80211_MODE_TURBO_A;
358 if (IEEE80211_IS_CHAN_108G(c))
359 return IEEE80211_MODE_TURBO_G;
360 if (IEEE80211_IS_CHAN_ST(c))
361 return IEEE80211_MODE_STURBO_A;
362 if (IEEE80211_IS_CHAN_FHSS(c))
363 return IEEE80211_MODE_FH;
364 if (IEEE80211_IS_CHAN_A(c))
365 return IEEE80211_MODE_11A;
366 if (IEEE80211_IS_CHAN_ANYG(c))
367 return IEEE80211_MODE_11G;
368 if (IEEE80211_IS_CHAN_B(c))
369 return IEEE80211_MODE_11B;
370 return IEEE80211_MODE_AUTO;
371}
372
373static void
374getroam(int s)
375{
376 if (gotroam)
377 return;
378 if (get80211(s, IEEE80211_IOC_ROAM,
379 &roamparams, sizeof(roamparams)) < 0)
380 err(1, "unable to get roaming parameters");
381 gotroam = 1;
382}
383
384static void
385setroam_cb(int s, void *arg)
386{
387 struct ieee80211_roamparams_req *roam = arg;
388 set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
389}
390
391static void
392gettxparams(int s)
393{
394 if (gottxparams)
395 return;
396 if (get80211(s, IEEE80211_IOC_TXPARAMS,
397 &txparams, sizeof(txparams)) < 0)
398 err(1, "unable to get transmit parameters");
399 gottxparams = 1;
400}
401
402static void
403settxparams_cb(int s, void *arg)
404{
405 struct ieee80211_txparams_req *txp = arg;
406 set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
407}
408
409static void
410getregdomain(int s)
411{
412 if (gotregdomain)
413 return;
414 if (get80211(s, IEEE80211_IOC_REGDOMAIN,
415 &regdomain, sizeof(regdomain)) < 0)
416 err(1, "unable to get regulatory domain info");
417 gotregdomain = 1;
418}
419
420static void
421getdevcaps(int s, struct ieee80211_devcaps_req *dc)
422{
423 if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
424 IEEE80211_DEVCAPS_SPACE(dc)) < 0)
425 err(1, "unable to get device capabilities");
426}
427
428static void
429setregdomain_cb(int s, void *arg)
430{
431 struct ieee80211_regdomain_req *req;
432 struct ieee80211_regdomain *rd = arg;
433 struct ieee80211_devcaps_req *dc;
434 struct regdata *rdp = getregdata();
435
436 if (rd->country != NO_COUNTRY) {
437 const struct country *cc;
438 /*
439 * Check current country seting to make sure it's
440 * compatible with the new regdomain. If not, then
441 * override it with any default country for this
442 * SKU. If we cannot arrange a match, then abort.
443 */
444 cc = lib80211_country_findbycc(rdp, rd->country);
445 if (cc == NULL)
446 errx(1, "unknown ISO country code %d", rd->country);
447 if (cc->rd->sku != rd->regdomain) {
448 const struct regdomain *rp;
449 /*
450 * Check if country is incompatible with regdomain.
451 * To enable multiple regdomains for a country code
452 * we permit a mismatch between the regdomain and
453 * the country's associated regdomain when the
454 * regdomain is setup w/o a default country. For
455 * example, US is bound to the FCC regdomain but
456 * we allow US to be combined with FCC3 because FCC3
457 * has not default country. This allows bogus
458 * combinations like FCC3+DK which are resolved when
459 * constructing the channel list by deferring to the
460 * regdomain to construct the channel list.
461 */
462 rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
463 if (rp == NULL)
464 errx(1, "country %s (%s) is not usable with "
465 "regdomain %d", cc->isoname, cc->name,
466 rd->regdomain);
467 else if (rp->cc != NULL && rp->cc != cc)
468 errx(1, "country %s (%s) is not usable with "
469 "regdomain %s", cc->isoname, cc->name,
470 rp->name);
471 }
472 }
473 /*
474 * Fetch the device capabilities and calculate the
475 * full set of netbands for which we request a new
476 * channel list be constructed. Once that's done we
477 * push the regdomain info + channel list to the kernel.
478 */
479 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
480 if (dc == NULL)
481 errx(1, "no space for device capabilities");
482 dc->dc_chaninfo.ic_nchans = MAXCHAN;
483 getdevcaps(s, dc);
484#if 0
485 if (verbose) {
486 printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
487 printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
488 printf("htcaps : 0x%x\n", dc->dc_htcaps);
489 memcpy(chaninfo, &dc->dc_chaninfo,
490 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
491 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
492 }
493#endif
494 req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
495 if (req == NULL)
496 errx(1, "no space for regdomain request");
497 req->rd = *rd;
498 regdomain_makechannels(req, dc);
499 if (verbose) {
500 LINE_INIT(':');
501 print_regdomain(rd, 1/*verbose*/);
502 LINE_BREAK();
503 /* blech, reallocate channel list for new data */
504 if (chaninfo != NULL)
505 free(chaninfo);
506 chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
507 if (chaninfo == NULL)
508 errx(1, "no space for channel list");
509 memcpy(chaninfo, &req->chaninfo,
510 IEEE80211_CHANINFO_SPACE(&req->chaninfo));
511 print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
512 }
513 if (req->chaninfo.ic_nchans == 0)
514 errx(1, "no channels calculated");
515 set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
516 IEEE80211_REGDOMAIN_SPACE(req), req);
517 free(req);
518 free(dc);
519}
520
521static int
522ieee80211_mhz2ieee(int freq, int flags)
523{
524 struct ieee80211_channel chan;
525 mapfreq(&chan, freq, flags);
526 return chan.ic_ieee;
527}
528
529static int
530isanyarg(const char *arg)
531{
532 return (strncmp(arg, "-", 1) == 0 ||
533 strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
534}
535
536static void
537set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
538{
539 int ssid;
540 int len;
541 u_int8_t data[IEEE80211_NWID_LEN];
542
543 ssid = 0;
544 len = strlen(val);
545 if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
546 ssid = atoi(val)-1;
547 val += 2;
548 }
549
550 bzero(data, sizeof(data));
551 len = sizeof(data);
552 if (get_string(val, NULL, data, &len) == NULL)
553 exit(1);
554
555 set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
556}
557
558static void
559set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
560{
561 int len;
562 u_int8_t data[33];
563
564 bzero(data, sizeof(data));
565 len = sizeof(data);
566 get_string(val, NULL, data, &len);
567
568 set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
569}
570
571/*
572 * Parse a channel specification for attributes/flags.
573 * The syntax is:
574 * freq/xx channel width (5,10,20,40,40+,40-)
575 * freq:mode channel mode (a,b,g,h,n,t,s,d)
576 *
577 * These can be combined in either order; e.g. 2437:ng/40.
578 * Modes are case insensitive.
579 *
580 * The result is not validated here; it's assumed to be
581 * checked against the channel table fetched from the kernel.
582 */
583static int
584getchannelflags(const char *val, int freq)
585{
586#define _CHAN_HT 0x80000000
587 const char *cp;
588 int flags;
589
590 flags = 0;
591
592 cp = strchr(val, ':');
593 if (cp != NULL) {
594 for (cp++; isalpha((int) *cp); cp++) {
595 /* accept mixed case */
596 int c = *cp;
597 if (isupper(c))
598 c = tolower(c);
599 switch (c) {
600 case 'a': /* 802.11a */
601 flags |= IEEE80211_CHAN_A;
602 break;
603 case 'b': /* 802.11b */
604 flags |= IEEE80211_CHAN_B;
605 break;
606 case 'g': /* 802.11g */
607 flags |= IEEE80211_CHAN_G;
608 break;
609 case 'h': /* ht = 802.11n */
610 case 'n': /* 802.11n */
611 flags |= _CHAN_HT; /* NB: private */
612 break;
613 case 'd': /* dt = Atheros Dynamic Turbo */
614 flags |= IEEE80211_CHAN_TURBO;
615 break;
616 case 't': /* ht, dt, st, t */
617 /* dt and unadorned t specify Dynamic Turbo */
618 if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
619 flags |= IEEE80211_CHAN_TURBO;
620 break;
621 case 's': /* st = Atheros Static Turbo */
622 flags |= IEEE80211_CHAN_STURBO;
623 break;
624 default:
625 errx(-1, "%s: Invalid channel attribute %c\n",
626 val, *cp);
627 }
628 }
629 }
630 cp = strchr(val, '/');
631 if (cp != NULL) {
632 char *ep;
633 u_long cw = strtoul(cp+1, &ep, 10);
634
635 switch (cw) {
636 case 5:
637 flags |= IEEE80211_CHAN_QUARTER;
638 break;
639 case 10:
640 flags |= IEEE80211_CHAN_HALF;
641 break;
642 case 20:
643 /* NB: this may be removed below */
644 flags |= IEEE80211_CHAN_HT20;
645 break;
646 case 40:
647 if (ep != NULL && *ep == '+')
648 flags |= IEEE80211_CHAN_HT40U;
649 else if (ep != NULL && *ep == '-')
650 flags |= IEEE80211_CHAN_HT40D;
651 break;
652 default:
653 errx(-1, "%s: Invalid channel width\n", val);
654 }
655 }
656 /*
657 * Cleanup specifications.
658 */
659 if ((flags & _CHAN_HT) == 0) {
660 /*
661 * If user specified freq/20 or freq/40 quietly remove
662 * HT cw attributes depending on channel use. To give
663 * an explicit 20/40 width for an HT channel you must
664 * indicate it is an HT channel since all HT channels
665 * are also usable for legacy operation; e.g. freq:n/40.
666 */
667 flags &= ~IEEE80211_CHAN_HT;
668 } else {
669 /*
670 * Remove private indicator that this is an HT channel
671 * and if no explicit channel width has been given
672 * provide the default settings.
673 */
674 flags &= ~_CHAN_HT;
675 if ((flags & IEEE80211_CHAN_HT) == 0) {
676 struct ieee80211_channel chan;
677 /*
678 * Consult the channel list to see if we can use
679 * HT40+ or HT40- (if both the map routines choose).
680 */
681 if (freq > 255)
682 mapfreq(&chan, freq, 0);
683 else
684 mapchan(&chan, freq, 0);
685 flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
686 }
687 }
688 return flags;
689#undef _CHAN_HT
690}
691
692static void
693getchannel(int s, struct ieee80211_channel *chan, const char *val)
694{
695 int v, flags;
696 char *eptr;
697
698 memset(chan, 0, sizeof(*chan));
699 if (isanyarg(val)) {
700 chan->ic_freq = IEEE80211_CHAN_ANY;
701 return;
702 }
703 getchaninfo(s);
704 errno = 0;
705 v = strtol(val, &eptr, 10);
706 if (val[0] == '\0' || val == eptr || errno == ERANGE ||
707 /* channel may be suffixed with nothing, :flag, or /width */
708 (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
709 errx(1, "invalid channel specification%s",
710 errno == ERANGE ? " (out of range)" : "");
711 flags = getchannelflags(val, v);
712 if (v > 255) { /* treat as frequency */
713 mapfreq(chan, v, flags);
714 } else {
715 mapchan(chan, v, flags);
716 }
717}
718
719static void
720set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
721{
722 struct ieee80211_channel chan;
723
724 getchannel(s, &chan, val);
725 set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
726}
727
728static void
729set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
730{
731 struct ieee80211_chanswitch_req csr;
732
733 getchannel(s, &csr.csa_chan, val);
734 csr.csa_mode = 1;
735 csr.csa_count = 5;
736 set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
737}
738
739static void
740set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
741{
742 int mode;
743
744 if (strcasecmp(val, "none") == 0) {
745 mode = IEEE80211_AUTH_NONE;
746 } else if (strcasecmp(val, "open") == 0) {
747 mode = IEEE80211_AUTH_OPEN;
748 } else if (strcasecmp(val, "shared") == 0) {
749 mode = IEEE80211_AUTH_SHARED;
750 } else if (strcasecmp(val, "8021x") == 0) {
751 mode = IEEE80211_AUTH_8021X;
752 } else if (strcasecmp(val, "wpa") == 0) {
753 mode = IEEE80211_AUTH_WPA;
754 } else {
755 errx(1, "unknown authmode");
756 }
757
758 set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
759}
760
761static void
762set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
763{
764 int mode;
765
766 if (strcasecmp(val, "off") == 0) {
767 mode = IEEE80211_POWERSAVE_OFF;
768 } else if (strcasecmp(val, "on") == 0) {
769 mode = IEEE80211_POWERSAVE_ON;
770 } else if (strcasecmp(val, "cam") == 0) {
771 mode = IEEE80211_POWERSAVE_CAM;
772 } else if (strcasecmp(val, "psp") == 0) {
773 mode = IEEE80211_POWERSAVE_PSP;
774 } else if (strcasecmp(val, "psp-cam") == 0) {
775 mode = IEEE80211_POWERSAVE_PSP_CAM;
776 } else {
777 errx(1, "unknown powersavemode");
778 }
779
780 set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
781}
782
783static void
784set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
785{
786 if (d == 0)
787 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
788 0, NULL);
789 else
790 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
791 0, NULL);
792}
793
794static void
795set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
796{
797 set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
798}
799
800static void
801set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
802{
803 int mode;
804
805 if (strcasecmp(val, "off") == 0) {
806 mode = IEEE80211_WEP_OFF;
807 } else if (strcasecmp(val, "on") == 0) {
808 mode = IEEE80211_WEP_ON;
809 } else if (strcasecmp(val, "mixed") == 0) {
810 mode = IEEE80211_WEP_MIXED;
811 } else {
812 errx(1, "unknown wep mode");
813 }
814
815 set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
816}
817
818static void
819set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
820{
821 set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
822}
823
824static int
825isundefarg(const char *arg)
826{
827 return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
828}
829
830static void
831set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
832{
833 if (isundefarg(val))
834 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
835 else
836 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
837}
838
839static void
840set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
841{
842 int key = 0;
843 int len;
844 u_int8_t data[IEEE80211_KEYBUF_SIZE];
845
846 if (isdigit((int)val[0]) && val[1] == ':') {
847 key = atoi(val)-1;
848 val += 2;
849 }
850
851 bzero(data, sizeof(data));
852 len = sizeof(data);
853 get_string(val, NULL, data, &len);
854
855 set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
856}
857
858/*
859 * This function is purely a NetBSD compatability interface. The NetBSD
860 * interface is too inflexible, but it's there so we'll support it since
861 * it's not all that hard.
862 */
863static void
864set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
865{
866 int txkey;
867 int i, len;
868 u_int8_t data[IEEE80211_KEYBUF_SIZE];
869
870 set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
871
872 if (isdigit((int)val[0]) && val[1] == ':') {
873 txkey = val[0]-'0'-1;
874 val += 2;
875
876 for (i = 0; i < 4; i++) {
877 bzero(data, sizeof(data));
878 len = sizeof(data);
879 val = get_string(val, ",", data, &len);
880 if (val == NULL)
881 exit(1);
882
883 set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
884 }
885 } else {
886 bzero(data, sizeof(data));
887 len = sizeof(data);
888 get_string(val, NULL, data, &len);
889 txkey = 0;
890
891 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
892
893 bzero(data, sizeof(data));
894 for (i = 1; i < 4; i++)
895 set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
896 }
897
898 set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
899}
900
901static void
902set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
903{
904 set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
905 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
906}
907
908static void
909set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
910{
911 int mode;
912
913 if (strcasecmp(val, "off") == 0) {
914 mode = IEEE80211_PROTMODE_OFF;
915 } else if (strcasecmp(val, "cts") == 0) {
916 mode = IEEE80211_PROTMODE_CTS;
917 } else if (strncasecmp(val, "rtscts", 3) == 0) {
918 mode = IEEE80211_PROTMODE_RTSCTS;
919 } else {
920 errx(1, "unknown protection mode");
921 }
922
923 set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
924}
925
926static void
927set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
928{
929 int mode;
930
931 if (strcasecmp(val, "off") == 0) {
932 mode = IEEE80211_PROTMODE_OFF;
933 } else if (strncasecmp(val, "rts", 3) == 0) {
934 mode = IEEE80211_PROTMODE_RTSCTS;
935 } else {
936 errx(1, "unknown protection mode");
937 }
938
939 set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
940}
941
942static void
943set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
944{
945 double v = atof(val);
946 int txpow;
947
948 txpow = (int) (2*v);
949 if (txpow != 2*v)
950 errx(-1, "invalid tx power (must be .5 dBm units)");
951 set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
952}
953
954#define IEEE80211_ROAMING_DEVICE 0
955#define IEEE80211_ROAMING_AUTO 1
956#define IEEE80211_ROAMING_MANUAL 2
957
958static void
959set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
960{
961 int mode;
962
963 if (strcasecmp(val, "device") == 0) {
964 mode = IEEE80211_ROAMING_DEVICE;
965 } else if (strcasecmp(val, "auto") == 0) {
966 mode = IEEE80211_ROAMING_AUTO;
967 } else if (strcasecmp(val, "manual") == 0) {
968 mode = IEEE80211_ROAMING_MANUAL;
969 } else {
970 errx(1, "unknown roaming mode");
971 }
972 set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
973}
974
975static void
976set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
977{
978 set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
979}
980
981static void
982set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
983{
984 set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
985}
986
987static void
988set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
989{
990 set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
991}
992
993static void
994set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
995{
996 set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
997}
998
999static void
1000set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1001{
1002 set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1003}
1004
1005static void
1006set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1007{
1008 struct ieee80211req_chanlist chanlist;
1009 char *temp, *cp, *tp;
1010
1011 temp = malloc(strlen(val) + 1);
1012 if (temp == NULL)
1013 errx(1, "malloc failed");
1014 strcpy(temp, val);
1015 memset(&chanlist, 0, sizeof(chanlist));
1016 cp = temp;
1017 for (;;) {
1018 int first, last, f, c;
1019
1020 tp = strchr(cp, ',');
1021 if (tp != NULL)
1022 *tp++ = '\0';
1023 switch (sscanf(cp, "%u-%u", &first, &last)) {
1024 case 1:
1025 if (first > IEEE80211_CHAN_MAX)
28 */
29
30/*-
31 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
32 * All rights reserved.
33 *
34 * This code is derived from software contributed to The NetBSD Foundation
35 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
36 * NASA Ames Research Center.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the NetBSD
49 * Foundation, Inc. and its contributors.
50 * 4. Neither the name of The NetBSD Foundation nor the names of its
51 * contributors may be used to endorse or promote products derived
52 * from this software without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
55 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
56 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
58 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
64 * POSSIBILITY OF SUCH DAMAGE.
65 */
66
67#include <sys/param.h>
68#include <sys/ioctl.h>
69#include <sys/socket.h>
70#include <sys/sysctl.h>
71#include <sys/time.h>
72
73#include <net/ethernet.h>
74#include <net/if.h>
75#include <net/if_dl.h>
76#include <net/if_types.h>
77#include <net/if_media.h>
78#include <net/route.h>
79
80#include <net80211/ieee80211_ioctl.h>
81
82#include <assert.h>
83#include <ctype.h>
84#include <err.h>
85#include <errno.h>
86#include <fcntl.h>
87#include <inttypes.h>
88#include <stdio.h>
89#include <stdlib.h>
90#include <string.h>
91#include <unistd.h>
92#include <stdarg.h>
93#include <stddef.h> /* NB: for offsetof */
94
95#include "ifconfig.h"
96#include "regdomain.h"
97
98#ifndef IEEE80211_FIXED_RATE_NONE
99#define IEEE80211_FIXED_RATE_NONE 0xff
100#endif
101
102#define REQ_ECM 0x01000000 /* enable if ECM set */
103#define REQ_OUTDOOR 0x02000000 /* enable for outdoor operation */
104#define REQ_FLAGS 0xff000000 /* private flags, don't pass to os */
105
106/* XXX need these publicly defined or similar */
107#ifndef IEEE80211_NODE_AUTH
108#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
109#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
110#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
111#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
112#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
113#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
114#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
115#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
116#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
117#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
118#define IEEE80211_NODE_MIMO_PS 0x1000 /* MIMO power save enabled */
119#define IEEE80211_NODE_MIMO_RTS 0x2000 /* send RTS in MIMO PS */
120#define IEEE80211_NODE_RIFS 0x4000 /* RIFS enabled */
121#endif
122
123#define MAXCHAN 1536 /* max 1.5K channels */
124
125#define MAXCOL 78
126static int col;
127static char spacer;
128
129static void LINE_INIT(char c);
130static void LINE_BREAK(void);
131static void LINE_CHECK(const char *fmt, ...);
132
133static const char *modename[] = {
134 "auto", "11a", "11b", "11g", "fh", "turboA", "turboG",
135 "sturbo", "11na", "11ng"
136};
137
138static void set80211(int s, int type, int val, int len, void *data);
139static int get80211(int s, int type, void *data, int len);
140static int get80211len(int s, int type, void *data, int len, int *plen);
141static int get80211val(int s, int type, int *val);
142static const char *get_string(const char *val, const char *sep,
143 u_int8_t *buf, int *lenp);
144static void print_string(const u_int8_t *buf, int len);
145static void print_regdomain(const struct ieee80211_regdomain *, int);
146static void print_channels(int, const struct ieee80211req_chaninfo *,
147 int allchans, int verbose);
148static void regdomain_makechannels(struct ieee80211_regdomain_req *,
149 const struct ieee80211_devcaps_req *);
150
151static struct ieee80211req_chaninfo *chaninfo;
152static struct ieee80211_regdomain regdomain;
153static int gotregdomain = 0;
154static struct ieee80211_roamparams_req roamparams;
155static int gotroam = 0;
156static struct ieee80211_txparams_req txparams;
157static int gottxparams = 0;
158static struct ieee80211_channel curchan;
159static int gotcurchan = 0;
160static struct ifmediareq *ifmr;
161static int htconf = 0;
162static int gothtconf = 0;
163
164static void
165gethtconf(int s)
166{
167 if (gothtconf)
168 return;
169 if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
170 warn("unable to get HT configuration information");
171 gothtconf = 1;
172}
173
174/*
175 * Collect channel info from the kernel. We use this (mostly)
176 * to handle mapping between frequency and IEEE channel number.
177 */
178static void
179getchaninfo(int s)
180{
181 if (chaninfo != NULL)
182 return;
183 chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
184 if (chaninfo == NULL)
185 errx(1, "no space for channel list");
186 if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
187 IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
188 err(1, "unable to get channel information");
189 ifmr = ifmedia_getstate(s);
190 gethtconf(s);
191}
192
193static struct regdata *
194getregdata(void)
195{
196 static struct regdata *rdp = NULL;
197 if (rdp == NULL) {
198 rdp = lib80211_alloc_regdata();
199 if (rdp == NULL)
200 errx(-1, "missing or corrupted regdomain database");
201 }
202 return rdp;
203}
204
205/*
206 * Given the channel at index i with attributes from,
207 * check if there is a channel with attributes to in
208 * the channel table. With suitable attributes this
209 * allows the caller to look for promotion; e.g. from
210 * 11b > 11g.
211 */
212static int
213canpromote(int i, int from, int to)
214{
215 const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
216 int j;
217
218 if ((fc->ic_flags & from) != from)
219 return i;
220 /* NB: quick check exploiting ordering of chans w/ same frequency */
221 if (i+1 < chaninfo->ic_nchans &&
222 chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
223 (chaninfo->ic_chans[i+1].ic_flags & to) == to)
224 return i+1;
225 /* brute force search in case channel list is not ordered */
226 for (j = 0; j < chaninfo->ic_nchans; j++) {
227 const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
228 if (j != i &&
229 tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
230 return j;
231 }
232 return i;
233}
234
235/*
236 * Handle channel promotion. When a channel is specified with
237 * only a frequency we want to promote it to the ``best'' channel
238 * available. The channel list has separate entries for 11b, 11g,
239 * 11a, and 11n[ga] channels so specifying a frequency w/o any
240 * attributes requires we upgrade, e.g. from 11b -> 11g. This
241 * gets complicated when the channel is specified on the same
242 * command line with a media request that constrains the available
243 * channe list (e.g. mode 11a); we want to honor that to avoid
244 * confusing behaviour.
245 */
246static int
247promote(int i)
248{
249 /*
250 * Query the current mode of the interface in case it's
251 * constrained (e.g. to 11a). We must do this carefully
252 * as there may be a pending ifmedia request in which case
253 * asking the kernel will give us the wrong answer. This
254 * is an unfortunate side-effect of the way ifconfig is
255 * structure for modularity (yech).
256 *
257 * NB: ifmr is actually setup in getchaninfo (above); we
258 * assume it's called coincident with to this call so
259 * we have a ``current setting''; otherwise we must pass
260 * the socket descriptor down to here so we can make
261 * the ifmedia_getstate call ourselves.
262 */
263 int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
264
265 /* when ambiguous promote to ``best'' */
266 /* NB: we abitrarily pick HT40+ over HT40- */
267 if (chanmode != IFM_IEEE80211_11B)
268 i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
269 if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
270 i = canpromote(i, IEEE80211_CHAN_G,
271 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
272 if (htconf & 2) {
273 i = canpromote(i, IEEE80211_CHAN_G,
274 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
275 i = canpromote(i, IEEE80211_CHAN_G,
276 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
277 }
278 }
279 if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
280 i = canpromote(i, IEEE80211_CHAN_A,
281 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
282 if (htconf & 2) {
283 i = canpromote(i, IEEE80211_CHAN_A,
284 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
285 i = canpromote(i, IEEE80211_CHAN_A,
286 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
287 }
288 }
289 return i;
290}
291
292static void
293mapfreq(struct ieee80211_channel *chan, int freq, int flags)
294{
295 int i;
296
297 for (i = 0; i < chaninfo->ic_nchans; i++) {
298 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
299
300 if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
301 if (flags == 0) {
302 /* when ambiguous promote to ``best'' */
303 c = &chaninfo->ic_chans[promote(i)];
304 }
305 *chan = *c;
306 return;
307 }
308 }
309 errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
310}
311
312static void
313mapchan(struct ieee80211_channel *chan, int ieee, int flags)
314{
315 int i;
316
317 for (i = 0; i < chaninfo->ic_nchans; i++) {
318 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
319
320 if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
321 if (flags == 0) {
322 /* when ambiguous promote to ``best'' */
323 c = &chaninfo->ic_chans[promote(i)];
324 }
325 *chan = *c;
326 return;
327 }
328 }
329 errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
330}
331
332static const struct ieee80211_channel *
333getcurchan(int s)
334{
335 if (gotcurchan)
336 return &curchan;
337 if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
338 int val;
339 /* fall back to legacy ioctl */
340 if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
341 err(-1, "cannot figure out current channel");
342 getchaninfo(s);
343 mapchan(&curchan, val, 0);
344 }
345 gotcurchan = 1;
346 return &curchan;
347}
348
349static enum ieee80211_phymode
350chan2mode(const struct ieee80211_channel *c)
351{
352 if (IEEE80211_IS_CHAN_HTA(c))
353 return IEEE80211_MODE_11NA;
354 if (IEEE80211_IS_CHAN_HTG(c))
355 return IEEE80211_MODE_11NG;
356 if (IEEE80211_IS_CHAN_108A(c))
357 return IEEE80211_MODE_TURBO_A;
358 if (IEEE80211_IS_CHAN_108G(c))
359 return IEEE80211_MODE_TURBO_G;
360 if (IEEE80211_IS_CHAN_ST(c))
361 return IEEE80211_MODE_STURBO_A;
362 if (IEEE80211_IS_CHAN_FHSS(c))
363 return IEEE80211_MODE_FH;
364 if (IEEE80211_IS_CHAN_A(c))
365 return IEEE80211_MODE_11A;
366 if (IEEE80211_IS_CHAN_ANYG(c))
367 return IEEE80211_MODE_11G;
368 if (IEEE80211_IS_CHAN_B(c))
369 return IEEE80211_MODE_11B;
370 return IEEE80211_MODE_AUTO;
371}
372
373static void
374getroam(int s)
375{
376 if (gotroam)
377 return;
378 if (get80211(s, IEEE80211_IOC_ROAM,
379 &roamparams, sizeof(roamparams)) < 0)
380 err(1, "unable to get roaming parameters");
381 gotroam = 1;
382}
383
384static void
385setroam_cb(int s, void *arg)
386{
387 struct ieee80211_roamparams_req *roam = arg;
388 set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
389}
390
391static void
392gettxparams(int s)
393{
394 if (gottxparams)
395 return;
396 if (get80211(s, IEEE80211_IOC_TXPARAMS,
397 &txparams, sizeof(txparams)) < 0)
398 err(1, "unable to get transmit parameters");
399 gottxparams = 1;
400}
401
402static void
403settxparams_cb(int s, void *arg)
404{
405 struct ieee80211_txparams_req *txp = arg;
406 set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
407}
408
409static void
410getregdomain(int s)
411{
412 if (gotregdomain)
413 return;
414 if (get80211(s, IEEE80211_IOC_REGDOMAIN,
415 &regdomain, sizeof(regdomain)) < 0)
416 err(1, "unable to get regulatory domain info");
417 gotregdomain = 1;
418}
419
420static void
421getdevcaps(int s, struct ieee80211_devcaps_req *dc)
422{
423 if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
424 IEEE80211_DEVCAPS_SPACE(dc)) < 0)
425 err(1, "unable to get device capabilities");
426}
427
428static void
429setregdomain_cb(int s, void *arg)
430{
431 struct ieee80211_regdomain_req *req;
432 struct ieee80211_regdomain *rd = arg;
433 struct ieee80211_devcaps_req *dc;
434 struct regdata *rdp = getregdata();
435
436 if (rd->country != NO_COUNTRY) {
437 const struct country *cc;
438 /*
439 * Check current country seting to make sure it's
440 * compatible with the new regdomain. If not, then
441 * override it with any default country for this
442 * SKU. If we cannot arrange a match, then abort.
443 */
444 cc = lib80211_country_findbycc(rdp, rd->country);
445 if (cc == NULL)
446 errx(1, "unknown ISO country code %d", rd->country);
447 if (cc->rd->sku != rd->regdomain) {
448 const struct regdomain *rp;
449 /*
450 * Check if country is incompatible with regdomain.
451 * To enable multiple regdomains for a country code
452 * we permit a mismatch between the regdomain and
453 * the country's associated regdomain when the
454 * regdomain is setup w/o a default country. For
455 * example, US is bound to the FCC regdomain but
456 * we allow US to be combined with FCC3 because FCC3
457 * has not default country. This allows bogus
458 * combinations like FCC3+DK which are resolved when
459 * constructing the channel list by deferring to the
460 * regdomain to construct the channel list.
461 */
462 rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
463 if (rp == NULL)
464 errx(1, "country %s (%s) is not usable with "
465 "regdomain %d", cc->isoname, cc->name,
466 rd->regdomain);
467 else if (rp->cc != NULL && rp->cc != cc)
468 errx(1, "country %s (%s) is not usable with "
469 "regdomain %s", cc->isoname, cc->name,
470 rp->name);
471 }
472 }
473 /*
474 * Fetch the device capabilities and calculate the
475 * full set of netbands for which we request a new
476 * channel list be constructed. Once that's done we
477 * push the regdomain info + channel list to the kernel.
478 */
479 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
480 if (dc == NULL)
481 errx(1, "no space for device capabilities");
482 dc->dc_chaninfo.ic_nchans = MAXCHAN;
483 getdevcaps(s, dc);
484#if 0
485 if (verbose) {
486 printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
487 printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
488 printf("htcaps : 0x%x\n", dc->dc_htcaps);
489 memcpy(chaninfo, &dc->dc_chaninfo,
490 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
491 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
492 }
493#endif
494 req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
495 if (req == NULL)
496 errx(1, "no space for regdomain request");
497 req->rd = *rd;
498 regdomain_makechannels(req, dc);
499 if (verbose) {
500 LINE_INIT(':');
501 print_regdomain(rd, 1/*verbose*/);
502 LINE_BREAK();
503 /* blech, reallocate channel list for new data */
504 if (chaninfo != NULL)
505 free(chaninfo);
506 chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
507 if (chaninfo == NULL)
508 errx(1, "no space for channel list");
509 memcpy(chaninfo, &req->chaninfo,
510 IEEE80211_CHANINFO_SPACE(&req->chaninfo));
511 print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
512 }
513 if (req->chaninfo.ic_nchans == 0)
514 errx(1, "no channels calculated");
515 set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
516 IEEE80211_REGDOMAIN_SPACE(req), req);
517 free(req);
518 free(dc);
519}
520
521static int
522ieee80211_mhz2ieee(int freq, int flags)
523{
524 struct ieee80211_channel chan;
525 mapfreq(&chan, freq, flags);
526 return chan.ic_ieee;
527}
528
529static int
530isanyarg(const char *arg)
531{
532 return (strncmp(arg, "-", 1) == 0 ||
533 strncasecmp(arg, "any", 3) == 0 || strncasecmp(arg, "off", 3) == 0);
534}
535
536static void
537set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
538{
539 int ssid;
540 int len;
541 u_int8_t data[IEEE80211_NWID_LEN];
542
543 ssid = 0;
544 len = strlen(val);
545 if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
546 ssid = atoi(val)-1;
547 val += 2;
548 }
549
550 bzero(data, sizeof(data));
551 len = sizeof(data);
552 if (get_string(val, NULL, data, &len) == NULL)
553 exit(1);
554
555 set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
556}
557
558static void
559set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
560{
561 int len;
562 u_int8_t data[33];
563
564 bzero(data, sizeof(data));
565 len = sizeof(data);
566 get_string(val, NULL, data, &len);
567
568 set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
569}
570
571/*
572 * Parse a channel specification for attributes/flags.
573 * The syntax is:
574 * freq/xx channel width (5,10,20,40,40+,40-)
575 * freq:mode channel mode (a,b,g,h,n,t,s,d)
576 *
577 * These can be combined in either order; e.g. 2437:ng/40.
578 * Modes are case insensitive.
579 *
580 * The result is not validated here; it's assumed to be
581 * checked against the channel table fetched from the kernel.
582 */
583static int
584getchannelflags(const char *val, int freq)
585{
586#define _CHAN_HT 0x80000000
587 const char *cp;
588 int flags;
589
590 flags = 0;
591
592 cp = strchr(val, ':');
593 if (cp != NULL) {
594 for (cp++; isalpha((int) *cp); cp++) {
595 /* accept mixed case */
596 int c = *cp;
597 if (isupper(c))
598 c = tolower(c);
599 switch (c) {
600 case 'a': /* 802.11a */
601 flags |= IEEE80211_CHAN_A;
602 break;
603 case 'b': /* 802.11b */
604 flags |= IEEE80211_CHAN_B;
605 break;
606 case 'g': /* 802.11g */
607 flags |= IEEE80211_CHAN_G;
608 break;
609 case 'h': /* ht = 802.11n */
610 case 'n': /* 802.11n */
611 flags |= _CHAN_HT; /* NB: private */
612 break;
613 case 'd': /* dt = Atheros Dynamic Turbo */
614 flags |= IEEE80211_CHAN_TURBO;
615 break;
616 case 't': /* ht, dt, st, t */
617 /* dt and unadorned t specify Dynamic Turbo */
618 if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
619 flags |= IEEE80211_CHAN_TURBO;
620 break;
621 case 's': /* st = Atheros Static Turbo */
622 flags |= IEEE80211_CHAN_STURBO;
623 break;
624 default:
625 errx(-1, "%s: Invalid channel attribute %c\n",
626 val, *cp);
627 }
628 }
629 }
630 cp = strchr(val, '/');
631 if (cp != NULL) {
632 char *ep;
633 u_long cw = strtoul(cp+1, &ep, 10);
634
635 switch (cw) {
636 case 5:
637 flags |= IEEE80211_CHAN_QUARTER;
638 break;
639 case 10:
640 flags |= IEEE80211_CHAN_HALF;
641 break;
642 case 20:
643 /* NB: this may be removed below */
644 flags |= IEEE80211_CHAN_HT20;
645 break;
646 case 40:
647 if (ep != NULL && *ep == '+')
648 flags |= IEEE80211_CHAN_HT40U;
649 else if (ep != NULL && *ep == '-')
650 flags |= IEEE80211_CHAN_HT40D;
651 break;
652 default:
653 errx(-1, "%s: Invalid channel width\n", val);
654 }
655 }
656 /*
657 * Cleanup specifications.
658 */
659 if ((flags & _CHAN_HT) == 0) {
660 /*
661 * If user specified freq/20 or freq/40 quietly remove
662 * HT cw attributes depending on channel use. To give
663 * an explicit 20/40 width for an HT channel you must
664 * indicate it is an HT channel since all HT channels
665 * are also usable for legacy operation; e.g. freq:n/40.
666 */
667 flags &= ~IEEE80211_CHAN_HT;
668 } else {
669 /*
670 * Remove private indicator that this is an HT channel
671 * and if no explicit channel width has been given
672 * provide the default settings.
673 */
674 flags &= ~_CHAN_HT;
675 if ((flags & IEEE80211_CHAN_HT) == 0) {
676 struct ieee80211_channel chan;
677 /*
678 * Consult the channel list to see if we can use
679 * HT40+ or HT40- (if both the map routines choose).
680 */
681 if (freq > 255)
682 mapfreq(&chan, freq, 0);
683 else
684 mapchan(&chan, freq, 0);
685 flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
686 }
687 }
688 return flags;
689#undef _CHAN_HT
690}
691
692static void
693getchannel(int s, struct ieee80211_channel *chan, const char *val)
694{
695 int v, flags;
696 char *eptr;
697
698 memset(chan, 0, sizeof(*chan));
699 if (isanyarg(val)) {
700 chan->ic_freq = IEEE80211_CHAN_ANY;
701 return;
702 }
703 getchaninfo(s);
704 errno = 0;
705 v = strtol(val, &eptr, 10);
706 if (val[0] == '\0' || val == eptr || errno == ERANGE ||
707 /* channel may be suffixed with nothing, :flag, or /width */
708 (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
709 errx(1, "invalid channel specification%s",
710 errno == ERANGE ? " (out of range)" : "");
711 flags = getchannelflags(val, v);
712 if (v > 255) { /* treat as frequency */
713 mapfreq(chan, v, flags);
714 } else {
715 mapchan(chan, v, flags);
716 }
717}
718
719static void
720set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
721{
722 struct ieee80211_channel chan;
723
724 getchannel(s, &chan, val);
725 set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
726}
727
728static void
729set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
730{
731 struct ieee80211_chanswitch_req csr;
732
733 getchannel(s, &csr.csa_chan, val);
734 csr.csa_mode = 1;
735 csr.csa_count = 5;
736 set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
737}
738
739static void
740set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
741{
742 int mode;
743
744 if (strcasecmp(val, "none") == 0) {
745 mode = IEEE80211_AUTH_NONE;
746 } else if (strcasecmp(val, "open") == 0) {
747 mode = IEEE80211_AUTH_OPEN;
748 } else if (strcasecmp(val, "shared") == 0) {
749 mode = IEEE80211_AUTH_SHARED;
750 } else if (strcasecmp(val, "8021x") == 0) {
751 mode = IEEE80211_AUTH_8021X;
752 } else if (strcasecmp(val, "wpa") == 0) {
753 mode = IEEE80211_AUTH_WPA;
754 } else {
755 errx(1, "unknown authmode");
756 }
757
758 set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
759}
760
761static void
762set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
763{
764 int mode;
765
766 if (strcasecmp(val, "off") == 0) {
767 mode = IEEE80211_POWERSAVE_OFF;
768 } else if (strcasecmp(val, "on") == 0) {
769 mode = IEEE80211_POWERSAVE_ON;
770 } else if (strcasecmp(val, "cam") == 0) {
771 mode = IEEE80211_POWERSAVE_CAM;
772 } else if (strcasecmp(val, "psp") == 0) {
773 mode = IEEE80211_POWERSAVE_PSP;
774 } else if (strcasecmp(val, "psp-cam") == 0) {
775 mode = IEEE80211_POWERSAVE_PSP_CAM;
776 } else {
777 errx(1, "unknown powersavemode");
778 }
779
780 set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
781}
782
783static void
784set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
785{
786 if (d == 0)
787 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
788 0, NULL);
789 else
790 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
791 0, NULL);
792}
793
794static void
795set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
796{
797 set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
798}
799
800static void
801set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
802{
803 int mode;
804
805 if (strcasecmp(val, "off") == 0) {
806 mode = IEEE80211_WEP_OFF;
807 } else if (strcasecmp(val, "on") == 0) {
808 mode = IEEE80211_WEP_ON;
809 } else if (strcasecmp(val, "mixed") == 0) {
810 mode = IEEE80211_WEP_MIXED;
811 } else {
812 errx(1, "unknown wep mode");
813 }
814
815 set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
816}
817
818static void
819set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
820{
821 set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
822}
823
824static int
825isundefarg(const char *arg)
826{
827 return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
828}
829
830static void
831set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
832{
833 if (isundefarg(val))
834 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
835 else
836 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
837}
838
839static void
840set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
841{
842 int key = 0;
843 int len;
844 u_int8_t data[IEEE80211_KEYBUF_SIZE];
845
846 if (isdigit((int)val[0]) && val[1] == ':') {
847 key = atoi(val)-1;
848 val += 2;
849 }
850
851 bzero(data, sizeof(data));
852 len = sizeof(data);
853 get_string(val, NULL, data, &len);
854
855 set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
856}
857
858/*
859 * This function is purely a NetBSD compatability interface. The NetBSD
860 * interface is too inflexible, but it's there so we'll support it since
861 * it's not all that hard.
862 */
863static void
864set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
865{
866 int txkey;
867 int i, len;
868 u_int8_t data[IEEE80211_KEYBUF_SIZE];
869
870 set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
871
872 if (isdigit((int)val[0]) && val[1] == ':') {
873 txkey = val[0]-'0'-1;
874 val += 2;
875
876 for (i = 0; i < 4; i++) {
877 bzero(data, sizeof(data));
878 len = sizeof(data);
879 val = get_string(val, ",", data, &len);
880 if (val == NULL)
881 exit(1);
882
883 set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
884 }
885 } else {
886 bzero(data, sizeof(data));
887 len = sizeof(data);
888 get_string(val, NULL, data, &len);
889 txkey = 0;
890
891 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
892
893 bzero(data, sizeof(data));
894 for (i = 1; i < 4; i++)
895 set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
896 }
897
898 set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
899}
900
901static void
902set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
903{
904 set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
905 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
906}
907
908static void
909set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
910{
911 int mode;
912
913 if (strcasecmp(val, "off") == 0) {
914 mode = IEEE80211_PROTMODE_OFF;
915 } else if (strcasecmp(val, "cts") == 0) {
916 mode = IEEE80211_PROTMODE_CTS;
917 } else if (strncasecmp(val, "rtscts", 3) == 0) {
918 mode = IEEE80211_PROTMODE_RTSCTS;
919 } else {
920 errx(1, "unknown protection mode");
921 }
922
923 set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
924}
925
926static void
927set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
928{
929 int mode;
930
931 if (strcasecmp(val, "off") == 0) {
932 mode = IEEE80211_PROTMODE_OFF;
933 } else if (strncasecmp(val, "rts", 3) == 0) {
934 mode = IEEE80211_PROTMODE_RTSCTS;
935 } else {
936 errx(1, "unknown protection mode");
937 }
938
939 set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
940}
941
942static void
943set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
944{
945 double v = atof(val);
946 int txpow;
947
948 txpow = (int) (2*v);
949 if (txpow != 2*v)
950 errx(-1, "invalid tx power (must be .5 dBm units)");
951 set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
952}
953
954#define IEEE80211_ROAMING_DEVICE 0
955#define IEEE80211_ROAMING_AUTO 1
956#define IEEE80211_ROAMING_MANUAL 2
957
958static void
959set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
960{
961 int mode;
962
963 if (strcasecmp(val, "device") == 0) {
964 mode = IEEE80211_ROAMING_DEVICE;
965 } else if (strcasecmp(val, "auto") == 0) {
966 mode = IEEE80211_ROAMING_AUTO;
967 } else if (strcasecmp(val, "manual") == 0) {
968 mode = IEEE80211_ROAMING_MANUAL;
969 } else {
970 errx(1, "unknown roaming mode");
971 }
972 set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
973}
974
975static void
976set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
977{
978 set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
979}
980
981static void
982set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
983{
984 set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
985}
986
987static void
988set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
989{
990 set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
991}
992
993static void
994set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
995{
996 set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
997}
998
999static void
1000set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1001{
1002 set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1003}
1004
1005static void
1006set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1007{
1008 struct ieee80211req_chanlist chanlist;
1009 char *temp, *cp, *tp;
1010
1011 temp = malloc(strlen(val) + 1);
1012 if (temp == NULL)
1013 errx(1, "malloc failed");
1014 strcpy(temp, val);
1015 memset(&chanlist, 0, sizeof(chanlist));
1016 cp = temp;
1017 for (;;) {
1018 int first, last, f, c;
1019
1020 tp = strchr(cp, ',');
1021 if (tp != NULL)
1022 *tp++ = '\0';
1023 switch (sscanf(cp, "%u-%u", &first, &last)) {
1024 case 1:
1025 if (first > IEEE80211_CHAN_MAX)
1026 errx(-1, "channel %u out of range, max %zu",
1026 errx(-1, "channel %u out of range, max %u",
1027 first, IEEE80211_CHAN_MAX);
1028 setbit(chanlist.ic_channels, first);
1029 break;
1030 case 2:
1031 if (first > IEEE80211_CHAN_MAX)
1027 first, IEEE80211_CHAN_MAX);
1028 setbit(chanlist.ic_channels, first);
1029 break;
1030 case 2:
1031 if (first > IEEE80211_CHAN_MAX)
1032 errx(-1, "channel %u out of range, max %zu",
1032 errx(-1, "channel %u out of range, max %u",
1033 first, IEEE80211_CHAN_MAX);
1034 if (last > IEEE80211_CHAN_MAX)
1033 first, IEEE80211_CHAN_MAX);
1034 if (last > IEEE80211_CHAN_MAX)
1035 errx(-1, "channel %u out of range, max %zu",
1035 errx(-1, "channel %u out of range, max %u",
1036 last, IEEE80211_CHAN_MAX);
1037 if (first > last)
1038 errx(-1, "void channel range, %u > %u",
1039 first, last);
1040 for (f = first; f <= last; f++)
1041 setbit(chanlist.ic_channels, f);
1042 break;
1043 }
1044 if (tp == NULL)
1045 break;
1046 c = *tp;
1047 while (isspace(c))
1048 tp++;
1049 if (!isdigit(c))
1050 break;
1051 cp = tp;
1052 }
1053 set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1054}
1055
1056static void
1057set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1058{
1059
1060 if (!isanyarg(val)) {
1061 char *temp;
1062 struct sockaddr_dl sdl;
1063
1064 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1065 if (temp == NULL)
1066 errx(1, "malloc failed");
1067 temp[0] = ':';
1068 strcpy(temp + 1, val);
1069 sdl.sdl_len = sizeof(sdl);
1070 link_addr(temp, &sdl);
1071 free(temp);
1072 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1073 errx(1, "malformed link-level address");
1074 set80211(s, IEEE80211_IOC_BSSID, 0,
1075 IEEE80211_ADDR_LEN, LLADDR(&sdl));
1076 } else {
1077 uint8_t zerobssid[IEEE80211_ADDR_LEN];
1078 memset(zerobssid, 0, sizeof(zerobssid));
1079 set80211(s, IEEE80211_IOC_BSSID, 0,
1080 IEEE80211_ADDR_LEN, zerobssid);
1081 }
1082}
1083
1084static int
1085getac(const char *ac)
1086{
1087 if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1088 return WME_AC_BE;
1089 if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1090 return WME_AC_BK;
1091 if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1092 return WME_AC_VI;
1093 if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1094 return WME_AC_VO;
1095 errx(1, "unknown wme access class %s", ac);
1096}
1097
1098static
1099DECL_CMD_FUNC2(set80211cwmin, ac, val)
1100{
1101 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1102}
1103
1104static
1105DECL_CMD_FUNC2(set80211cwmax, ac, val)
1106{
1107 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1108}
1109
1110static
1111DECL_CMD_FUNC2(set80211aifs, ac, val)
1112{
1113 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1114}
1115
1116static
1117DECL_CMD_FUNC2(set80211txoplimit, ac, val)
1118{
1119 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1120}
1121
1122static
1123DECL_CMD_FUNC(set80211acm, ac, d)
1124{
1125 set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1126}
1127static
1128DECL_CMD_FUNC(set80211noacm, ac, d)
1129{
1130 set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1131}
1132
1133static
1134DECL_CMD_FUNC(set80211ackpolicy, ac, d)
1135{
1136 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1137}
1138static
1139DECL_CMD_FUNC(set80211noackpolicy, ac, d)
1140{
1141 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1142}
1143
1144static
1145DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1146{
1147 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1148 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1149}
1150
1151static
1152DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1153{
1154 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1155 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1156}
1157
1158static
1159DECL_CMD_FUNC2(set80211bssaifs, ac, val)
1160{
1161 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1162 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1163}
1164
1165static
1166DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1167{
1168 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1169 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1170}
1171
1172static
1173DECL_CMD_FUNC(set80211dtimperiod, val, d)
1174{
1175 set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1176}
1177
1178static
1179DECL_CMD_FUNC(set80211bintval, val, d)
1180{
1181 set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1182}
1183
1184static void
1185set80211macmac(int s, int op, const char *val)
1186{
1187 char *temp;
1188 struct sockaddr_dl sdl;
1189
1190 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1191 if (temp == NULL)
1192 errx(1, "malloc failed");
1193 temp[0] = ':';
1194 strcpy(temp + 1, val);
1195 sdl.sdl_len = sizeof(sdl);
1196 link_addr(temp, &sdl);
1197 free(temp);
1198 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1199 errx(1, "malformed link-level address");
1200 set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1201}
1202
1203static
1204DECL_CMD_FUNC(set80211addmac, val, d)
1205{
1206 set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1207}
1208
1209static
1210DECL_CMD_FUNC(set80211delmac, val, d)
1211{
1212 set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1213}
1214
1215static
1216DECL_CMD_FUNC(set80211kickmac, val, d)
1217{
1218 char *temp;
1219 struct sockaddr_dl sdl;
1220 struct ieee80211req_mlme mlme;
1221
1222 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1223 if (temp == NULL)
1224 errx(1, "malloc failed");
1225 temp[0] = ':';
1226 strcpy(temp + 1, val);
1227 sdl.sdl_len = sizeof(sdl);
1228 link_addr(temp, &sdl);
1229 free(temp);
1230 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1231 errx(1, "malformed link-level address");
1232 memset(&mlme, 0, sizeof(mlme));
1233 mlme.im_op = IEEE80211_MLME_DEAUTH;
1234 mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1235 memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1236 set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1237}
1238
1239static
1240DECL_CMD_FUNC(set80211maccmd, val, d)
1241{
1242 set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1243}
1244
1245static void
1246set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1247{
1248 set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1249}
1250
1251static void
1252set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1253{
1254 set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1255}
1256
1257static
1258DECL_CMD_FUNC(set80211bgscanidle, val, d)
1259{
1260 set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1261}
1262
1263static
1264DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1265{
1266 set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1267}
1268
1269static
1270DECL_CMD_FUNC(set80211scanvalid, val, d)
1271{
1272 set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1273}
1274
1275/*
1276 * Parse an optional trailing specification of which netbands
1277 * to apply a parameter to. This is basically the same syntax
1278 * as used for channels but you can concatenate to specify
1279 * multiple. For example:
1280 * 14:abg apply to 11a, 11b, and 11g
1281 * 6:ht apply to 11na and 11ng
1282 * We don't make a big effort to catch silly things; this is
1283 * really a convenience mechanism.
1284 */
1285static int
1286getmodeflags(const char *val)
1287{
1288 const char *cp;
1289 int flags;
1290
1291 flags = 0;
1292
1293 cp = strchr(val, ':');
1294 if (cp != NULL) {
1295 for (cp++; isalpha((int) *cp); cp++) {
1296 /* accept mixed case */
1297 int c = *cp;
1298 if (isupper(c))
1299 c = tolower(c);
1300 switch (c) {
1301 case 'a': /* 802.11a */
1302 flags |= IEEE80211_CHAN_A;
1303 break;
1304 case 'b': /* 802.11b */
1305 flags |= IEEE80211_CHAN_B;
1306 break;
1307 case 'g': /* 802.11g */
1308 flags |= IEEE80211_CHAN_G;
1309 break;
1310 case 'h': /* ht = 802.11n */
1311 case 'n': /* 802.11n */
1312 flags |= IEEE80211_CHAN_HT;
1313 break;
1314 case 'd': /* dt = Atheros Dynamic Turbo */
1315 flags |= IEEE80211_CHAN_TURBO;
1316 break;
1317 case 't': /* ht, dt, st, t */
1318 /* dt and unadorned t specify Dynamic Turbo */
1319 if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1320 flags |= IEEE80211_CHAN_TURBO;
1321 break;
1322 case 's': /* st = Atheros Static Turbo */
1323 flags |= IEEE80211_CHAN_STURBO;
1324 break;
1325 default:
1326 errx(-1, "%s: Invalid mode attribute %c\n",
1327 val, *cp);
1328 }
1329 }
1330 }
1331 return flags;
1332}
1333
1334#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
1335#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
1336
1337#define _APPLY(_flags, _base, _param, _v) do { \
1338 if (_flags & IEEE80211_CHAN_HT) { \
1339 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1340 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1341 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1342 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1343 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1344 else \
1345 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1346 } \
1347 if (_flags & IEEE80211_CHAN_TURBO) { \
1348 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1349 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1350 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1351 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1352 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1353 else \
1354 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1355 } \
1356 if (_flags & IEEE80211_CHAN_STURBO) \
1357 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1358 if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1359 _base.params[IEEE80211_MODE_11A]._param = _v; \
1360 if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1361 _base.params[IEEE80211_MODE_11G]._param = _v; \
1362 if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1363 _base.params[IEEE80211_MODE_11B]._param = _v; \
1364} while (0)
1365#define _APPLY1(_flags, _base, _param, _v) do { \
1366 if (_flags & IEEE80211_CHAN_HT) { \
1367 if (_flags & IEEE80211_CHAN_5GHZ) \
1368 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1369 else \
1370 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1371 } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
1372 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1373 else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
1374 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1375 else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
1376 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1377 else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1378 _base.params[IEEE80211_MODE_11A]._param = _v; \
1379 else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1380 _base.params[IEEE80211_MODE_11G]._param = _v; \
1381 else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1382 _base.params[IEEE80211_MODE_11B]._param = _v; \
1383} while (0)
1384#define _APPLY_RATE(_flags, _base, _param, _v) do { \
1385 if (_flags & IEEE80211_CHAN_HT) { \
1386 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1387 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1388 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1389 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1390 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1391 else \
1392 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1393 } \
1394 if (_flags & IEEE80211_CHAN_TURBO) { \
1395 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1396 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1397 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1398 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1399 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1400 else \
1401 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1402 } \
1403 if (_flags & IEEE80211_CHAN_STURBO) \
1404 _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
1405 if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1406 _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
1407 if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1408 _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1409 if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1410 _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1411} while (0)
1412#define _APPLY_RATE1(_flags, _base, _param, _v) do { \
1413 if (_flags & IEEE80211_CHAN_HT) { \
1414 if (_flags & IEEE80211_CHAN_5GHZ) \
1415 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1416 else \
1417 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1418 } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
1419 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1420 else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
1421 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1422 else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
1423 _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
1424 else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1425 _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
1426 else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1427 _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1428 else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1429 _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1430} while (0)
1431
1432static
1433DECL_CMD_FUNC(set80211roamrssi, val, d)
1434{
1435 double v = atof(val);
1436 int rssi, flags;
1437
1438 rssi = (int) (2*v);
1439 if (rssi != 2*v)
1440 errx(-1, "invalid rssi (must be .5 dBm units)");
1441 flags = getmodeflags(val);
1442 getroam(s);
1443 if (flags == 0) { /* NB: no flags => current channel */
1444 flags = getcurchan(s)->ic_flags;
1445 _APPLY1(flags, roamparams, rssi, rssi);
1446 } else
1447 _APPLY(flags, roamparams, rssi, rssi);
1448 callback_register(setroam_cb, &roamparams);
1449}
1450
1451static
1452DECL_CMD_FUNC(set80211roamrate, val, d)
1453{
1454 int v = atoi(val), flags;
1455
1456 flags = getmodeflags(val);
1457 getroam(s);
1458 if (flags == 0) { /* NB: no flags => current channel */
1459 flags = getcurchan(s)->ic_flags;
1460 _APPLY_RATE1(flags, roamparams, rate, v);
1461 } else
1462 _APPLY_RATE(flags, roamparams, rate, v);
1463 callback_register(setroam_cb, &roamparams);
1464}
1465
1466static
1467DECL_CMD_FUNC(set80211mcastrate, val, d)
1468{
1469 int v = atoi(val), flags;
1470
1471 flags = getmodeflags(val);
1472 gettxparams(s);
1473 if (flags == 0) { /* NB: no flags => current channel */
1474 flags = getcurchan(s)->ic_flags;
1475 _APPLY_RATE1(flags, txparams, mcastrate, v);
1476 } else
1477 _APPLY_RATE(flags, txparams, mcastrate, v);
1478 callback_register(settxparams_cb, &txparams);
1479}
1480
1481static
1482DECL_CMD_FUNC(set80211mgtrate, val, d)
1483{
1484 int v = atoi(val), flags;
1485
1486 flags = getmodeflags(val);
1487 gettxparams(s);
1488 if (flags == 0) { /* NB: no flags => current channel */
1489 flags = getcurchan(s)->ic_flags;
1490 _APPLY_RATE1(flags, txparams, mgmtrate, v);
1491 } else
1492 _APPLY_RATE(flags, txparams, mgmtrate, v);
1493 callback_register(settxparams_cb, &txparams);
1494}
1495
1496static
1497DECL_CMD_FUNC(set80211ucastrate, val, d)
1498{
1499 int v, flags;
1500
1501 gettxparams(s);
1502 flags = getmodeflags(val);
1503 if (isanyarg(val)) {
1504 if (flags == 0) { /* NB: no flags => current channel */
1505 flags = getcurchan(s)->ic_flags;
1506 _APPLY1(flags, txparams, ucastrate,
1507 IEEE80211_FIXED_RATE_NONE);
1508 } else
1509 _APPLY(flags, txparams, ucastrate,
1510 IEEE80211_FIXED_RATE_NONE);
1511 } else {
1512 v = atoi(val);
1513 if (flags == 0) { /* NB: no flags => current channel */
1514 flags = getcurchan(s)->ic_flags;
1515 _APPLY_RATE1(flags, txparams, ucastrate, v);
1516 } else
1517 _APPLY_RATE(flags, txparams, ucastrate, v);
1518 }
1519 callback_register(settxparams_cb, &txparams);
1520}
1521
1522static
1523DECL_CMD_FUNC(set80211maxretry, val, d)
1524{
1525 int v = atoi(val), flags;
1526
1527 flags = getmodeflags(val);
1528 gettxparams(s);
1529 if (flags == 0) { /* NB: no flags => current channel */
1530 flags = getcurchan(s)->ic_flags;
1531 _APPLY1(flags, txparams, maxretry, v);
1532 } else
1533 _APPLY(flags, txparams, maxretry, v);
1534 callback_register(settxparams_cb, &txparams);
1535}
1536#undef _APPLY_RATE
1537#undef _APPLY
1538#undef IEEE80211_CHAN_HTA
1539#undef IEEE80211_CHAN_HTG
1540
1541static
1542DECL_CMD_FUNC(set80211fragthreshold, val, d)
1543{
1544 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1545 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1546}
1547
1548static
1549DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1550{
1551 set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1552 isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1553}
1554
1555static void
1556set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1557{
1558 set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1559}
1560
1561static void
1562set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1563{
1564 set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1565}
1566
1567static void
1568set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1569{
1570 set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1571}
1572
1573static void
1574set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1575{
1576 set80211(s, IEEE80211_IOC_SHORTGI,
1577 d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1578 0, NULL);
1579}
1580
1581static void
1582set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1583{
1584 int ampdu;
1585
1586 if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1587 errx(-1, "cannot get AMPDU setting");
1588 if (d < 0) {
1589 d = -d;
1590 ampdu &= ~d;
1591 } else
1592 ampdu |= d;
1593 set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1594}
1595
1596static
1597DECL_CMD_FUNC(set80211ampdulimit, val, d)
1598{
1599 int v;
1600
1601 switch (atoi(val)) {
1602 case 8:
1603 case 8*1024:
1604 v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1605 break;
1606 case 16:
1607 case 16*1024:
1608 v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1609 break;
1610 case 32:
1611 case 32*1024:
1612 v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1613 break;
1614 case 64:
1615 case 64*1024:
1616 v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1617 break;
1618 default:
1619 errx(-1, "invalid A-MPDU limit %s", val);
1620 }
1621 set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1622}
1623
1624static
1625DECL_CMD_FUNC(set80211ampdudensity, val, d)
1626{
1627 int v;
1628
1629 if (isanyarg(val) || strcasecmp(val, "na") == 0)
1630 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1631 else switch ((int)(atof(val)*4)) {
1632 case 0:
1633 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1634 break;
1635 case 1:
1636 v = IEEE80211_HTCAP_MPDUDENSITY_025;
1637 break;
1638 case 2:
1639 v = IEEE80211_HTCAP_MPDUDENSITY_05;
1640 break;
1641 case 4:
1642 v = IEEE80211_HTCAP_MPDUDENSITY_1;
1643 break;
1644 case 8:
1645 v = IEEE80211_HTCAP_MPDUDENSITY_2;
1646 break;
1647 case 16:
1648 v = IEEE80211_HTCAP_MPDUDENSITY_4;
1649 break;
1650 case 32:
1651 v = IEEE80211_HTCAP_MPDUDENSITY_8;
1652 break;
1653 case 64:
1654 v = IEEE80211_HTCAP_MPDUDENSITY_16;
1655 break;
1656 default:
1657 errx(-1, "invalid A-MPDU density %s", val);
1658 }
1659 set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1660}
1661
1662static void
1663set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1664{
1665 int amsdu;
1666
1667 if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1668 err(-1, "cannot get AMSDU setting");
1669 if (d < 0) {
1670 d = -d;
1671 amsdu &= ~d;
1672 } else
1673 amsdu |= d;
1674 set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1675}
1676
1677static
1678DECL_CMD_FUNC(set80211amsdulimit, val, d)
1679{
1680 set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1681}
1682
1683static void
1684set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1685{
1686 set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1687}
1688
1689static void
1690set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1691{
1692 set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1693}
1694
1695static void
1696set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1697{
1698 set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1699 htconf = d;
1700}
1701
1702static void
1703set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1704{
1705 set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1706}
1707
1708static void
1709set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1710{
1711 set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1712}
1713
1714static void
1715set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1716{
1717 set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1718}
1719
1720static void
1721set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1722{
1723 set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1724}
1725
1726static void
1727set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1728{
1729 set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1730}
1731
1732static void
1733set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1734{
1735 set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1736}
1737
1738static
1739DECL_CMD_FUNC(set80211tdmaslot, val, d)
1740{
1741 set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1742}
1743
1744static
1745DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1746{
1747 set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1748}
1749
1750static
1751DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1752{
1753 set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1754}
1755
1756static
1757DECL_CMD_FUNC(set80211tdmabintval, val, d)
1758{
1759 set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1760}
1761
1762static int
1763regdomain_sort(const void *a, const void *b)
1764{
1765#define CHAN_ALL \
1766 (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1767 const struct ieee80211_channel *ca = a;
1768 const struct ieee80211_channel *cb = b;
1769
1770 return ca->ic_freq == cb->ic_freq ?
1771 (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1772 ca->ic_freq - cb->ic_freq;
1773#undef CHAN_ALL
1774}
1775
1776static const struct ieee80211_channel *
1777chanlookup(const struct ieee80211_channel chans[], int nchans,
1778 int freq, int flags)
1779{
1780 int i;
1781
1782 flags &= IEEE80211_CHAN_ALLTURBO;
1783 for (i = 0; i < nchans; i++) {
1784 const struct ieee80211_channel *c = &chans[i];
1785 if (c->ic_freq == freq &&
1786 (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1787 return c;
1788 }
1789 return NULL;
1790}
1791
1792static int
1793chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1794{
1795 int i;
1796
1797 for (i = 0; i < nchans; i++) {
1798 const struct ieee80211_channel *c = &chans[i];
1799 if ((c->ic_flags & flags) == flags)
1800 return 1;
1801 }
1802 return 0;
1803}
1804
1805static void
1806regdomain_addchans(struct ieee80211req_chaninfo *ci,
1807 const netband_head *bands,
1808 const struct ieee80211_regdomain *reg,
1809 uint32_t chanFlags,
1810 const struct ieee80211req_chaninfo *avail)
1811{
1812 const struct netband *nb;
1813 const struct freqband *b;
1814 struct ieee80211_channel *c, *prev;
1815 int freq, channelSep, hasHalfChans, hasQuarterChans;
1816
1817 channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
1818 hasHalfChans = chanfind(avail->ic_chans, avail->ic_nchans,
1819 IEEE80211_CHAN_HALF |
1820 (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1821 hasQuarterChans = chanfind(avail->ic_chans, avail->ic_nchans,
1822 IEEE80211_CHAN_QUARTER |
1823 (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1824 LIST_FOREACH(nb, bands, next) {
1825 b = nb->band;
1826 if (verbose) {
1827 printf("%s:", __func__);
1828 printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
1829 printb(" bandFlags", nb->flags | b->flags,
1830 IEEE80211_CHAN_BITS);
1831 putchar('\n');
1832 }
1833 prev = NULL;
1834 for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) {
1835 uint32_t flags = nb->flags | b->flags;
1836
1837 /* check if device can operate on this frequency */
1838 /*
1839 * XXX GSM frequency mapping is handled in the kernel
1840 * so we cannot find them in the calibration table;
1841 * just construct the list and the kernel will reject
1842 * if it's wrong.
1843 */
1844 if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL &&
1845 (flags & IEEE80211_CHAN_GSM) == 0) {
1846 if (verbose) {
1847 printf("%u: skip, ", freq);
1848 printb("flags", chanFlags,
1849 IEEE80211_CHAN_BITS);
1850 printf(" not available\n");
1851 }
1852 continue;
1853 }
1854 if ((flags & IEEE80211_CHAN_HALF) && !hasHalfChans) {
1855 if (verbose)
1856 printf("%u: skip, device does not "
1857 "support half-rate channel\n",
1858 freq);
1859 continue;
1860 }
1861 if ((flags & IEEE80211_CHAN_QUARTER) &&
1862 !hasQuarterChans) {
1863 if (verbose)
1864 printf("%u: skip, device does not "
1865 "support quarter-rate channel\n",
1866 freq);
1867 continue;
1868 }
1869 if ((flags & IEEE80211_CHAN_HT20) &&
1870 (chanFlags & IEEE80211_CHAN_HT20) == 0) {
1871 if (verbose)
1872 printf("%u: skip, device does not "
1873 "support HT20 operation\n", freq);
1874 continue;
1875 }
1876 if ((flags & IEEE80211_CHAN_HT40) &&
1877 (chanFlags & IEEE80211_CHAN_HT40) == 0) {
1878 if (verbose)
1879 printf("%u: skip, device does not "
1880 "support HT40 operation\n", freq);
1881 continue;
1882 }
1883 if ((flags & REQ_ECM) && !reg->ecm) {
1884 if (verbose)
1885 printf("%u: skip, ECM channel\n", freq);
1886 continue;
1887 }
1888 if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
1889 if (verbose)
1890 printf("%u: skip, outdoor channel\n", freq);
1891 continue;
1892 }
1893 if ((flags & IEEE80211_CHAN_HT40) &&
1894 prev != NULL && (freq - prev->ic_freq) < channelSep) {
1895 if (verbose)
1896 printf("%u: skip, only %u channel "
1897 "separation, need %d\n", freq,
1898 freq - prev->ic_freq, channelSep);
1899 continue;
1900 }
1901 if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
1902 if (verbose)
1903 printf("%u: skip, channel table full\n",
1904 freq);
1905 break;
1906 }
1907 c = &ci->ic_chans[ci->ic_nchans++];
1908 memset(c, 0, sizeof(*c));
1909 c->ic_freq = freq;
1910 c->ic_flags = chanFlags |
1911 (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40));
1912 if (c->ic_flags & IEEE80211_CHAN_DFS)
1913 c->ic_maxregpower = nb->maxPowerDFS;
1914 else
1915 c->ic_maxregpower = nb->maxPower;
1916 if (verbose) {
1917 printf("[%3d] add freq %u ",
1918 ci->ic_nchans-1, c->ic_freq);
1919 printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
1920 printf(" power %u\n", c->ic_maxregpower);
1921 }
1922 /* NB: kernel fills in other fields */
1923 prev = c;
1924 }
1925 }
1926}
1927
1928static void
1929regdomain_makechannels(
1930 struct ieee80211_regdomain_req *req,
1931 const struct ieee80211_devcaps_req *dc)
1932{
1933 struct regdata *rdp = getregdata();
1934 const struct country *cc;
1935 const struct ieee80211_regdomain *reg = &req->rd;
1936 struct ieee80211req_chaninfo *ci = &req->chaninfo;
1937 const struct regdomain *rd;
1938
1939 /*
1940 * Locate construction table for new channel list. We treat
1941 * the regdomain/SKU as definitive so a country can be in
1942 * multiple with different properties (e.g. US in FCC+FCC3).
1943 * If no regdomain is specified then we fallback on the country
1944 * code to find the associated regdomain since countries always
1945 * belong to at least one regdomain.
1946 */
1947 if (reg->regdomain == 0) {
1948 cc = lib80211_country_findbycc(rdp, reg->country);
1949 if (cc == NULL)
1950 errx(1, "internal error, country %d not found",
1951 reg->country);
1952 rd = cc->rd;
1953 } else
1954 rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
1955 if (rd == NULL)
1956 errx(1, "internal error, regdomain %d not found",
1957 reg->regdomain);
1958 if (rd->sku != SKU_DEBUG) {
1959 /*
1960 * regdomain_addchans incrememnts the channel count for
1961 * each channel it adds so initialize ic_nchans to zero.
1962 * Note that we know we have enough space to hold all possible
1963 * channels because the devcaps list size was used to
1964 * allocate our request.
1965 */
1966 ci->ic_nchans = 0;
1967 if (!LIST_EMPTY(&rd->bands_11b))
1968 regdomain_addchans(ci, &rd->bands_11b, reg,
1969 IEEE80211_CHAN_B, &dc->dc_chaninfo);
1970 if (!LIST_EMPTY(&rd->bands_11g))
1971 regdomain_addchans(ci, &rd->bands_11g, reg,
1972 IEEE80211_CHAN_G, &dc->dc_chaninfo);
1973 if (!LIST_EMPTY(&rd->bands_11a))
1974 regdomain_addchans(ci, &rd->bands_11a, reg,
1975 IEEE80211_CHAN_A, &dc->dc_chaninfo);
1976 if (!LIST_EMPTY(&rd->bands_11na)) {
1977 regdomain_addchans(ci, &rd->bands_11na, reg,
1978 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
1979 &dc->dc_chaninfo);
1980 regdomain_addchans(ci, &rd->bands_11na, reg,
1981 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
1982 &dc->dc_chaninfo);
1983 regdomain_addchans(ci, &rd->bands_11na, reg,
1984 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
1985 &dc->dc_chaninfo);
1986 }
1987 if (!LIST_EMPTY(&rd->bands_11ng)) {
1988 regdomain_addchans(ci, &rd->bands_11ng, reg,
1989 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
1990 &dc->dc_chaninfo);
1991 regdomain_addchans(ci, &rd->bands_11ng, reg,
1992 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
1993 &dc->dc_chaninfo);
1994 regdomain_addchans(ci, &rd->bands_11ng, reg,
1995 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
1996 &dc->dc_chaninfo);
1997 }
1998 qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
1999 regdomain_sort);
2000 } else
2001 memcpy(ci, &dc->dc_chaninfo,
2002 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2003}
2004
2005static void
2006list_countries(void)
2007{
2008 struct regdata *rdp = getregdata();
2009 const struct country *cp;
2010 const struct regdomain *dp;
2011 int i;
2012
2013 i = 0;
2014 printf("\nCountry codes:\n");
2015 LIST_FOREACH(cp, &rdp->countries, next) {
2016 printf("%2s %-15.15s%s", cp->isoname,
2017 cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2018 i++;
2019 }
2020 i = 0;
2021 printf("\nRegulatory domains:\n");
2022 LIST_FOREACH(dp, &rdp->domains, next) {
2023 printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2024 i++;
2025 }
2026 printf("\n");
2027}
2028
2029static void
2030defaultcountry(const struct regdomain *rd)
2031{
2032 struct regdata *rdp = getregdata();
2033 const struct country *cc;
2034
2035 cc = lib80211_country_findbycc(rdp, rd->cc->code);
2036 if (cc == NULL)
2037 errx(1, "internal error, ISO country code %d not "
2038 "defined for regdomain %s", rd->cc->code, rd->name);
2039 regdomain.country = cc->code;
2040 regdomain.isocc[0] = cc->isoname[0];
2041 regdomain.isocc[1] = cc->isoname[1];
2042}
2043
2044static
2045DECL_CMD_FUNC(set80211regdomain, val, d)
2046{
2047 struct regdata *rdp = getregdata();
2048 const struct regdomain *rd;
2049
2050 rd = lib80211_regdomain_findbyname(rdp, val);
2051 if (rd == NULL) {
2052 char *eptr;
2053 long sku = strtol(val, &eptr, 0);
2054
2055 if (eptr != val)
2056 rd = lib80211_regdomain_findbysku(rdp, sku);
2057 if (eptr == val || rd == NULL)
2058 errx(1, "unknown regdomain %s", val);
2059 }
2060 getregdomain(s);
2061 regdomain.regdomain = rd->sku;
2062 if (regdomain.country == 0 && rd->cc != NULL) {
2063 /*
2064 * No country code setup and there's a default
2065 * one for this regdomain fill it in.
2066 */
2067 defaultcountry(rd);
2068 }
2069 callback_register(setregdomain_cb, &regdomain);
2070}
2071
2072static
2073DECL_CMD_FUNC(set80211country, val, d)
2074{
2075 struct regdata *rdp = getregdata();
2076 const struct country *cc;
2077
2078 cc = lib80211_country_findbyname(rdp, val);
2079 if (cc == NULL) {
2080 char *eptr;
2081 long code = strtol(val, &eptr, 0);
2082
2083 if (eptr != val)
2084 cc = lib80211_country_findbycc(rdp, code);
2085 if (eptr == val || cc == NULL)
2086 errx(1, "unknown ISO country code %s", val);
2087 }
2088 getregdomain(s);
2089 regdomain.regdomain = cc->rd->sku;
2090 regdomain.country = cc->code;
2091 regdomain.isocc[0] = cc->isoname[0];
2092 regdomain.isocc[1] = cc->isoname[1];
2093 callback_register(setregdomain_cb, &regdomain);
2094}
2095
2096static void
2097set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2098{
2099 getregdomain(s);
2100 regdomain.location = d;
2101 callback_register(setregdomain_cb, &regdomain);
2102}
2103
2104static void
2105set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2106{
2107 getregdomain(s);
2108 regdomain.ecm = d;
2109 callback_register(setregdomain_cb, &regdomain);
2110}
2111
2112static void
2113LINE_INIT(char c)
2114{
2115 spacer = c;
2116 if (c == '\t')
2117 col = 8;
2118 else
2119 col = 1;
2120}
2121
2122static void
2123LINE_BREAK(void)
2124{
2125 if (spacer != '\t') {
2126 printf("\n");
2127 spacer = '\t';
2128 }
2129 col = 8; /* 8-col tab */
2130}
2131
2132static void
2133LINE_CHECK(const char *fmt, ...)
2134{
2135 char buf[80];
2136 va_list ap;
2137 int n;
2138
2139 va_start(ap, fmt);
2140 n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2141 va_end(ap);
2142 col += 1+n;
2143 if (col > MAXCOL) {
2144 LINE_BREAK();
2145 col += n;
2146 }
2147 buf[0] = spacer;
2148 printf("%s", buf);
2149 spacer = ' ';
2150}
2151
2152static int
2153getmaxrate(const uint8_t rates[15], uint8_t nrates)
2154{
2155 int i, maxrate = -1;
2156
2157 for (i = 0; i < nrates; i++) {
2158 int rate = rates[i] & IEEE80211_RATE_VAL;
2159 if (rate > maxrate)
2160 maxrate = rate;
2161 }
2162 return maxrate / 2;
2163}
2164
2165static const char *
2166getcaps(int capinfo)
2167{
2168 static char capstring[32];
2169 char *cp = capstring;
2170
2171 if (capinfo & IEEE80211_CAPINFO_ESS)
2172 *cp++ = 'E';
2173 if (capinfo & IEEE80211_CAPINFO_IBSS)
2174 *cp++ = 'I';
2175 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2176 *cp++ = 'c';
2177 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2178 *cp++ = 'C';
2179 if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2180 *cp++ = 'P';
2181 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2182 *cp++ = 'S';
2183 if (capinfo & IEEE80211_CAPINFO_PBCC)
2184 *cp++ = 'B';
2185 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2186 *cp++ = 'A';
2187 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2188 *cp++ = 's';
2189 if (capinfo & IEEE80211_CAPINFO_RSN)
2190 *cp++ = 'R';
2191 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2192 *cp++ = 'D';
2193 *cp = '\0';
2194 return capstring;
2195}
2196
2197static const char *
2198getflags(int flags)
2199{
2200 static char flagstring[32];
2201 char *cp = flagstring;
2202
2203 if (flags & IEEE80211_NODE_AUTH)
2204 *cp++ = 'A';
2205 if (flags & IEEE80211_NODE_QOS)
2206 *cp++ = 'Q';
2207 if (flags & IEEE80211_NODE_ERP)
2208 *cp++ = 'E';
2209 if (flags & IEEE80211_NODE_PWR_MGT)
2210 *cp++ = 'P';
2211 if (flags & IEEE80211_NODE_HT) {
2212 *cp++ = 'H';
2213 if (flags & IEEE80211_NODE_HTCOMPAT)
2214 *cp++ = '+';
2215 }
2216 if (flags & IEEE80211_NODE_WPS)
2217 *cp++ = 'W';
2218 if (flags & IEEE80211_NODE_TSN)
2219 *cp++ = 'N';
2220 if (flags & IEEE80211_NODE_AMPDU_TX)
2221 *cp++ = 'T';
2222 if (flags & IEEE80211_NODE_AMPDU_RX)
2223 *cp++ = 'R';
2224 if (flags & IEEE80211_NODE_MIMO_PS) {
2225 *cp++ = 'M';
2226 if (flags & IEEE80211_NODE_MIMO_RTS)
2227 *cp++ = '+';
2228 }
2229 if (flags & IEEE80211_NODE_RIFS)
2230 *cp++ = 'I';
2231 *cp = '\0';
2232 return flagstring;
2233}
2234
2235static void
2236printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2237{
2238 printf("%s", tag);
2239 if (verbose) {
2240 maxlen -= strlen(tag)+2;
2241 if (2*ielen > maxlen)
2242 maxlen--;
2243 printf("<");
2244 for (; ielen > 0; ie++, ielen--) {
2245 if (maxlen-- <= 0)
2246 break;
2247 printf("%02x", *ie);
2248 }
2249 if (ielen != 0)
2250 printf("-");
2251 printf(">");
2252 }
2253}
2254
2255#define LE_READ_2(p) \
2256 ((u_int16_t) \
2257 ((((const u_int8_t *)(p))[0] ) | \
2258 (((const u_int8_t *)(p))[1] << 8)))
2259#define LE_READ_4(p) \
2260 ((u_int32_t) \
2261 ((((const u_int8_t *)(p))[0] ) | \
2262 (((const u_int8_t *)(p))[1] << 8) | \
2263 (((const u_int8_t *)(p))[2] << 16) | \
2264 (((const u_int8_t *)(p))[3] << 24)))
2265
2266/*
2267 * NB: The decoding routines assume a properly formatted ie
2268 * which should be safe as the kernel only retains them
2269 * if they parse ok.
2270 */
2271
2272static void
2273printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2274{
2275#define MS(_v, _f) (((_v) & _f) >> _f##_S)
2276 static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2277 const struct ieee80211_wme_param *wme =
2278 (const struct ieee80211_wme_param *) ie;
2279 int i;
2280
2281 printf("%s", tag);
2282 if (!verbose)
2283 return;
2284 printf("<qosinfo 0x%x", wme->param_qosInfo);
2285 ie += offsetof(struct ieee80211_wme_param, params_acParams);
2286 for (i = 0; i < WME_NUM_AC; i++) {
2287 const struct ieee80211_wme_acparams *ac =
2288 &wme->params_acParams[i];
2289
2290 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2291 , acnames[i]
2292 , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2293 , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2294 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2295 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2296 , LE_READ_2(&ac->acp_txop)
2297 );
2298 }
2299 printf(">");
2300#undef MS
2301}
2302
2303static void
2304printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2305{
2306 printf("%s", tag);
2307 if (verbose) {
2308 const struct ieee80211_wme_info *wme =
2309 (const struct ieee80211_wme_info *) ie;
2310 printf("<version 0x%x info 0x%x>",
2311 wme->wme_version, wme->wme_info);
2312 }
2313}
2314
2315static void
2316printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2317{
2318 printf("%s", tag);
2319 if (verbose) {
2320 const struct ieee80211_ie_htcap *htcap =
2321 (const struct ieee80211_ie_htcap *) ie;
2322 const char *sep;
2323 int i, j;
2324
2325 printf("<cap 0x%x param 0x%x",
2326 LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2327 printf(" mcsset[");
2328 sep = "";
2329 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2330 if (isset(htcap->hc_mcsset, i)) {
2331 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2332 if (isclr(htcap->hc_mcsset, j))
2333 break;
2334 j--;
2335 if (i == j)
2336 printf("%s%u", sep, i);
2337 else
2338 printf("%s%u-%u", sep, i, j);
2339 i += j-i;
2340 sep = ",";
2341 }
2342 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2343 LE_READ_2(&htcap->hc_extcap),
2344 LE_READ_4(&htcap->hc_txbf),
2345 htcap->hc_antenna);
2346 }
2347}
2348
2349static void
2350printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2351{
2352 printf("%s", tag);
2353 if (verbose) {
2354 const struct ieee80211_ie_htinfo *htinfo =
2355 (const struct ieee80211_ie_htinfo *) ie;
2356 const char *sep;
2357 int i, j;
2358
2359 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2360 htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2361 LE_READ_2(&htinfo->hi_byte45));
2362 printf(" basicmcs[");
2363 sep = "";
2364 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2365 if (isset(htinfo->hi_basicmcsset, i)) {
2366 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2367 if (isclr(htinfo->hi_basicmcsset, j))
2368 break;
2369 j--;
2370 if (i == j)
2371 printf("%s%u", sep, i);
2372 else
2373 printf("%s%u-%u", sep, i, j);
2374 i += j-i;
2375 sep = ",";
2376 }
2377 printf("]>");
2378 }
2379}
2380
2381static void
2382printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2383{
2384
2385 printf("%s", tag);
2386 if (verbose) {
2387 const struct ieee80211_ath_ie *ath =
2388 (const struct ieee80211_ath_ie *)ie;
2389
2390 printf("<");
2391 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2392 printf("DTURBO,");
2393 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2394 printf("COMP,");
2395 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2396 printf("FF,");
2397 if (ath->ath_capability & ATHEROS_CAP_XR)
2398 printf("XR,");
2399 if (ath->ath_capability & ATHEROS_CAP_AR)
2400 printf("AR,");
2401 if (ath->ath_capability & ATHEROS_CAP_BURST)
2402 printf("BURST,");
2403 if (ath->ath_capability & ATHEROS_CAP_WME)
2404 printf("WME,");
2405 if (ath->ath_capability & ATHEROS_CAP_BOOST)
2406 printf("BOOST,");
2407 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2408 }
2409}
2410
2411static const char *
2412wpa_cipher(const u_int8_t *sel)
2413{
2414#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2415 u_int32_t w = LE_READ_4(sel);
2416
2417 switch (w) {
2418 case WPA_SEL(WPA_CSE_NULL):
2419 return "NONE";
2420 case WPA_SEL(WPA_CSE_WEP40):
2421 return "WEP40";
2422 case WPA_SEL(WPA_CSE_WEP104):
2423 return "WEP104";
2424 case WPA_SEL(WPA_CSE_TKIP):
2425 return "TKIP";
2426 case WPA_SEL(WPA_CSE_CCMP):
2427 return "AES-CCMP";
2428 }
2429 return "?"; /* NB: so 1<< is discarded */
2430#undef WPA_SEL
2431}
2432
2433static const char *
2434wpa_keymgmt(const u_int8_t *sel)
2435{
2436#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2437 u_int32_t w = LE_READ_4(sel);
2438
2439 switch (w) {
2440 case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2441 return "8021X-UNSPEC";
2442 case WPA_SEL(WPA_ASE_8021X_PSK):
2443 return "8021X-PSK";
2444 case WPA_SEL(WPA_ASE_NONE):
2445 return "NONE";
2446 }
2447 return "?";
2448#undef WPA_SEL
2449}
2450
2451static void
2452printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2453{
2454 u_int8_t len = ie[1];
2455
2456 printf("%s", tag);
2457 if (verbose) {
2458 const char *sep;
2459 int n;
2460
2461 ie += 6, len -= 4; /* NB: len is payload only */
2462
2463 printf("<v%u", LE_READ_2(ie));
2464 ie += 2, len -= 2;
2465
2466 printf(" mc:%s", wpa_cipher(ie));
2467 ie += 4, len -= 4;
2468
2469 /* unicast ciphers */
2470 n = LE_READ_2(ie);
2471 ie += 2, len -= 2;
2472 sep = " uc:";
2473 for (; n > 0; n--) {
2474 printf("%s%s", sep, wpa_cipher(ie));
2475 ie += 4, len -= 4;
2476 sep = "+";
2477 }
2478
2479 /* key management algorithms */
2480 n = LE_READ_2(ie);
2481 ie += 2, len -= 2;
2482 sep = " km:";
2483 for (; n > 0; n--) {
2484 printf("%s%s", sep, wpa_keymgmt(ie));
2485 ie += 4, len -= 4;
2486 sep = "+";
2487 }
2488
2489 if (len > 2) /* optional capabilities */
2490 printf(", caps 0x%x", LE_READ_2(ie));
2491 printf(">");
2492 }
2493}
2494
2495static const char *
2496rsn_cipher(const u_int8_t *sel)
2497{
2498#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2499 u_int32_t w = LE_READ_4(sel);
2500
2501 switch (w) {
2502 case RSN_SEL(RSN_CSE_NULL):
2503 return "NONE";
2504 case RSN_SEL(RSN_CSE_WEP40):
2505 return "WEP40";
2506 case RSN_SEL(RSN_CSE_WEP104):
2507 return "WEP104";
2508 case RSN_SEL(RSN_CSE_TKIP):
2509 return "TKIP";
2510 case RSN_SEL(RSN_CSE_CCMP):
2511 return "AES-CCMP";
2512 case RSN_SEL(RSN_CSE_WRAP):
2513 return "AES-OCB";
2514 }
2515 return "?";
2516#undef WPA_SEL
2517}
2518
2519static const char *
2520rsn_keymgmt(const u_int8_t *sel)
2521{
2522#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2523 u_int32_t w = LE_READ_4(sel);
2524
2525 switch (w) {
2526 case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2527 return "8021X-UNSPEC";
2528 case RSN_SEL(RSN_ASE_8021X_PSK):
2529 return "8021X-PSK";
2530 case RSN_SEL(RSN_ASE_NONE):
2531 return "NONE";
2532 }
2533 return "?";
2534#undef RSN_SEL
2535}
2536
2537static void
2538printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2539{
2540 printf("%s", tag);
2541 if (verbose) {
2542 const char *sep;
2543 int n;
2544
2545 ie += 2, ielen -= 2;
2546
2547 printf("<v%u", LE_READ_2(ie));
2548 ie += 2, ielen -= 2;
2549
2550 printf(" mc:%s", rsn_cipher(ie));
2551 ie += 4, ielen -= 4;
2552
2553 /* unicast ciphers */
2554 n = LE_READ_2(ie);
2555 ie += 2, ielen -= 2;
2556 sep = " uc:";
2557 for (; n > 0; n--) {
2558 printf("%s%s", sep, rsn_cipher(ie));
2559 ie += 4, ielen -= 4;
2560 sep = "+";
2561 }
2562
2563 /* key management algorithms */
2564 n = LE_READ_2(ie);
2565 ie += 2, ielen -= 2;
2566 sep = " km:";
2567 for (; n > 0; n--) {
2568 printf("%s%s", sep, rsn_keymgmt(ie));
2569 ie += 4, ielen -= 4;
2570 sep = "+";
2571 }
2572
2573 if (ielen > 2) /* optional capabilities */
2574 printf(", caps 0x%x", LE_READ_2(ie));
2575 /* XXXPMKID */
2576 printf(">");
2577 }
2578}
2579
2580/* XXX move to a public include file */
2581#define IEEE80211_WPS_DEV_PASS_ID 0x1012
2582#define IEEE80211_WPS_SELECTED_REG 0x1041
2583#define IEEE80211_WPS_SETUP_STATE 0x1044
2584#define IEEE80211_WPS_UUID_E 0x1047
2585#define IEEE80211_WPS_VERSION 0x104a
2586
2587#define BE_READ_2(p) \
2588 ((u_int16_t) \
2589 ((((const u_int8_t *)(p))[1] ) | \
2590 (((const u_int8_t *)(p))[0] << 8)))
2591
2592static void
2593printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2594{
2595#define N(a) (sizeof(a) / sizeof(a[0]))
2596 u_int8_t len = ie[1];
2597
2598 printf("%s", tag);
2599 if (verbose) {
2600 static const char *dev_pass_id[] = {
2601 "D", /* Default (PIN) */
2602 "U", /* User-specified */
2603 "M", /* Machine-specified */
2604 "K", /* Rekey */
2605 "P", /* PushButton */
2606 "R" /* Registrar-specified */
2607 };
2608 int n;
2609
2610 ie +=6, len -= 4; /* NB: len is payload only */
2611
2612 /* WPS IE in Beacon and Probe Resp frames have different fields */
2613 printf("<");
2614 while (len) {
2615 uint16_t tlv_type = BE_READ_2(ie);
2616 uint16_t tlv_len = BE_READ_2(ie + 2);
2617
2618 ie += 4, len -= 4;
2619
2620 switch (tlv_type) {
2621 case IEEE80211_WPS_VERSION:
2622 printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2623 break;
2624 case IEEE80211_WPS_SETUP_STATE:
2625 /* Only 1 and 2 are valid */
2626 if (*ie == 0 || *ie >= 3)
2627 printf(" state:B");
2628 else
2629 printf(" st:%s", *ie == 1 ? "N" : "C");
2630 break;
2631 case IEEE80211_WPS_SELECTED_REG:
2632 printf(" sel:%s", *ie ? "T" : "F");
2633 break;
2634 case IEEE80211_WPS_DEV_PASS_ID:
2635 n = LE_READ_2(ie);
2636 if (n < N(dev_pass_id))
2637 printf(" dpi:%s", dev_pass_id[n]);
2638 break;
2639 case IEEE80211_WPS_UUID_E:
2640 printf(" uuid-e:");
2641 for (n = 0; n < (tlv_len - 1); n++)
2642 printf("%02x-", ie[n]);
2643 printf("%02x", ie[n]);
2644 break;
2645 }
2646 ie += tlv_len, len -= tlv_len;
2647 }
2648 printf(">");
2649 }
2650#undef N
2651}
2652
2653static void
2654printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2655{
2656 printf("%s", tag);
2657 if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2658 const struct ieee80211_tdma_param *tdma =
2659 (const struct ieee80211_tdma_param *) ie;
2660
2661 /* XXX tstamp */
2662 printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2663 tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2664 LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2665 tdma->tdma_inuse[0]);
2666 }
2667}
2668
2669/*
2670 * Copy the ssid string contents into buf, truncating to fit. If the
2671 * ssid is entirely printable then just copy intact. Otherwise convert
2672 * to hexadecimal. If the result is truncated then replace the last
2673 * three characters with "...".
2674 */
2675static int
2676copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2677{
2678 const u_int8_t *p;
2679 size_t maxlen;
2680 int i;
2681
2682 if (essid_len > bufsize)
2683 maxlen = bufsize;
2684 else
2685 maxlen = essid_len;
2686 /* determine printable or not */
2687 for (i = 0, p = essid; i < maxlen; i++, p++) {
2688 if (*p < ' ' || *p > 0x7e)
2689 break;
2690 }
2691 if (i != maxlen) { /* not printable, print as hex */
2692 if (bufsize < 3)
2693 return 0;
2694 strlcpy(buf, "0x", bufsize);
2695 bufsize -= 2;
2696 p = essid;
2697 for (i = 0; i < maxlen && bufsize >= 2; i++) {
2698 sprintf(&buf[2+2*i], "%02x", p[i]);
2699 bufsize -= 2;
2700 }
2701 if (i != essid_len)
2702 memcpy(&buf[2+2*i-3], "...", 3);
2703 } else { /* printable, truncate as needed */
2704 memcpy(buf, essid, maxlen);
2705 if (maxlen != essid_len)
2706 memcpy(&buf[maxlen-3], "...", 3);
2707 }
2708 return maxlen;
2709}
2710
2711static void
2712printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2713{
2714 char ssid[2*IEEE80211_NWID_LEN+1];
2715
2716 printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2717}
2718
2719static void
2720printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2721{
2722 const char *sep;
2723 int i;
2724
2725 printf("%s", tag);
2726 sep = "<";
2727 for (i = 2; i < ielen; i++) {
2728 printf("%s%s%d", sep,
2729 ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2730 ie[i] & IEEE80211_RATE_VAL);
2731 sep = ",";
2732 }
2733 printf(">");
2734}
2735
2736static void
2737printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2738{
2739 const struct ieee80211_country_ie *cie =
2740 (const struct ieee80211_country_ie *) ie;
2741 int i, nbands, schan, nchan;
2742
2743 printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
2744 nbands = (cie->len - 3) / sizeof(cie->band[0]);
2745 for (i = 0; i < nbands; i++) {
2746 schan = cie->band[i].schan;
2747 nchan = cie->band[i].nchan;
2748 if (nchan != 1)
2749 printf(" %u-%u,%u", schan, schan + nchan-1,
2750 cie->band[i].maxtxpwr);
2751 else
2752 printf(" %u,%u", schan, cie->band[i].maxtxpwr);
2753 }
2754 printf(">");
2755}
2756
2757/* unaligned little endian access */
2758#define LE_READ_4(p) \
2759 ((u_int32_t) \
2760 ((((const u_int8_t *)(p))[0] ) | \
2761 (((const u_int8_t *)(p))[1] << 8) | \
2762 (((const u_int8_t *)(p))[2] << 16) | \
2763 (((const u_int8_t *)(p))[3] << 24)))
2764
2765static __inline int
2766iswpaoui(const u_int8_t *frm)
2767{
2768 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
2769}
2770
2771static __inline int
2772iswmeinfo(const u_int8_t *frm)
2773{
2774 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2775 frm[6] == WME_INFO_OUI_SUBTYPE;
2776}
2777
2778static __inline int
2779iswmeparam(const u_int8_t *frm)
2780{
2781 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2782 frm[6] == WME_PARAM_OUI_SUBTYPE;
2783}
2784
2785static __inline int
2786isatherosoui(const u_int8_t *frm)
2787{
2788 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
2789}
2790
2791static __inline int
2792istdmaoui(const uint8_t *frm)
2793{
2794 return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
2795}
2796
2797static __inline int
2798iswpsoui(const uint8_t *frm)
2799{
2800 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
2801}
2802
2803static const char *
2804iename(int elemid)
2805{
2806 switch (elemid) {
2807 case IEEE80211_ELEMID_FHPARMS: return " FHPARMS";
2808 case IEEE80211_ELEMID_CFPARMS: return " CFPARMS";
2809 case IEEE80211_ELEMID_TIM: return " TIM";
2810 case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
2811 case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
2812 case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
2813 case IEEE80211_ELEMID_PWRCAP: return " PWRCAP";
2814 case IEEE80211_ELEMID_TPCREQ: return " TPCREQ";
2815 case IEEE80211_ELEMID_TPCREP: return " TPCREP";
2816 case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
2817 case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
2818 case IEEE80211_ELEMID_MEASREQ: return " MEASREQ";
2819 case IEEE80211_ELEMID_MEASREP: return " MEASREP";
2820 case IEEE80211_ELEMID_QUIET: return " QUIET";
2821 case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS";
2822 case IEEE80211_ELEMID_TPC: return " TPC";
2823 case IEEE80211_ELEMID_CCKM: return " CCKM";
2824 }
2825 return " ???";
2826}
2827
2828static void
2829printies(const u_int8_t *vp, int ielen, int maxcols)
2830{
2831 while (ielen > 0) {
2832 switch (vp[0]) {
2833 case IEEE80211_ELEMID_SSID:
2834 if (verbose)
2835 printssid(" SSID", vp, 2+vp[1], maxcols);
2836 break;
2837 case IEEE80211_ELEMID_RATES:
2838 case IEEE80211_ELEMID_XRATES:
2839 if (verbose)
2840 printrates(vp[0] == IEEE80211_ELEMID_RATES ?
2841 " RATES" : " XRATES", vp, 2+vp[1], maxcols);
2842 break;
2843 case IEEE80211_ELEMID_DSPARMS:
2844 if (verbose)
2845 printf(" DSPARMS<%u>", vp[2]);
2846 break;
2847 case IEEE80211_ELEMID_COUNTRY:
2848 if (verbose)
2849 printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
2850 break;
2851 case IEEE80211_ELEMID_ERP:
2852 if (verbose)
2853 printf(" ERP<0x%x>", vp[2]);
2854 break;
2855 case IEEE80211_ELEMID_VENDOR:
2856 if (iswpaoui(vp))
2857 printwpaie(" WPA", vp, 2+vp[1], maxcols);
2858 else if (iswmeinfo(vp))
2859 printwmeinfo(" WME", vp, 2+vp[1], maxcols);
2860 else if (iswmeparam(vp))
2861 printwmeparam(" WME", vp, 2+vp[1], maxcols);
2862 else if (isatherosoui(vp))
2863 printathie(" ATH", vp, 2+vp[1], maxcols);
2864 else if (iswpsoui(vp))
2865 printwpsie(" WPS", vp, 2+vp[1], maxcols);
2866 else if (istdmaoui(vp))
2867 printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
2868 else if (verbose)
2869 printie(" VEN", vp, 2+vp[1], maxcols);
2870 break;
2871 case IEEE80211_ELEMID_RSN:
2872 printrsnie(" RSN", vp, 2+vp[1], maxcols);
2873 break;
2874 case IEEE80211_ELEMID_HTCAP:
2875 printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
2876 break;
2877 case IEEE80211_ELEMID_HTINFO:
2878 if (verbose)
2879 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
2880 break;
2881 default:
2882 if (verbose)
2883 printie(iename(vp[0]), vp, 2+vp[1], maxcols);
2884 break;
2885 }
2886 ielen -= 2+vp[1];
2887 vp += 2+vp[1];
2888 }
2889}
2890
2891static void
2892printmimo(const struct ieee80211_mimo_info *mi)
2893{
2894 /* NB: don't muddy display unless there's something to show */
2895 if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
2896 /* XXX ignore EVM for now */
2897 printf(" (rssi %d:%d:%d nf %d:%d:%d)",
2898 mi->rssi[0], mi->rssi[1], mi->rssi[2],
2899 mi->noise[0], mi->noise[1], mi->noise[2]);
2900 }
2901}
2902
2903static void
2904list_scan(int s)
2905{
2906 uint8_t buf[24*1024];
2907 char ssid[IEEE80211_NWID_LEN+1];
2908 const uint8_t *cp;
2909 int len, ssidmax;
2910
2911 if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
2912 errx(1, "unable to get scan results");
2913 if (len < sizeof(struct ieee80211req_scan_result))
2914 return;
2915
2916 getchaninfo(s);
2917
2918 ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
2919 printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n"
2920 , ssidmax, ssidmax, "SSID"
2921 , "BSSID"
2922 , "CHAN"
2923 , "RATE"
2924 , " S:N"
2925 , "INT"
2926 , "CAPS"
2927 );
2928 cp = buf;
2929 do {
2930 const struct ieee80211req_scan_result *sr;
2931 const uint8_t *vp;
2932
2933 sr = (const struct ieee80211req_scan_result *) cp;
2934 vp = cp + sr->isr_ie_off;
2935 printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s"
2936 , ssidmax
2937 , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
2938 , ssid
2939 , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
2940 , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
2941 , getmaxrate(sr->isr_rates, sr->isr_nrates)
2942 , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
2943 , sr->isr_intval
2944 , getcaps(sr->isr_capinfo)
2945 );
2946 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
2947 printf("\n");
2948 cp += sr->isr_len, len -= sr->isr_len;
2949 } while (len >= sizeof(struct ieee80211req_scan_result));
2950}
2951
2952#ifdef __FreeBSD__
2953#include <net80211/ieee80211_freebsd.h>
2954#endif
2955#ifdef __NetBSD__
2956#include <net80211/ieee80211_netbsd.h>
2957#endif
2958
2959static void
2960scan_and_wait(int s)
2961{
2962 struct ieee80211_scan_req sr;
2963 struct ieee80211req ireq;
2964 int sroute;
2965
2966 sroute = socket(PF_ROUTE, SOCK_RAW, 0);
2967 if (sroute < 0) {
2968 perror("socket(PF_ROUTE,SOCK_RAW)");
2969 return;
2970 }
2971 (void) memset(&ireq, 0, sizeof(ireq));
2972 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2973 ireq.i_type = IEEE80211_IOC_SCAN_REQ;
2974
2975 memset(&sr, 0, sizeof(sr));
2976 sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
2977 | IEEE80211_IOC_SCAN_NOPICK
2978 | IEEE80211_IOC_SCAN_ONCE;
2979 sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
2980 sr.sr_nssid = 0;
2981
2982 ireq.i_data = &sr;
2983 ireq.i_len = sizeof(sr);
2984 /* NB: only root can trigger a scan so ignore errors */
2985 if (ioctl(s, SIOCS80211, &ireq) >= 0) {
2986 char buf[2048];
2987 struct if_announcemsghdr *ifan;
2988 struct rt_msghdr *rtm;
2989
2990 do {
2991 if (read(sroute, buf, sizeof(buf)) < 0) {
2992 perror("read(PF_ROUTE)");
2993 break;
2994 }
2995 rtm = (struct rt_msghdr *) buf;
2996 if (rtm->rtm_version != RTM_VERSION)
2997 break;
2998 ifan = (struct if_announcemsghdr *) rtm;
2999 } while (rtm->rtm_type != RTM_IEEE80211 ||
3000 ifan->ifan_what != RTM_IEEE80211_SCAN);
3001 }
3002 close(sroute);
3003}
3004
3005static
3006DECL_CMD_FUNC(set80211scan, val, d)
3007{
3008 scan_and_wait(s);
3009 list_scan(s);
3010}
3011
3012static enum ieee80211_opmode get80211opmode(int s);
3013
3014static int
3015gettxseq(const struct ieee80211req_sta_info *si)
3016{
3017#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
3018
3019 int i, txseq;
3020
3021 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3022 return si->isi_txseqs[0];
3023 /* XXX not right but usually what folks want */
3024 txseq = 0;
3025 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3026 if (si->isi_txseqs[i] > txseq)
3027 txseq = si->isi_txseqs[i];
3028 return txseq;
3029#undef IEEE80211_NODE_QOS
3030}
3031
3032static int
3033getrxseq(const struct ieee80211req_sta_info *si)
3034{
3035#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
3036
3037 int i, rxseq;
3038
3039 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3040 return si->isi_rxseqs[0];
3041 /* XXX not right but usually what folks want */
3042 rxseq = 0;
3043 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3044 if (si->isi_rxseqs[i] > rxseq)
3045 rxseq = si->isi_rxseqs[i];
3046 return rxseq;
3047#undef IEEE80211_NODE_QOS
3048}
3049
3050static void
3051list_stations(int s)
3052{
3053 union {
3054 struct ieee80211req_sta_req req;
3055 uint8_t buf[24*1024];
3056 } u;
3057 enum ieee80211_opmode opmode = get80211opmode(s);
3058 const uint8_t *cp;
3059 int len;
3060
3061 /* broadcast address =>'s get all stations */
3062 (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3063 if (opmode == IEEE80211_M_STA) {
3064 /*
3065 * Get information about the associated AP.
3066 */
3067 (void) get80211(s, IEEE80211_IOC_BSSID,
3068 u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3069 }
3070 if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3071 errx(1, "unable to get station information");
3072 if (len < sizeof(struct ieee80211req_sta_info))
3073 return;
3074
3075 getchaninfo(s);
3076
3077 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
3078 , "ADDR"
3079 , "AID"
3080 , "CHAN"
3081 , "RATE"
3082 , "RSSI"
3083 , "IDLE"
3084 , "TXSEQ"
3085 , "RXSEQ"
3086 , "CAPS"
3087 , "FLAG"
3088 );
3089 cp = (const uint8_t *) u.req.info;
3090 do {
3091 const struct ieee80211req_sta_info *si;
3092
3093 si = (const struct ieee80211req_sta_info *) cp;
3094 if (si->isi_len < sizeof(*si))
3095 break;
3096 printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
3097 , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
3098 , IEEE80211_AID(si->isi_associd)
3099 , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
3100 , si->isi_txmbps/2
3101 , si->isi_rssi/2.
3102 , si->isi_inact
3103 , gettxseq(si)
3104 , getrxseq(si)
3105 , getcaps(si->isi_capinfo)
3106 , getflags(si->isi_state)
3107 );
3108 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3109 printmimo(&si->isi_mimo);
3110 printf("\n");
3111 cp += si->isi_len, len -= si->isi_len;
3112 } while (len >= sizeof(struct ieee80211req_sta_info));
3113}
3114
3115static const char *
3116get_chaninfo(const struct ieee80211_channel *c, int precise,
3117 char buf[], size_t bsize)
3118{
3119 buf[0] = '\0';
3120 if (IEEE80211_IS_CHAN_FHSS(c))
3121 strlcat(buf, " FHSS", bsize);
3122 if (IEEE80211_IS_CHAN_A(c))
3123 strlcat(buf, " 11a", bsize);
3124 else if (IEEE80211_IS_CHAN_ANYG(c))
3125 strlcat(buf, " 11g", bsize);
3126 else if (IEEE80211_IS_CHAN_B(c))
3127 strlcat(buf, " 11b", bsize);
3128 if (IEEE80211_IS_CHAN_HALF(c))
3129 strlcat(buf, "/10Mhz", bsize);
3130 if (IEEE80211_IS_CHAN_QUARTER(c))
3131 strlcat(buf, "/5Mhz", bsize);
3132 if (IEEE80211_IS_CHAN_TURBO(c))
3133 strlcat(buf, " Turbo", bsize);
3134 if (precise) {
3135 if (IEEE80211_IS_CHAN_HT20(c))
3136 strlcat(buf, " ht/20", bsize);
3137 else if (IEEE80211_IS_CHAN_HT40D(c))
3138 strlcat(buf, " ht/40-", bsize);
3139 else if (IEEE80211_IS_CHAN_HT40U(c))
3140 strlcat(buf, " ht/40+", bsize);
3141 } else {
3142 if (IEEE80211_IS_CHAN_HT(c))
3143 strlcat(buf, " ht", bsize);
3144 }
3145 return buf;
3146}
3147
3148static void
3149print_chaninfo(const struct ieee80211_channel *c, int verb)
3150{
3151 char buf[14];
3152
3153 printf("Channel %3u : %u%c Mhz%-14.14s",
3154 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3155 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3156 get_chaninfo(c, verb, buf, sizeof(buf)));
3157}
3158
3159static int
3160chanpref(const struct ieee80211_channel *c)
3161{
3162 if (IEEE80211_IS_CHAN_HT40(c))
3163 return 40;
3164 if (IEEE80211_IS_CHAN_HT20(c))
3165 return 30;
3166 if (IEEE80211_IS_CHAN_HALF(c))
3167 return 10;
3168 if (IEEE80211_IS_CHAN_QUARTER(c))
3169 return 5;
3170 if (IEEE80211_IS_CHAN_TURBO(c))
3171 return 25;
3172 if (IEEE80211_IS_CHAN_A(c))
3173 return 20;
3174 if (IEEE80211_IS_CHAN_G(c))
3175 return 20;
3176 if (IEEE80211_IS_CHAN_B(c))
3177 return 15;
3178 if (IEEE80211_IS_CHAN_PUREG(c))
3179 return 15;
3180 return 0;
3181}
3182
3183static void
3184print_channels(int s, const struct ieee80211req_chaninfo *chans,
3185 int allchans, int verb)
3186{
3187 struct ieee80211req_chaninfo *achans;
3188 uint8_t reported[IEEE80211_CHAN_BYTES];
3189 const struct ieee80211_channel *c;
3190 int i, half;
3191
3192 achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3193 if (achans == NULL)
3194 errx(1, "no space for active channel list");
3195 achans->ic_nchans = 0;
3196 memset(reported, 0, sizeof(reported));
3197 if (!allchans) {
3198 struct ieee80211req_chanlist active;
3199
3200 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3201 errx(1, "unable to get active channel list");
3202 for (i = 0; i < chans->ic_nchans; i++) {
3203 c = &chans->ic_chans[i];
3204 if (!isset(active.ic_channels, c->ic_ieee))
3205 continue;
3206 /*
3207 * Suppress compatible duplicates unless
3208 * verbose. The kernel gives us it's
3209 * complete channel list which has separate
3210 * entries for 11g/11b and 11a/turbo.
3211 */
3212 if (isset(reported, c->ic_ieee) && !verb) {
3213 /* XXX we assume duplicates are adjacent */
3214 achans->ic_chans[achans->ic_nchans-1] = *c;
3215 } else {
3216 achans->ic_chans[achans->ic_nchans++] = *c;
3217 setbit(reported, c->ic_ieee);
3218 }
3219 }
3220 } else {
3221 for (i = 0; i < chans->ic_nchans; i++) {
3222 c = &chans->ic_chans[i];
3223 /* suppress duplicates as above */
3224 if (isset(reported, c->ic_ieee) && !verb) {
3225 /* XXX we assume duplicates are adjacent */
3226 struct ieee80211_channel *a =
3227 &achans->ic_chans[achans->ic_nchans-1];
3228 if (chanpref(c) > chanpref(a))
3229 *a = *c;
3230 } else {
3231 achans->ic_chans[achans->ic_nchans++] = *c;
3232 setbit(reported, c->ic_ieee);
3233 }
3234 }
3235 }
3236 half = achans->ic_nchans / 2;
3237 if (achans->ic_nchans % 2)
3238 half++;
3239
3240 for (i = 0; i < achans->ic_nchans / 2; i++) {
3241 print_chaninfo(&achans->ic_chans[i], verb);
3242 print_chaninfo(&achans->ic_chans[half+i], verb);
3243 printf("\n");
3244 }
3245 if (achans->ic_nchans % 2) {
3246 print_chaninfo(&achans->ic_chans[i], verb);
3247 printf("\n");
3248 }
3249 free(achans);
3250}
3251
3252static void
3253list_channels(int s, int allchans)
3254{
3255 getchaninfo(s);
3256 print_channels(s, chaninfo, allchans, verbose);
3257}
3258
3259static void
3260print_txpow(const struct ieee80211_channel *c)
3261{
3262 printf("Channel %3u : %u Mhz %3.1f reg %2d ",
3263 c->ic_ieee, c->ic_freq,
3264 c->ic_maxpower/2., c->ic_maxregpower);
3265}
3266
3267static void
3268print_txpow_verbose(const struct ieee80211_channel *c)
3269{
3270 print_chaninfo(c, 1);
3271 printf("min %4.1f dBm max %3.1f dBm reg %2d dBm",
3272 c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3273 /* indicate where regulatory cap limits power use */
3274 if (c->ic_maxpower > 2*c->ic_maxregpower)
3275 printf(" <");
3276}
3277
3278static void
3279list_txpow(int s)
3280{
3281 struct ieee80211req_chaninfo *achans;
3282 uint8_t reported[IEEE80211_CHAN_BYTES];
3283 struct ieee80211_channel *c, *prev;
3284 int i, half;
3285
3286 getchaninfo(s);
3287 achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3288 if (achans == NULL)
3289 errx(1, "no space for active channel list");
3290 achans->ic_nchans = 0;
3291 memset(reported, 0, sizeof(reported));
3292 for (i = 0; i < chaninfo->ic_nchans; i++) {
3293 c = &chaninfo->ic_chans[i];
3294 /* suppress duplicates as above */
3295 if (isset(reported, c->ic_ieee) && !verbose) {
3296 /* XXX we assume duplicates are adjacent */
3297 prev = &achans->ic_chans[achans->ic_nchans-1];
3298 /* display highest power on channel */
3299 if (c->ic_maxpower > prev->ic_maxpower)
3300 *prev = *c;
3301 } else {
3302 achans->ic_chans[achans->ic_nchans++] = *c;
3303 setbit(reported, c->ic_ieee);
3304 }
3305 }
3306 if (!verbose) {
3307 half = achans->ic_nchans / 2;
3308 if (achans->ic_nchans % 2)
3309 half++;
3310
3311 for (i = 0; i < achans->ic_nchans / 2; i++) {
3312 print_txpow(&achans->ic_chans[i]);
3313 print_txpow(&achans->ic_chans[half+i]);
3314 printf("\n");
3315 }
3316 if (achans->ic_nchans % 2) {
3317 print_txpow(&achans->ic_chans[i]);
3318 printf("\n");
3319 }
3320 } else {
3321 for (i = 0; i < achans->ic_nchans; i++) {
3322 print_txpow_verbose(&achans->ic_chans[i]);
3323 printf("\n");
3324 }
3325 }
3326 free(achans);
3327}
3328
3329static void
3330list_keys(int s)
3331{
3332}
3333
3334#define IEEE80211_C_BITS \
3335 "\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \
3336 "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3337 "\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3338 "\37TXFRAG\40TDMA"
3339
3340static void
3341list_capabilities(int s)
3342{
3343 struct ieee80211_devcaps_req *dc;
3344
3345 if (verbose)
3346 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3347 else
3348 dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3349 if (dc == NULL)
3350 errx(1, "no space for device capabilities");
3351 dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3352 getdevcaps(s, dc);
3353 printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3354 if (dc->dc_cryptocaps != 0 || verbose) {
3355 putchar('\n');
3356 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3357 }
3358 if (dc->dc_htcaps != 0 || verbose) {
3359 putchar('\n');
3360 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3361 }
3362 putchar('\n');
3363 if (verbose) {
3364 chaninfo = &dc->dc_chaninfo; /* XXX */
3365 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3366 }
3367 free(dc);
3368}
3369
3370static int
3371get80211wme(int s, int param, int ac, int *val)
3372{
3373 struct ieee80211req ireq;
3374
3375 (void) memset(&ireq, 0, sizeof(ireq));
3376 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3377 ireq.i_type = param;
3378 ireq.i_len = ac;
3379 if (ioctl(s, SIOCG80211, &ireq) < 0) {
3380 warn("cannot get WME parameter %d, ac %d%s",
3381 param, ac & IEEE80211_WMEPARAM_VAL,
3382 ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3383 return -1;
3384 }
3385 *val = ireq.i_val;
3386 return 0;
3387}
3388
3389static void
3390list_wme_aci(int s, const char *tag, int ac)
3391{
3392 int val;
3393
3394 printf("\t%s", tag);
3395
3396 /* show WME BSS parameters */
3397 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3398 printf(" cwmin %2u", val);
3399 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3400 printf(" cwmax %2u", val);
3401 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3402 printf(" aifs %2u", val);
3403 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3404 printf(" txopLimit %3u", val);
3405 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3406 if (val)
3407 printf(" acm");
3408 else if (verbose)
3409 printf(" -acm");
3410 }
3411 /* !BSS only */
3412 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3413 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3414 if (!val)
3415 printf(" -ack");
3416 else if (verbose)
3417 printf(" ack");
3418 }
3419 }
3420 printf("\n");
3421}
3422
3423static void
3424list_wme(int s)
3425{
3426 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3427 int ac;
3428
3429 if (verbose) {
3430 /* display both BSS and local settings */
3431 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3432 again:
3433 if (ac & IEEE80211_WMEPARAM_BSS)
3434 list_wme_aci(s, " ", ac);
3435 else
3436 list_wme_aci(s, acnames[ac], ac);
3437 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3438 ac |= IEEE80211_WMEPARAM_BSS;
3439 goto again;
3440 } else
3441 ac &= ~IEEE80211_WMEPARAM_BSS;
3442 }
3443 } else {
3444 /* display only channel settings */
3445 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3446 list_wme_aci(s, acnames[ac], ac);
3447 }
3448}
3449
3450static void
3451list_roam(int s)
3452{
3453 const struct ieee80211_roamparam *rp;
3454 int mode;
3455
3456 getroam(s);
3457 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3458 rp = &roamparams.params[mode];
3459 if (rp->rssi == 0 && rp->rate == 0)
3460 continue;
3461 if (rp->rssi & 1)
3462 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s",
3463 modename[mode], rp->rssi/2, rp->rate/2);
3464 else
3465 LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s",
3466 modename[mode], rp->rssi/2, rp->rate/2);
3467 }
3468 for (; mode < IEEE80211_MODE_MAX; mode++) {
3469 rp = &roamparams.params[mode];
3470 if (rp->rssi == 0 && rp->rate == 0)
3471 continue;
3472 if (rp->rssi & 1)
3473 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ",
3474 modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3475 else
3476 LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ",
3477 modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3478 }
3479}
3480
3481static void
3482list_txparams(int s)
3483{
3484 const struct ieee80211_txparam *tp;
3485 int mode;
3486
3487 gettxparams(s);
3488 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3489 tp = &txparams.params[mode];
3490 if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3491 continue;
3492 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3493 LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s "
3494 "mcast %2u Mb/s maxretry %u",
3495 modename[mode], tp->mgmtrate/2,
3496 tp->mcastrate/2, tp->maxretry);
3497 else
3498 LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s "
3499 "mcast %2u Mb/s maxretry %u",
3500 modename[mode], tp->ucastrate/2, tp->mgmtrate/2,
3501 tp->mcastrate/2, tp->maxretry);
3502 }
3503 for (; mode < IEEE80211_MODE_MAX; mode++) {
3504 tp = &txparams.params[mode];
3505 if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3506 continue;
3507 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3508 LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS "
3509 "mcast %2u MCS maxretry %u",
3510 modename[mode], tp->mgmtrate &~ 0x80,
3511 tp->mcastrate &~ 0x80, tp->maxretry);
3512 else
3513 LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS "
3514 "mcast %2u MCS maxretry %u",
3515 modename[mode], tp->ucastrate &~ 0x80,
3516 tp->mgmtrate &~ 0x80,
3517 tp->mcastrate &~ 0x80, tp->maxretry);
3518 }
3519}
3520
3521static void
3522printpolicy(int policy)
3523{
3524 switch (policy) {
3525 case IEEE80211_MACCMD_POLICY_OPEN:
3526 printf("policy: open\n");
3527 break;
3528 case IEEE80211_MACCMD_POLICY_ALLOW:
3529 printf("policy: allow\n");
3530 break;
3531 case IEEE80211_MACCMD_POLICY_DENY:
3532 printf("policy: deny\n");
3533 break;
3534 case IEEE80211_MACCMD_POLICY_RADIUS:
3535 printf("policy: radius\n");
3536 break;
3537 default:
3538 printf("policy: unknown (%u)\n", policy);
3539 break;
3540 }
3541}
3542
3543static void
3544list_mac(int s)
3545{
3546 struct ieee80211req ireq;
3547 struct ieee80211req_maclist *acllist;
3548 int i, nacls, policy, len;
3549 uint8_t *data;
3550 char c;
3551
3552 (void) memset(&ireq, 0, sizeof(ireq));
3553 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3554 ireq.i_type = IEEE80211_IOC_MACCMD;
3555 ireq.i_val = IEEE80211_MACCMD_POLICY;
3556 if (ioctl(s, SIOCG80211, &ireq) < 0) {
3557 if (errno == EINVAL) {
3558 printf("No acl policy loaded\n");
3559 return;
3560 }
3561 err(1, "unable to get mac policy");
3562 }
3563 policy = ireq.i_val;
3564 if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3565 c = '*';
3566 } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3567 c = '+';
3568 } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3569 c = '-';
3570 } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3571 c = 'r'; /* NB: should never have entries */
3572 } else {
3573 printf("policy: unknown (%u)\n", policy);
3574 c = '?';
3575 }
3576 if (verbose || c == '?')
3577 printpolicy(policy);
3578
3579 ireq.i_val = IEEE80211_MACCMD_LIST;
3580 ireq.i_len = 0;
3581 if (ioctl(s, SIOCG80211, &ireq) < 0)
3582 err(1, "unable to get mac acl list size");
3583 if (ireq.i_len == 0) { /* NB: no acls */
3584 if (!(verbose || c == '?'))
3585 printpolicy(policy);
3586 return;
3587 }
3588 len = ireq.i_len;
3589
3590 data = malloc(len);
3591 if (data == NULL)
3592 err(1, "out of memory for acl list");
3593
3594 ireq.i_data = data;
3595 if (ioctl(s, SIOCG80211, &ireq) < 0)
3596 err(1, "unable to get mac acl list");
3597 nacls = len / sizeof(*acllist);
3598 acllist = (struct ieee80211req_maclist *) data;
3599 for (i = 0; i < nacls; i++)
3600 printf("%c%s\n", c, ether_ntoa(
3601 (const struct ether_addr *) acllist[i].ml_macaddr));
3602 free(data);
3603}
3604
3605static void
3606print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3607{
3608 if ((reg->regdomain != 0 &&
3609 reg->regdomain != reg->country) || verb) {
3610 const struct regdomain *rd =
3611 lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3612 if (rd == NULL)
3613 LINE_CHECK("regdomain %d", reg->regdomain);
3614 else
3615 LINE_CHECK("regdomain %s", rd->name);
3616 }
3617 if (reg->country != 0 || verb) {
3618 const struct country *cc =
3619 lib80211_country_findbycc(getregdata(), reg->country);
3620 if (cc == NULL)
3621 LINE_CHECK("country %d", reg->country);
3622 else
3623 LINE_CHECK("country %s", cc->isoname);
3624 }
3625 if (reg->location == 'I')
3626 LINE_CHECK("indoor");
3627 else if (reg->location == 'O')
3628 LINE_CHECK("outdoor");
3629 else if (verb)
3630 LINE_CHECK("anywhere");
3631 if (reg->ecm)
3632 LINE_CHECK("ecm");
3633 else if (verb)
3634 LINE_CHECK("-ecm");
3635}
3636
3637static void
3638list_regdomain(int s, int channelsalso)
3639{
3640 getregdomain(s);
3641 if (channelsalso) {
3642 getchaninfo(s);
3643 spacer = ':';
3644 print_regdomain(&regdomain, 1);
3645 LINE_BREAK();
3646 print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3647 } else
3648 print_regdomain(&regdomain, verbose);
3649}
3650
3651static
3652DECL_CMD_FUNC(set80211list, arg, d)
3653{
3654#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
3655
3656 LINE_INIT('\t');
3657
3658 if (iseq(arg, "sta"))
3659 list_stations(s);
3660 else if (iseq(arg, "scan") || iseq(arg, "ap"))
3661 list_scan(s);
3662 else if (iseq(arg, "chan") || iseq(arg, "freq"))
3663 list_channels(s, 1);
3664 else if (iseq(arg, "active"))
3665 list_channels(s, 0);
3666 else if (iseq(arg, "keys"))
3667 list_keys(s);
3668 else if (iseq(arg, "caps"))
3669 list_capabilities(s);
3670 else if (iseq(arg, "wme") || iseq(arg, "wmm"))
3671 list_wme(s);
3672 else if (iseq(arg, "mac"))
3673 list_mac(s);
3674 else if (iseq(arg, "txpow"))
3675 list_txpow(s);
3676 else if (iseq(arg, "roam"))
3677 list_roam(s);
3678 else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
3679 list_txparams(s);
3680 else if (iseq(arg, "regdomain"))
3681 list_regdomain(s, 1);
3682 else if (iseq(arg, "countries"))
3683 list_countries();
3684 else
3685 errx(1, "Don't know how to list %s for %s", arg, name);
3686 LINE_BREAK();
3687#undef iseq
3688}
3689
3690static enum ieee80211_opmode
3691get80211opmode(int s)
3692{
3693 struct ifmediareq ifmr;
3694
3695 (void) memset(&ifmr, 0, sizeof(ifmr));
3696 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
3697
3698 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
3699 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
3700 if (ifmr.ifm_current & IFM_FLAG0)
3701 return IEEE80211_M_AHDEMO;
3702 else
3703 return IEEE80211_M_IBSS;
3704 }
3705 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
3706 return IEEE80211_M_HOSTAP;
3707 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
3708 return IEEE80211_M_MONITOR;
3709 }
3710 return IEEE80211_M_STA;
3711}
3712
3713#if 0
3714static void
3715printcipher(int s, struct ieee80211req *ireq, int keylenop)
3716{
3717 switch (ireq->i_val) {
3718 case IEEE80211_CIPHER_WEP:
3719 ireq->i_type = keylenop;
3720 if (ioctl(s, SIOCG80211, ireq) != -1)
3721 printf("WEP-%s",
3722 ireq->i_len <= 5 ? "40" :
3723 ireq->i_len <= 13 ? "104" : "128");
3724 else
3725 printf("WEP");
3726 break;
3727 case IEEE80211_CIPHER_TKIP:
3728 printf("TKIP");
3729 break;
3730 case IEEE80211_CIPHER_AES_OCB:
3731 printf("AES-OCB");
3732 break;
3733 case IEEE80211_CIPHER_AES_CCM:
3734 printf("AES-CCM");
3735 break;
3736 case IEEE80211_CIPHER_CKIP:
3737 printf("CKIP");
3738 break;
3739 case IEEE80211_CIPHER_NONE:
3740 printf("NONE");
3741 break;
3742 default:
3743 printf("UNKNOWN (0x%x)", ireq->i_val);
3744 break;
3745 }
3746}
3747#endif
3748
3749static void
3750printkey(const struct ieee80211req_key *ik)
3751{
3752 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
3753 int keylen = ik->ik_keylen;
3754 int printcontents;
3755
3756 printcontents = printkeys &&
3757 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
3758 if (printcontents)
3759 LINE_BREAK();
3760 switch (ik->ik_type) {
3761 case IEEE80211_CIPHER_WEP:
3762 /* compatibility */
3763 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
3764 keylen <= 5 ? "40-bit" :
3765 keylen <= 13 ? "104-bit" : "128-bit");
3766 break;
3767 case IEEE80211_CIPHER_TKIP:
3768 if (keylen > 128/8)
3769 keylen -= 128/8; /* ignore MIC for now */
3770 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3771 break;
3772 case IEEE80211_CIPHER_AES_OCB:
3773 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3774 break;
3775 case IEEE80211_CIPHER_AES_CCM:
3776 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3777 break;
3778 case IEEE80211_CIPHER_CKIP:
3779 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3780 break;
3781 case IEEE80211_CIPHER_NONE:
3782 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3783 break;
3784 default:
3785 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
3786 ik->ik_type, ik->ik_keyix+1, 8*keylen);
3787 break;
3788 }
3789 if (printcontents) {
3790 int i;
3791
3792 printf(" <");
3793 for (i = 0; i < keylen; i++)
3794 printf("%02x", ik->ik_keydata[i]);
3795 printf(">");
3796 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3797 (ik->ik_keyrsc != 0 || verbose))
3798 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
3799 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3800 (ik->ik_keytsc != 0 || verbose))
3801 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
3802 if (ik->ik_flags != 0 && verbose) {
3803 const char *sep = " ";
3804
3805 if (ik->ik_flags & IEEE80211_KEY_XMIT)
3806 printf("%stx", sep), sep = "+";
3807 if (ik->ik_flags & IEEE80211_KEY_RECV)
3808 printf("%srx", sep), sep = "+";
3809 if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
3810 printf("%sdef", sep), sep = "+";
3811 }
3812 LINE_BREAK();
3813 }
3814}
3815
3816static void
3817printrate(const char *tag, int v, int defrate, int defmcs)
3818{
3819 if (v == 11)
3820 LINE_CHECK("%s 5.5", tag);
3821 else if (v & 0x80) {
3822 if (v != defmcs)
3823 LINE_CHECK("%s %d", tag, v &~ 0x80);
3824 } else {
3825 if (v != defrate)
3826 LINE_CHECK("%s %d", tag, v/2);
3827 }
3828}
3829
3830static int
3831getssid(int s, int ix, void *data, size_t len, int *plen)
3832{
3833 struct ieee80211req ireq;
3834
3835 (void) memset(&ireq, 0, sizeof(ireq));
3836 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3837 ireq.i_type = IEEE80211_IOC_SSID;
3838 ireq.i_val = ix;
3839 ireq.i_data = data;
3840 ireq.i_len = len;
3841 if (ioctl(s, SIOCG80211, &ireq) < 0)
3842 return -1;
3843 *plen = ireq.i_len;
3844 return 0;
3845}
3846
3847static void
3848ieee80211_status(int s)
3849{
3850 static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
3851 enum ieee80211_opmode opmode = get80211opmode(s);
3852 int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
3853 uint8_t data[32];
3854 const struct ieee80211_channel *c;
3855 const struct ieee80211_roamparam *rp;
3856 const struct ieee80211_txparam *tp;
3857
3858 if (getssid(s, -1, data, sizeof(data), &len) < 0) {
3859 /* If we can't get the SSID, this isn't an 802.11 device. */
3860 return;
3861 }
3862
3863 /*
3864 * Invalidate cached state so printing status for multiple
3865 * if's doesn't reuse the first interfaces' cached state.
3866 */
3867 gotcurchan = 0;
3868 gotroam = 0;
3869 gottxparams = 0;
3870 gothtconf = 0;
3871 gotregdomain = 0;
3872
3873 if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
3874 num = 0;
3875 printf("\tssid ");
3876 if (num > 1) {
3877 for (i = 0; i < num; i++) {
3878 if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
3879 printf(" %d:", i + 1);
3880 print_string(data, len);
3881 }
3882 }
3883 } else
3884 print_string(data, len);
3885
3886 c = getcurchan(s);
3887 if (c->ic_freq != IEEE80211_CHAN_ANY) {
3888 char buf[14];
3889 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
3890 get_chaninfo(c, 1, buf, sizeof(buf)));
3891 } else if (verbose)
3892 printf(" channel UNDEF");
3893
3894 if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
3895 (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
3896 printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
3897
3898 if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
3899 printf("\n\tstationname ");
3900 print_string(data, len);
3901 }
3902
3903 spacer = ' '; /* force first break */
3904 LINE_BREAK();
3905
3906 list_regdomain(s, 0);
3907
3908 wpa = 0;
3909 if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
3910 switch (val) {
3911 case IEEE80211_AUTH_NONE:
3912 LINE_CHECK("authmode NONE");
3913 break;
3914 case IEEE80211_AUTH_OPEN:
3915 LINE_CHECK("authmode OPEN");
3916 break;
3917 case IEEE80211_AUTH_SHARED:
3918 LINE_CHECK("authmode SHARED");
3919 break;
3920 case IEEE80211_AUTH_8021X:
3921 LINE_CHECK("authmode 802.1x");
3922 break;
3923 case IEEE80211_AUTH_WPA:
3924 if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
3925 wpa = 1; /* default to WPA1 */
3926 switch (wpa) {
3927 case 2:
3928 LINE_CHECK("authmode WPA2/802.11i");
3929 break;
3930 case 3:
3931 LINE_CHECK("authmode WPA1+WPA2/802.11i");
3932 break;
3933 default:
3934 LINE_CHECK("authmode WPA");
3935 break;
3936 }
3937 break;
3938 case IEEE80211_AUTH_AUTO:
3939 LINE_CHECK("authmode AUTO");
3940 break;
3941 default:
3942 LINE_CHECK("authmode UNKNOWN (0x%x)", val);
3943 break;
3944 }
3945 }
3946
3947 if (wpa || verbose) {
3948 if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
3949 if (val)
3950 LINE_CHECK("wps");
3951 else if (verbose)
3952 LINE_CHECK("-wps");
3953 }
3954 if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
3955 if (val)
3956 LINE_CHECK("tsn");
3957 else if (verbose)
3958 LINE_CHECK("-tsn");
3959 }
3960 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
3961 if (val)
3962 LINE_CHECK("countermeasures");
3963 else if (verbose)
3964 LINE_CHECK("-countermeasures");
3965 }
3966#if 0
3967 /* XXX not interesting with WPA done in user space */
3968 ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
3969 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3970 }
3971
3972 ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
3973 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3974 LINE_CHECK("mcastcipher ");
3975 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
3976 spacer = ' ';
3977 }
3978
3979 ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
3980 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3981 LINE_CHECK("ucastcipher ");
3982 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
3983 }
3984
3985 if (wpa & 2) {
3986 ireq.i_type = IEEE80211_IOC_RSNCAPS;
3987 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3988 LINE_CHECK("RSN caps 0x%x", ireq.i_val);
3989 spacer = ' ';
3990 }
3991 }
3992
3993 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
3994 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3995 }
3996#endif
3997 }
3998
3999 if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4000 wepmode != IEEE80211_WEP_NOSUP) {
4001 int firstkey;
4002
4003 switch (wepmode) {
4004 case IEEE80211_WEP_OFF:
4005 LINE_CHECK("privacy OFF");
4006 break;
4007 case IEEE80211_WEP_ON:
4008 LINE_CHECK("privacy ON");
4009 break;
4010 case IEEE80211_WEP_MIXED:
4011 LINE_CHECK("privacy MIXED");
4012 break;
4013 default:
4014 LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4015 break;
4016 }
4017
4018 /*
4019 * If we get here then we've got WEP support so we need
4020 * to print WEP status.
4021 */
4022
4023 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4024 warn("WEP support, but no tx key!");
4025 goto end;
4026 }
4027 if (val != -1)
4028 LINE_CHECK("deftxkey %d", val+1);
4029 else if (wepmode != IEEE80211_WEP_OFF || verbose)
4030 LINE_CHECK("deftxkey UNDEF");
4031
4032 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4033 warn("WEP support, but no NUMWEPKEYS support!");
4034 goto end;
4035 }
4036
4037 firstkey = 1;
4038 for (i = 0; i < num; i++) {
4039 struct ieee80211req_key ik;
4040
4041 memset(&ik, 0, sizeof(ik));
4042 ik.ik_keyix = i;
4043 if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4044 warn("WEP support, but can get keys!");
4045 goto end;
4046 }
4047 if (ik.ik_keylen != 0) {
4048 if (verbose)
4049 LINE_BREAK();
4050 printkey(&ik);
4051 firstkey = 0;
4052 }
4053 }
4054end:
4055 ;
4056 }
4057
4058 if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4059 val != IEEE80211_POWERSAVE_NOSUP ) {
4060 if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4061 switch (val) {
4062 case IEEE80211_POWERSAVE_OFF:
4063 LINE_CHECK("powersavemode OFF");
4064 break;
4065 case IEEE80211_POWERSAVE_CAM:
4066 LINE_CHECK("powersavemode CAM");
4067 break;
4068 case IEEE80211_POWERSAVE_PSP:
4069 LINE_CHECK("powersavemode PSP");
4070 break;
4071 case IEEE80211_POWERSAVE_PSP_CAM:
4072 LINE_CHECK("powersavemode PSP-CAM");
4073 break;
4074 }
4075 if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4076 LINE_CHECK("powersavesleep %d", val);
4077 }
4078 }
4079
4080 if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4081 if (val & 1)
4082 LINE_CHECK("txpower %d.5", val/2);
4083 else
4084 LINE_CHECK("txpower %d", val/2);
4085 }
4086 if (verbose) {
4087 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4088 LINE_CHECK("txpowmax %.1f", val/2.);
4089 }
4090
4091 if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4092 if (val)
4093 LINE_CHECK("dotd");
4094 else if (verbose)
4095 LINE_CHECK("-dotd");
4096 }
4097
4098 if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4099 if (val != IEEE80211_RTS_MAX || verbose)
4100 LINE_CHECK("rtsthreshold %d", val);
4101 }
4102
4103 if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4104 if (val != IEEE80211_FRAG_MAX || verbose)
4105 LINE_CHECK("fragthreshold %d", val);
4106 }
4107 if (opmode == IEEE80211_M_STA || verbose) {
4108 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4109 if (val != IEEE80211_HWBMISS_MAX || verbose)
4110 LINE_CHECK("bmiss %d", val);
4111 }
4112 }
4113
4114 if (!verbose) {
4115 gettxparams(s);
4116 tp = &txparams.params[chan2mode(c)];
4117 printrate("ucastrate", tp->ucastrate,
4118 IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4119 printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0);
4120 printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0);
4121 if (tp->maxretry != 6) /* XXX */
4122 LINE_CHECK("maxretry %d", tp->maxretry);
4123 } else {
4124 LINE_BREAK();
4125 list_txparams(s);
4126 }
4127
4128 bgscaninterval = -1;
4129 (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4130
4131 if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4132 if (val != bgscaninterval || verbose)
4133 LINE_CHECK("scanvalid %u", val);
4134 }
4135
4136 bgscan = 0;
4137 if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4138 if (bgscan)
4139 LINE_CHECK("bgscan");
4140 else if (verbose)
4141 LINE_CHECK("-bgscan");
4142 }
4143 if (bgscan || verbose) {
4144 if (bgscaninterval != -1)
4145 LINE_CHECK("bgscanintvl %u", bgscaninterval);
4146 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4147 LINE_CHECK("bgscanidle %u", val);
4148 if (!verbose) {
4149 getroam(s);
4150 rp = &roamparams.params[chan2mode(c)];
4151 if (rp->rssi & 1)
4152 LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4153 else
4154 LINE_CHECK("roam:rssi %u", rp->rssi/2);
4155 LINE_CHECK("roam:rate %u", rp->rate/2);
4156 } else {
4157 LINE_BREAK();
4158 list_roam(s);
4159 }
4160 }
4161
4162 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4163 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4164 if (val)
4165 LINE_CHECK("pureg");
4166 else if (verbose)
4167 LINE_CHECK("-pureg");
4168 }
4169 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4170 switch (val) {
4171 case IEEE80211_PROTMODE_OFF:
4172 LINE_CHECK("protmode OFF");
4173 break;
4174 case IEEE80211_PROTMODE_CTS:
4175 LINE_CHECK("protmode CTS");
4176 break;
4177 case IEEE80211_PROTMODE_RTSCTS:
4178 LINE_CHECK("protmode RTSCTS");
4179 break;
4180 default:
4181 LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4182 break;
4183 }
4184 }
4185 }
4186
4187 if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4188 gethtconf(s);
4189 switch (htconf & 3) {
4190 case 0:
4191 case 2:
4192 LINE_CHECK("-ht");
4193 break;
4194 case 1:
4195 LINE_CHECK("ht20");
4196 break;
4197 case 3:
4198 if (verbose)
4199 LINE_CHECK("ht");
4200 break;
4201 }
4202 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4203 if (!val)
4204 LINE_CHECK("-htcompat");
4205 else if (verbose)
4206 LINE_CHECK("htcompat");
4207 }
4208 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4209 switch (val) {
4210 case 0:
4211 LINE_CHECK("-ampdu");
4212 break;
4213 case 1:
4214 LINE_CHECK("ampdutx -ampdurx");
4215 break;
4216 case 2:
4217 LINE_CHECK("-ampdutx ampdurx");
4218 break;
4219 case 3:
4220 if (verbose)
4221 LINE_CHECK("ampdu");
4222 break;
4223 }
4224 }
4225 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4226 switch (val) {
4227 case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4228 LINE_CHECK("ampdulimit 8k");
4229 break;
4230 case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4231 LINE_CHECK("ampdulimit 16k");
4232 break;
4233 case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4234 LINE_CHECK("ampdulimit 32k");
4235 break;
4236 case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4237 LINE_CHECK("ampdulimit 64k");
4238 break;
4239 }
4240 }
4241 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4242 switch (val) {
4243 case IEEE80211_HTCAP_MPDUDENSITY_NA:
4244 if (verbose)
4245 LINE_CHECK("ampdudensity NA");
4246 break;
4247 case IEEE80211_HTCAP_MPDUDENSITY_025:
4248 LINE_CHECK("ampdudensity .25");
4249 break;
4250 case IEEE80211_HTCAP_MPDUDENSITY_05:
4251 LINE_CHECK("ampdudensity .5");
4252 break;
4253 case IEEE80211_HTCAP_MPDUDENSITY_1:
4254 LINE_CHECK("ampdudensity 1");
4255 break;
4256 case IEEE80211_HTCAP_MPDUDENSITY_2:
4257 LINE_CHECK("ampdudensity 2");
4258 break;
4259 case IEEE80211_HTCAP_MPDUDENSITY_4:
4260 LINE_CHECK("ampdudensity 4");
4261 break;
4262 case IEEE80211_HTCAP_MPDUDENSITY_8:
4263 LINE_CHECK("ampdudensity 8");
4264 break;
4265 case IEEE80211_HTCAP_MPDUDENSITY_16:
4266 LINE_CHECK("ampdudensity 16");
4267 break;
4268 }
4269 }
4270 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4271 switch (val) {
4272 case 0:
4273 LINE_CHECK("-amsdu");
4274 break;
4275 case 1:
4276 LINE_CHECK("amsdutx -amsdurx");
4277 break;
4278 case 2:
4279 LINE_CHECK("-amsdutx amsdurx");
4280 break;
4281 case 3:
4282 if (verbose)
4283 LINE_CHECK("amsdu");
4284 break;
4285 }
4286 }
4287 /* XXX amsdu limit */
4288 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4289 if (val)
4290 LINE_CHECK("shortgi");
4291 else if (verbose)
4292 LINE_CHECK("-shortgi");
4293 }
4294 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4295 if (val == IEEE80211_PROTMODE_OFF)
4296 LINE_CHECK("htprotmode OFF");
4297 else if (val != IEEE80211_PROTMODE_RTSCTS)
4298 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4299 else if (verbose)
4300 LINE_CHECK("htprotmode RTSCTS");
4301 }
4302 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4303 if (val)
4304 LINE_CHECK("puren");
4305 else if (verbose)
4306 LINE_CHECK("-puren");
4307 }
4308 if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4309 if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4310 LINE_CHECK("smpsdyn");
4311 else if (val == IEEE80211_HTCAP_SMPS_ENA)
4312 LINE_CHECK("smps");
4313 else if (verbose)
4314 LINE_CHECK("-smps");
4315 }
4316 if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4317 if (val)
4318 LINE_CHECK("rifs");
4319 else if (verbose)
4320 LINE_CHECK("-rifs");
4321 }
4322 }
4323
4324 if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4325 if (wme)
4326 LINE_CHECK("wme");
4327 else if (verbose)
4328 LINE_CHECK("-wme");
4329 } else
4330 wme = 0;
4331
4332 if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4333 if (val)
4334 LINE_CHECK("burst");
4335 else if (verbose)
4336 LINE_CHECK("-burst");
4337 }
4338
4339 if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4340 if (val)
4341 LINE_CHECK("ff");
4342 else if (verbose)
4343 LINE_CHECK("-ff");
4344 }
4345 if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4346 if (val)
4347 LINE_CHECK("dturbo");
4348 else if (verbose)
4349 LINE_CHECK("-dturbo");
4350 }
4351 if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4352 if (val)
4353 LINE_CHECK("dwds");
4354 else if (verbose)
4355 LINE_CHECK("-dwds");
4356 }
4357
4358 if (opmode == IEEE80211_M_HOSTAP) {
4359 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4360 if (val)
4361 LINE_CHECK("hidessid");
4362 else if (verbose)
4363 LINE_CHECK("-hidessid");
4364 }
4365 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4366 if (!val)
4367 LINE_CHECK("-apbridge");
4368 else if (verbose)
4369 LINE_CHECK("apbridge");
4370 }
4371 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4372 LINE_CHECK("dtimperiod %u", val);
4373
4374 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4375 if (!val)
4376 LINE_CHECK("-doth");
4377 else if (verbose)
4378 LINE_CHECK("doth");
4379 }
4380 if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4381 if (!val)
4382 LINE_CHECK("-dfs");
4383 else if (verbose)
4384 LINE_CHECK("dfs");
4385 }
4386 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4387 if (!val)
4388 LINE_CHECK("-inact");
4389 else if (verbose)
4390 LINE_CHECK("inact");
4391 }
4392 } else {
4393 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4394 if (val != IEEE80211_ROAMING_AUTO || verbose) {
4395 switch (val) {
4396 case IEEE80211_ROAMING_DEVICE:
4397 LINE_CHECK("roaming DEVICE");
4398 break;
4399 case IEEE80211_ROAMING_AUTO:
4400 LINE_CHECK("roaming AUTO");
4401 break;
4402 case IEEE80211_ROAMING_MANUAL:
4403 LINE_CHECK("roaming MANUAL");
4404 break;
4405 default:
4406 LINE_CHECK("roaming UNKNOWN (0x%x)",
4407 val);
4408 break;
4409 }
4410 }
4411 }
4412 }
4413
4414 if (opmode == IEEE80211_M_AHDEMO) {
4415 if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4416 LINE_CHECK("tdmaslot %u", val);
4417 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4418 LINE_CHECK("tdmaslotcnt %u", val);
4419 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4420 LINE_CHECK("tdmaslotlen %u", val);
4421 if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4422 LINE_CHECK("tdmabintval %u", val);
4423 } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4424 /* XXX default define not visible */
4425 if (val != 100 || verbose)
4426 LINE_CHECK("bintval %u", val);
4427 }
4428
4429 if (wme && verbose) {
4430 LINE_BREAK();
4431 list_wme(s);
4432 }
4433 LINE_BREAK();
4434}
4435
4436static int
4437get80211(int s, int type, void *data, int len)
4438{
4439 struct ieee80211req ireq;
4440
4441 (void) memset(&ireq, 0, sizeof(ireq));
4442 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4443 ireq.i_type = type;
4444 ireq.i_data = data;
4445 ireq.i_len = len;
4446 return ioctl(s, SIOCG80211, &ireq);
4447}
4448
4449static int
4450get80211len(int s, int type, void *data, int len, int *plen)
4451{
4452 struct ieee80211req ireq;
4453
4454 (void) memset(&ireq, 0, sizeof(ireq));
4455 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4456 ireq.i_type = type;
4457 ireq.i_len = len;
4458 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */
4459 ireq.i_data = data;
4460 if (ioctl(s, SIOCG80211, &ireq) < 0)
4461 return -1;
4462 *plen = ireq.i_len;
4463 return 0;
4464}
4465
4466static int
4467get80211val(int s, int type, int *val)
4468{
4469 struct ieee80211req ireq;
4470
4471 (void) memset(&ireq, 0, sizeof(ireq));
4472 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4473 ireq.i_type = type;
4474 if (ioctl(s, SIOCG80211, &ireq) < 0)
4475 return -1;
4476 *val = ireq.i_val;
4477 return 0;
4478}
4479
4480static void
4481set80211(int s, int type, int val, int len, void *data)
4482{
4483 struct ieee80211req ireq;
4484
4485 (void) memset(&ireq, 0, sizeof(ireq));
4486 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4487 ireq.i_type = type;
4488 ireq.i_val = val;
4489 ireq.i_len = len;
4490 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */
4491 ireq.i_data = data;
4492 if (ioctl(s, SIOCS80211, &ireq) < 0)
4493 err(1, "SIOCS80211");
4494}
4495
4496static const char *
4497get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4498{
4499 int len;
4500 int hexstr;
4501 u_int8_t *p;
4502
4503 len = *lenp;
4504 p = buf;
4505 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4506 if (hexstr)
4507 val += 2;
4508 for (;;) {
4509 if (*val == '\0')
4510 break;
4511 if (sep != NULL && strchr(sep, *val) != NULL) {
4512 val++;
4513 break;
4514 }
4515 if (hexstr) {
4516 if (!isxdigit((u_char)val[0])) {
4517 warnx("bad hexadecimal digits");
4518 return NULL;
4519 }
4520 if (!isxdigit((u_char)val[1])) {
4521 warnx("odd count hexadecimal digits");
4522 return NULL;
4523 }
4524 }
4525 if (p >= buf + len) {
4526 if (hexstr)
4527 warnx("hexadecimal digits too long");
4528 else
4529 warnx("string too long");
4530 return NULL;
4531 }
4532 if (hexstr) {
4533#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4534 *p++ = (tohex((u_char)val[0]) << 4) |
4535 tohex((u_char)val[1]);
4536#undef tohex
4537 val += 2;
4538 } else
4539 *p++ = *val++;
4540 }
4541 len = p - buf;
4542 /* The string "-" is treated as the empty string. */
4543 if (!hexstr && len == 1 && buf[0] == '-') {
4544 len = 0;
4545 memset(buf, 0, *lenp);
4546 } else if (len < *lenp)
4547 memset(p, 0, *lenp - len);
4548 *lenp = len;
4549 return val;
4550}
4551
4552static void
4553print_string(const u_int8_t *buf, int len)
4554{
4555 int i;
4556 int hasspc;
4557
4558 i = 0;
4559 hasspc = 0;
4560 for (; i < len; i++) {
4561 if (!isprint(buf[i]) && buf[i] != '\0')
4562 break;
4563 if (isspace(buf[i]))
4564 hasspc++;
4565 }
4566 if (i == len) {
4567 if (hasspc || len == 0 || buf[0] == '\0')
4568 printf("\"%.*s\"", len, buf);
4569 else
4570 printf("%.*s", len, buf);
4571 } else {
4572 printf("0x");
4573 for (i = 0; i < len; i++)
4574 printf("%02x", buf[i]);
4575 }
4576}
4577
4578/*
4579 * Virtual AP cloning support.
4580 */
4581static struct ieee80211_clone_params params = {
4582 .icp_opmode = IEEE80211_M_STA, /* default to station mode */
4583};
4584
4585static void
4586wlan_create(int s, struct ifreq *ifr)
4587{
4588 static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4589
4590 if (params.icp_parent[0] == '\0')
4591 errx(1, "must specify a parent when creating a wlan device");
4592 if (params.icp_opmode == IEEE80211_M_WDS &&
4593 memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
4594 errx(1, "no bssid specified for WDS (use wlanbssid)");
4595 ifr->ifr_data = (caddr_t) &params;
4596 if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
4597 err(1, "SIOCIFCREATE2");
4598}
4599
4600static
4601DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
4602{
4603 strlcpy(params.icp_parent, arg, IFNAMSIZ);
4604 clone_setcallback(wlan_create);
4605}
4606
4607static
4608DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
4609{
4610 const struct ether_addr *ea;
4611
4612 ea = ether_aton(arg);
4613 if (ea == NULL)
4614 errx(1, "%s: cannot parse bssid", arg);
4615 memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
4616 clone_setcallback(wlan_create);
4617}
4618
4619static
4620DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
4621{
4622 const struct ether_addr *ea;
4623
4624 ea = ether_aton(arg);
4625 if (ea == NULL)
4626 errx(1, "%s: cannot parse addres", arg);
4627 memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
4628 params.icp_flags |= IEEE80211_CLONE_MACADDR;
4629 clone_setcallback(wlan_create);
4630}
4631
4632static
4633DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
4634{
4635#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
4636 if (iseq(arg, "sta"))
4637 params.icp_opmode = IEEE80211_M_STA;
4638 else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
4639 params.icp_opmode = IEEE80211_M_AHDEMO;
4640 else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
4641 params.icp_opmode = IEEE80211_M_IBSS;
4642 else if (iseq(arg, "ap") || iseq(arg, "host"))
4643 params.icp_opmode = IEEE80211_M_HOSTAP;
4644 else if (iseq(arg, "wds"))
4645 params.icp_opmode = IEEE80211_M_WDS;
4646 else if (iseq(arg, "monitor"))
4647 params.icp_opmode = IEEE80211_M_MONITOR;
4648 else if (iseq(arg, "tdma")) {
4649 params.icp_opmode = IEEE80211_M_AHDEMO;
4650 params.icp_flags |= IEEE80211_CLONE_TDMA;
4651 } else
4652 errx(1, "Don't know to create %s for %s", arg, name);
4653 clone_setcallback(wlan_create);
4654#undef iseq
4655}
4656
4657static void
4658set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
4659{
4660 /* NB: inverted sense */
4661 if (d)
4662 params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
4663 else
4664 params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
4665 clone_setcallback(wlan_create);
4666}
4667
4668static void
4669set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
4670{
4671 if (d)
4672 params.icp_flags |= IEEE80211_CLONE_BSSID;
4673 else
4674 params.icp_flags &= ~IEEE80211_CLONE_BSSID;
4675 clone_setcallback(wlan_create);
4676}
4677
4678static void
4679set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
4680{
4681 if (d)
4682 params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
4683 else
4684 params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
4685 clone_setcallback(wlan_create);
4686}
4687
4688static struct cmd ieee80211_cmds[] = {
4689 DEF_CMD_ARG("ssid", set80211ssid),
4690 DEF_CMD_ARG("nwid", set80211ssid),
4691 DEF_CMD_ARG("stationname", set80211stationname),
4692 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */
4693 DEF_CMD_ARG("channel", set80211channel),
4694 DEF_CMD_ARG("authmode", set80211authmode),
4695 DEF_CMD_ARG("powersavemode", set80211powersavemode),
4696 DEF_CMD("powersave", 1, set80211powersave),
4697 DEF_CMD("-powersave", 0, set80211powersave),
4698 DEF_CMD_ARG("powersavesleep", set80211powersavesleep),
4699 DEF_CMD_ARG("wepmode", set80211wepmode),
4700 DEF_CMD("wep", 1, set80211wep),
4701 DEF_CMD("-wep", 0, set80211wep),
4702 DEF_CMD_ARG("deftxkey", set80211weptxkey),
4703 DEF_CMD_ARG("weptxkey", set80211weptxkey),
4704 DEF_CMD_ARG("wepkey", set80211wepkey),
4705 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */
4706 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */
4707 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold),
4708 DEF_CMD_ARG("protmode", set80211protmode),
4709 DEF_CMD_ARG("txpower", set80211txpower),
4710 DEF_CMD_ARG("roaming", set80211roaming),
4711 DEF_CMD("wme", 1, set80211wme),
4712 DEF_CMD("-wme", 0, set80211wme),
4713 DEF_CMD("wmm", 1, set80211wme),
4714 DEF_CMD("-wmm", 0, set80211wme),
4715 DEF_CMD("hidessid", 1, set80211hidessid),
4716 DEF_CMD("-hidessid", 0, set80211hidessid),
4717 DEF_CMD("apbridge", 1, set80211apbridge),
4718 DEF_CMD("-apbridge", 0, set80211apbridge),
4719 DEF_CMD_ARG("chanlist", set80211chanlist),
4720 DEF_CMD_ARG("bssid", set80211bssid),
4721 DEF_CMD_ARG("ap", set80211bssid),
4722 DEF_CMD("scan", 0, set80211scan),
4723 DEF_CMD_ARG("list", set80211list),
4724 DEF_CMD_ARG2("cwmin", set80211cwmin),
4725 DEF_CMD_ARG2("cwmax", set80211cwmax),
4726 DEF_CMD_ARG2("aifs", set80211aifs),
4727 DEF_CMD_ARG2("txoplimit", set80211txoplimit),
4728 DEF_CMD_ARG("acm", set80211acm),
4729 DEF_CMD_ARG("-acm", set80211noacm),
4730 DEF_CMD_ARG("ack", set80211ackpolicy),
4731 DEF_CMD_ARG("-ack", set80211noackpolicy),
4732 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin),
4733 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax),
4734 DEF_CMD_ARG2("bss:aifs", set80211bssaifs),
4735 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit),
4736 DEF_CMD_ARG("dtimperiod", set80211dtimperiod),
4737 DEF_CMD_ARG("bintval", set80211bintval),
4738 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd),
4739 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd),
4740 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd),
4741 DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd),
4742 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd),
4743 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd),
4744 DEF_CMD_ARG("mac:add", set80211addmac),
4745 DEF_CMD_ARG("mac:del", set80211delmac),
4746 DEF_CMD_ARG("mac:kick", set80211kickmac),
4747 DEF_CMD("pureg", 1, set80211pureg),
4748 DEF_CMD("-pureg", 0, set80211pureg),
4749 DEF_CMD("ff", 1, set80211fastframes),
4750 DEF_CMD("-ff", 0, set80211fastframes),
4751 DEF_CMD("dturbo", 1, set80211dturbo),
4752 DEF_CMD("-dturbo", 0, set80211dturbo),
4753 DEF_CMD("bgscan", 1, set80211bgscan),
4754 DEF_CMD("-bgscan", 0, set80211bgscan),
4755 DEF_CMD_ARG("bgscanidle", set80211bgscanidle),
4756 DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl),
4757 DEF_CMD_ARG("scanvalid", set80211scanvalid),
4758 DEF_CMD_ARG("roam:rssi", set80211roamrssi),
4759 DEF_CMD_ARG("roam:rate", set80211roamrate),
4760 DEF_CMD_ARG("mcastrate", set80211mcastrate),
4761 DEF_CMD_ARG("ucastrate", set80211ucastrate),
4762 DEF_CMD_ARG("mgtrate", set80211mgtrate),
4763 DEF_CMD_ARG("mgmtrate", set80211mgtrate),
4764 DEF_CMD_ARG("maxretry", set80211maxretry),
4765 DEF_CMD_ARG("fragthreshold", set80211fragthreshold),
4766 DEF_CMD("burst", 1, set80211burst),
4767 DEF_CMD("-burst", 0, set80211burst),
4768 DEF_CMD_ARG("bmiss", set80211bmissthreshold),
4769 DEF_CMD_ARG("bmissthreshold", set80211bmissthreshold),
4770 DEF_CMD("shortgi", 1, set80211shortgi),
4771 DEF_CMD("-shortgi", 0, set80211shortgi),
4772 DEF_CMD("ampdurx", 2, set80211ampdu),
4773 DEF_CMD("-ampdurx", -2, set80211ampdu),
4774 DEF_CMD("ampdutx", 1, set80211ampdu),
4775 DEF_CMD("-ampdutx", -1, set80211ampdu),
4776 DEF_CMD("ampdu", 3, set80211ampdu), /* NB: tx+rx */
4777 DEF_CMD("-ampdu", -3, set80211ampdu),
4778 DEF_CMD_ARG("ampdulimit", set80211ampdulimit),
4779 DEF_CMD_ARG("ampdudensity", set80211ampdudensity),
4780 DEF_CMD("amsdurx", 2, set80211amsdu),
4781 DEF_CMD("-amsdurx", -2, set80211amsdu),
4782 DEF_CMD("amsdutx", 1, set80211amsdu),
4783 DEF_CMD("-amsdutx", -1, set80211amsdu),
4784 DEF_CMD("amsdu", 3, set80211amsdu), /* NB: tx+rx */
4785 DEF_CMD("-amsdu", -3, set80211amsdu),
4786 DEF_CMD_ARG("amsdulimit", set80211amsdulimit),
4787 DEF_CMD("puren", 1, set80211puren),
4788 DEF_CMD("-puren", 0, set80211puren),
4789 DEF_CMD("doth", 1, set80211doth),
4790 DEF_CMD("-doth", 0, set80211doth),
4791 DEF_CMD("dfs", 1, set80211dfs),
4792 DEF_CMD("-dfs", 0, set80211dfs),
4793 DEF_CMD("htcompat", 1, set80211htcompat),
4794 DEF_CMD("-htcompat", 0, set80211htcompat),
4795 DEF_CMD("dwds", 1, set80211dwds),
4796 DEF_CMD("-dwds", 0, set80211dwds),
4797 DEF_CMD("inact", 1, set80211inact),
4798 DEF_CMD("-inact", 0, set80211inact),
4799 DEF_CMD("tsn", 1, set80211tsn),
4800 DEF_CMD("-tsn", 0, set80211tsn),
4801 DEF_CMD_ARG("regdomain", set80211regdomain),
4802 DEF_CMD_ARG("country", set80211country),
4803 DEF_CMD("indoor", 'I', set80211location),
4804 DEF_CMD("-indoor", 'O', set80211location),
4805 DEF_CMD("outdoor", 'O', set80211location),
4806 DEF_CMD("-outdoor", 'I', set80211location),
4807 DEF_CMD("anywhere", ' ', set80211location),
4808 DEF_CMD("ecm", 1, set80211ecm),
4809 DEF_CMD("-ecm", 0, set80211ecm),
4810 DEF_CMD("dotd", 1, set80211dotd),
4811 DEF_CMD("-dotd", 0, set80211dotd),
4812 DEF_CMD_ARG("htprotmode", set80211htprotmode),
4813 DEF_CMD("ht20", 1, set80211htconf),
4814 DEF_CMD("-ht20", 0, set80211htconf),
4815 DEF_CMD("ht40", 3, set80211htconf), /* NB: 20+40 */
4816 DEF_CMD("-ht40", 0, set80211htconf),
4817 DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */
4818 DEF_CMD("-ht", 0, set80211htconf),
4819 DEF_CMD("rifs", 1, set80211rifs),
4820 DEF_CMD("-rifs", 0, set80211rifs),
4821 DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps),
4822 DEF_CMD("smpsdyn", IEEE80211_HTCAP_SMPS_DYNAMIC, set80211smps),
4823 DEF_CMD("-smps", IEEE80211_HTCAP_SMPS_OFF, set80211smps),
4824 /* XXX for testing */
4825 DEF_CMD_ARG("chanswitch", set80211chanswitch),
4826
4827 DEF_CMD_ARG("tdmaslot", set80211tdmaslot),
4828 DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt),
4829 DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen),
4830 DEF_CMD_ARG("tdmabintval", set80211tdmabintval),
4831
4832 /* vap cloning support */
4833 DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr),
4834 DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid),
4835 DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev),
4836 DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode),
4837 DEF_CLONE_CMD("beacons", 1, set80211clone_beacons),
4838 DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons),
4839 DEF_CLONE_CMD("bssid", 1, set80211clone_bssid),
4840 DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid),
4841 DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy),
4842 DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy),
4843};
4844static struct afswtch af_ieee80211 = {
4845 .af_name = "af_ieee80211",
4846 .af_af = AF_UNSPEC,
4847 .af_other_status = ieee80211_status,
4848};
4849
4850static __constructor void
4851ieee80211_ctor(void)
4852{
4853#define N(a) (sizeof(a) / sizeof(a[0]))
4854 int i;
4855
4856 for (i = 0; i < N(ieee80211_cmds); i++)
4857 cmd_register(&ieee80211_cmds[i]);
4858 af_register(&af_ieee80211);
4859#undef N
4860}
1036 last, IEEE80211_CHAN_MAX);
1037 if (first > last)
1038 errx(-1, "void channel range, %u > %u",
1039 first, last);
1040 for (f = first; f <= last; f++)
1041 setbit(chanlist.ic_channels, f);
1042 break;
1043 }
1044 if (tp == NULL)
1045 break;
1046 c = *tp;
1047 while (isspace(c))
1048 tp++;
1049 if (!isdigit(c))
1050 break;
1051 cp = tp;
1052 }
1053 set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
1054}
1055
1056static void
1057set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1058{
1059
1060 if (!isanyarg(val)) {
1061 char *temp;
1062 struct sockaddr_dl sdl;
1063
1064 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1065 if (temp == NULL)
1066 errx(1, "malloc failed");
1067 temp[0] = ':';
1068 strcpy(temp + 1, val);
1069 sdl.sdl_len = sizeof(sdl);
1070 link_addr(temp, &sdl);
1071 free(temp);
1072 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1073 errx(1, "malformed link-level address");
1074 set80211(s, IEEE80211_IOC_BSSID, 0,
1075 IEEE80211_ADDR_LEN, LLADDR(&sdl));
1076 } else {
1077 uint8_t zerobssid[IEEE80211_ADDR_LEN];
1078 memset(zerobssid, 0, sizeof(zerobssid));
1079 set80211(s, IEEE80211_IOC_BSSID, 0,
1080 IEEE80211_ADDR_LEN, zerobssid);
1081 }
1082}
1083
1084static int
1085getac(const char *ac)
1086{
1087 if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
1088 return WME_AC_BE;
1089 if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
1090 return WME_AC_BK;
1091 if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
1092 return WME_AC_VI;
1093 if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
1094 return WME_AC_VO;
1095 errx(1, "unknown wme access class %s", ac);
1096}
1097
1098static
1099DECL_CMD_FUNC2(set80211cwmin, ac, val)
1100{
1101 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1102}
1103
1104static
1105DECL_CMD_FUNC2(set80211cwmax, ac, val)
1106{
1107 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1108}
1109
1110static
1111DECL_CMD_FUNC2(set80211aifs, ac, val)
1112{
1113 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1114}
1115
1116static
1117DECL_CMD_FUNC2(set80211txoplimit, ac, val)
1118{
1119 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1120}
1121
1122static
1123DECL_CMD_FUNC(set80211acm, ac, d)
1124{
1125 set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1126}
1127static
1128DECL_CMD_FUNC(set80211noacm, ac, d)
1129{
1130 set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1131}
1132
1133static
1134DECL_CMD_FUNC(set80211ackpolicy, ac, d)
1135{
1136 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1137}
1138static
1139DECL_CMD_FUNC(set80211noackpolicy, ac, d)
1140{
1141 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1142}
1143
1144static
1145DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1146{
1147 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1148 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1149}
1150
1151static
1152DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1153{
1154 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1155 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1156}
1157
1158static
1159DECL_CMD_FUNC2(set80211bssaifs, ac, val)
1160{
1161 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1162 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1163}
1164
1165static
1166DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1167{
1168 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1169 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1170}
1171
1172static
1173DECL_CMD_FUNC(set80211dtimperiod, val, d)
1174{
1175 set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1176}
1177
1178static
1179DECL_CMD_FUNC(set80211bintval, val, d)
1180{
1181 set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1182}
1183
1184static void
1185set80211macmac(int s, int op, const char *val)
1186{
1187 char *temp;
1188 struct sockaddr_dl sdl;
1189
1190 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1191 if (temp == NULL)
1192 errx(1, "malloc failed");
1193 temp[0] = ':';
1194 strcpy(temp + 1, val);
1195 sdl.sdl_len = sizeof(sdl);
1196 link_addr(temp, &sdl);
1197 free(temp);
1198 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1199 errx(1, "malformed link-level address");
1200 set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1201}
1202
1203static
1204DECL_CMD_FUNC(set80211addmac, val, d)
1205{
1206 set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1207}
1208
1209static
1210DECL_CMD_FUNC(set80211delmac, val, d)
1211{
1212 set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1213}
1214
1215static
1216DECL_CMD_FUNC(set80211kickmac, val, d)
1217{
1218 char *temp;
1219 struct sockaddr_dl sdl;
1220 struct ieee80211req_mlme mlme;
1221
1222 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1223 if (temp == NULL)
1224 errx(1, "malloc failed");
1225 temp[0] = ':';
1226 strcpy(temp + 1, val);
1227 sdl.sdl_len = sizeof(sdl);
1228 link_addr(temp, &sdl);
1229 free(temp);
1230 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1231 errx(1, "malformed link-level address");
1232 memset(&mlme, 0, sizeof(mlme));
1233 mlme.im_op = IEEE80211_MLME_DEAUTH;
1234 mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1235 memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
1236 set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
1237}
1238
1239static
1240DECL_CMD_FUNC(set80211maccmd, val, d)
1241{
1242 set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1243}
1244
1245static void
1246set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
1247{
1248 set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1249}
1250
1251static void
1252set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1253{
1254 set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
1255}
1256
1257static
1258DECL_CMD_FUNC(set80211bgscanidle, val, d)
1259{
1260 set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1261}
1262
1263static
1264DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1265{
1266 set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1267}
1268
1269static
1270DECL_CMD_FUNC(set80211scanvalid, val, d)
1271{
1272 set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1273}
1274
1275/*
1276 * Parse an optional trailing specification of which netbands
1277 * to apply a parameter to. This is basically the same syntax
1278 * as used for channels but you can concatenate to specify
1279 * multiple. For example:
1280 * 14:abg apply to 11a, 11b, and 11g
1281 * 6:ht apply to 11na and 11ng
1282 * We don't make a big effort to catch silly things; this is
1283 * really a convenience mechanism.
1284 */
1285static int
1286getmodeflags(const char *val)
1287{
1288 const char *cp;
1289 int flags;
1290
1291 flags = 0;
1292
1293 cp = strchr(val, ':');
1294 if (cp != NULL) {
1295 for (cp++; isalpha((int) *cp); cp++) {
1296 /* accept mixed case */
1297 int c = *cp;
1298 if (isupper(c))
1299 c = tolower(c);
1300 switch (c) {
1301 case 'a': /* 802.11a */
1302 flags |= IEEE80211_CHAN_A;
1303 break;
1304 case 'b': /* 802.11b */
1305 flags |= IEEE80211_CHAN_B;
1306 break;
1307 case 'g': /* 802.11g */
1308 flags |= IEEE80211_CHAN_G;
1309 break;
1310 case 'h': /* ht = 802.11n */
1311 case 'n': /* 802.11n */
1312 flags |= IEEE80211_CHAN_HT;
1313 break;
1314 case 'd': /* dt = Atheros Dynamic Turbo */
1315 flags |= IEEE80211_CHAN_TURBO;
1316 break;
1317 case 't': /* ht, dt, st, t */
1318 /* dt and unadorned t specify Dynamic Turbo */
1319 if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1320 flags |= IEEE80211_CHAN_TURBO;
1321 break;
1322 case 's': /* st = Atheros Static Turbo */
1323 flags |= IEEE80211_CHAN_STURBO;
1324 break;
1325 default:
1326 errx(-1, "%s: Invalid mode attribute %c\n",
1327 val, *cp);
1328 }
1329 }
1330 }
1331 return flags;
1332}
1333
1334#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
1335#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
1336
1337#define _APPLY(_flags, _base, _param, _v) do { \
1338 if (_flags & IEEE80211_CHAN_HT) { \
1339 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1340 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1341 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1342 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1343 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1344 else \
1345 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1346 } \
1347 if (_flags & IEEE80211_CHAN_TURBO) { \
1348 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1349 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1350 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1351 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1352 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1353 else \
1354 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1355 } \
1356 if (_flags & IEEE80211_CHAN_STURBO) \
1357 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1358 if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1359 _base.params[IEEE80211_MODE_11A]._param = _v; \
1360 if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1361 _base.params[IEEE80211_MODE_11G]._param = _v; \
1362 if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1363 _base.params[IEEE80211_MODE_11B]._param = _v; \
1364} while (0)
1365#define _APPLY1(_flags, _base, _param, _v) do { \
1366 if (_flags & IEEE80211_CHAN_HT) { \
1367 if (_flags & IEEE80211_CHAN_5GHZ) \
1368 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1369 else \
1370 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1371 } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
1372 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1373 else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
1374 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1375 else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
1376 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1377 else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1378 _base.params[IEEE80211_MODE_11A]._param = _v; \
1379 else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1380 _base.params[IEEE80211_MODE_11G]._param = _v; \
1381 else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1382 _base.params[IEEE80211_MODE_11B]._param = _v; \
1383} while (0)
1384#define _APPLY_RATE(_flags, _base, _param, _v) do { \
1385 if (_flags & IEEE80211_CHAN_HT) { \
1386 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1387 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1388 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1389 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1390 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1391 else \
1392 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1393 } \
1394 if (_flags & IEEE80211_CHAN_TURBO) { \
1395 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1396 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1397 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1398 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1399 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1400 else \
1401 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1402 } \
1403 if (_flags & IEEE80211_CHAN_STURBO) \
1404 _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
1405 if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1406 _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
1407 if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1408 _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1409 if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1410 _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1411} while (0)
1412#define _APPLY_RATE1(_flags, _base, _param, _v) do { \
1413 if (_flags & IEEE80211_CHAN_HT) { \
1414 if (_flags & IEEE80211_CHAN_5GHZ) \
1415 _base.params[IEEE80211_MODE_11NA]._param = _v|0x80; \
1416 else \
1417 _base.params[IEEE80211_MODE_11NG]._param = _v|0x80; \
1418 } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
1419 _base.params[IEEE80211_MODE_TURBO_A]._param = 2*_v; \
1420 else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
1421 _base.params[IEEE80211_MODE_TURBO_G]._param = 2*_v; \
1422 else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
1423 _base.params[IEEE80211_MODE_STURBO_A]._param = 2*_v; \
1424 else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1425 _base.params[IEEE80211_MODE_11A]._param = 2*_v; \
1426 else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1427 _base.params[IEEE80211_MODE_11G]._param = (_v == 5 ? 11 : 2*_v);\
1428 else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1429 _base.params[IEEE80211_MODE_11B]._param = (_v == 5 ? 11 : 2*_v);\
1430} while (0)
1431
1432static
1433DECL_CMD_FUNC(set80211roamrssi, val, d)
1434{
1435 double v = atof(val);
1436 int rssi, flags;
1437
1438 rssi = (int) (2*v);
1439 if (rssi != 2*v)
1440 errx(-1, "invalid rssi (must be .5 dBm units)");
1441 flags = getmodeflags(val);
1442 getroam(s);
1443 if (flags == 0) { /* NB: no flags => current channel */
1444 flags = getcurchan(s)->ic_flags;
1445 _APPLY1(flags, roamparams, rssi, rssi);
1446 } else
1447 _APPLY(flags, roamparams, rssi, rssi);
1448 callback_register(setroam_cb, &roamparams);
1449}
1450
1451static
1452DECL_CMD_FUNC(set80211roamrate, val, d)
1453{
1454 int v = atoi(val), flags;
1455
1456 flags = getmodeflags(val);
1457 getroam(s);
1458 if (flags == 0) { /* NB: no flags => current channel */
1459 flags = getcurchan(s)->ic_flags;
1460 _APPLY_RATE1(flags, roamparams, rate, v);
1461 } else
1462 _APPLY_RATE(flags, roamparams, rate, v);
1463 callback_register(setroam_cb, &roamparams);
1464}
1465
1466static
1467DECL_CMD_FUNC(set80211mcastrate, val, d)
1468{
1469 int v = atoi(val), flags;
1470
1471 flags = getmodeflags(val);
1472 gettxparams(s);
1473 if (flags == 0) { /* NB: no flags => current channel */
1474 flags = getcurchan(s)->ic_flags;
1475 _APPLY_RATE1(flags, txparams, mcastrate, v);
1476 } else
1477 _APPLY_RATE(flags, txparams, mcastrate, v);
1478 callback_register(settxparams_cb, &txparams);
1479}
1480
1481static
1482DECL_CMD_FUNC(set80211mgtrate, val, d)
1483{
1484 int v = atoi(val), flags;
1485
1486 flags = getmodeflags(val);
1487 gettxparams(s);
1488 if (flags == 0) { /* NB: no flags => current channel */
1489 flags = getcurchan(s)->ic_flags;
1490 _APPLY_RATE1(flags, txparams, mgmtrate, v);
1491 } else
1492 _APPLY_RATE(flags, txparams, mgmtrate, v);
1493 callback_register(settxparams_cb, &txparams);
1494}
1495
1496static
1497DECL_CMD_FUNC(set80211ucastrate, val, d)
1498{
1499 int v, flags;
1500
1501 gettxparams(s);
1502 flags = getmodeflags(val);
1503 if (isanyarg(val)) {
1504 if (flags == 0) { /* NB: no flags => current channel */
1505 flags = getcurchan(s)->ic_flags;
1506 _APPLY1(flags, txparams, ucastrate,
1507 IEEE80211_FIXED_RATE_NONE);
1508 } else
1509 _APPLY(flags, txparams, ucastrate,
1510 IEEE80211_FIXED_RATE_NONE);
1511 } else {
1512 v = atoi(val);
1513 if (flags == 0) { /* NB: no flags => current channel */
1514 flags = getcurchan(s)->ic_flags;
1515 _APPLY_RATE1(flags, txparams, ucastrate, v);
1516 } else
1517 _APPLY_RATE(flags, txparams, ucastrate, v);
1518 }
1519 callback_register(settxparams_cb, &txparams);
1520}
1521
1522static
1523DECL_CMD_FUNC(set80211maxretry, val, d)
1524{
1525 int v = atoi(val), flags;
1526
1527 flags = getmodeflags(val);
1528 gettxparams(s);
1529 if (flags == 0) { /* NB: no flags => current channel */
1530 flags = getcurchan(s)->ic_flags;
1531 _APPLY1(flags, txparams, maxretry, v);
1532 } else
1533 _APPLY(flags, txparams, maxretry, v);
1534 callback_register(settxparams_cb, &txparams);
1535}
1536#undef _APPLY_RATE
1537#undef _APPLY
1538#undef IEEE80211_CHAN_HTA
1539#undef IEEE80211_CHAN_HTG
1540
1541static
1542DECL_CMD_FUNC(set80211fragthreshold, val, d)
1543{
1544 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1545 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1546}
1547
1548static
1549DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1550{
1551 set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1552 isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1553}
1554
1555static void
1556set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1557{
1558 set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1559}
1560
1561static void
1562set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1563{
1564 set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1565}
1566
1567static void
1568set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1569{
1570 set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1571}
1572
1573static void
1574set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1575{
1576 set80211(s, IEEE80211_IOC_SHORTGI,
1577 d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1578 0, NULL);
1579}
1580
1581static void
1582set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1583{
1584 int ampdu;
1585
1586 if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1587 errx(-1, "cannot get AMPDU setting");
1588 if (d < 0) {
1589 d = -d;
1590 ampdu &= ~d;
1591 } else
1592 ampdu |= d;
1593 set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1594}
1595
1596static
1597DECL_CMD_FUNC(set80211ampdulimit, val, d)
1598{
1599 int v;
1600
1601 switch (atoi(val)) {
1602 case 8:
1603 case 8*1024:
1604 v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1605 break;
1606 case 16:
1607 case 16*1024:
1608 v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1609 break;
1610 case 32:
1611 case 32*1024:
1612 v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1613 break;
1614 case 64:
1615 case 64*1024:
1616 v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1617 break;
1618 default:
1619 errx(-1, "invalid A-MPDU limit %s", val);
1620 }
1621 set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1622}
1623
1624static
1625DECL_CMD_FUNC(set80211ampdudensity, val, d)
1626{
1627 int v;
1628
1629 if (isanyarg(val) || strcasecmp(val, "na") == 0)
1630 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1631 else switch ((int)(atof(val)*4)) {
1632 case 0:
1633 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1634 break;
1635 case 1:
1636 v = IEEE80211_HTCAP_MPDUDENSITY_025;
1637 break;
1638 case 2:
1639 v = IEEE80211_HTCAP_MPDUDENSITY_05;
1640 break;
1641 case 4:
1642 v = IEEE80211_HTCAP_MPDUDENSITY_1;
1643 break;
1644 case 8:
1645 v = IEEE80211_HTCAP_MPDUDENSITY_2;
1646 break;
1647 case 16:
1648 v = IEEE80211_HTCAP_MPDUDENSITY_4;
1649 break;
1650 case 32:
1651 v = IEEE80211_HTCAP_MPDUDENSITY_8;
1652 break;
1653 case 64:
1654 v = IEEE80211_HTCAP_MPDUDENSITY_16;
1655 break;
1656 default:
1657 errx(-1, "invalid A-MPDU density %s", val);
1658 }
1659 set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1660}
1661
1662static void
1663set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1664{
1665 int amsdu;
1666
1667 if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1668 err(-1, "cannot get AMSDU setting");
1669 if (d < 0) {
1670 d = -d;
1671 amsdu &= ~d;
1672 } else
1673 amsdu |= d;
1674 set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1675}
1676
1677static
1678DECL_CMD_FUNC(set80211amsdulimit, val, d)
1679{
1680 set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1681}
1682
1683static void
1684set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1685{
1686 set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1687}
1688
1689static void
1690set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1691{
1692 set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1693}
1694
1695static void
1696set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1697{
1698 set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1699 htconf = d;
1700}
1701
1702static void
1703set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1704{
1705 set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1706}
1707
1708static void
1709set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1710{
1711 set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1712}
1713
1714static void
1715set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1716{
1717 set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1718}
1719
1720static void
1721set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1722{
1723 set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1724}
1725
1726static void
1727set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1728{
1729 set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1730}
1731
1732static void
1733set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1734{
1735 set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1736}
1737
1738static
1739DECL_CMD_FUNC(set80211tdmaslot, val, d)
1740{
1741 set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1742}
1743
1744static
1745DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1746{
1747 set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1748}
1749
1750static
1751DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1752{
1753 set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1754}
1755
1756static
1757DECL_CMD_FUNC(set80211tdmabintval, val, d)
1758{
1759 set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1760}
1761
1762static int
1763regdomain_sort(const void *a, const void *b)
1764{
1765#define CHAN_ALL \
1766 (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1767 const struct ieee80211_channel *ca = a;
1768 const struct ieee80211_channel *cb = b;
1769
1770 return ca->ic_freq == cb->ic_freq ?
1771 (ca->ic_flags & CHAN_ALL) - (cb->ic_flags & CHAN_ALL) :
1772 ca->ic_freq - cb->ic_freq;
1773#undef CHAN_ALL
1774}
1775
1776static const struct ieee80211_channel *
1777chanlookup(const struct ieee80211_channel chans[], int nchans,
1778 int freq, int flags)
1779{
1780 int i;
1781
1782 flags &= IEEE80211_CHAN_ALLTURBO;
1783 for (i = 0; i < nchans; i++) {
1784 const struct ieee80211_channel *c = &chans[i];
1785 if (c->ic_freq == freq &&
1786 (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
1787 return c;
1788 }
1789 return NULL;
1790}
1791
1792static int
1793chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1794{
1795 int i;
1796
1797 for (i = 0; i < nchans; i++) {
1798 const struct ieee80211_channel *c = &chans[i];
1799 if ((c->ic_flags & flags) == flags)
1800 return 1;
1801 }
1802 return 0;
1803}
1804
1805static void
1806regdomain_addchans(struct ieee80211req_chaninfo *ci,
1807 const netband_head *bands,
1808 const struct ieee80211_regdomain *reg,
1809 uint32_t chanFlags,
1810 const struct ieee80211req_chaninfo *avail)
1811{
1812 const struct netband *nb;
1813 const struct freqband *b;
1814 struct ieee80211_channel *c, *prev;
1815 int freq, channelSep, hasHalfChans, hasQuarterChans;
1816
1817 channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
1818 hasHalfChans = chanfind(avail->ic_chans, avail->ic_nchans,
1819 IEEE80211_CHAN_HALF |
1820 (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1821 hasQuarterChans = chanfind(avail->ic_chans, avail->ic_nchans,
1822 IEEE80211_CHAN_QUARTER |
1823 (chanFlags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1824 LIST_FOREACH(nb, bands, next) {
1825 b = nb->band;
1826 if (verbose) {
1827 printf("%s:", __func__);
1828 printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
1829 printb(" bandFlags", nb->flags | b->flags,
1830 IEEE80211_CHAN_BITS);
1831 putchar('\n');
1832 }
1833 prev = NULL;
1834 for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) {
1835 uint32_t flags = nb->flags | b->flags;
1836
1837 /* check if device can operate on this frequency */
1838 /*
1839 * XXX GSM frequency mapping is handled in the kernel
1840 * so we cannot find them in the calibration table;
1841 * just construct the list and the kernel will reject
1842 * if it's wrong.
1843 */
1844 if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL &&
1845 (flags & IEEE80211_CHAN_GSM) == 0) {
1846 if (verbose) {
1847 printf("%u: skip, ", freq);
1848 printb("flags", chanFlags,
1849 IEEE80211_CHAN_BITS);
1850 printf(" not available\n");
1851 }
1852 continue;
1853 }
1854 if ((flags & IEEE80211_CHAN_HALF) && !hasHalfChans) {
1855 if (verbose)
1856 printf("%u: skip, device does not "
1857 "support half-rate channel\n",
1858 freq);
1859 continue;
1860 }
1861 if ((flags & IEEE80211_CHAN_QUARTER) &&
1862 !hasQuarterChans) {
1863 if (verbose)
1864 printf("%u: skip, device does not "
1865 "support quarter-rate channel\n",
1866 freq);
1867 continue;
1868 }
1869 if ((flags & IEEE80211_CHAN_HT20) &&
1870 (chanFlags & IEEE80211_CHAN_HT20) == 0) {
1871 if (verbose)
1872 printf("%u: skip, device does not "
1873 "support HT20 operation\n", freq);
1874 continue;
1875 }
1876 if ((flags & IEEE80211_CHAN_HT40) &&
1877 (chanFlags & IEEE80211_CHAN_HT40) == 0) {
1878 if (verbose)
1879 printf("%u: skip, device does not "
1880 "support HT40 operation\n", freq);
1881 continue;
1882 }
1883 if ((flags & REQ_ECM) && !reg->ecm) {
1884 if (verbose)
1885 printf("%u: skip, ECM channel\n", freq);
1886 continue;
1887 }
1888 if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
1889 if (verbose)
1890 printf("%u: skip, outdoor channel\n", freq);
1891 continue;
1892 }
1893 if ((flags & IEEE80211_CHAN_HT40) &&
1894 prev != NULL && (freq - prev->ic_freq) < channelSep) {
1895 if (verbose)
1896 printf("%u: skip, only %u channel "
1897 "separation, need %d\n", freq,
1898 freq - prev->ic_freq, channelSep);
1899 continue;
1900 }
1901 if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
1902 if (verbose)
1903 printf("%u: skip, channel table full\n",
1904 freq);
1905 break;
1906 }
1907 c = &ci->ic_chans[ci->ic_nchans++];
1908 memset(c, 0, sizeof(*c));
1909 c->ic_freq = freq;
1910 c->ic_flags = chanFlags |
1911 (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40));
1912 if (c->ic_flags & IEEE80211_CHAN_DFS)
1913 c->ic_maxregpower = nb->maxPowerDFS;
1914 else
1915 c->ic_maxregpower = nb->maxPower;
1916 if (verbose) {
1917 printf("[%3d] add freq %u ",
1918 ci->ic_nchans-1, c->ic_freq);
1919 printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
1920 printf(" power %u\n", c->ic_maxregpower);
1921 }
1922 /* NB: kernel fills in other fields */
1923 prev = c;
1924 }
1925 }
1926}
1927
1928static void
1929regdomain_makechannels(
1930 struct ieee80211_regdomain_req *req,
1931 const struct ieee80211_devcaps_req *dc)
1932{
1933 struct regdata *rdp = getregdata();
1934 const struct country *cc;
1935 const struct ieee80211_regdomain *reg = &req->rd;
1936 struct ieee80211req_chaninfo *ci = &req->chaninfo;
1937 const struct regdomain *rd;
1938
1939 /*
1940 * Locate construction table for new channel list. We treat
1941 * the regdomain/SKU as definitive so a country can be in
1942 * multiple with different properties (e.g. US in FCC+FCC3).
1943 * If no regdomain is specified then we fallback on the country
1944 * code to find the associated regdomain since countries always
1945 * belong to at least one regdomain.
1946 */
1947 if (reg->regdomain == 0) {
1948 cc = lib80211_country_findbycc(rdp, reg->country);
1949 if (cc == NULL)
1950 errx(1, "internal error, country %d not found",
1951 reg->country);
1952 rd = cc->rd;
1953 } else
1954 rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
1955 if (rd == NULL)
1956 errx(1, "internal error, regdomain %d not found",
1957 reg->regdomain);
1958 if (rd->sku != SKU_DEBUG) {
1959 /*
1960 * regdomain_addchans incrememnts the channel count for
1961 * each channel it adds so initialize ic_nchans to zero.
1962 * Note that we know we have enough space to hold all possible
1963 * channels because the devcaps list size was used to
1964 * allocate our request.
1965 */
1966 ci->ic_nchans = 0;
1967 if (!LIST_EMPTY(&rd->bands_11b))
1968 regdomain_addchans(ci, &rd->bands_11b, reg,
1969 IEEE80211_CHAN_B, &dc->dc_chaninfo);
1970 if (!LIST_EMPTY(&rd->bands_11g))
1971 regdomain_addchans(ci, &rd->bands_11g, reg,
1972 IEEE80211_CHAN_G, &dc->dc_chaninfo);
1973 if (!LIST_EMPTY(&rd->bands_11a))
1974 regdomain_addchans(ci, &rd->bands_11a, reg,
1975 IEEE80211_CHAN_A, &dc->dc_chaninfo);
1976 if (!LIST_EMPTY(&rd->bands_11na)) {
1977 regdomain_addchans(ci, &rd->bands_11na, reg,
1978 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
1979 &dc->dc_chaninfo);
1980 regdomain_addchans(ci, &rd->bands_11na, reg,
1981 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
1982 &dc->dc_chaninfo);
1983 regdomain_addchans(ci, &rd->bands_11na, reg,
1984 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
1985 &dc->dc_chaninfo);
1986 }
1987 if (!LIST_EMPTY(&rd->bands_11ng)) {
1988 regdomain_addchans(ci, &rd->bands_11ng, reg,
1989 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
1990 &dc->dc_chaninfo);
1991 regdomain_addchans(ci, &rd->bands_11ng, reg,
1992 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
1993 &dc->dc_chaninfo);
1994 regdomain_addchans(ci, &rd->bands_11ng, reg,
1995 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
1996 &dc->dc_chaninfo);
1997 }
1998 qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
1999 regdomain_sort);
2000 } else
2001 memcpy(ci, &dc->dc_chaninfo,
2002 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2003}
2004
2005static void
2006list_countries(void)
2007{
2008 struct regdata *rdp = getregdata();
2009 const struct country *cp;
2010 const struct regdomain *dp;
2011 int i;
2012
2013 i = 0;
2014 printf("\nCountry codes:\n");
2015 LIST_FOREACH(cp, &rdp->countries, next) {
2016 printf("%2s %-15.15s%s", cp->isoname,
2017 cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2018 i++;
2019 }
2020 i = 0;
2021 printf("\nRegulatory domains:\n");
2022 LIST_FOREACH(dp, &rdp->domains, next) {
2023 printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2024 i++;
2025 }
2026 printf("\n");
2027}
2028
2029static void
2030defaultcountry(const struct regdomain *rd)
2031{
2032 struct regdata *rdp = getregdata();
2033 const struct country *cc;
2034
2035 cc = lib80211_country_findbycc(rdp, rd->cc->code);
2036 if (cc == NULL)
2037 errx(1, "internal error, ISO country code %d not "
2038 "defined for regdomain %s", rd->cc->code, rd->name);
2039 regdomain.country = cc->code;
2040 regdomain.isocc[0] = cc->isoname[0];
2041 regdomain.isocc[1] = cc->isoname[1];
2042}
2043
2044static
2045DECL_CMD_FUNC(set80211regdomain, val, d)
2046{
2047 struct regdata *rdp = getregdata();
2048 const struct regdomain *rd;
2049
2050 rd = lib80211_regdomain_findbyname(rdp, val);
2051 if (rd == NULL) {
2052 char *eptr;
2053 long sku = strtol(val, &eptr, 0);
2054
2055 if (eptr != val)
2056 rd = lib80211_regdomain_findbysku(rdp, sku);
2057 if (eptr == val || rd == NULL)
2058 errx(1, "unknown regdomain %s", val);
2059 }
2060 getregdomain(s);
2061 regdomain.regdomain = rd->sku;
2062 if (regdomain.country == 0 && rd->cc != NULL) {
2063 /*
2064 * No country code setup and there's a default
2065 * one for this regdomain fill it in.
2066 */
2067 defaultcountry(rd);
2068 }
2069 callback_register(setregdomain_cb, &regdomain);
2070}
2071
2072static
2073DECL_CMD_FUNC(set80211country, val, d)
2074{
2075 struct regdata *rdp = getregdata();
2076 const struct country *cc;
2077
2078 cc = lib80211_country_findbyname(rdp, val);
2079 if (cc == NULL) {
2080 char *eptr;
2081 long code = strtol(val, &eptr, 0);
2082
2083 if (eptr != val)
2084 cc = lib80211_country_findbycc(rdp, code);
2085 if (eptr == val || cc == NULL)
2086 errx(1, "unknown ISO country code %s", val);
2087 }
2088 getregdomain(s);
2089 regdomain.regdomain = cc->rd->sku;
2090 regdomain.country = cc->code;
2091 regdomain.isocc[0] = cc->isoname[0];
2092 regdomain.isocc[1] = cc->isoname[1];
2093 callback_register(setregdomain_cb, &regdomain);
2094}
2095
2096static void
2097set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2098{
2099 getregdomain(s);
2100 regdomain.location = d;
2101 callback_register(setregdomain_cb, &regdomain);
2102}
2103
2104static void
2105set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2106{
2107 getregdomain(s);
2108 regdomain.ecm = d;
2109 callback_register(setregdomain_cb, &regdomain);
2110}
2111
2112static void
2113LINE_INIT(char c)
2114{
2115 spacer = c;
2116 if (c == '\t')
2117 col = 8;
2118 else
2119 col = 1;
2120}
2121
2122static void
2123LINE_BREAK(void)
2124{
2125 if (spacer != '\t') {
2126 printf("\n");
2127 spacer = '\t';
2128 }
2129 col = 8; /* 8-col tab */
2130}
2131
2132static void
2133LINE_CHECK(const char *fmt, ...)
2134{
2135 char buf[80];
2136 va_list ap;
2137 int n;
2138
2139 va_start(ap, fmt);
2140 n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2141 va_end(ap);
2142 col += 1+n;
2143 if (col > MAXCOL) {
2144 LINE_BREAK();
2145 col += n;
2146 }
2147 buf[0] = spacer;
2148 printf("%s", buf);
2149 spacer = ' ';
2150}
2151
2152static int
2153getmaxrate(const uint8_t rates[15], uint8_t nrates)
2154{
2155 int i, maxrate = -1;
2156
2157 for (i = 0; i < nrates; i++) {
2158 int rate = rates[i] & IEEE80211_RATE_VAL;
2159 if (rate > maxrate)
2160 maxrate = rate;
2161 }
2162 return maxrate / 2;
2163}
2164
2165static const char *
2166getcaps(int capinfo)
2167{
2168 static char capstring[32];
2169 char *cp = capstring;
2170
2171 if (capinfo & IEEE80211_CAPINFO_ESS)
2172 *cp++ = 'E';
2173 if (capinfo & IEEE80211_CAPINFO_IBSS)
2174 *cp++ = 'I';
2175 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2176 *cp++ = 'c';
2177 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2178 *cp++ = 'C';
2179 if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2180 *cp++ = 'P';
2181 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2182 *cp++ = 'S';
2183 if (capinfo & IEEE80211_CAPINFO_PBCC)
2184 *cp++ = 'B';
2185 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2186 *cp++ = 'A';
2187 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2188 *cp++ = 's';
2189 if (capinfo & IEEE80211_CAPINFO_RSN)
2190 *cp++ = 'R';
2191 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2192 *cp++ = 'D';
2193 *cp = '\0';
2194 return capstring;
2195}
2196
2197static const char *
2198getflags(int flags)
2199{
2200 static char flagstring[32];
2201 char *cp = flagstring;
2202
2203 if (flags & IEEE80211_NODE_AUTH)
2204 *cp++ = 'A';
2205 if (flags & IEEE80211_NODE_QOS)
2206 *cp++ = 'Q';
2207 if (flags & IEEE80211_NODE_ERP)
2208 *cp++ = 'E';
2209 if (flags & IEEE80211_NODE_PWR_MGT)
2210 *cp++ = 'P';
2211 if (flags & IEEE80211_NODE_HT) {
2212 *cp++ = 'H';
2213 if (flags & IEEE80211_NODE_HTCOMPAT)
2214 *cp++ = '+';
2215 }
2216 if (flags & IEEE80211_NODE_WPS)
2217 *cp++ = 'W';
2218 if (flags & IEEE80211_NODE_TSN)
2219 *cp++ = 'N';
2220 if (flags & IEEE80211_NODE_AMPDU_TX)
2221 *cp++ = 'T';
2222 if (flags & IEEE80211_NODE_AMPDU_RX)
2223 *cp++ = 'R';
2224 if (flags & IEEE80211_NODE_MIMO_PS) {
2225 *cp++ = 'M';
2226 if (flags & IEEE80211_NODE_MIMO_RTS)
2227 *cp++ = '+';
2228 }
2229 if (flags & IEEE80211_NODE_RIFS)
2230 *cp++ = 'I';
2231 *cp = '\0';
2232 return flagstring;
2233}
2234
2235static void
2236printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2237{
2238 printf("%s", tag);
2239 if (verbose) {
2240 maxlen -= strlen(tag)+2;
2241 if (2*ielen > maxlen)
2242 maxlen--;
2243 printf("<");
2244 for (; ielen > 0; ie++, ielen--) {
2245 if (maxlen-- <= 0)
2246 break;
2247 printf("%02x", *ie);
2248 }
2249 if (ielen != 0)
2250 printf("-");
2251 printf(">");
2252 }
2253}
2254
2255#define LE_READ_2(p) \
2256 ((u_int16_t) \
2257 ((((const u_int8_t *)(p))[0] ) | \
2258 (((const u_int8_t *)(p))[1] << 8)))
2259#define LE_READ_4(p) \
2260 ((u_int32_t) \
2261 ((((const u_int8_t *)(p))[0] ) | \
2262 (((const u_int8_t *)(p))[1] << 8) | \
2263 (((const u_int8_t *)(p))[2] << 16) | \
2264 (((const u_int8_t *)(p))[3] << 24)))
2265
2266/*
2267 * NB: The decoding routines assume a properly formatted ie
2268 * which should be safe as the kernel only retains them
2269 * if they parse ok.
2270 */
2271
2272static void
2273printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2274{
2275#define MS(_v, _f) (((_v) & _f) >> _f##_S)
2276 static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2277 const struct ieee80211_wme_param *wme =
2278 (const struct ieee80211_wme_param *) ie;
2279 int i;
2280
2281 printf("%s", tag);
2282 if (!verbose)
2283 return;
2284 printf("<qosinfo 0x%x", wme->param_qosInfo);
2285 ie += offsetof(struct ieee80211_wme_param, params_acParams);
2286 for (i = 0; i < WME_NUM_AC; i++) {
2287 const struct ieee80211_wme_acparams *ac =
2288 &wme->params_acParams[i];
2289
2290 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2291 , acnames[i]
2292 , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2293 , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2294 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2295 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2296 , LE_READ_2(&ac->acp_txop)
2297 );
2298 }
2299 printf(">");
2300#undef MS
2301}
2302
2303static void
2304printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2305{
2306 printf("%s", tag);
2307 if (verbose) {
2308 const struct ieee80211_wme_info *wme =
2309 (const struct ieee80211_wme_info *) ie;
2310 printf("<version 0x%x info 0x%x>",
2311 wme->wme_version, wme->wme_info);
2312 }
2313}
2314
2315static void
2316printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2317{
2318 printf("%s", tag);
2319 if (verbose) {
2320 const struct ieee80211_ie_htcap *htcap =
2321 (const struct ieee80211_ie_htcap *) ie;
2322 const char *sep;
2323 int i, j;
2324
2325 printf("<cap 0x%x param 0x%x",
2326 LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2327 printf(" mcsset[");
2328 sep = "";
2329 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2330 if (isset(htcap->hc_mcsset, i)) {
2331 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2332 if (isclr(htcap->hc_mcsset, j))
2333 break;
2334 j--;
2335 if (i == j)
2336 printf("%s%u", sep, i);
2337 else
2338 printf("%s%u-%u", sep, i, j);
2339 i += j-i;
2340 sep = ",";
2341 }
2342 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2343 LE_READ_2(&htcap->hc_extcap),
2344 LE_READ_4(&htcap->hc_txbf),
2345 htcap->hc_antenna);
2346 }
2347}
2348
2349static void
2350printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2351{
2352 printf("%s", tag);
2353 if (verbose) {
2354 const struct ieee80211_ie_htinfo *htinfo =
2355 (const struct ieee80211_ie_htinfo *) ie;
2356 const char *sep;
2357 int i, j;
2358
2359 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2360 htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2361 LE_READ_2(&htinfo->hi_byte45));
2362 printf(" basicmcs[");
2363 sep = "";
2364 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2365 if (isset(htinfo->hi_basicmcsset, i)) {
2366 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2367 if (isclr(htinfo->hi_basicmcsset, j))
2368 break;
2369 j--;
2370 if (i == j)
2371 printf("%s%u", sep, i);
2372 else
2373 printf("%s%u-%u", sep, i, j);
2374 i += j-i;
2375 sep = ",";
2376 }
2377 printf("]>");
2378 }
2379}
2380
2381static void
2382printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2383{
2384
2385 printf("%s", tag);
2386 if (verbose) {
2387 const struct ieee80211_ath_ie *ath =
2388 (const struct ieee80211_ath_ie *)ie;
2389
2390 printf("<");
2391 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2392 printf("DTURBO,");
2393 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2394 printf("COMP,");
2395 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2396 printf("FF,");
2397 if (ath->ath_capability & ATHEROS_CAP_XR)
2398 printf("XR,");
2399 if (ath->ath_capability & ATHEROS_CAP_AR)
2400 printf("AR,");
2401 if (ath->ath_capability & ATHEROS_CAP_BURST)
2402 printf("BURST,");
2403 if (ath->ath_capability & ATHEROS_CAP_WME)
2404 printf("WME,");
2405 if (ath->ath_capability & ATHEROS_CAP_BOOST)
2406 printf("BOOST,");
2407 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2408 }
2409}
2410
2411static const char *
2412wpa_cipher(const u_int8_t *sel)
2413{
2414#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2415 u_int32_t w = LE_READ_4(sel);
2416
2417 switch (w) {
2418 case WPA_SEL(WPA_CSE_NULL):
2419 return "NONE";
2420 case WPA_SEL(WPA_CSE_WEP40):
2421 return "WEP40";
2422 case WPA_SEL(WPA_CSE_WEP104):
2423 return "WEP104";
2424 case WPA_SEL(WPA_CSE_TKIP):
2425 return "TKIP";
2426 case WPA_SEL(WPA_CSE_CCMP):
2427 return "AES-CCMP";
2428 }
2429 return "?"; /* NB: so 1<< is discarded */
2430#undef WPA_SEL
2431}
2432
2433static const char *
2434wpa_keymgmt(const u_int8_t *sel)
2435{
2436#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2437 u_int32_t w = LE_READ_4(sel);
2438
2439 switch (w) {
2440 case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2441 return "8021X-UNSPEC";
2442 case WPA_SEL(WPA_ASE_8021X_PSK):
2443 return "8021X-PSK";
2444 case WPA_SEL(WPA_ASE_NONE):
2445 return "NONE";
2446 }
2447 return "?";
2448#undef WPA_SEL
2449}
2450
2451static void
2452printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2453{
2454 u_int8_t len = ie[1];
2455
2456 printf("%s", tag);
2457 if (verbose) {
2458 const char *sep;
2459 int n;
2460
2461 ie += 6, len -= 4; /* NB: len is payload only */
2462
2463 printf("<v%u", LE_READ_2(ie));
2464 ie += 2, len -= 2;
2465
2466 printf(" mc:%s", wpa_cipher(ie));
2467 ie += 4, len -= 4;
2468
2469 /* unicast ciphers */
2470 n = LE_READ_2(ie);
2471 ie += 2, len -= 2;
2472 sep = " uc:";
2473 for (; n > 0; n--) {
2474 printf("%s%s", sep, wpa_cipher(ie));
2475 ie += 4, len -= 4;
2476 sep = "+";
2477 }
2478
2479 /* key management algorithms */
2480 n = LE_READ_2(ie);
2481 ie += 2, len -= 2;
2482 sep = " km:";
2483 for (; n > 0; n--) {
2484 printf("%s%s", sep, wpa_keymgmt(ie));
2485 ie += 4, len -= 4;
2486 sep = "+";
2487 }
2488
2489 if (len > 2) /* optional capabilities */
2490 printf(", caps 0x%x", LE_READ_2(ie));
2491 printf(">");
2492 }
2493}
2494
2495static const char *
2496rsn_cipher(const u_int8_t *sel)
2497{
2498#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2499 u_int32_t w = LE_READ_4(sel);
2500
2501 switch (w) {
2502 case RSN_SEL(RSN_CSE_NULL):
2503 return "NONE";
2504 case RSN_SEL(RSN_CSE_WEP40):
2505 return "WEP40";
2506 case RSN_SEL(RSN_CSE_WEP104):
2507 return "WEP104";
2508 case RSN_SEL(RSN_CSE_TKIP):
2509 return "TKIP";
2510 case RSN_SEL(RSN_CSE_CCMP):
2511 return "AES-CCMP";
2512 case RSN_SEL(RSN_CSE_WRAP):
2513 return "AES-OCB";
2514 }
2515 return "?";
2516#undef WPA_SEL
2517}
2518
2519static const char *
2520rsn_keymgmt(const u_int8_t *sel)
2521{
2522#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2523 u_int32_t w = LE_READ_4(sel);
2524
2525 switch (w) {
2526 case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2527 return "8021X-UNSPEC";
2528 case RSN_SEL(RSN_ASE_8021X_PSK):
2529 return "8021X-PSK";
2530 case RSN_SEL(RSN_ASE_NONE):
2531 return "NONE";
2532 }
2533 return "?";
2534#undef RSN_SEL
2535}
2536
2537static void
2538printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2539{
2540 printf("%s", tag);
2541 if (verbose) {
2542 const char *sep;
2543 int n;
2544
2545 ie += 2, ielen -= 2;
2546
2547 printf("<v%u", LE_READ_2(ie));
2548 ie += 2, ielen -= 2;
2549
2550 printf(" mc:%s", rsn_cipher(ie));
2551 ie += 4, ielen -= 4;
2552
2553 /* unicast ciphers */
2554 n = LE_READ_2(ie);
2555 ie += 2, ielen -= 2;
2556 sep = " uc:";
2557 for (; n > 0; n--) {
2558 printf("%s%s", sep, rsn_cipher(ie));
2559 ie += 4, ielen -= 4;
2560 sep = "+";
2561 }
2562
2563 /* key management algorithms */
2564 n = LE_READ_2(ie);
2565 ie += 2, ielen -= 2;
2566 sep = " km:";
2567 for (; n > 0; n--) {
2568 printf("%s%s", sep, rsn_keymgmt(ie));
2569 ie += 4, ielen -= 4;
2570 sep = "+";
2571 }
2572
2573 if (ielen > 2) /* optional capabilities */
2574 printf(", caps 0x%x", LE_READ_2(ie));
2575 /* XXXPMKID */
2576 printf(">");
2577 }
2578}
2579
2580/* XXX move to a public include file */
2581#define IEEE80211_WPS_DEV_PASS_ID 0x1012
2582#define IEEE80211_WPS_SELECTED_REG 0x1041
2583#define IEEE80211_WPS_SETUP_STATE 0x1044
2584#define IEEE80211_WPS_UUID_E 0x1047
2585#define IEEE80211_WPS_VERSION 0x104a
2586
2587#define BE_READ_2(p) \
2588 ((u_int16_t) \
2589 ((((const u_int8_t *)(p))[1] ) | \
2590 (((const u_int8_t *)(p))[0] << 8)))
2591
2592static void
2593printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2594{
2595#define N(a) (sizeof(a) / sizeof(a[0]))
2596 u_int8_t len = ie[1];
2597
2598 printf("%s", tag);
2599 if (verbose) {
2600 static const char *dev_pass_id[] = {
2601 "D", /* Default (PIN) */
2602 "U", /* User-specified */
2603 "M", /* Machine-specified */
2604 "K", /* Rekey */
2605 "P", /* PushButton */
2606 "R" /* Registrar-specified */
2607 };
2608 int n;
2609
2610 ie +=6, len -= 4; /* NB: len is payload only */
2611
2612 /* WPS IE in Beacon and Probe Resp frames have different fields */
2613 printf("<");
2614 while (len) {
2615 uint16_t tlv_type = BE_READ_2(ie);
2616 uint16_t tlv_len = BE_READ_2(ie + 2);
2617
2618 ie += 4, len -= 4;
2619
2620 switch (tlv_type) {
2621 case IEEE80211_WPS_VERSION:
2622 printf("v:%d.%d", *ie >> 4, *ie & 0xf);
2623 break;
2624 case IEEE80211_WPS_SETUP_STATE:
2625 /* Only 1 and 2 are valid */
2626 if (*ie == 0 || *ie >= 3)
2627 printf(" state:B");
2628 else
2629 printf(" st:%s", *ie == 1 ? "N" : "C");
2630 break;
2631 case IEEE80211_WPS_SELECTED_REG:
2632 printf(" sel:%s", *ie ? "T" : "F");
2633 break;
2634 case IEEE80211_WPS_DEV_PASS_ID:
2635 n = LE_READ_2(ie);
2636 if (n < N(dev_pass_id))
2637 printf(" dpi:%s", dev_pass_id[n]);
2638 break;
2639 case IEEE80211_WPS_UUID_E:
2640 printf(" uuid-e:");
2641 for (n = 0; n < (tlv_len - 1); n++)
2642 printf("%02x-", ie[n]);
2643 printf("%02x", ie[n]);
2644 break;
2645 }
2646 ie += tlv_len, len -= tlv_len;
2647 }
2648 printf(">");
2649 }
2650#undef N
2651}
2652
2653static void
2654printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2655{
2656 printf("%s", tag);
2657 if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
2658 const struct ieee80211_tdma_param *tdma =
2659 (const struct ieee80211_tdma_param *) ie;
2660
2661 /* XXX tstamp */
2662 printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
2663 tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
2664 LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
2665 tdma->tdma_inuse[0]);
2666 }
2667}
2668
2669/*
2670 * Copy the ssid string contents into buf, truncating to fit. If the
2671 * ssid is entirely printable then just copy intact. Otherwise convert
2672 * to hexadecimal. If the result is truncated then replace the last
2673 * three characters with "...".
2674 */
2675static int
2676copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
2677{
2678 const u_int8_t *p;
2679 size_t maxlen;
2680 int i;
2681
2682 if (essid_len > bufsize)
2683 maxlen = bufsize;
2684 else
2685 maxlen = essid_len;
2686 /* determine printable or not */
2687 for (i = 0, p = essid; i < maxlen; i++, p++) {
2688 if (*p < ' ' || *p > 0x7e)
2689 break;
2690 }
2691 if (i != maxlen) { /* not printable, print as hex */
2692 if (bufsize < 3)
2693 return 0;
2694 strlcpy(buf, "0x", bufsize);
2695 bufsize -= 2;
2696 p = essid;
2697 for (i = 0; i < maxlen && bufsize >= 2; i++) {
2698 sprintf(&buf[2+2*i], "%02x", p[i]);
2699 bufsize -= 2;
2700 }
2701 if (i != essid_len)
2702 memcpy(&buf[2+2*i-3], "...", 3);
2703 } else { /* printable, truncate as needed */
2704 memcpy(buf, essid, maxlen);
2705 if (maxlen != essid_len)
2706 memcpy(&buf[maxlen-3], "...", 3);
2707 }
2708 return maxlen;
2709}
2710
2711static void
2712printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2713{
2714 char ssid[2*IEEE80211_NWID_LEN+1];
2715
2716 printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
2717}
2718
2719static void
2720printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2721{
2722 const char *sep;
2723 int i;
2724
2725 printf("%s", tag);
2726 sep = "<";
2727 for (i = 2; i < ielen; i++) {
2728 printf("%s%s%d", sep,
2729 ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
2730 ie[i] & IEEE80211_RATE_VAL);
2731 sep = ",";
2732 }
2733 printf(">");
2734}
2735
2736static void
2737printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2738{
2739 const struct ieee80211_country_ie *cie =
2740 (const struct ieee80211_country_ie *) ie;
2741 int i, nbands, schan, nchan;
2742
2743 printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
2744 nbands = (cie->len - 3) / sizeof(cie->band[0]);
2745 for (i = 0; i < nbands; i++) {
2746 schan = cie->band[i].schan;
2747 nchan = cie->band[i].nchan;
2748 if (nchan != 1)
2749 printf(" %u-%u,%u", schan, schan + nchan-1,
2750 cie->band[i].maxtxpwr);
2751 else
2752 printf(" %u,%u", schan, cie->band[i].maxtxpwr);
2753 }
2754 printf(">");
2755}
2756
2757/* unaligned little endian access */
2758#define LE_READ_4(p) \
2759 ((u_int32_t) \
2760 ((((const u_int8_t *)(p))[0] ) | \
2761 (((const u_int8_t *)(p))[1] << 8) | \
2762 (((const u_int8_t *)(p))[2] << 16) | \
2763 (((const u_int8_t *)(p))[3] << 24)))
2764
2765static __inline int
2766iswpaoui(const u_int8_t *frm)
2767{
2768 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
2769}
2770
2771static __inline int
2772iswmeinfo(const u_int8_t *frm)
2773{
2774 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2775 frm[6] == WME_INFO_OUI_SUBTYPE;
2776}
2777
2778static __inline int
2779iswmeparam(const u_int8_t *frm)
2780{
2781 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
2782 frm[6] == WME_PARAM_OUI_SUBTYPE;
2783}
2784
2785static __inline int
2786isatherosoui(const u_int8_t *frm)
2787{
2788 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
2789}
2790
2791static __inline int
2792istdmaoui(const uint8_t *frm)
2793{
2794 return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
2795}
2796
2797static __inline int
2798iswpsoui(const uint8_t *frm)
2799{
2800 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
2801}
2802
2803static const char *
2804iename(int elemid)
2805{
2806 switch (elemid) {
2807 case IEEE80211_ELEMID_FHPARMS: return " FHPARMS";
2808 case IEEE80211_ELEMID_CFPARMS: return " CFPARMS";
2809 case IEEE80211_ELEMID_TIM: return " TIM";
2810 case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
2811 case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
2812 case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
2813 case IEEE80211_ELEMID_PWRCAP: return " PWRCAP";
2814 case IEEE80211_ELEMID_TPCREQ: return " TPCREQ";
2815 case IEEE80211_ELEMID_TPCREP: return " TPCREP";
2816 case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
2817 case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
2818 case IEEE80211_ELEMID_MEASREQ: return " MEASREQ";
2819 case IEEE80211_ELEMID_MEASREP: return " MEASREP";
2820 case IEEE80211_ELEMID_QUIET: return " QUIET";
2821 case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS";
2822 case IEEE80211_ELEMID_TPC: return " TPC";
2823 case IEEE80211_ELEMID_CCKM: return " CCKM";
2824 }
2825 return " ???";
2826}
2827
2828static void
2829printies(const u_int8_t *vp, int ielen, int maxcols)
2830{
2831 while (ielen > 0) {
2832 switch (vp[0]) {
2833 case IEEE80211_ELEMID_SSID:
2834 if (verbose)
2835 printssid(" SSID", vp, 2+vp[1], maxcols);
2836 break;
2837 case IEEE80211_ELEMID_RATES:
2838 case IEEE80211_ELEMID_XRATES:
2839 if (verbose)
2840 printrates(vp[0] == IEEE80211_ELEMID_RATES ?
2841 " RATES" : " XRATES", vp, 2+vp[1], maxcols);
2842 break;
2843 case IEEE80211_ELEMID_DSPARMS:
2844 if (verbose)
2845 printf(" DSPARMS<%u>", vp[2]);
2846 break;
2847 case IEEE80211_ELEMID_COUNTRY:
2848 if (verbose)
2849 printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
2850 break;
2851 case IEEE80211_ELEMID_ERP:
2852 if (verbose)
2853 printf(" ERP<0x%x>", vp[2]);
2854 break;
2855 case IEEE80211_ELEMID_VENDOR:
2856 if (iswpaoui(vp))
2857 printwpaie(" WPA", vp, 2+vp[1], maxcols);
2858 else if (iswmeinfo(vp))
2859 printwmeinfo(" WME", vp, 2+vp[1], maxcols);
2860 else if (iswmeparam(vp))
2861 printwmeparam(" WME", vp, 2+vp[1], maxcols);
2862 else if (isatherosoui(vp))
2863 printathie(" ATH", vp, 2+vp[1], maxcols);
2864 else if (iswpsoui(vp))
2865 printwpsie(" WPS", vp, 2+vp[1], maxcols);
2866 else if (istdmaoui(vp))
2867 printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
2868 else if (verbose)
2869 printie(" VEN", vp, 2+vp[1], maxcols);
2870 break;
2871 case IEEE80211_ELEMID_RSN:
2872 printrsnie(" RSN", vp, 2+vp[1], maxcols);
2873 break;
2874 case IEEE80211_ELEMID_HTCAP:
2875 printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
2876 break;
2877 case IEEE80211_ELEMID_HTINFO:
2878 if (verbose)
2879 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
2880 break;
2881 default:
2882 if (verbose)
2883 printie(iename(vp[0]), vp, 2+vp[1], maxcols);
2884 break;
2885 }
2886 ielen -= 2+vp[1];
2887 vp += 2+vp[1];
2888 }
2889}
2890
2891static void
2892printmimo(const struct ieee80211_mimo_info *mi)
2893{
2894 /* NB: don't muddy display unless there's something to show */
2895 if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
2896 /* XXX ignore EVM for now */
2897 printf(" (rssi %d:%d:%d nf %d:%d:%d)",
2898 mi->rssi[0], mi->rssi[1], mi->rssi[2],
2899 mi->noise[0], mi->noise[1], mi->noise[2]);
2900 }
2901}
2902
2903static void
2904list_scan(int s)
2905{
2906 uint8_t buf[24*1024];
2907 char ssid[IEEE80211_NWID_LEN+1];
2908 const uint8_t *cp;
2909 int len, ssidmax;
2910
2911 if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
2912 errx(1, "unable to get scan results");
2913 if (len < sizeof(struct ieee80211req_scan_result))
2914 return;
2915
2916 getchaninfo(s);
2917
2918 ssidmax = verbose ? IEEE80211_NWID_LEN : 14;
2919 printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n"
2920 , ssidmax, ssidmax, "SSID"
2921 , "BSSID"
2922 , "CHAN"
2923 , "RATE"
2924 , " S:N"
2925 , "INT"
2926 , "CAPS"
2927 );
2928 cp = buf;
2929 do {
2930 const struct ieee80211req_scan_result *sr;
2931 const uint8_t *vp;
2932
2933 sr = (const struct ieee80211req_scan_result *) cp;
2934 vp = cp + sr->isr_ie_off;
2935 printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s"
2936 , ssidmax
2937 , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
2938 , ssid
2939 , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
2940 , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
2941 , getmaxrate(sr->isr_rates, sr->isr_nrates)
2942 , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
2943 , sr->isr_intval
2944 , getcaps(sr->isr_capinfo)
2945 );
2946 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
2947 printf("\n");
2948 cp += sr->isr_len, len -= sr->isr_len;
2949 } while (len >= sizeof(struct ieee80211req_scan_result));
2950}
2951
2952#ifdef __FreeBSD__
2953#include <net80211/ieee80211_freebsd.h>
2954#endif
2955#ifdef __NetBSD__
2956#include <net80211/ieee80211_netbsd.h>
2957#endif
2958
2959static void
2960scan_and_wait(int s)
2961{
2962 struct ieee80211_scan_req sr;
2963 struct ieee80211req ireq;
2964 int sroute;
2965
2966 sroute = socket(PF_ROUTE, SOCK_RAW, 0);
2967 if (sroute < 0) {
2968 perror("socket(PF_ROUTE,SOCK_RAW)");
2969 return;
2970 }
2971 (void) memset(&ireq, 0, sizeof(ireq));
2972 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
2973 ireq.i_type = IEEE80211_IOC_SCAN_REQ;
2974
2975 memset(&sr, 0, sizeof(sr));
2976 sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
2977 | IEEE80211_IOC_SCAN_NOPICK
2978 | IEEE80211_IOC_SCAN_ONCE;
2979 sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
2980 sr.sr_nssid = 0;
2981
2982 ireq.i_data = &sr;
2983 ireq.i_len = sizeof(sr);
2984 /* NB: only root can trigger a scan so ignore errors */
2985 if (ioctl(s, SIOCS80211, &ireq) >= 0) {
2986 char buf[2048];
2987 struct if_announcemsghdr *ifan;
2988 struct rt_msghdr *rtm;
2989
2990 do {
2991 if (read(sroute, buf, sizeof(buf)) < 0) {
2992 perror("read(PF_ROUTE)");
2993 break;
2994 }
2995 rtm = (struct rt_msghdr *) buf;
2996 if (rtm->rtm_version != RTM_VERSION)
2997 break;
2998 ifan = (struct if_announcemsghdr *) rtm;
2999 } while (rtm->rtm_type != RTM_IEEE80211 ||
3000 ifan->ifan_what != RTM_IEEE80211_SCAN);
3001 }
3002 close(sroute);
3003}
3004
3005static
3006DECL_CMD_FUNC(set80211scan, val, d)
3007{
3008 scan_and_wait(s);
3009 list_scan(s);
3010}
3011
3012static enum ieee80211_opmode get80211opmode(int s);
3013
3014static int
3015gettxseq(const struct ieee80211req_sta_info *si)
3016{
3017#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
3018
3019 int i, txseq;
3020
3021 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3022 return si->isi_txseqs[0];
3023 /* XXX not right but usually what folks want */
3024 txseq = 0;
3025 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3026 if (si->isi_txseqs[i] > txseq)
3027 txseq = si->isi_txseqs[i];
3028 return txseq;
3029#undef IEEE80211_NODE_QOS
3030}
3031
3032static int
3033getrxseq(const struct ieee80211req_sta_info *si)
3034{
3035#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
3036
3037 int i, rxseq;
3038
3039 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3040 return si->isi_rxseqs[0];
3041 /* XXX not right but usually what folks want */
3042 rxseq = 0;
3043 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3044 if (si->isi_rxseqs[i] > rxseq)
3045 rxseq = si->isi_rxseqs[i];
3046 return rxseq;
3047#undef IEEE80211_NODE_QOS
3048}
3049
3050static void
3051list_stations(int s)
3052{
3053 union {
3054 struct ieee80211req_sta_req req;
3055 uint8_t buf[24*1024];
3056 } u;
3057 enum ieee80211_opmode opmode = get80211opmode(s);
3058 const uint8_t *cp;
3059 int len;
3060
3061 /* broadcast address =>'s get all stations */
3062 (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
3063 if (opmode == IEEE80211_M_STA) {
3064 /*
3065 * Get information about the associated AP.
3066 */
3067 (void) get80211(s, IEEE80211_IOC_BSSID,
3068 u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
3069 }
3070 if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
3071 errx(1, "unable to get station information");
3072 if (len < sizeof(struct ieee80211req_sta_info))
3073 return;
3074
3075 getchaninfo(s);
3076
3077 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n"
3078 , "ADDR"
3079 , "AID"
3080 , "CHAN"
3081 , "RATE"
3082 , "RSSI"
3083 , "IDLE"
3084 , "TXSEQ"
3085 , "RXSEQ"
3086 , "CAPS"
3087 , "FLAG"
3088 );
3089 cp = (const uint8_t *) u.req.info;
3090 do {
3091 const struct ieee80211req_sta_info *si;
3092
3093 si = (const struct ieee80211req_sta_info *) cp;
3094 if (si->isi_len < sizeof(*si))
3095 break;
3096 printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s"
3097 , ether_ntoa((const struct ether_addr*) si->isi_macaddr)
3098 , IEEE80211_AID(si->isi_associd)
3099 , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags)
3100 , si->isi_txmbps/2
3101 , si->isi_rssi/2.
3102 , si->isi_inact
3103 , gettxseq(si)
3104 , getrxseq(si)
3105 , getcaps(si->isi_capinfo)
3106 , getflags(si->isi_state)
3107 );
3108 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3109 printmimo(&si->isi_mimo);
3110 printf("\n");
3111 cp += si->isi_len, len -= si->isi_len;
3112 } while (len >= sizeof(struct ieee80211req_sta_info));
3113}
3114
3115static const char *
3116get_chaninfo(const struct ieee80211_channel *c, int precise,
3117 char buf[], size_t bsize)
3118{
3119 buf[0] = '\0';
3120 if (IEEE80211_IS_CHAN_FHSS(c))
3121 strlcat(buf, " FHSS", bsize);
3122 if (IEEE80211_IS_CHAN_A(c))
3123 strlcat(buf, " 11a", bsize);
3124 else if (IEEE80211_IS_CHAN_ANYG(c))
3125 strlcat(buf, " 11g", bsize);
3126 else if (IEEE80211_IS_CHAN_B(c))
3127 strlcat(buf, " 11b", bsize);
3128 if (IEEE80211_IS_CHAN_HALF(c))
3129 strlcat(buf, "/10Mhz", bsize);
3130 if (IEEE80211_IS_CHAN_QUARTER(c))
3131 strlcat(buf, "/5Mhz", bsize);
3132 if (IEEE80211_IS_CHAN_TURBO(c))
3133 strlcat(buf, " Turbo", bsize);
3134 if (precise) {
3135 if (IEEE80211_IS_CHAN_HT20(c))
3136 strlcat(buf, " ht/20", bsize);
3137 else if (IEEE80211_IS_CHAN_HT40D(c))
3138 strlcat(buf, " ht/40-", bsize);
3139 else if (IEEE80211_IS_CHAN_HT40U(c))
3140 strlcat(buf, " ht/40+", bsize);
3141 } else {
3142 if (IEEE80211_IS_CHAN_HT(c))
3143 strlcat(buf, " ht", bsize);
3144 }
3145 return buf;
3146}
3147
3148static void
3149print_chaninfo(const struct ieee80211_channel *c, int verb)
3150{
3151 char buf[14];
3152
3153 printf("Channel %3u : %u%c Mhz%-14.14s",
3154 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3155 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3156 get_chaninfo(c, verb, buf, sizeof(buf)));
3157}
3158
3159static int
3160chanpref(const struct ieee80211_channel *c)
3161{
3162 if (IEEE80211_IS_CHAN_HT40(c))
3163 return 40;
3164 if (IEEE80211_IS_CHAN_HT20(c))
3165 return 30;
3166 if (IEEE80211_IS_CHAN_HALF(c))
3167 return 10;
3168 if (IEEE80211_IS_CHAN_QUARTER(c))
3169 return 5;
3170 if (IEEE80211_IS_CHAN_TURBO(c))
3171 return 25;
3172 if (IEEE80211_IS_CHAN_A(c))
3173 return 20;
3174 if (IEEE80211_IS_CHAN_G(c))
3175 return 20;
3176 if (IEEE80211_IS_CHAN_B(c))
3177 return 15;
3178 if (IEEE80211_IS_CHAN_PUREG(c))
3179 return 15;
3180 return 0;
3181}
3182
3183static void
3184print_channels(int s, const struct ieee80211req_chaninfo *chans,
3185 int allchans, int verb)
3186{
3187 struct ieee80211req_chaninfo *achans;
3188 uint8_t reported[IEEE80211_CHAN_BYTES];
3189 const struct ieee80211_channel *c;
3190 int i, half;
3191
3192 achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3193 if (achans == NULL)
3194 errx(1, "no space for active channel list");
3195 achans->ic_nchans = 0;
3196 memset(reported, 0, sizeof(reported));
3197 if (!allchans) {
3198 struct ieee80211req_chanlist active;
3199
3200 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3201 errx(1, "unable to get active channel list");
3202 for (i = 0; i < chans->ic_nchans; i++) {
3203 c = &chans->ic_chans[i];
3204 if (!isset(active.ic_channels, c->ic_ieee))
3205 continue;
3206 /*
3207 * Suppress compatible duplicates unless
3208 * verbose. The kernel gives us it's
3209 * complete channel list which has separate
3210 * entries for 11g/11b and 11a/turbo.
3211 */
3212 if (isset(reported, c->ic_ieee) && !verb) {
3213 /* XXX we assume duplicates are adjacent */
3214 achans->ic_chans[achans->ic_nchans-1] = *c;
3215 } else {
3216 achans->ic_chans[achans->ic_nchans++] = *c;
3217 setbit(reported, c->ic_ieee);
3218 }
3219 }
3220 } else {
3221 for (i = 0; i < chans->ic_nchans; i++) {
3222 c = &chans->ic_chans[i];
3223 /* suppress duplicates as above */
3224 if (isset(reported, c->ic_ieee) && !verb) {
3225 /* XXX we assume duplicates are adjacent */
3226 struct ieee80211_channel *a =
3227 &achans->ic_chans[achans->ic_nchans-1];
3228 if (chanpref(c) > chanpref(a))
3229 *a = *c;
3230 } else {
3231 achans->ic_chans[achans->ic_nchans++] = *c;
3232 setbit(reported, c->ic_ieee);
3233 }
3234 }
3235 }
3236 half = achans->ic_nchans / 2;
3237 if (achans->ic_nchans % 2)
3238 half++;
3239
3240 for (i = 0; i < achans->ic_nchans / 2; i++) {
3241 print_chaninfo(&achans->ic_chans[i], verb);
3242 print_chaninfo(&achans->ic_chans[half+i], verb);
3243 printf("\n");
3244 }
3245 if (achans->ic_nchans % 2) {
3246 print_chaninfo(&achans->ic_chans[i], verb);
3247 printf("\n");
3248 }
3249 free(achans);
3250}
3251
3252static void
3253list_channels(int s, int allchans)
3254{
3255 getchaninfo(s);
3256 print_channels(s, chaninfo, allchans, verbose);
3257}
3258
3259static void
3260print_txpow(const struct ieee80211_channel *c)
3261{
3262 printf("Channel %3u : %u Mhz %3.1f reg %2d ",
3263 c->ic_ieee, c->ic_freq,
3264 c->ic_maxpower/2., c->ic_maxregpower);
3265}
3266
3267static void
3268print_txpow_verbose(const struct ieee80211_channel *c)
3269{
3270 print_chaninfo(c, 1);
3271 printf("min %4.1f dBm max %3.1f dBm reg %2d dBm",
3272 c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3273 /* indicate where regulatory cap limits power use */
3274 if (c->ic_maxpower > 2*c->ic_maxregpower)
3275 printf(" <");
3276}
3277
3278static void
3279list_txpow(int s)
3280{
3281 struct ieee80211req_chaninfo *achans;
3282 uint8_t reported[IEEE80211_CHAN_BYTES];
3283 struct ieee80211_channel *c, *prev;
3284 int i, half;
3285
3286 getchaninfo(s);
3287 achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3288 if (achans == NULL)
3289 errx(1, "no space for active channel list");
3290 achans->ic_nchans = 0;
3291 memset(reported, 0, sizeof(reported));
3292 for (i = 0; i < chaninfo->ic_nchans; i++) {
3293 c = &chaninfo->ic_chans[i];
3294 /* suppress duplicates as above */
3295 if (isset(reported, c->ic_ieee) && !verbose) {
3296 /* XXX we assume duplicates are adjacent */
3297 prev = &achans->ic_chans[achans->ic_nchans-1];
3298 /* display highest power on channel */
3299 if (c->ic_maxpower > prev->ic_maxpower)
3300 *prev = *c;
3301 } else {
3302 achans->ic_chans[achans->ic_nchans++] = *c;
3303 setbit(reported, c->ic_ieee);
3304 }
3305 }
3306 if (!verbose) {
3307 half = achans->ic_nchans / 2;
3308 if (achans->ic_nchans % 2)
3309 half++;
3310
3311 for (i = 0; i < achans->ic_nchans / 2; i++) {
3312 print_txpow(&achans->ic_chans[i]);
3313 print_txpow(&achans->ic_chans[half+i]);
3314 printf("\n");
3315 }
3316 if (achans->ic_nchans % 2) {
3317 print_txpow(&achans->ic_chans[i]);
3318 printf("\n");
3319 }
3320 } else {
3321 for (i = 0; i < achans->ic_nchans; i++) {
3322 print_txpow_verbose(&achans->ic_chans[i]);
3323 printf("\n");
3324 }
3325 }
3326 free(achans);
3327}
3328
3329static void
3330list_keys(int s)
3331{
3332}
3333
3334#define IEEE80211_C_BITS \
3335 "\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \
3336 "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3337 "\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3338 "\37TXFRAG\40TDMA"
3339
3340static void
3341list_capabilities(int s)
3342{
3343 struct ieee80211_devcaps_req *dc;
3344
3345 if (verbose)
3346 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3347 else
3348 dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3349 if (dc == NULL)
3350 errx(1, "no space for device capabilities");
3351 dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3352 getdevcaps(s, dc);
3353 printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3354 if (dc->dc_cryptocaps != 0 || verbose) {
3355 putchar('\n');
3356 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3357 }
3358 if (dc->dc_htcaps != 0 || verbose) {
3359 putchar('\n');
3360 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3361 }
3362 putchar('\n');
3363 if (verbose) {
3364 chaninfo = &dc->dc_chaninfo; /* XXX */
3365 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3366 }
3367 free(dc);
3368}
3369
3370static int
3371get80211wme(int s, int param, int ac, int *val)
3372{
3373 struct ieee80211req ireq;
3374
3375 (void) memset(&ireq, 0, sizeof(ireq));
3376 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3377 ireq.i_type = param;
3378 ireq.i_len = ac;
3379 if (ioctl(s, SIOCG80211, &ireq) < 0) {
3380 warn("cannot get WME parameter %d, ac %d%s",
3381 param, ac & IEEE80211_WMEPARAM_VAL,
3382 ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3383 return -1;
3384 }
3385 *val = ireq.i_val;
3386 return 0;
3387}
3388
3389static void
3390list_wme_aci(int s, const char *tag, int ac)
3391{
3392 int val;
3393
3394 printf("\t%s", tag);
3395
3396 /* show WME BSS parameters */
3397 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3398 printf(" cwmin %2u", val);
3399 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3400 printf(" cwmax %2u", val);
3401 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3402 printf(" aifs %2u", val);
3403 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3404 printf(" txopLimit %3u", val);
3405 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3406 if (val)
3407 printf(" acm");
3408 else if (verbose)
3409 printf(" -acm");
3410 }
3411 /* !BSS only */
3412 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3413 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3414 if (!val)
3415 printf(" -ack");
3416 else if (verbose)
3417 printf(" ack");
3418 }
3419 }
3420 printf("\n");
3421}
3422
3423static void
3424list_wme(int s)
3425{
3426 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3427 int ac;
3428
3429 if (verbose) {
3430 /* display both BSS and local settings */
3431 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3432 again:
3433 if (ac & IEEE80211_WMEPARAM_BSS)
3434 list_wme_aci(s, " ", ac);
3435 else
3436 list_wme_aci(s, acnames[ac], ac);
3437 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3438 ac |= IEEE80211_WMEPARAM_BSS;
3439 goto again;
3440 } else
3441 ac &= ~IEEE80211_WMEPARAM_BSS;
3442 }
3443 } else {
3444 /* display only channel settings */
3445 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3446 list_wme_aci(s, acnames[ac], ac);
3447 }
3448}
3449
3450static void
3451list_roam(int s)
3452{
3453 const struct ieee80211_roamparam *rp;
3454 int mode;
3455
3456 getroam(s);
3457 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3458 rp = &roamparams.params[mode];
3459 if (rp->rssi == 0 && rp->rate == 0)
3460 continue;
3461 if (rp->rssi & 1)
3462 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s",
3463 modename[mode], rp->rssi/2, rp->rate/2);
3464 else
3465 LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s",
3466 modename[mode], rp->rssi/2, rp->rate/2);
3467 }
3468 for (; mode < IEEE80211_MODE_MAX; mode++) {
3469 rp = &roamparams.params[mode];
3470 if (rp->rssi == 0 && rp->rate == 0)
3471 continue;
3472 if (rp->rssi & 1)
3473 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ",
3474 modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3475 else
3476 LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ",
3477 modename[mode], rp->rssi/2, rp->rate &~ 0x80);
3478 }
3479}
3480
3481static void
3482list_txparams(int s)
3483{
3484 const struct ieee80211_txparam *tp;
3485 int mode;
3486
3487 gettxparams(s);
3488 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) {
3489 tp = &txparams.params[mode];
3490 if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3491 continue;
3492 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3493 LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s "
3494 "mcast %2u Mb/s maxretry %u",
3495 modename[mode], tp->mgmtrate/2,
3496 tp->mcastrate/2, tp->maxretry);
3497 else
3498 LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s "
3499 "mcast %2u Mb/s maxretry %u",
3500 modename[mode], tp->ucastrate/2, tp->mgmtrate/2,
3501 tp->mcastrate/2, tp->maxretry);
3502 }
3503 for (; mode < IEEE80211_MODE_MAX; mode++) {
3504 tp = &txparams.params[mode];
3505 if (tp->mgmtrate == 0 && tp->mcastrate == 0)
3506 continue;
3507 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
3508 LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS "
3509 "mcast %2u MCS maxretry %u",
3510 modename[mode], tp->mgmtrate &~ 0x80,
3511 tp->mcastrate &~ 0x80, tp->maxretry);
3512 else
3513 LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS "
3514 "mcast %2u MCS maxretry %u",
3515 modename[mode], tp->ucastrate &~ 0x80,
3516 tp->mgmtrate &~ 0x80,
3517 tp->mcastrate &~ 0x80, tp->maxretry);
3518 }
3519}
3520
3521static void
3522printpolicy(int policy)
3523{
3524 switch (policy) {
3525 case IEEE80211_MACCMD_POLICY_OPEN:
3526 printf("policy: open\n");
3527 break;
3528 case IEEE80211_MACCMD_POLICY_ALLOW:
3529 printf("policy: allow\n");
3530 break;
3531 case IEEE80211_MACCMD_POLICY_DENY:
3532 printf("policy: deny\n");
3533 break;
3534 case IEEE80211_MACCMD_POLICY_RADIUS:
3535 printf("policy: radius\n");
3536 break;
3537 default:
3538 printf("policy: unknown (%u)\n", policy);
3539 break;
3540 }
3541}
3542
3543static void
3544list_mac(int s)
3545{
3546 struct ieee80211req ireq;
3547 struct ieee80211req_maclist *acllist;
3548 int i, nacls, policy, len;
3549 uint8_t *data;
3550 char c;
3551
3552 (void) memset(&ireq, 0, sizeof(ireq));
3553 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */
3554 ireq.i_type = IEEE80211_IOC_MACCMD;
3555 ireq.i_val = IEEE80211_MACCMD_POLICY;
3556 if (ioctl(s, SIOCG80211, &ireq) < 0) {
3557 if (errno == EINVAL) {
3558 printf("No acl policy loaded\n");
3559 return;
3560 }
3561 err(1, "unable to get mac policy");
3562 }
3563 policy = ireq.i_val;
3564 if (policy == IEEE80211_MACCMD_POLICY_OPEN) {
3565 c = '*';
3566 } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) {
3567 c = '+';
3568 } else if (policy == IEEE80211_MACCMD_POLICY_DENY) {
3569 c = '-';
3570 } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) {
3571 c = 'r'; /* NB: should never have entries */
3572 } else {
3573 printf("policy: unknown (%u)\n", policy);
3574 c = '?';
3575 }
3576 if (verbose || c == '?')
3577 printpolicy(policy);
3578
3579 ireq.i_val = IEEE80211_MACCMD_LIST;
3580 ireq.i_len = 0;
3581 if (ioctl(s, SIOCG80211, &ireq) < 0)
3582 err(1, "unable to get mac acl list size");
3583 if (ireq.i_len == 0) { /* NB: no acls */
3584 if (!(verbose || c == '?'))
3585 printpolicy(policy);
3586 return;
3587 }
3588 len = ireq.i_len;
3589
3590 data = malloc(len);
3591 if (data == NULL)
3592 err(1, "out of memory for acl list");
3593
3594 ireq.i_data = data;
3595 if (ioctl(s, SIOCG80211, &ireq) < 0)
3596 err(1, "unable to get mac acl list");
3597 nacls = len / sizeof(*acllist);
3598 acllist = (struct ieee80211req_maclist *) data;
3599 for (i = 0; i < nacls; i++)
3600 printf("%c%s\n", c, ether_ntoa(
3601 (const struct ether_addr *) acllist[i].ml_macaddr));
3602 free(data);
3603}
3604
3605static void
3606print_regdomain(const struct ieee80211_regdomain *reg, int verb)
3607{
3608 if ((reg->regdomain != 0 &&
3609 reg->regdomain != reg->country) || verb) {
3610 const struct regdomain *rd =
3611 lib80211_regdomain_findbysku(getregdata(), reg->regdomain);
3612 if (rd == NULL)
3613 LINE_CHECK("regdomain %d", reg->regdomain);
3614 else
3615 LINE_CHECK("regdomain %s", rd->name);
3616 }
3617 if (reg->country != 0 || verb) {
3618 const struct country *cc =
3619 lib80211_country_findbycc(getregdata(), reg->country);
3620 if (cc == NULL)
3621 LINE_CHECK("country %d", reg->country);
3622 else
3623 LINE_CHECK("country %s", cc->isoname);
3624 }
3625 if (reg->location == 'I')
3626 LINE_CHECK("indoor");
3627 else if (reg->location == 'O')
3628 LINE_CHECK("outdoor");
3629 else if (verb)
3630 LINE_CHECK("anywhere");
3631 if (reg->ecm)
3632 LINE_CHECK("ecm");
3633 else if (verb)
3634 LINE_CHECK("-ecm");
3635}
3636
3637static void
3638list_regdomain(int s, int channelsalso)
3639{
3640 getregdomain(s);
3641 if (channelsalso) {
3642 getchaninfo(s);
3643 spacer = ':';
3644 print_regdomain(&regdomain, 1);
3645 LINE_BREAK();
3646 print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/);
3647 } else
3648 print_regdomain(&regdomain, verbose);
3649}
3650
3651static
3652DECL_CMD_FUNC(set80211list, arg, d)
3653{
3654#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
3655
3656 LINE_INIT('\t');
3657
3658 if (iseq(arg, "sta"))
3659 list_stations(s);
3660 else if (iseq(arg, "scan") || iseq(arg, "ap"))
3661 list_scan(s);
3662 else if (iseq(arg, "chan") || iseq(arg, "freq"))
3663 list_channels(s, 1);
3664 else if (iseq(arg, "active"))
3665 list_channels(s, 0);
3666 else if (iseq(arg, "keys"))
3667 list_keys(s);
3668 else if (iseq(arg, "caps"))
3669 list_capabilities(s);
3670 else if (iseq(arg, "wme") || iseq(arg, "wmm"))
3671 list_wme(s);
3672 else if (iseq(arg, "mac"))
3673 list_mac(s);
3674 else if (iseq(arg, "txpow"))
3675 list_txpow(s);
3676 else if (iseq(arg, "roam"))
3677 list_roam(s);
3678 else if (iseq(arg, "txparam") || iseq(arg, "txparm"))
3679 list_txparams(s);
3680 else if (iseq(arg, "regdomain"))
3681 list_regdomain(s, 1);
3682 else if (iseq(arg, "countries"))
3683 list_countries();
3684 else
3685 errx(1, "Don't know how to list %s for %s", arg, name);
3686 LINE_BREAK();
3687#undef iseq
3688}
3689
3690static enum ieee80211_opmode
3691get80211opmode(int s)
3692{
3693 struct ifmediareq ifmr;
3694
3695 (void) memset(&ifmr, 0, sizeof(ifmr));
3696 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
3697
3698 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
3699 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
3700 if (ifmr.ifm_current & IFM_FLAG0)
3701 return IEEE80211_M_AHDEMO;
3702 else
3703 return IEEE80211_M_IBSS;
3704 }
3705 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
3706 return IEEE80211_M_HOSTAP;
3707 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
3708 return IEEE80211_M_MONITOR;
3709 }
3710 return IEEE80211_M_STA;
3711}
3712
3713#if 0
3714static void
3715printcipher(int s, struct ieee80211req *ireq, int keylenop)
3716{
3717 switch (ireq->i_val) {
3718 case IEEE80211_CIPHER_WEP:
3719 ireq->i_type = keylenop;
3720 if (ioctl(s, SIOCG80211, ireq) != -1)
3721 printf("WEP-%s",
3722 ireq->i_len <= 5 ? "40" :
3723 ireq->i_len <= 13 ? "104" : "128");
3724 else
3725 printf("WEP");
3726 break;
3727 case IEEE80211_CIPHER_TKIP:
3728 printf("TKIP");
3729 break;
3730 case IEEE80211_CIPHER_AES_OCB:
3731 printf("AES-OCB");
3732 break;
3733 case IEEE80211_CIPHER_AES_CCM:
3734 printf("AES-CCM");
3735 break;
3736 case IEEE80211_CIPHER_CKIP:
3737 printf("CKIP");
3738 break;
3739 case IEEE80211_CIPHER_NONE:
3740 printf("NONE");
3741 break;
3742 default:
3743 printf("UNKNOWN (0x%x)", ireq->i_val);
3744 break;
3745 }
3746}
3747#endif
3748
3749static void
3750printkey(const struct ieee80211req_key *ik)
3751{
3752 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
3753 int keylen = ik->ik_keylen;
3754 int printcontents;
3755
3756 printcontents = printkeys &&
3757 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
3758 if (printcontents)
3759 LINE_BREAK();
3760 switch (ik->ik_type) {
3761 case IEEE80211_CIPHER_WEP:
3762 /* compatibility */
3763 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1,
3764 keylen <= 5 ? "40-bit" :
3765 keylen <= 13 ? "104-bit" : "128-bit");
3766 break;
3767 case IEEE80211_CIPHER_TKIP:
3768 if (keylen > 128/8)
3769 keylen -= 128/8; /* ignore MIC for now */
3770 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3771 break;
3772 case IEEE80211_CIPHER_AES_OCB:
3773 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3774 break;
3775 case IEEE80211_CIPHER_AES_CCM:
3776 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3777 break;
3778 case IEEE80211_CIPHER_CKIP:
3779 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3780 break;
3781 case IEEE80211_CIPHER_NONE:
3782 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen);
3783 break;
3784 default:
3785 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit",
3786 ik->ik_type, ik->ik_keyix+1, 8*keylen);
3787 break;
3788 }
3789 if (printcontents) {
3790 int i;
3791
3792 printf(" <");
3793 for (i = 0; i < keylen; i++)
3794 printf("%02x", ik->ik_keydata[i]);
3795 printf(">");
3796 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3797 (ik->ik_keyrsc != 0 || verbose))
3798 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc);
3799 if (ik->ik_type != IEEE80211_CIPHER_WEP &&
3800 (ik->ik_keytsc != 0 || verbose))
3801 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc);
3802 if (ik->ik_flags != 0 && verbose) {
3803 const char *sep = " ";
3804
3805 if (ik->ik_flags & IEEE80211_KEY_XMIT)
3806 printf("%stx", sep), sep = "+";
3807 if (ik->ik_flags & IEEE80211_KEY_RECV)
3808 printf("%srx", sep), sep = "+";
3809 if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
3810 printf("%sdef", sep), sep = "+";
3811 }
3812 LINE_BREAK();
3813 }
3814}
3815
3816static void
3817printrate(const char *tag, int v, int defrate, int defmcs)
3818{
3819 if (v == 11)
3820 LINE_CHECK("%s 5.5", tag);
3821 else if (v & 0x80) {
3822 if (v != defmcs)
3823 LINE_CHECK("%s %d", tag, v &~ 0x80);
3824 } else {
3825 if (v != defrate)
3826 LINE_CHECK("%s %d", tag, v/2);
3827 }
3828}
3829
3830static int
3831getssid(int s, int ix, void *data, size_t len, int *plen)
3832{
3833 struct ieee80211req ireq;
3834
3835 (void) memset(&ireq, 0, sizeof(ireq));
3836 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
3837 ireq.i_type = IEEE80211_IOC_SSID;
3838 ireq.i_val = ix;
3839 ireq.i_data = data;
3840 ireq.i_len = len;
3841 if (ioctl(s, SIOCG80211, &ireq) < 0)
3842 return -1;
3843 *plen = ireq.i_len;
3844 return 0;
3845}
3846
3847static void
3848ieee80211_status(int s)
3849{
3850 static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
3851 enum ieee80211_opmode opmode = get80211opmode(s);
3852 int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode;
3853 uint8_t data[32];
3854 const struct ieee80211_channel *c;
3855 const struct ieee80211_roamparam *rp;
3856 const struct ieee80211_txparam *tp;
3857
3858 if (getssid(s, -1, data, sizeof(data), &len) < 0) {
3859 /* If we can't get the SSID, this isn't an 802.11 device. */
3860 return;
3861 }
3862
3863 /*
3864 * Invalidate cached state so printing status for multiple
3865 * if's doesn't reuse the first interfaces' cached state.
3866 */
3867 gotcurchan = 0;
3868 gotroam = 0;
3869 gottxparams = 0;
3870 gothtconf = 0;
3871 gotregdomain = 0;
3872
3873 if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0)
3874 num = 0;
3875 printf("\tssid ");
3876 if (num > 1) {
3877 for (i = 0; i < num; i++) {
3878 if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) {
3879 printf(" %d:", i + 1);
3880 print_string(data, len);
3881 }
3882 }
3883 } else
3884 print_string(data, len);
3885
3886 c = getcurchan(s);
3887 if (c->ic_freq != IEEE80211_CHAN_ANY) {
3888 char buf[14];
3889 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq,
3890 get_chaninfo(c, 1, buf, sizeof(buf)));
3891 } else if (verbose)
3892 printf(" channel UNDEF");
3893
3894 if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 &&
3895 (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose))
3896 printf(" bssid %s", ether_ntoa((struct ether_addr *)data));
3897
3898 if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) {
3899 printf("\n\tstationname ");
3900 print_string(data, len);
3901 }
3902
3903 spacer = ' '; /* force first break */
3904 LINE_BREAK();
3905
3906 list_regdomain(s, 0);
3907
3908 wpa = 0;
3909 if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) {
3910 switch (val) {
3911 case IEEE80211_AUTH_NONE:
3912 LINE_CHECK("authmode NONE");
3913 break;
3914 case IEEE80211_AUTH_OPEN:
3915 LINE_CHECK("authmode OPEN");
3916 break;
3917 case IEEE80211_AUTH_SHARED:
3918 LINE_CHECK("authmode SHARED");
3919 break;
3920 case IEEE80211_AUTH_8021X:
3921 LINE_CHECK("authmode 802.1x");
3922 break;
3923 case IEEE80211_AUTH_WPA:
3924 if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0)
3925 wpa = 1; /* default to WPA1 */
3926 switch (wpa) {
3927 case 2:
3928 LINE_CHECK("authmode WPA2/802.11i");
3929 break;
3930 case 3:
3931 LINE_CHECK("authmode WPA1+WPA2/802.11i");
3932 break;
3933 default:
3934 LINE_CHECK("authmode WPA");
3935 break;
3936 }
3937 break;
3938 case IEEE80211_AUTH_AUTO:
3939 LINE_CHECK("authmode AUTO");
3940 break;
3941 default:
3942 LINE_CHECK("authmode UNKNOWN (0x%x)", val);
3943 break;
3944 }
3945 }
3946
3947 if (wpa || verbose) {
3948 if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) {
3949 if (val)
3950 LINE_CHECK("wps");
3951 else if (verbose)
3952 LINE_CHECK("-wps");
3953 }
3954 if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) {
3955 if (val)
3956 LINE_CHECK("tsn");
3957 else if (verbose)
3958 LINE_CHECK("-tsn");
3959 }
3960 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) {
3961 if (val)
3962 LINE_CHECK("countermeasures");
3963 else if (verbose)
3964 LINE_CHECK("-countermeasures");
3965 }
3966#if 0
3967 /* XXX not interesting with WPA done in user space */
3968 ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
3969 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3970 }
3971
3972 ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
3973 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3974 LINE_CHECK("mcastcipher ");
3975 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
3976 spacer = ' ';
3977 }
3978
3979 ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
3980 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3981 LINE_CHECK("ucastcipher ");
3982 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
3983 }
3984
3985 if (wpa & 2) {
3986 ireq.i_type = IEEE80211_IOC_RSNCAPS;
3987 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3988 LINE_CHECK("RSN caps 0x%x", ireq.i_val);
3989 spacer = ' ';
3990 }
3991 }
3992
3993 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
3994 if (ioctl(s, SIOCG80211, &ireq) != -1) {
3995 }
3996#endif
3997 }
3998
3999 if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 &&
4000 wepmode != IEEE80211_WEP_NOSUP) {
4001 int firstkey;
4002
4003 switch (wepmode) {
4004 case IEEE80211_WEP_OFF:
4005 LINE_CHECK("privacy OFF");
4006 break;
4007 case IEEE80211_WEP_ON:
4008 LINE_CHECK("privacy ON");
4009 break;
4010 case IEEE80211_WEP_MIXED:
4011 LINE_CHECK("privacy MIXED");
4012 break;
4013 default:
4014 LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode);
4015 break;
4016 }
4017
4018 /*
4019 * If we get here then we've got WEP support so we need
4020 * to print WEP status.
4021 */
4022
4023 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) {
4024 warn("WEP support, but no tx key!");
4025 goto end;
4026 }
4027 if (val != -1)
4028 LINE_CHECK("deftxkey %d", val+1);
4029 else if (wepmode != IEEE80211_WEP_OFF || verbose)
4030 LINE_CHECK("deftxkey UNDEF");
4031
4032 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) {
4033 warn("WEP support, but no NUMWEPKEYS support!");
4034 goto end;
4035 }
4036
4037 firstkey = 1;
4038 for (i = 0; i < num; i++) {
4039 struct ieee80211req_key ik;
4040
4041 memset(&ik, 0, sizeof(ik));
4042 ik.ik_keyix = i;
4043 if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) {
4044 warn("WEP support, but can get keys!");
4045 goto end;
4046 }
4047 if (ik.ik_keylen != 0) {
4048 if (verbose)
4049 LINE_BREAK();
4050 printkey(&ik);
4051 firstkey = 0;
4052 }
4053 }
4054end:
4055 ;
4056 }
4057
4058 if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 &&
4059 val != IEEE80211_POWERSAVE_NOSUP ) {
4060 if (val != IEEE80211_POWERSAVE_OFF || verbose) {
4061 switch (val) {
4062 case IEEE80211_POWERSAVE_OFF:
4063 LINE_CHECK("powersavemode OFF");
4064 break;
4065 case IEEE80211_POWERSAVE_CAM:
4066 LINE_CHECK("powersavemode CAM");
4067 break;
4068 case IEEE80211_POWERSAVE_PSP:
4069 LINE_CHECK("powersavemode PSP");
4070 break;
4071 case IEEE80211_POWERSAVE_PSP_CAM:
4072 LINE_CHECK("powersavemode PSP-CAM");
4073 break;
4074 }
4075 if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1)
4076 LINE_CHECK("powersavesleep %d", val);
4077 }
4078 }
4079
4080 if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) {
4081 if (val & 1)
4082 LINE_CHECK("txpower %d.5", val/2);
4083 else
4084 LINE_CHECK("txpower %d", val/2);
4085 }
4086 if (verbose) {
4087 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1)
4088 LINE_CHECK("txpowmax %.1f", val/2.);
4089 }
4090
4091 if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) {
4092 if (val)
4093 LINE_CHECK("dotd");
4094 else if (verbose)
4095 LINE_CHECK("-dotd");
4096 }
4097
4098 if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) {
4099 if (val != IEEE80211_RTS_MAX || verbose)
4100 LINE_CHECK("rtsthreshold %d", val);
4101 }
4102
4103 if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) {
4104 if (val != IEEE80211_FRAG_MAX || verbose)
4105 LINE_CHECK("fragthreshold %d", val);
4106 }
4107 if (opmode == IEEE80211_M_STA || verbose) {
4108 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) {
4109 if (val != IEEE80211_HWBMISS_MAX || verbose)
4110 LINE_CHECK("bmiss %d", val);
4111 }
4112 }
4113
4114 if (!verbose) {
4115 gettxparams(s);
4116 tp = &txparams.params[chan2mode(c)];
4117 printrate("ucastrate", tp->ucastrate,
4118 IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE);
4119 printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0);
4120 printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0);
4121 if (tp->maxretry != 6) /* XXX */
4122 LINE_CHECK("maxretry %d", tp->maxretry);
4123 } else {
4124 LINE_BREAK();
4125 list_txparams(s);
4126 }
4127
4128 bgscaninterval = -1;
4129 (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval);
4130
4131 if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) {
4132 if (val != bgscaninterval || verbose)
4133 LINE_CHECK("scanvalid %u", val);
4134 }
4135
4136 bgscan = 0;
4137 if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) {
4138 if (bgscan)
4139 LINE_CHECK("bgscan");
4140 else if (verbose)
4141 LINE_CHECK("-bgscan");
4142 }
4143 if (bgscan || verbose) {
4144 if (bgscaninterval != -1)
4145 LINE_CHECK("bgscanintvl %u", bgscaninterval);
4146 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1)
4147 LINE_CHECK("bgscanidle %u", val);
4148 if (!verbose) {
4149 getroam(s);
4150 rp = &roamparams.params[chan2mode(c)];
4151 if (rp->rssi & 1)
4152 LINE_CHECK("roam:rssi %u.5", rp->rssi/2);
4153 else
4154 LINE_CHECK("roam:rssi %u", rp->rssi/2);
4155 LINE_CHECK("roam:rate %u", rp->rate/2);
4156 } else {
4157 LINE_BREAK();
4158 list_roam(s);
4159 }
4160 }
4161
4162 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) {
4163 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) {
4164 if (val)
4165 LINE_CHECK("pureg");
4166 else if (verbose)
4167 LINE_CHECK("-pureg");
4168 }
4169 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) {
4170 switch (val) {
4171 case IEEE80211_PROTMODE_OFF:
4172 LINE_CHECK("protmode OFF");
4173 break;
4174 case IEEE80211_PROTMODE_CTS:
4175 LINE_CHECK("protmode CTS");
4176 break;
4177 case IEEE80211_PROTMODE_RTSCTS:
4178 LINE_CHECK("protmode RTSCTS");
4179 break;
4180 default:
4181 LINE_CHECK("protmode UNKNOWN (0x%x)", val);
4182 break;
4183 }
4184 }
4185 }
4186
4187 if (IEEE80211_IS_CHAN_HT(c) || verbose) {
4188 gethtconf(s);
4189 switch (htconf & 3) {
4190 case 0:
4191 case 2:
4192 LINE_CHECK("-ht");
4193 break;
4194 case 1:
4195 LINE_CHECK("ht20");
4196 break;
4197 case 3:
4198 if (verbose)
4199 LINE_CHECK("ht");
4200 break;
4201 }
4202 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) {
4203 if (!val)
4204 LINE_CHECK("-htcompat");
4205 else if (verbose)
4206 LINE_CHECK("htcompat");
4207 }
4208 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) {
4209 switch (val) {
4210 case 0:
4211 LINE_CHECK("-ampdu");
4212 break;
4213 case 1:
4214 LINE_CHECK("ampdutx -ampdurx");
4215 break;
4216 case 2:
4217 LINE_CHECK("-ampdutx ampdurx");
4218 break;
4219 case 3:
4220 if (verbose)
4221 LINE_CHECK("ampdu");
4222 break;
4223 }
4224 }
4225 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) {
4226 switch (val) {
4227 case IEEE80211_HTCAP_MAXRXAMPDU_8K:
4228 LINE_CHECK("ampdulimit 8k");
4229 break;
4230 case IEEE80211_HTCAP_MAXRXAMPDU_16K:
4231 LINE_CHECK("ampdulimit 16k");
4232 break;
4233 case IEEE80211_HTCAP_MAXRXAMPDU_32K:
4234 LINE_CHECK("ampdulimit 32k");
4235 break;
4236 case IEEE80211_HTCAP_MAXRXAMPDU_64K:
4237 LINE_CHECK("ampdulimit 64k");
4238 break;
4239 }
4240 }
4241 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) {
4242 switch (val) {
4243 case IEEE80211_HTCAP_MPDUDENSITY_NA:
4244 if (verbose)
4245 LINE_CHECK("ampdudensity NA");
4246 break;
4247 case IEEE80211_HTCAP_MPDUDENSITY_025:
4248 LINE_CHECK("ampdudensity .25");
4249 break;
4250 case IEEE80211_HTCAP_MPDUDENSITY_05:
4251 LINE_CHECK("ampdudensity .5");
4252 break;
4253 case IEEE80211_HTCAP_MPDUDENSITY_1:
4254 LINE_CHECK("ampdudensity 1");
4255 break;
4256 case IEEE80211_HTCAP_MPDUDENSITY_2:
4257 LINE_CHECK("ampdudensity 2");
4258 break;
4259 case IEEE80211_HTCAP_MPDUDENSITY_4:
4260 LINE_CHECK("ampdudensity 4");
4261 break;
4262 case IEEE80211_HTCAP_MPDUDENSITY_8:
4263 LINE_CHECK("ampdudensity 8");
4264 break;
4265 case IEEE80211_HTCAP_MPDUDENSITY_16:
4266 LINE_CHECK("ampdudensity 16");
4267 break;
4268 }
4269 }
4270 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) {
4271 switch (val) {
4272 case 0:
4273 LINE_CHECK("-amsdu");
4274 break;
4275 case 1:
4276 LINE_CHECK("amsdutx -amsdurx");
4277 break;
4278 case 2:
4279 LINE_CHECK("-amsdutx amsdurx");
4280 break;
4281 case 3:
4282 if (verbose)
4283 LINE_CHECK("amsdu");
4284 break;
4285 }
4286 }
4287 /* XXX amsdu limit */
4288 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) {
4289 if (val)
4290 LINE_CHECK("shortgi");
4291 else if (verbose)
4292 LINE_CHECK("-shortgi");
4293 }
4294 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) {
4295 if (val == IEEE80211_PROTMODE_OFF)
4296 LINE_CHECK("htprotmode OFF");
4297 else if (val != IEEE80211_PROTMODE_RTSCTS)
4298 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val);
4299 else if (verbose)
4300 LINE_CHECK("htprotmode RTSCTS");
4301 }
4302 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) {
4303 if (val)
4304 LINE_CHECK("puren");
4305 else if (verbose)
4306 LINE_CHECK("-puren");
4307 }
4308 if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) {
4309 if (val == IEEE80211_HTCAP_SMPS_DYNAMIC)
4310 LINE_CHECK("smpsdyn");
4311 else if (val == IEEE80211_HTCAP_SMPS_ENA)
4312 LINE_CHECK("smps");
4313 else if (verbose)
4314 LINE_CHECK("-smps");
4315 }
4316 if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) {
4317 if (val)
4318 LINE_CHECK("rifs");
4319 else if (verbose)
4320 LINE_CHECK("-rifs");
4321 }
4322 }
4323
4324 if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
4325 if (wme)
4326 LINE_CHECK("wme");
4327 else if (verbose)
4328 LINE_CHECK("-wme");
4329 } else
4330 wme = 0;
4331
4332 if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) {
4333 if (val)
4334 LINE_CHECK("burst");
4335 else if (verbose)
4336 LINE_CHECK("-burst");
4337 }
4338
4339 if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) {
4340 if (val)
4341 LINE_CHECK("ff");
4342 else if (verbose)
4343 LINE_CHECK("-ff");
4344 }
4345 if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) {
4346 if (val)
4347 LINE_CHECK("dturbo");
4348 else if (verbose)
4349 LINE_CHECK("-dturbo");
4350 }
4351 if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) {
4352 if (val)
4353 LINE_CHECK("dwds");
4354 else if (verbose)
4355 LINE_CHECK("-dwds");
4356 }
4357
4358 if (opmode == IEEE80211_M_HOSTAP) {
4359 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) {
4360 if (val)
4361 LINE_CHECK("hidessid");
4362 else if (verbose)
4363 LINE_CHECK("-hidessid");
4364 }
4365 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) {
4366 if (!val)
4367 LINE_CHECK("-apbridge");
4368 else if (verbose)
4369 LINE_CHECK("apbridge");
4370 }
4371 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1)
4372 LINE_CHECK("dtimperiod %u", val);
4373
4374 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) {
4375 if (!val)
4376 LINE_CHECK("-doth");
4377 else if (verbose)
4378 LINE_CHECK("doth");
4379 }
4380 if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) {
4381 if (!val)
4382 LINE_CHECK("-dfs");
4383 else if (verbose)
4384 LINE_CHECK("dfs");
4385 }
4386 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) {
4387 if (!val)
4388 LINE_CHECK("-inact");
4389 else if (verbose)
4390 LINE_CHECK("inact");
4391 }
4392 } else {
4393 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) {
4394 if (val != IEEE80211_ROAMING_AUTO || verbose) {
4395 switch (val) {
4396 case IEEE80211_ROAMING_DEVICE:
4397 LINE_CHECK("roaming DEVICE");
4398 break;
4399 case IEEE80211_ROAMING_AUTO:
4400 LINE_CHECK("roaming AUTO");
4401 break;
4402 case IEEE80211_ROAMING_MANUAL:
4403 LINE_CHECK("roaming MANUAL");
4404 break;
4405 default:
4406 LINE_CHECK("roaming UNKNOWN (0x%x)",
4407 val);
4408 break;
4409 }
4410 }
4411 }
4412 }
4413
4414 if (opmode == IEEE80211_M_AHDEMO) {
4415 if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
4416 LINE_CHECK("tdmaslot %u", val);
4417 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
4418 LINE_CHECK("tdmaslotcnt %u", val);
4419 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
4420 LINE_CHECK("tdmaslotlen %u", val);
4421 if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
4422 LINE_CHECK("tdmabintval %u", val);
4423 } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
4424 /* XXX default define not visible */
4425 if (val != 100 || verbose)
4426 LINE_CHECK("bintval %u", val);
4427 }
4428
4429 if (wme && verbose) {
4430 LINE_BREAK();
4431 list_wme(s);
4432 }
4433 LINE_BREAK();
4434}
4435
4436static int
4437get80211(int s, int type, void *data, int len)
4438{
4439 struct ieee80211req ireq;
4440
4441 (void) memset(&ireq, 0, sizeof(ireq));
4442 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4443 ireq.i_type = type;
4444 ireq.i_data = data;
4445 ireq.i_len = len;
4446 return ioctl(s, SIOCG80211, &ireq);
4447}
4448
4449static int
4450get80211len(int s, int type, void *data, int len, int *plen)
4451{
4452 struct ieee80211req ireq;
4453
4454 (void) memset(&ireq, 0, sizeof(ireq));
4455 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4456 ireq.i_type = type;
4457 ireq.i_len = len;
4458 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */
4459 ireq.i_data = data;
4460 if (ioctl(s, SIOCG80211, &ireq) < 0)
4461 return -1;
4462 *plen = ireq.i_len;
4463 return 0;
4464}
4465
4466static int
4467get80211val(int s, int type, int *val)
4468{
4469 struct ieee80211req ireq;
4470
4471 (void) memset(&ireq, 0, sizeof(ireq));
4472 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4473 ireq.i_type = type;
4474 if (ioctl(s, SIOCG80211, &ireq) < 0)
4475 return -1;
4476 *val = ireq.i_val;
4477 return 0;
4478}
4479
4480static void
4481set80211(int s, int type, int val, int len, void *data)
4482{
4483 struct ieee80211req ireq;
4484
4485 (void) memset(&ireq, 0, sizeof(ireq));
4486 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
4487 ireq.i_type = type;
4488 ireq.i_val = val;
4489 ireq.i_len = len;
4490 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */
4491 ireq.i_data = data;
4492 if (ioctl(s, SIOCS80211, &ireq) < 0)
4493 err(1, "SIOCS80211");
4494}
4495
4496static const char *
4497get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
4498{
4499 int len;
4500 int hexstr;
4501 u_int8_t *p;
4502
4503 len = *lenp;
4504 p = buf;
4505 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
4506 if (hexstr)
4507 val += 2;
4508 for (;;) {
4509 if (*val == '\0')
4510 break;
4511 if (sep != NULL && strchr(sep, *val) != NULL) {
4512 val++;
4513 break;
4514 }
4515 if (hexstr) {
4516 if (!isxdigit((u_char)val[0])) {
4517 warnx("bad hexadecimal digits");
4518 return NULL;
4519 }
4520 if (!isxdigit((u_char)val[1])) {
4521 warnx("odd count hexadecimal digits");
4522 return NULL;
4523 }
4524 }
4525 if (p >= buf + len) {
4526 if (hexstr)
4527 warnx("hexadecimal digits too long");
4528 else
4529 warnx("string too long");
4530 return NULL;
4531 }
4532 if (hexstr) {
4533#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
4534 *p++ = (tohex((u_char)val[0]) << 4) |
4535 tohex((u_char)val[1]);
4536#undef tohex
4537 val += 2;
4538 } else
4539 *p++ = *val++;
4540 }
4541 len = p - buf;
4542 /* The string "-" is treated as the empty string. */
4543 if (!hexstr && len == 1 && buf[0] == '-') {
4544 len = 0;
4545 memset(buf, 0, *lenp);
4546 } else if (len < *lenp)
4547 memset(p, 0, *lenp - len);
4548 *lenp = len;
4549 return val;
4550}
4551
4552static void
4553print_string(const u_int8_t *buf, int len)
4554{
4555 int i;
4556 int hasspc;
4557
4558 i = 0;
4559 hasspc = 0;
4560 for (; i < len; i++) {
4561 if (!isprint(buf[i]) && buf[i] != '\0')
4562 break;
4563 if (isspace(buf[i]))
4564 hasspc++;
4565 }
4566 if (i == len) {
4567 if (hasspc || len == 0 || buf[0] == '\0')
4568 printf("\"%.*s\"", len, buf);
4569 else
4570 printf("%.*s", len, buf);
4571 } else {
4572 printf("0x");
4573 for (i = 0; i < len; i++)
4574 printf("%02x", buf[i]);
4575 }
4576}
4577
4578/*
4579 * Virtual AP cloning support.
4580 */
4581static struct ieee80211_clone_params params = {
4582 .icp_opmode = IEEE80211_M_STA, /* default to station mode */
4583};
4584
4585static void
4586wlan_create(int s, struct ifreq *ifr)
4587{
4588 static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
4589
4590 if (params.icp_parent[0] == '\0')
4591 errx(1, "must specify a parent when creating a wlan device");
4592 if (params.icp_opmode == IEEE80211_M_WDS &&
4593 memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0)
4594 errx(1, "no bssid specified for WDS (use wlanbssid)");
4595 ifr->ifr_data = (caddr_t) &params;
4596 if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
4597 err(1, "SIOCIFCREATE2");
4598}
4599
4600static
4601DECL_CMD_FUNC(set80211clone_wlandev, arg, d)
4602{
4603 strlcpy(params.icp_parent, arg, IFNAMSIZ);
4604 clone_setcallback(wlan_create);
4605}
4606
4607static
4608DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d)
4609{
4610 const struct ether_addr *ea;
4611
4612 ea = ether_aton(arg);
4613 if (ea == NULL)
4614 errx(1, "%s: cannot parse bssid", arg);
4615 memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN);
4616 clone_setcallback(wlan_create);
4617}
4618
4619static
4620DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d)
4621{
4622 const struct ether_addr *ea;
4623
4624 ea = ether_aton(arg);
4625 if (ea == NULL)
4626 errx(1, "%s: cannot parse addres", arg);
4627 memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN);
4628 params.icp_flags |= IEEE80211_CLONE_MACADDR;
4629 clone_setcallback(wlan_create);
4630}
4631
4632static
4633DECL_CMD_FUNC(set80211clone_wlanmode, arg, d)
4634{
4635#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0)
4636 if (iseq(arg, "sta"))
4637 params.icp_opmode = IEEE80211_M_STA;
4638 else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo"))
4639 params.icp_opmode = IEEE80211_M_AHDEMO;
4640 else if (iseq(arg, "ibss") || iseq(arg, "adhoc"))
4641 params.icp_opmode = IEEE80211_M_IBSS;
4642 else if (iseq(arg, "ap") || iseq(arg, "host"))
4643 params.icp_opmode = IEEE80211_M_HOSTAP;
4644 else if (iseq(arg, "wds"))
4645 params.icp_opmode = IEEE80211_M_WDS;
4646 else if (iseq(arg, "monitor"))
4647 params.icp_opmode = IEEE80211_M_MONITOR;
4648 else if (iseq(arg, "tdma")) {
4649 params.icp_opmode = IEEE80211_M_AHDEMO;
4650 params.icp_flags |= IEEE80211_CLONE_TDMA;
4651 } else
4652 errx(1, "Don't know to create %s for %s", arg, name);
4653 clone_setcallback(wlan_create);
4654#undef iseq
4655}
4656
4657static void
4658set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp)
4659{
4660 /* NB: inverted sense */
4661 if (d)
4662 params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS;
4663 else
4664 params.icp_flags |= IEEE80211_CLONE_NOBEACONS;
4665 clone_setcallback(wlan_create);
4666}
4667
4668static void
4669set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp)
4670{
4671 if (d)
4672 params.icp_flags |= IEEE80211_CLONE_BSSID;
4673 else
4674 params.icp_flags &= ~IEEE80211_CLONE_BSSID;
4675 clone_setcallback(wlan_create);
4676}
4677
4678static void
4679set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp)
4680{
4681 if (d)
4682 params.icp_flags |= IEEE80211_CLONE_WDSLEGACY;
4683 else
4684 params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY;
4685 clone_setcallback(wlan_create);
4686}
4687
4688static struct cmd ieee80211_cmds[] = {
4689 DEF_CMD_ARG("ssid", set80211ssid),
4690 DEF_CMD_ARG("nwid", set80211ssid),
4691 DEF_CMD_ARG("stationname", set80211stationname),
4692 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */
4693 DEF_CMD_ARG("channel", set80211channel),
4694 DEF_CMD_ARG("authmode", set80211authmode),
4695 DEF_CMD_ARG("powersavemode", set80211powersavemode),
4696 DEF_CMD("powersave", 1, set80211powersave),
4697 DEF_CMD("-powersave", 0, set80211powersave),
4698 DEF_CMD_ARG("powersavesleep", set80211powersavesleep),
4699 DEF_CMD_ARG("wepmode", set80211wepmode),
4700 DEF_CMD("wep", 1, set80211wep),
4701 DEF_CMD("-wep", 0, set80211wep),
4702 DEF_CMD_ARG("deftxkey", set80211weptxkey),
4703 DEF_CMD_ARG("weptxkey", set80211weptxkey),
4704 DEF_CMD_ARG("wepkey", set80211wepkey),
4705 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */
4706 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */
4707 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold),
4708 DEF_CMD_ARG("protmode", set80211protmode),
4709 DEF_CMD_ARG("txpower", set80211txpower),
4710 DEF_CMD_ARG("roaming", set80211roaming),
4711 DEF_CMD("wme", 1, set80211wme),
4712 DEF_CMD("-wme", 0, set80211wme),
4713 DEF_CMD("wmm", 1, set80211wme),
4714 DEF_CMD("-wmm", 0, set80211wme),
4715 DEF_CMD("hidessid", 1, set80211hidessid),
4716 DEF_CMD("-hidessid", 0, set80211hidessid),
4717 DEF_CMD("apbridge", 1, set80211apbridge),
4718 DEF_CMD("-apbridge", 0, set80211apbridge),
4719 DEF_CMD_ARG("chanlist", set80211chanlist),
4720 DEF_CMD_ARG("bssid", set80211bssid),
4721 DEF_CMD_ARG("ap", set80211bssid),
4722 DEF_CMD("scan", 0, set80211scan),
4723 DEF_CMD_ARG("list", set80211list),
4724 DEF_CMD_ARG2("cwmin", set80211cwmin),
4725 DEF_CMD_ARG2("cwmax", set80211cwmax),
4726 DEF_CMD_ARG2("aifs", set80211aifs),
4727 DEF_CMD_ARG2("txoplimit", set80211txoplimit),
4728 DEF_CMD_ARG("acm", set80211acm),
4729 DEF_CMD_ARG("-acm", set80211noacm),
4730 DEF_CMD_ARG("ack", set80211ackpolicy),
4731 DEF_CMD_ARG("-ack", set80211noackpolicy),
4732 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin),
4733 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax),
4734 DEF_CMD_ARG2("bss:aifs", set80211bssaifs),
4735 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit),
4736 DEF_CMD_ARG("dtimperiod", set80211dtimperiod),
4737 DEF_CMD_ARG("bintval", set80211bintval),
4738 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd),
4739 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd),
4740 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd),
4741 DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd),
4742 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd),
4743 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd),
4744 DEF_CMD_ARG("mac:add", set80211addmac),
4745 DEF_CMD_ARG("mac:del", set80211delmac),
4746 DEF_CMD_ARG("mac:kick", set80211kickmac),
4747 DEF_CMD("pureg", 1, set80211pureg),
4748 DEF_CMD("-pureg", 0, set80211pureg),
4749 DEF_CMD("ff", 1, set80211fastframes),
4750 DEF_CMD("-ff", 0, set80211fastframes),
4751 DEF_CMD("dturbo", 1, set80211dturbo),
4752 DEF_CMD("-dturbo", 0, set80211dturbo),
4753 DEF_CMD("bgscan", 1, set80211bgscan),
4754 DEF_CMD("-bgscan", 0, set80211bgscan),
4755 DEF_CMD_ARG("bgscanidle", set80211bgscanidle),
4756 DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl),
4757 DEF_CMD_ARG("scanvalid", set80211scanvalid),
4758 DEF_CMD_ARG("roam:rssi", set80211roamrssi),
4759 DEF_CMD_ARG("roam:rate", set80211roamrate),
4760 DEF_CMD_ARG("mcastrate", set80211mcastrate),
4761 DEF_CMD_ARG("ucastrate", set80211ucastrate),
4762 DEF_CMD_ARG("mgtrate", set80211mgtrate),
4763 DEF_CMD_ARG("mgmtrate", set80211mgtrate),
4764 DEF_CMD_ARG("maxretry", set80211maxretry),
4765 DEF_CMD_ARG("fragthreshold", set80211fragthreshold),
4766 DEF_CMD("burst", 1, set80211burst),
4767 DEF_CMD("-burst", 0, set80211burst),
4768 DEF_CMD_ARG("bmiss", set80211bmissthreshold),
4769 DEF_CMD_ARG("bmissthreshold", set80211bmissthreshold),
4770 DEF_CMD("shortgi", 1, set80211shortgi),
4771 DEF_CMD("-shortgi", 0, set80211shortgi),
4772 DEF_CMD("ampdurx", 2, set80211ampdu),
4773 DEF_CMD("-ampdurx", -2, set80211ampdu),
4774 DEF_CMD("ampdutx", 1, set80211ampdu),
4775 DEF_CMD("-ampdutx", -1, set80211ampdu),
4776 DEF_CMD("ampdu", 3, set80211ampdu), /* NB: tx+rx */
4777 DEF_CMD("-ampdu", -3, set80211ampdu),
4778 DEF_CMD_ARG("ampdulimit", set80211ampdulimit),
4779 DEF_CMD_ARG("ampdudensity", set80211ampdudensity),
4780 DEF_CMD("amsdurx", 2, set80211amsdu),
4781 DEF_CMD("-amsdurx", -2, set80211amsdu),
4782 DEF_CMD("amsdutx", 1, set80211amsdu),
4783 DEF_CMD("-amsdutx", -1, set80211amsdu),
4784 DEF_CMD("amsdu", 3, set80211amsdu), /* NB: tx+rx */
4785 DEF_CMD("-amsdu", -3, set80211amsdu),
4786 DEF_CMD_ARG("amsdulimit", set80211amsdulimit),
4787 DEF_CMD("puren", 1, set80211puren),
4788 DEF_CMD("-puren", 0, set80211puren),
4789 DEF_CMD("doth", 1, set80211doth),
4790 DEF_CMD("-doth", 0, set80211doth),
4791 DEF_CMD("dfs", 1, set80211dfs),
4792 DEF_CMD("-dfs", 0, set80211dfs),
4793 DEF_CMD("htcompat", 1, set80211htcompat),
4794 DEF_CMD("-htcompat", 0, set80211htcompat),
4795 DEF_CMD("dwds", 1, set80211dwds),
4796 DEF_CMD("-dwds", 0, set80211dwds),
4797 DEF_CMD("inact", 1, set80211inact),
4798 DEF_CMD("-inact", 0, set80211inact),
4799 DEF_CMD("tsn", 1, set80211tsn),
4800 DEF_CMD("-tsn", 0, set80211tsn),
4801 DEF_CMD_ARG("regdomain", set80211regdomain),
4802 DEF_CMD_ARG("country", set80211country),
4803 DEF_CMD("indoor", 'I', set80211location),
4804 DEF_CMD("-indoor", 'O', set80211location),
4805 DEF_CMD("outdoor", 'O', set80211location),
4806 DEF_CMD("-outdoor", 'I', set80211location),
4807 DEF_CMD("anywhere", ' ', set80211location),
4808 DEF_CMD("ecm", 1, set80211ecm),
4809 DEF_CMD("-ecm", 0, set80211ecm),
4810 DEF_CMD("dotd", 1, set80211dotd),
4811 DEF_CMD("-dotd", 0, set80211dotd),
4812 DEF_CMD_ARG("htprotmode", set80211htprotmode),
4813 DEF_CMD("ht20", 1, set80211htconf),
4814 DEF_CMD("-ht20", 0, set80211htconf),
4815 DEF_CMD("ht40", 3, set80211htconf), /* NB: 20+40 */
4816 DEF_CMD("-ht40", 0, set80211htconf),
4817 DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */
4818 DEF_CMD("-ht", 0, set80211htconf),
4819 DEF_CMD("rifs", 1, set80211rifs),
4820 DEF_CMD("-rifs", 0, set80211rifs),
4821 DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps),
4822 DEF_CMD("smpsdyn", IEEE80211_HTCAP_SMPS_DYNAMIC, set80211smps),
4823 DEF_CMD("-smps", IEEE80211_HTCAP_SMPS_OFF, set80211smps),
4824 /* XXX for testing */
4825 DEF_CMD_ARG("chanswitch", set80211chanswitch),
4826
4827 DEF_CMD_ARG("tdmaslot", set80211tdmaslot),
4828 DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt),
4829 DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen),
4830 DEF_CMD_ARG("tdmabintval", set80211tdmabintval),
4831
4832 /* vap cloning support */
4833 DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr),
4834 DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid),
4835 DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev),
4836 DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode),
4837 DEF_CLONE_CMD("beacons", 1, set80211clone_beacons),
4838 DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons),
4839 DEF_CLONE_CMD("bssid", 1, set80211clone_bssid),
4840 DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid),
4841 DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy),
4842 DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy),
4843};
4844static struct afswtch af_ieee80211 = {
4845 .af_name = "af_ieee80211",
4846 .af_af = AF_UNSPEC,
4847 .af_other_status = ieee80211_status,
4848};
4849
4850static __constructor void
4851ieee80211_ctor(void)
4852{
4853#define N(a) (sizeof(a) / sizeof(a[0]))
4854 int i;
4855
4856 for (i = 0; i < N(ieee80211_cmds); i++)
4857 cmd_register(&ieee80211_cmds[i]);
4858 af_register(&af_ieee80211);
4859#undef N
4860}