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