ifconfig(8): Print more WPS attributes in verbose "list scan" output
[dragonfly.git] / sbin / ifconfig / ifieee80211.c
CommitLineData
984263bc
MD
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.
b50e4759
MD
12 * 3. The name of The Aerospace Corporation may not be used to endorse or
13 * promote products derived from this software.
984263bc
MD
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 *
dc4301ae 27 * $FreeBSD: head/sbin/ifconfig/ifieee80211.c 203970 2010-02-16 21:39:20Z imp $
984263bc
MD
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.
984263bc
MD
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>
841ab66c 70#include <net/if_media.h>
984263bc 71#include <net/route.h>
841ab66c 72
354573f1 73#include <netproto/802_11/ieee80211_ioctl.h>
dc4301ae
RP
74#include <netproto/802_11/ieee80211_dragonfly.h>
75#include <netproto/802_11/ieee80211_superg.h>
76#include <netproto/802_11/ieee80211_tdma.h>
77#include <netproto/802_11/ieee80211_mesh.h>
2ecde614 78#include <netproto/802_11/ieee80211_wps.h>
984263bc 79
dc4301ae 80#include <assert.h>
984263bc
MD
81#include <ctype.h>
82#include <err.h>
83#include <errno.h>
84#include <fcntl.h>
841ab66c 85#include <inttypes.h>
d5e6c24f
AL
86#include <langinfo.h>
87#include <locale.h>
88#include <stdarg.h>
89#include <stddef.h>
984263bc
MD
90#include <stdio.h>
91#include <stdlib.h>
92#include <string.h>
93#include <unistd.h>
94
95#include "ifconfig.h"
dc4301ae
RP
96#include "regdomain.h"
97
98#ifndef IEEE80211_FIXED_RATE_NONE
99#define IEEE80211_FIXED_RATE_NONE 0xff
100#endif
101
102/* XXX need these publicly defined or similar */
103#ifndef IEEE80211_NODE_AUTH
104#define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */
105#define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */
106#define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */
107#define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */
108#define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */
109#define IEEE80211_NODE_HT 0x000040 /* HT enabled */
110#define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */
111#define IEEE80211_NODE_WPS 0x000100 /* WPS association */
112#define IEEE80211_NODE_TSN 0x000200 /* TSN association */
113#define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */
114#define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */
115#define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */
116#define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */
117#define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */
118#define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */
119#define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */
120#define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */
121#define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */
122#define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */
123#endif
124
125#define MAXCHAN 1536 /* max 1.5K channels */
126
127#define MAXCOL 78
128static int col;
129static char spacer;
130
131static void LINE_INIT(char c);
132static void LINE_BREAK(void);
b58f1e66 133static void LINE_CHECK(const char *fmt, ...) __printflike(1, 2);
dc4301ae
RP
134
135static const char *modename[IEEE80211_MODE_MAX] = {
136 [IEEE80211_MODE_AUTO] = "auto",
137 [IEEE80211_MODE_11A] = "11a",
138 [IEEE80211_MODE_11B] = "11b",
139 [IEEE80211_MODE_11G] = "11g",
140 [IEEE80211_MODE_FH] = "fh",
141 [IEEE80211_MODE_TURBO_A] = "turboA",
142 [IEEE80211_MODE_TURBO_G] = "turboG",
143 [IEEE80211_MODE_STURBO_A] = "sturbo",
144 [IEEE80211_MODE_11NA] = "11na",
145 [IEEE80211_MODE_11NG] = "11ng",
146 [IEEE80211_MODE_HALF] = "half",
147 [IEEE80211_MODE_QUARTER] = "quarter"
148};
984263bc 149
dc4301ae
RP
150static void set80211(int s, int type, int val, int len, void *data);
151static int get80211(int s, int type, void *data, int len);
46158ff5 152static int get80211len(int s, int type, void *data, size_t len, size_t *plen);
dc4301ae 153static int get80211val(int s, int type, int *val);
984263bc
MD
154static const char *get_string(const char *val, const char *sep,
155 u_int8_t *buf, int *lenp);
156static void print_string(const u_int8_t *buf, int len);
dc4301ae
RP
157static void print_regdomain(const struct ieee80211_regdomain *, int);
158static void print_channels(int, const struct ieee80211req_chaninfo *,
159 int allchans, int verbose);
160static void regdomain_makechannels(struct ieee80211_regdomain_req *,
161 const struct ieee80211_devcaps_req *);
162static const char *mesh_linkstate_string(uint8_t state);
163
164static struct ieee80211req_chaninfo *chaninfo;
165static struct ieee80211_regdomain regdomain;
166static int gotregdomain = 0;
167static struct ieee80211_roamparams_req roamparams;
168static int gotroam = 0;
169static struct ieee80211_txparams_req txparams;
170static int gottxparams = 0;
171static struct ieee80211_channel curchan;
172static int gotcurchan = 0;
173static struct ifmediareq *ifmr;
174static int htconf = 0;
46158ff5
AL
175static int gothtconf = 0;
176
177static int
178iseq(const char *a, const char *b)
179{
180 return (strcasecmp(a, b) == 0);
181}
182
183static int
184ismatch(const char *a, const char *b)
185{
186 return (strncasecmp(a, b, strlen(b)) == 0);
187}
dc4301ae
RP
188
189static void
190gethtconf(int s)
191{
192 if (gothtconf)
193 return;
194 if (get80211val(s, IEEE80211_IOC_HTCONF, &htconf) < 0)
195 warn("unable to get HT configuration information");
196 gothtconf = 1;
197}
198
199/*
200 * Collect channel info from the kernel. We use this (mostly)
201 * to handle mapping between frequency and IEEE channel number.
202 */
203static void
204getchaninfo(int s)
205{
206 if (chaninfo != NULL)
207 return;
208 chaninfo = malloc(IEEE80211_CHANINFO_SIZE(MAXCHAN));
209 if (chaninfo == NULL)
210 errx(1, "no space for channel list");
211 if (get80211(s, IEEE80211_IOC_CHANINFO, chaninfo,
212 IEEE80211_CHANINFO_SIZE(MAXCHAN)) < 0)
213 err(1, "unable to get channel information");
214 ifmr = ifmedia_getstate(s);
215 gethtconf(s);
216}
217
218static struct regdata *
219getregdata(void)
220{
221 static struct regdata *rdp = NULL;
222 if (rdp == NULL) {
223 rdp = lib80211_alloc_regdata();
224 if (rdp == NULL)
225 errx(-1, "missing or corrupted regdomain database");
226 }
227 return rdp;
228}
229
230/*
231 * Given the channel at index i with attributes from,
232 * check if there is a channel with attributes to in
233 * the channel table. With suitable attributes this
234 * allows the caller to look for promotion; e.g. from
235 * 11b > 11g.
236 */
237static int
46158ff5 238canpromote(u_int i, uint32_t from, uint32_t to)
dc4301ae
RP
239{
240 const struct ieee80211_channel *fc = &chaninfo->ic_chans[i];
46158ff5 241 u_int j;
dc4301ae
RP
242
243 if ((fc->ic_flags & from) != from)
244 return i;
245 /* NB: quick check exploiting ordering of chans w/ same frequency */
246 if (i+1 < chaninfo->ic_nchans &&
247 chaninfo->ic_chans[i+1].ic_freq == fc->ic_freq &&
248 (chaninfo->ic_chans[i+1].ic_flags & to) == to)
249 return i+1;
250 /* brute force search in case channel list is not ordered */
251 for (j = 0; j < chaninfo->ic_nchans; j++) {
252 const struct ieee80211_channel *tc = &chaninfo->ic_chans[j];
253 if (j != i &&
254 tc->ic_freq == fc->ic_freq && (tc->ic_flags & to) == to)
255 return j;
256 }
257 return i;
258}
259
260/*
261 * Handle channel promotion. When a channel is specified with
262 * only a frequency we want to promote it to the ``best'' channel
263 * available. The channel list has separate entries for 11b, 11g,
264 * 11a, and 11n[ga] channels so specifying a frequency w/o any
265 * attributes requires we upgrade, e.g. from 11b -> 11g. This
266 * gets complicated when the channel is specified on the same
267 * command line with a media request that constrains the available
268 * channe list (e.g. mode 11a); we want to honor that to avoid
269 * confusing behaviour.
270 */
271static int
272promote(int i)
273{
274 /*
275 * Query the current mode of the interface in case it's
276 * constrained (e.g. to 11a). We must do this carefully
277 * as there may be a pending ifmedia request in which case
278 * asking the kernel will give us the wrong answer. This
279 * is an unfortunate side-effect of the way ifconfig is
280 * structure for modularity (yech).
281 *
282 * NB: ifmr is actually setup in getchaninfo (above); we
283 * assume it's called coincident with to this call so
284 * we have a ``current setting''; otherwise we must pass
285 * the socket descriptor down to here so we can make
286 * the ifmedia_getstate call ourselves.
287 */
288 int chanmode = ifmr != NULL ? IFM_MODE(ifmr->ifm_current) : IFM_AUTO;
289
290 /* when ambiguous promote to ``best'' */
291 /* NB: we abitrarily pick HT40+ over HT40- */
292 if (chanmode != IFM_IEEE80211_11B)
293 i = canpromote(i, IEEE80211_CHAN_B, IEEE80211_CHAN_G);
294 if (chanmode != IFM_IEEE80211_11G && (htconf & 1)) {
295 i = canpromote(i, IEEE80211_CHAN_G,
296 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20);
297 if (htconf & 2) {
298 i = canpromote(i, IEEE80211_CHAN_G,
299 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D);
300 i = canpromote(i, IEEE80211_CHAN_G,
301 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U);
302 }
303 }
304 if (chanmode != IFM_IEEE80211_11A && (htconf & 1)) {
305 i = canpromote(i, IEEE80211_CHAN_A,
306 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20);
307 if (htconf & 2) {
308 i = canpromote(i, IEEE80211_CHAN_A,
309 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D);
310 i = canpromote(i, IEEE80211_CHAN_A,
311 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U);
312 }
313 }
314 return i;
315}
316
317static void
46158ff5 318mapfreq(struct ieee80211_channel *chan, uint16_t freq, uint32_t flags)
dc4301ae 319{
46158ff5 320 u_int i;
dc4301ae
RP
321
322 for (i = 0; i < chaninfo->ic_nchans; i++) {
323 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
324
325 if (c->ic_freq == freq && (c->ic_flags & flags) == flags) {
326 if (flags == 0) {
327 /* when ambiguous promote to ``best'' */
328 c = &chaninfo->ic_chans[promote(i)];
329 }
330 *chan = *c;
331 return;
332 }
333 }
334 errx(1, "unknown/undefined frequency %u/0x%x", freq, flags);
335}
336
337static void
46158ff5 338mapchan(struct ieee80211_channel *chan, uint8_t ieee, uint32_t flags)
dc4301ae 339{
46158ff5 340 u_int i;
dc4301ae
RP
341
342 for (i = 0; i < chaninfo->ic_nchans; i++) {
343 const struct ieee80211_channel *c = &chaninfo->ic_chans[i];
344
345 if (c->ic_ieee == ieee && (c->ic_flags & flags) == flags) {
346 if (flags == 0) {
347 /* when ambiguous promote to ``best'' */
348 c = &chaninfo->ic_chans[promote(i)];
349 }
350 *chan = *c;
351 return;
352 }
353 }
354 errx(1, "unknown/undefined channel number %d flags 0x%x", ieee, flags);
355}
356
357static const struct ieee80211_channel *
358getcurchan(int s)
359{
360 if (gotcurchan)
361 return &curchan;
362 if (get80211(s, IEEE80211_IOC_CURCHAN, &curchan, sizeof(curchan)) < 0) {
363 int val;
364 /* fall back to legacy ioctl */
365 if (get80211val(s, IEEE80211_IOC_CHANNEL, &val) < 0)
366 err(-1, "cannot figure out current channel");
367 getchaninfo(s);
368 mapchan(&curchan, val, 0);
369 }
370 gotcurchan = 1;
371 return &curchan;
372}
373
374static enum ieee80211_phymode
375chan2mode(const struct ieee80211_channel *c)
376{
377 if (IEEE80211_IS_CHAN_HTA(c))
378 return IEEE80211_MODE_11NA;
379 if (IEEE80211_IS_CHAN_HTG(c))
380 return IEEE80211_MODE_11NG;
381 if (IEEE80211_IS_CHAN_108A(c))
382 return IEEE80211_MODE_TURBO_A;
383 if (IEEE80211_IS_CHAN_108G(c))
384 return IEEE80211_MODE_TURBO_G;
385 if (IEEE80211_IS_CHAN_ST(c))
386 return IEEE80211_MODE_STURBO_A;
387 if (IEEE80211_IS_CHAN_FHSS(c))
388 return IEEE80211_MODE_FH;
389 if (IEEE80211_IS_CHAN_HALF(c))
390 return IEEE80211_MODE_HALF;
391 if (IEEE80211_IS_CHAN_QUARTER(c))
392 return IEEE80211_MODE_QUARTER;
393 if (IEEE80211_IS_CHAN_A(c))
394 return IEEE80211_MODE_11A;
395 if (IEEE80211_IS_CHAN_ANYG(c))
396 return IEEE80211_MODE_11G;
397 if (IEEE80211_IS_CHAN_B(c))
398 return IEEE80211_MODE_11B;
399 return IEEE80211_MODE_AUTO;
400}
401
402static void
403getroam(int s)
404{
405 if (gotroam)
406 return;
407 if (get80211(s, IEEE80211_IOC_ROAM,
408 &roamparams, sizeof(roamparams)) < 0)
409 err(1, "unable to get roaming parameters");
410 gotroam = 1;
411}
412
413static void
414setroam_cb(int s, void *arg)
415{
416 struct ieee80211_roamparams_req *roam = arg;
417 set80211(s, IEEE80211_IOC_ROAM, 0, sizeof(*roam), roam);
418}
419
420static void
421gettxparams(int s)
422{
423 if (gottxparams)
424 return;
425 if (get80211(s, IEEE80211_IOC_TXPARAMS,
426 &txparams, sizeof(txparams)) < 0)
427 err(1, "unable to get transmit parameters");
428 gottxparams = 1;
429}
430
431static void
432settxparams_cb(int s, void *arg)
433{
434 struct ieee80211_txparams_req *txp = arg;
435 set80211(s, IEEE80211_IOC_TXPARAMS, 0, sizeof(*txp), txp);
436}
437
438static void
439getregdomain(int s)
440{
441 if (gotregdomain)
442 return;
443 if (get80211(s, IEEE80211_IOC_REGDOMAIN,
444 &regdomain, sizeof(regdomain)) < 0)
445 err(1, "unable to get regulatory domain info");
446 gotregdomain = 1;
447}
448
449static void
450getdevcaps(int s, struct ieee80211_devcaps_req *dc)
451{
452 if (get80211(s, IEEE80211_IOC_DEVCAPS, dc,
453 IEEE80211_DEVCAPS_SPACE(dc)) < 0)
454 err(1, "unable to get device capabilities");
455}
456
457static void
458setregdomain_cb(int s, void *arg)
459{
460 struct ieee80211_regdomain_req *req;
461 struct ieee80211_regdomain *rd = arg;
462 struct ieee80211_devcaps_req *dc;
463 struct regdata *rdp = getregdata();
464
465 if (rd->country != NO_COUNTRY) {
466 const struct country *cc;
467 /*
468 * Check current country seting to make sure it's
469 * compatible with the new regdomain. If not, then
470 * override it with any default country for this
471 * SKU. If we cannot arrange a match, then abort.
472 */
473 cc = lib80211_country_findbycc(rdp, rd->country);
474 if (cc == NULL)
475 errx(1, "unknown ISO country code %d", rd->country);
476 if (cc->rd->sku != rd->regdomain) {
477 const struct regdomain *rp;
478 /*
479 * Check if country is incompatible with regdomain.
480 * To enable multiple regdomains for a country code
481 * we permit a mismatch between the regdomain and
482 * the country's associated regdomain when the
483 * regdomain is setup w/o a default country. For
484 * example, US is bound to the FCC regdomain but
485 * we allow US to be combined with FCC3 because FCC3
486 * has not default country. This allows bogus
487 * combinations like FCC3+DK which are resolved when
488 * constructing the channel list by deferring to the
489 * regdomain to construct the channel list.
490 */
491 rp = lib80211_regdomain_findbysku(rdp, rd->regdomain);
492 if (rp == NULL)
493 errx(1, "country %s (%s) is not usable with "
494 "regdomain %d", cc->isoname, cc->name,
495 rd->regdomain);
496 else if (rp->cc != NULL && rp->cc != cc)
497 errx(1, "country %s (%s) is not usable with "
498 "regdomain %s", cc->isoname, cc->name,
499 rp->name);
500 }
501 }
502 /*
503 * Fetch the device capabilities and calculate the
504 * full set of netbands for which we request a new
505 * channel list be constructed. Once that's done we
506 * push the regdomain info + channel list to the kernel.
507 */
508 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
509 if (dc == NULL)
510 errx(1, "no space for device capabilities");
511 dc->dc_chaninfo.ic_nchans = MAXCHAN;
512 getdevcaps(s, dc);
513#if 0
514 if (verbose) {
515 printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
516 printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
517 printf("htcaps : 0x%x\n", dc->dc_htcaps);
518 memcpy(chaninfo, &dc->dc_chaninfo,
519 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
520 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
521 }
522#endif
523 req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
524 if (req == NULL)
525 errx(1, "no space for regdomain request");
526 req->rd = *rd;
527 regdomain_makechannels(req, dc);
528 if (verbose) {
529 LINE_INIT(':');
530 print_regdomain(rd, 1/*verbose*/);
531 LINE_BREAK();
532 /* blech, reallocate channel list for new data */
533 if (chaninfo != NULL)
534 free(chaninfo);
535 chaninfo = malloc(IEEE80211_CHANINFO_SPACE(&req->chaninfo));
536 if (chaninfo == NULL)
537 errx(1, "no space for channel list");
538 memcpy(chaninfo, &req->chaninfo,
539 IEEE80211_CHANINFO_SPACE(&req->chaninfo));
540 print_channels(s, &req->chaninfo, 1/*allchans*/, 1/*verbose*/);
541 }
542 if (req->chaninfo.ic_nchans == 0)
543 errx(1, "no channels calculated");
544 set80211(s, IEEE80211_IOC_REGDOMAIN, 0,
545 IEEE80211_REGDOMAIN_SPACE(req), req);
546 free(req);
547 free(dc);
548}
549
550static int
551ieee80211_mhz2ieee(int freq, int flags)
552{
553 struct ieee80211_channel chan;
554 mapfreq(&chan, freq, flags);
555 return chan.ic_ieee;
556}
984263bc 557
841ab66c
SZ
558static int
559isanyarg(const char *arg)
560{
46158ff5
AL
561 return (ismatch(arg, "-") ||
562 ismatch(arg, "any") ||
563 ismatch(arg, "off"));
841ab66c
SZ
564}
565
ca74a0a2 566static void
841ab66c 567set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
568{
569 int ssid;
570 int len;
841ab66c 571 u_int8_t data[IEEE80211_NWID_LEN];
984263bc
MD
572
573 ssid = 0;
b50e4759 574 len = strlen(val);
dc4301ae 575 if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
984263bc
MD
576 ssid = atoi(val)-1;
577 val += 2;
578 }
579
580 bzero(data, sizeof(data));
581 len = sizeof(data);
841ab66c
SZ
582 if (get_string(val, NULL, data, &len) == NULL)
583 exit(1);
984263bc
MD
584
585 set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
586}
587
dc4301ae
RP
588static void
589set80211meshid(const char *val, int d, int s, const struct afswtch *rafp)
590{
591 int len;
592 u_int8_t data[IEEE80211_NWID_LEN];
593
594 memset(data, 0, sizeof(data));
595 len = sizeof(data);
596 if (get_string(val, NULL, data, &len) == NULL)
597 exit(1);
598
599 set80211(s, IEEE80211_IOC_MESH_ID, 0, len, data);
46158ff5 600}
dc4301ae 601
ca74a0a2 602static void
841ab66c 603set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
604{
605 int len;
606 u_int8_t data[33];
607
608 bzero(data, sizeof(data));
609 len = sizeof(data);
610 get_string(val, NULL, data, &len);
611
612 set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
613}
614
841ab66c 615/*
dc4301ae
RP
616 * Parse a channel specification for attributes/flags.
617 * The syntax is:
618 * freq/xx channel width (5,10,20,40,40+,40-)
619 * freq:mode channel mode (a,b,g,h,n,t,s,d)
620 *
621 * These can be combined in either order; e.g. 2437:ng/40.
622 * Modes are case insensitive.
623 *
624 * The result is not validated here; it's assumed to be
625 * checked against the channel table fetched from the kernel.
46158ff5 626 */
dc4301ae
RP
627static int
628getchannelflags(const char *val, int freq)
841ab66c 629{
dc4301ae
RP
630#define _CHAN_HT 0x80000000
631 const char *cp;
632 int flags;
633
634 flags = 0;
635
636 cp = strchr(val, ':');
637 if (cp != NULL) {
638 for (cp++; isalpha((int) *cp); cp++) {
639 /* accept mixed case */
640 int c = *cp;
641 if (isupper(c))
642 c = tolower(c);
643 switch (c) {
644 case 'a': /* 802.11a */
645 flags |= IEEE80211_CHAN_A;
646 break;
647 case 'b': /* 802.11b */
648 flags |= IEEE80211_CHAN_B;
649 break;
650 case 'g': /* 802.11g */
651 flags |= IEEE80211_CHAN_G;
652 break;
653 case 'h': /* ht = 802.11n */
654 case 'n': /* 802.11n */
655 flags |= _CHAN_HT; /* NB: private */
656 break;
657 case 'd': /* dt = Atheros Dynamic Turbo */
658 flags |= IEEE80211_CHAN_TURBO;
659 break;
660 case 't': /* ht, dt, st, t */
661 /* dt and unadorned t specify Dynamic Turbo */
662 if ((flags & (IEEE80211_CHAN_STURBO|_CHAN_HT)) == 0)
663 flags |= IEEE80211_CHAN_TURBO;
664 break;
665 case 's': /* st = Atheros Static Turbo */
666 flags |= IEEE80211_CHAN_STURBO;
667 break;
668 default:
669 errx(-1, "%s: Invalid channel attribute %c\n",
670 val, *cp);
671 }
672 }
673 }
674 cp = strchr(val, '/');
675 if (cp != NULL) {
676 char *ep;
677 u_long cw = strtoul(cp+1, &ep, 10);
678
679 switch (cw) {
680 case 5:
681 flags |= IEEE80211_CHAN_QUARTER;
682 break;
683 case 10:
684 flags |= IEEE80211_CHAN_HALF;
685 break;
686 case 20:
687 /* NB: this may be removed below */
688 flags |= IEEE80211_CHAN_HT20;
689 break;
690 case 40:
691 if (ep != NULL && *ep == '+')
692 flags |= IEEE80211_CHAN_HT40U;
693 else if (ep != NULL && *ep == '-')
694 flags |= IEEE80211_CHAN_HT40D;
695 break;
696 default:
697 errx(-1, "%s: Invalid channel width\n", val);
698 }
699 }
700 /*
701 * Cleanup specifications.
46158ff5 702 */
dc4301ae
RP
703 if ((flags & _CHAN_HT) == 0) {
704 /*
705 * If user specified freq/20 or freq/40 quietly remove
706 * HT cw attributes depending on channel use. To give
707 * an explicit 20/40 width for an HT channel you must
708 * indicate it is an HT channel since all HT channels
709 * are also usable for legacy operation; e.g. freq:n/40.
710 */
711 flags &= ~IEEE80211_CHAN_HT;
712 } else {
713 /*
714 * Remove private indicator that this is an HT channel
715 * and if no explicit channel width has been given
716 * provide the default settings.
717 */
718 flags &= ~_CHAN_HT;
719 if ((flags & IEEE80211_CHAN_HT) == 0) {
720 struct ieee80211_channel chan;
721 /*
722 * Consult the channel list to see if we can use
723 * HT40+ or HT40- (if both the map routines choose).
724 */
725 if (freq > 255)
726 mapfreq(&chan, freq, 0);
727 else
728 mapchan(&chan, freq, 0);
729 flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
730 }
731 }
732 return flags;
733#undef _CHAN_HT
841ab66c
SZ
734}
735
dc4301ae
RP
736static void
737getchannel(int s, struct ieee80211_channel *chan, const char *val)
841ab66c 738{
dc4301ae
RP
739 int v, flags;
740 char *eptr;
741
742 memset(chan, 0, sizeof(*chan));
743 if (isanyarg(val)) {
744 chan->ic_freq = IEEE80211_CHAN_ANY;
745 return;
746 }
747 getchaninfo(s);
748 errno = 0;
749 v = strtol(val, &eptr, 10);
750 if (val[0] == '\0' || val == eptr || errno == ERANGE ||
751 /* channel may be suffixed with nothing, :flag, or /width */
752 (eptr[0] != '\0' && eptr[0] != ':' && eptr[0] != '/'))
753 errx(1, "invalid channel specification%s",
754 errno == ERANGE ? " (out of range)" : "");
755 flags = getchannelflags(val, v);
756 if (v > 255) { /* treat as frequency */
757 mapfreq(chan, v, flags);
758 } else {
759 mapchan(chan, v, flags);
760 }
841ab66c
SZ
761}
762
ca74a0a2 763static void
841ab66c 764set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
984263bc 765{
dc4301ae
RP
766 struct ieee80211_channel chan;
767
768 getchannel(s, &chan, val);
769 set80211(s, IEEE80211_IOC_CURCHAN, 0, sizeof(chan), &chan);
770}
771
772static void
773set80211chanswitch(const char *val, int d, int s, const struct afswtch *rafp)
774{
775 struct ieee80211_chanswitch_req csr;
776
777 getchannel(s, &csr.csa_chan, val);
778 csr.csa_mode = 1;
779 csr.csa_count = 5;
780 set80211(s, IEEE80211_IOC_CHANSWITCH, 0, sizeof(csr), &csr);
984263bc
MD
781}
782
ca74a0a2 783static void
841ab66c 784set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
785{
786 int mode;
787
46158ff5 788 if (iseq(val, "none")) {
984263bc 789 mode = IEEE80211_AUTH_NONE;
46158ff5 790 } else if (iseq(val, "open")) {
984263bc 791 mode = IEEE80211_AUTH_OPEN;
46158ff5 792 } else if (iseq(val, "shared")) {
984263bc 793 mode = IEEE80211_AUTH_SHARED;
46158ff5 794 } else if (iseq(val, "8021x")) {
841ab66c 795 mode = IEEE80211_AUTH_8021X;
46158ff5 796 } else if (iseq(val, "wpa")) {
841ab66c 797 mode = IEEE80211_AUTH_WPA;
984263bc 798 } else {
841ab66c 799 errx(1, "unknown authmode");
984263bc
MD
800 }
801
802 set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
803}
804
ca74a0a2 805static void
841ab66c 806set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
807{
808 int mode;
809
46158ff5 810 if (iseq(val, "off")) {
984263bc 811 mode = IEEE80211_POWERSAVE_OFF;
46158ff5 812 } else if (iseq(val, "on")) {
984263bc 813 mode = IEEE80211_POWERSAVE_ON;
46158ff5 814 } else if (iseq(val, "cam")) {
984263bc 815 mode = IEEE80211_POWERSAVE_CAM;
46158ff5 816 } else if (iseq(val, "psp")) {
984263bc 817 mode = IEEE80211_POWERSAVE_PSP;
46158ff5 818 } else if (iseq(val, "psp-cam")) {
984263bc
MD
819 mode = IEEE80211_POWERSAVE_PSP_CAM;
820 } else {
841ab66c 821 errx(1, "unknown powersavemode");
984263bc
MD
822 }
823
824 set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
825}
826
ca74a0a2 827static void
841ab66c 828set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
829{
830 if (d == 0)
831 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
832 0, NULL);
833 else
834 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON,
835 0, NULL);
836}
837
ca74a0a2 838static void
841ab66c 839set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
840{
841 set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
842}
843
ca74a0a2 844static void
841ab66c 845set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
846{
847 int mode;
848
46158ff5 849 if (iseq(val, "off")) {
984263bc 850 mode = IEEE80211_WEP_OFF;
46158ff5 851 } else if (iseq(val, "on")) {
984263bc 852 mode = IEEE80211_WEP_ON;
46158ff5 853 } else if (iseq(val, "mixed")) {
984263bc
MD
854 mode = IEEE80211_WEP_MIXED;
855 } else {
841ab66c 856 errx(1, "unknown wep mode");
984263bc
MD
857 }
858
859 set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
860}
861
ca74a0a2 862static void
841ab66c 863set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
864{
865 set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
866}
867
841ab66c
SZ
868static int
869isundefarg(const char *arg)
870{
46158ff5 871 return (strcmp(arg, "-") == 0 || ismatch(arg, "undef"));
841ab66c
SZ
872}
873
ca74a0a2 874static void
841ab66c 875set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
984263bc 876{
841ab66c
SZ
877 if (isundefarg(val))
878 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
879 else
880 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
984263bc
MD
881}
882
ca74a0a2 883static void
841ab66c 884set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
885{
886 int key = 0;
887 int len;
b50e4759 888 u_int8_t data[IEEE80211_KEYBUF_SIZE];
984263bc 889
dc4301ae 890 if (isdigit((int)val[0]) && val[1] == ':') {
984263bc
MD
891 key = atoi(val)-1;
892 val += 2;
893 }
894
895 bzero(data, sizeof(data));
896 len = sizeof(data);
897 get_string(val, NULL, data, &len);
898
899 set80211(s, IEEE80211_IOC_WEPKEY, key, len, data);
900}
901
902/*
05e9456a 903 * This function is purely a NetBSD compatibility interface. The NetBSD
841ab66c 904 * interface is too inflexible, but it's there so we'll support it since
984263bc
MD
905 * it's not all that hard.
906 */
ca74a0a2 907static void
841ab66c 908set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
984263bc
MD
909{
910 int txkey;
911 int i, len;
b50e4759 912 u_int8_t data[IEEE80211_KEYBUF_SIZE];
984263bc
MD
913
914 set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
915
dc4301ae 916 if (isdigit((int)val[0]) && val[1] == ':') {
984263bc
MD
917 txkey = val[0]-'0'-1;
918 val += 2;
919
b50e4759 920 for (i = 0; i < 4; i++) {
984263bc
MD
921 bzero(data, sizeof(data));
922 len = sizeof(data);
923 val = get_string(val, ",", data, &len);
841ab66c
SZ
924 if (val == NULL)
925 exit(1);
984263bc
MD
926
927 set80211(s, IEEE80211_IOC_WEPKEY, i, len, data);
928 }
929 } else {
930 bzero(data, sizeof(data));
931 len = sizeof(data);
932 get_string(val, NULL, data, &len);
933 txkey = 0;
934
935 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data);
936
937 bzero(data, sizeof(data));
b50e4759 938 for (i = 1; i < 4; i++)
984263bc
MD
939 set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data);
940 }
941
942 set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
943}
944
ca74a0a2 945static void
841ab66c 946set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
b50e4759 947{
841ab66c
SZ
948 set80211(s, IEEE80211_IOC_RTSTHRESHOLD,
949 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL);
b50e4759
MD
950}
951
ca74a0a2 952static void
841ab66c 953set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
b50e4759
MD
954{
955 int mode;
956
46158ff5 957 if (iseq(val, "off")) {
b50e4759 958 mode = IEEE80211_PROTMODE_OFF;
46158ff5 959 } else if (iseq(val, "cts")) {
b50e4759 960 mode = IEEE80211_PROTMODE_CTS;
46158ff5 961 } else if (ismatch(val, "rts")) {
b50e4759
MD
962 mode = IEEE80211_PROTMODE_RTSCTS;
963 } else {
841ab66c 964 errx(1, "unknown protection mode");
b50e4759
MD
965 }
966
967 set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
968}
969
dc4301ae
RP
970static void
971set80211htprotmode(const char *val, int d, int s, const struct afswtch *rafp)
972{
973 int mode;
974
46158ff5 975 if (iseq(val, "off")) {
dc4301ae 976 mode = IEEE80211_PROTMODE_OFF;
46158ff5 977 } else if (ismatch(val, "rts")) {
dc4301ae
RP
978 mode = IEEE80211_PROTMODE_RTSCTS;
979 } else {
980 errx(1, "unknown protection mode");
981 }
982
983 set80211(s, IEEE80211_IOC_HTPROTMODE, mode, 0, NULL);
984}
985
ca74a0a2 986static void
841ab66c 987set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
b50e4759 988{
dc4301ae
RP
989 double v = atof(val);
990 int txpow;
991
992 txpow = (int) (2*v);
993 if (txpow != 2*v)
994 errx(-1, "invalid tx power (must be .5 dBm units)");
995 set80211(s, IEEE80211_IOC_TXPOWER, txpow, 0, NULL);
b50e4759
MD
996}
997
841ab66c
SZ
998#define IEEE80211_ROAMING_DEVICE 0
999#define IEEE80211_ROAMING_AUTO 1
1000#define IEEE80211_ROAMING_MANUAL 2
1001
ca74a0a2 1002static void
841ab66c 1003set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
984263bc 1004{
841ab66c
SZ
1005 int mode;
1006
46158ff5 1007 if (iseq(val, "device")) {
841ab66c 1008 mode = IEEE80211_ROAMING_DEVICE;
46158ff5 1009 } else if (iseq(val, "auto")) {
841ab66c 1010 mode = IEEE80211_ROAMING_AUTO;
46158ff5 1011 } else if (iseq(val, "manual")) {
841ab66c
SZ
1012 mode = IEEE80211_ROAMING_MANUAL;
1013 } else {
1014 errx(1, "unknown roaming mode");
1015 }
1016 set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
1017}
984263bc 1018
841ab66c
SZ
1019static void
1020set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
1021{
1022 set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
1023}
984263bc 1024
841ab66c
SZ
1025static void
1026set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
1027{
1028 set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
1029}
1030
1031static void
1032set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
1033{
1034 set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
1035}
1036
dc4301ae
RP
1037static void
1038set80211fastframes(const char *val, int d, int s, const struct afswtch *rafp)
1039{
1040 set80211(s, IEEE80211_IOC_FF, d, 0, NULL);
1041}
1042
1043static void
1044set80211dturbo(const char *val, int d, int s, const struct afswtch *rafp)
1045{
1046 set80211(s, IEEE80211_IOC_TURBOP, d, 0, NULL);
1047}
1048
841ab66c
SZ
1049static void
1050set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
1051{
1052 struct ieee80211req_chanlist chanlist;
841ab66c
SZ
1053 char *temp, *cp, *tp;
1054
2d1bf902 1055 temp = strdup(val);
841ab66c 1056 if (temp == NULL)
2d1bf902 1057 errx(1, "strdup failed");
841ab66c
SZ
1058 memset(&chanlist, 0, sizeof(chanlist));
1059 cp = temp;
1060 for (;;) {
dc4301ae 1061 int first, last, f, c;
841ab66c
SZ
1062
1063 tp = strchr(cp, ',');
1064 if (tp != NULL)
1065 *tp++ = '\0';
1066 switch (sscanf(cp, "%u-%u", &first, &last)) {
1067 case 1:
dc4301ae
RP
1068 if (first > IEEE80211_CHAN_MAX)
1069 errx(-1, "channel %u out of range, max %u",
1070 first, IEEE80211_CHAN_MAX);
841ab66c
SZ
1071 setbit(chanlist.ic_channels, first);
1072 break;
1073 case 2:
dc4301ae
RP
1074 if (first > IEEE80211_CHAN_MAX)
1075 errx(-1, "channel %u out of range, max %u",
1076 first, IEEE80211_CHAN_MAX);
1077 if (last > IEEE80211_CHAN_MAX)
1078 errx(-1, "channel %u out of range, max %u",
1079 last, IEEE80211_CHAN_MAX);
841ab66c
SZ
1080 if (first > last)
1081 errx(-1, "void channel range, %u > %u",
1082 first, last);
1083 for (f = first; f <= last; f++)
1084 setbit(chanlist.ic_channels, f);
1085 break;
984263bc 1086 }
841ab66c
SZ
1087 if (tp == NULL)
1088 break;
dc4301ae
RP
1089 c = *tp;
1090 while (isspace(c))
841ab66c 1091 tp++;
dc4301ae 1092 if (!isdigit(c))
841ab66c
SZ
1093 break;
1094 cp = tp;
984263bc 1095 }
dc4301ae 1096 set80211(s, IEEE80211_IOC_CHANLIST, 0, sizeof(chanlist), &chanlist);
841ab66c 1097}
984263bc 1098
841ab66c
SZ
1099static void
1100set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
1101{
1102
1103 if (!isanyarg(val)) {
1104 char *temp;
1105 struct sockaddr_dl sdl;
1106
1107 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1108 if (temp == NULL)
1109 errx(1, "malloc failed");
1110 temp[0] = ':';
1111 strcpy(temp + 1, val);
1112 sdl.sdl_len = sizeof(sdl);
1113 link_addr(temp, &sdl);
1114 free(temp);
1115 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1116 errx(1, "malformed link-level address");
1117 set80211(s, IEEE80211_IOC_BSSID, 0,
1118 IEEE80211_ADDR_LEN, LLADDR(&sdl));
1119 } else {
1120 uint8_t zerobssid[IEEE80211_ADDR_LEN];
1121 memset(zerobssid, 0, sizeof(zerobssid));
1122 set80211(s, IEEE80211_IOC_BSSID, 0,
1123 IEEE80211_ADDR_LEN, zerobssid);
984263bc 1124 }
841ab66c 1125}
984263bc 1126
841ab66c
SZ
1127static int
1128getac(const char *ac)
1129{
46158ff5 1130 if (iseq(ac, "ac_be") || iseq(ac, "be"))
841ab66c 1131 return WME_AC_BE;
46158ff5 1132 if (iseq(ac, "ac_bk") || iseq(ac, "bk"))
841ab66c 1133 return WME_AC_BK;
46158ff5 1134 if (iseq(ac, "ac_vi") || iseq(ac, "vi"))
841ab66c 1135 return WME_AC_VI;
46158ff5 1136 if (iseq(ac, "ac_vo") || iseq(ac, "vo"))
841ab66c
SZ
1137 return WME_AC_VO;
1138 errx(1, "unknown wme access class %s", ac);
1139}
1140
1141static
1142DECL_CMD_FUNC2(set80211cwmin, ac, val)
1143{
1144 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
1145}
1146
1147static
1148DECL_CMD_FUNC2(set80211cwmax, ac, val)
1149{
1150 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
1151}
1152
1153static
1154DECL_CMD_FUNC2(set80211aifs, ac, val)
1155{
1156 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
1157}
1158
1159static
1160DECL_CMD_FUNC2(set80211txoplimit, ac, val)
1161{
1162 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
1163}
1164
1165static
1166DECL_CMD_FUNC(set80211acm, ac, d)
1167{
1168 set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL);
1169}
1170static
1171DECL_CMD_FUNC(set80211noacm, ac, d)
1172{
1173 set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL);
1174}
1175
1176static
1177DECL_CMD_FUNC(set80211ackpolicy, ac, d)
1178{
1179 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL);
1180}
1181static
1182DECL_CMD_FUNC(set80211noackpolicy, ac, d)
1183{
1184 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL);
1185}
1186
1187static
1188DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
1189{
1190 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
1191 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1192}
1193
1194static
1195DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
1196{
1197 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
1198 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1199}
1200
1201static
1202DECL_CMD_FUNC2(set80211bssaifs, ac, val)
1203{
1204 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
1205 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1206}
1207
1208static
1209DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
1210{
1211 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
1212 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
1213}
1214
1215static
1216DECL_CMD_FUNC(set80211dtimperiod, val, d)
1217{
1218 set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
1219}
1220
1221static
1222DECL_CMD_FUNC(set80211bintval, val, d)
1223{
1224 set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
1225}
1226
1227static void
1228set80211macmac(int s, int op, const char *val)
1229{
1230 char *temp;
1231 struct sockaddr_dl sdl;
1232
1233 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1234 if (temp == NULL)
1235 errx(1, "malloc failed");
1236 temp[0] = ':';
1237 strcpy(temp + 1, val);
1238 sdl.sdl_len = sizeof(sdl);
1239 link_addr(temp, &sdl);
1240 free(temp);
1241 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1242 errx(1, "malformed link-level address");
1243 set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
1244}
1245
1246static
1247DECL_CMD_FUNC(set80211addmac, val, d)
1248{
1249 set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
1250}
1251
1252static
1253DECL_CMD_FUNC(set80211delmac, val, d)
1254{
1255 set80211macmac(s, IEEE80211_IOC_DELMAC, val);
1256}
1257
1258static
1259DECL_CMD_FUNC(set80211kickmac, val, d)
1260{
1261 char *temp;
1262 struct sockaddr_dl sdl;
1263 struct ieee80211req_mlme mlme;
1264
1265 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1266 if (temp == NULL)
1267 errx(1, "malloc failed");
1268 temp[0] = ':';
1269 strcpy(temp + 1, val);
1270 sdl.sdl_len = sizeof(sdl);
1271 link_addr(temp, &sdl);
1272 free(temp);
1273 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1274 errx(1, "malformed link-level address");
1275 memset(&mlme, 0, sizeof(mlme));
1276 mlme.im_op = IEEE80211_MLME_DEAUTH;
1277 mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE;
1278 memcpy(mlme.im_macaddr, LLADDR(&sdl), IEEE80211_ADDR_LEN);
dc4301ae 1279 set80211(s, IEEE80211_IOC_MLME, 0, sizeof(mlme), &mlme);
841ab66c
SZ
1280}
1281
1282static
1283DECL_CMD_FUNC(set80211maccmd, val, d)
1284{
1285 set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
1286}
1287
1288static void
dc4301ae 1289set80211meshrtmac(int s, int req, const char *val)
841ab66c 1290{
dc4301ae
RP
1291 char *temp;
1292 struct sockaddr_dl sdl;
1293
1294 temp = malloc(strlen(val) + 2); /* ':' and '\0' */
1295 if (temp == NULL)
1296 errx(1, "malloc failed");
1297 temp[0] = ':';
1298 strcpy(temp + 1, val);
1299 sdl.sdl_len = sizeof(sdl);
1300 link_addr(temp, &sdl);
1301 free(temp);
1302 if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
1303 errx(1, "malformed link-level address");
1304 set80211(s, IEEE80211_IOC_MESH_RTCMD, req,
1305 IEEE80211_ADDR_LEN, LLADDR(&sdl));
841ab66c
SZ
1306}
1307
dc4301ae
RP
1308static
1309DECL_CMD_FUNC(set80211addmeshrt, val, d)
841ab66c 1310{
dc4301ae 1311 set80211meshrtmac(s, IEEE80211_MESH_RTCMD_ADD, val);
841ab66c
SZ
1312}
1313
dc4301ae
RP
1314static
1315DECL_CMD_FUNC(set80211delmeshrt, val, d)
b9334f94 1316{
dc4301ae 1317 set80211meshrtmac(s, IEEE80211_MESH_RTCMD_DELETE, val);
b9334f94
SZ
1318}
1319
841ab66c 1320static
dc4301ae 1321DECL_CMD_FUNC(set80211meshrtcmd, val, d)
841ab66c 1322{
dc4301ae 1323 set80211(s, IEEE80211_IOC_MESH_RTCMD, d, 0, NULL);
841ab66c
SZ
1324}
1325
1326static
dc4301ae 1327DECL_CMD_FUNC(set80211hwmprootmode, val, d)
841ab66c 1328{
dc4301ae
RP
1329 int mode;
1330
46158ff5 1331 if (iseq(val, "normal"))
dc4301ae 1332 mode = IEEE80211_HWMP_ROOTMODE_NORMAL;
46158ff5 1333 else if (iseq(val, "proactive"))
dc4301ae 1334 mode = IEEE80211_HWMP_ROOTMODE_PROACTIVE;
46158ff5 1335 else if (iseq(val, "rann"))
dc4301ae
RP
1336 mode = IEEE80211_HWMP_ROOTMODE_RANN;
1337 else
1338 mode = IEEE80211_HWMP_ROOTMODE_DISABLED;
1339 set80211(s, IEEE80211_IOC_HWMP_ROOTMODE, mode, 0, NULL);
841ab66c
SZ
1340}
1341
c36e937b 1342static
dc4301ae 1343DECL_CMD_FUNC(set80211hwmpmaxhops, val, d)
c36e937b 1344{
dc4301ae 1345 set80211(s, IEEE80211_IOC_HWMP_MAXHOPS, atoi(val), 0, NULL);
c36e937b
SZ
1346}
1347
dc4301ae
RP
1348static void
1349set80211pureg(const char *val, int d, int s, const struct afswtch *rafp)
841ab66c 1350{
dc4301ae
RP
1351 set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL);
1352}
841ab66c 1353
dc4301ae
RP
1354static void
1355set80211bgscan(const char *val, int d, int s, const struct afswtch *rafp)
1356{
1357 set80211(s, IEEE80211_IOC_BGSCAN, d, 0, NULL);
841ab66c 1358}
984263bc 1359
dc4301ae
RP
1360static
1361DECL_CMD_FUNC(set80211bgscanidle, val, d)
841ab66c 1362{
dc4301ae
RP
1363 set80211(s, IEEE80211_IOC_BGSCAN_IDLE, atoi(val), 0, NULL);
1364}
1365
1366static
1367DECL_CMD_FUNC(set80211bgscanintvl, val, d)
1368{
1369 set80211(s, IEEE80211_IOC_BGSCAN_INTERVAL, atoi(val), 0, NULL);
1370}
1371
1372static
1373DECL_CMD_FUNC(set80211scanvalid, val, d)
1374{
1375 set80211(s, IEEE80211_IOC_SCANVALID, atoi(val), 0, NULL);
1376}
1377
1378/*
1379 * Parse an optional trailing specification of which netbands
1380 * to apply a parameter to. This is basically the same syntax
1381 * as used for channels but you can concatenate to specify
1382 * multiple. For example:
1383 * 14:abg apply to 11a, 11b, and 11g
1384 * 6:ht apply to 11na and 11ng
1385 * We don't make a big effort to catch silly things; this is
1386 * really a convenience mechanism.
1387 */
1388static int
1389getmodeflags(const char *val)
1390{
1391 const char *cp;
1392 int flags;
1393
1394 flags = 0;
1395
1396 cp = strchr(val, ':');
1397 if (cp != NULL) {
1398 for (cp++; isalpha((int) *cp); cp++) {
1399 /* accept mixed case */
1400 int c = *cp;
1401 if (isupper(c))
1402 c = tolower(c);
1403 switch (c) {
1404 case 'a': /* 802.11a */
1405 flags |= IEEE80211_CHAN_A;
1406 break;
1407 case 'b': /* 802.11b */
1408 flags |= IEEE80211_CHAN_B;
1409 break;
1410 case 'g': /* 802.11g */
1411 flags |= IEEE80211_CHAN_G;
1412 break;
1413 case 'n': /* 802.11n */
1414 flags |= IEEE80211_CHAN_HT;
1415 break;
1416 case 'd': /* dt = Atheros Dynamic Turbo */
1417 flags |= IEEE80211_CHAN_TURBO;
1418 break;
1419 case 't': /* ht, dt, st, t */
1420 /* dt and unadorned t specify Dynamic Turbo */
1421 if ((flags & (IEEE80211_CHAN_STURBO|IEEE80211_CHAN_HT)) == 0)
1422 flags |= IEEE80211_CHAN_TURBO;
1423 break;
1424 case 's': /* st = Atheros Static Turbo */
1425 flags |= IEEE80211_CHAN_STURBO;
1426 break;
1427 case 'h': /* 1/2-width channels */
1428 flags |= IEEE80211_CHAN_HALF;
1429 break;
1430 case 'q': /* 1/4-width channels */
1431 flags |= IEEE80211_CHAN_QUARTER;
1432 break;
1433 default:
1434 errx(-1, "%s: Invalid mode attribute %c\n",
1435 val, *cp);
1436 }
1437 }
1438 }
1439 return flags;
1440}
1441
dc4301ae
RP
1442#define _APPLY(_flags, _base, _param, _v) do { \
1443 if (_flags & IEEE80211_CHAN_HT) { \
1444 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1445 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1446 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1447 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1448 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1449 else \
1450 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1451 } \
1452 if (_flags & IEEE80211_CHAN_TURBO) { \
1453 if ((_flags & (IEEE80211_CHAN_5GHZ|IEEE80211_CHAN_2GHZ)) == 0) {\
1454 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1455 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1456 } else if (_flags & IEEE80211_CHAN_5GHZ) \
1457 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1458 else \
1459 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1460 } \
1461 if (_flags & IEEE80211_CHAN_STURBO) \
1462 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1463 if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1464 _base.params[IEEE80211_MODE_11A]._param = _v; \
1465 if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1466 _base.params[IEEE80211_MODE_11G]._param = _v; \
1467 if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1468 _base.params[IEEE80211_MODE_11B]._param = _v; \
1469 if (_flags & IEEE80211_CHAN_HALF) \
1470 _base.params[IEEE80211_MODE_HALF]._param = _v; \
1471 if (_flags & IEEE80211_CHAN_QUARTER) \
1472 _base.params[IEEE80211_MODE_QUARTER]._param = _v; \
1473} while (0)
1474#define _APPLY1(_flags, _base, _param, _v) do { \
1475 if (_flags & IEEE80211_CHAN_HT) { \
1476 if (_flags & IEEE80211_CHAN_5GHZ) \
1477 _base.params[IEEE80211_MODE_11NA]._param = _v; \
1478 else \
1479 _base.params[IEEE80211_MODE_11NG]._param = _v; \
1480 } else if ((_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) \
1481 _base.params[IEEE80211_MODE_TURBO_A]._param = _v; \
1482 else if ((_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) \
1483 _base.params[IEEE80211_MODE_TURBO_G]._param = _v; \
1484 else if ((_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) \
1485 _base.params[IEEE80211_MODE_STURBO_A]._param = _v; \
1486 else if (_flags & IEEE80211_CHAN_HALF) \
1487 _base.params[IEEE80211_MODE_HALF]._param = _v; \
1488 else if (_flags & IEEE80211_CHAN_QUARTER) \
1489 _base.params[IEEE80211_MODE_QUARTER]._param = _v; \
1490 else if ((_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) \
1491 _base.params[IEEE80211_MODE_11A]._param = _v; \
1492 else if ((_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) \
1493 _base.params[IEEE80211_MODE_11G]._param = _v; \
1494 else if ((_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) \
1495 _base.params[IEEE80211_MODE_11B]._param = _v; \
1496} while (0)
1497#define _APPLY_RATE(_flags, _base, _param, _v) do { \
1498 if (_flags & IEEE80211_CHAN_HT) { \
1499 (_v) = (_v / 2) | IEEE80211_RATE_MCS; \
1500 } \
1501 _APPLY(_flags, _base, _param, _v); \
1502} while (0)
1503#define _APPLY_RATE1(_flags, _base, _param, _v) do { \
1504 if (_flags & IEEE80211_CHAN_HT) { \
1505 (_v) = (_v / 2) | IEEE80211_RATE_MCS; \
1506 } \
1507 _APPLY1(_flags, _base, _param, _v); \
1508} while (0)
1509
1510static
1511DECL_CMD_FUNC(set80211roamrssi, val, d)
1512{
1513 double v = atof(val);
1514 int rssi, flags;
1515
1516 rssi = (int) (2*v);
1517 if (rssi != 2*v)
1518 errx(-1, "invalid rssi (must be .5 dBm units)");
1519 flags = getmodeflags(val);
1520 getroam(s);
1521 if (flags == 0) { /* NB: no flags => current channel */
1522 flags = getcurchan(s)->ic_flags;
1523 _APPLY1(flags, roamparams, rssi, rssi);
1524 } else
1525 _APPLY(flags, roamparams, rssi, rssi);
1526 callback_register(setroam_cb, &roamparams);
1527}
1528
1529static int
1530getrate(const char *val, const char *tag)
1531{
1532 double v = atof(val);
1533 int rate;
1534
1535 rate = (int) (2*v);
1536 if (rate != 2*v)
1537 errx(-1, "invalid %s rate (must be .5 Mb/s units)", tag);
1538 return rate; /* NB: returns 2x the specified value */
1539}
1540
1541static
1542DECL_CMD_FUNC(set80211roamrate, val, d)
1543{
1544 int rate, flags;
1545
1546 rate = getrate(val, "roam");
1547 flags = getmodeflags(val);
1548 getroam(s);
1549 if (flags == 0) { /* NB: no flags => current channel */
1550 flags = getcurchan(s)->ic_flags;
1551 _APPLY_RATE1(flags, roamparams, rate, rate);
1552 } else
1553 _APPLY_RATE(flags, roamparams, rate, rate);
1554 callback_register(setroam_cb, &roamparams);
1555}
1556
1557static
1558DECL_CMD_FUNC(set80211mcastrate, val, d)
1559{
1560 int rate, flags;
1561
1562 rate = getrate(val, "mcast");
1563 flags = getmodeflags(val);
1564 gettxparams(s);
1565 if (flags == 0) { /* NB: no flags => current channel */
1566 flags = getcurchan(s)->ic_flags;
1567 _APPLY_RATE1(flags, txparams, mcastrate, rate);
1568 } else
1569 _APPLY_RATE(flags, txparams, mcastrate, rate);
1570 callback_register(settxparams_cb, &txparams);
1571}
1572
1573static
1574DECL_CMD_FUNC(set80211mgtrate, val, d)
1575{
1576 int rate, flags;
1577
1578 rate = getrate(val, "mgmt");
1579 flags = getmodeflags(val);
1580 gettxparams(s);
1581 if (flags == 0) { /* NB: no flags => current channel */
1582 flags = getcurchan(s)->ic_flags;
1583 _APPLY_RATE1(flags, txparams, mgmtrate, rate);
1584 } else
1585 _APPLY_RATE(flags, txparams, mgmtrate, rate);
1586 callback_register(settxparams_cb, &txparams);
1587}
1588
1589static
1590DECL_CMD_FUNC(set80211ucastrate, val, d)
1591{
1592 int flags;
1593
1594 gettxparams(s);
1595 flags = getmodeflags(val);
1596 if (isanyarg(val)) {
1597 if (flags == 0) { /* NB: no flags => current channel */
1598 flags = getcurchan(s)->ic_flags;
1599 _APPLY1(flags, txparams, ucastrate,
1600 IEEE80211_FIXED_RATE_NONE);
1601 } else
1602 _APPLY(flags, txparams, ucastrate,
1603 IEEE80211_FIXED_RATE_NONE);
1604 } else {
1605 int rate = getrate(val, "ucast");
1606 if (flags == 0) { /* NB: no flags => current channel */
1607 flags = getcurchan(s)->ic_flags;
1608 _APPLY_RATE1(flags, txparams, ucastrate, rate);
1609 } else
1610 _APPLY_RATE(flags, txparams, ucastrate, rate);
1611 }
1612 callback_register(settxparams_cb, &txparams);
1613}
1614
1615static
1616DECL_CMD_FUNC(set80211maxretry, val, d)
1617{
1618 int v = atoi(val), flags;
1619
1620 flags = getmodeflags(val);
1621 gettxparams(s);
1622 if (flags == 0) { /* NB: no flags => current channel */
1623 flags = getcurchan(s)->ic_flags;
1624 _APPLY1(flags, txparams, maxretry, v);
1625 } else
1626 _APPLY(flags, txparams, maxretry, v);
1627 callback_register(settxparams_cb, &txparams);
1628}
1629#undef _APPLY_RATE
1630#undef _APPLY
dc4301ae
RP
1631
1632static
1633DECL_CMD_FUNC(set80211fragthreshold, val, d)
1634{
1635 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD,
1636 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL);
1637}
1638
1639static
1640DECL_CMD_FUNC(set80211bmissthreshold, val, d)
1641{
1642 set80211(s, IEEE80211_IOC_BMISSTHRESHOLD,
1643 isundefarg(val) ? IEEE80211_HWBMISS_MAX : atoi(val), 0, NULL);
1644}
1645
1646static void
1647set80211burst(const char *val, int d, int s, const struct afswtch *rafp)
1648{
1649 set80211(s, IEEE80211_IOC_BURST, d, 0, NULL);
1650}
1651
1652static void
1653set80211doth(const char *val, int d, int s, const struct afswtch *rafp)
1654{
1655 set80211(s, IEEE80211_IOC_DOTH, d, 0, NULL);
1656}
1657
1658static void
1659set80211dfs(const char *val, int d, int s, const struct afswtch *rafp)
1660{
1661 set80211(s, IEEE80211_IOC_DFS, d, 0, NULL);
1662}
1663
1664static void
1665set80211shortgi(const char *val, int d, int s, const struct afswtch *rafp)
1666{
1667 set80211(s, IEEE80211_IOC_SHORTGI,
1668 d ? (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40) : 0,
1669 0, NULL);
1670}
1671
1672static void
1673set80211ampdu(const char *val, int d, int s, const struct afswtch *rafp)
1674{
1675 int ampdu;
1676
1677 if (get80211val(s, IEEE80211_IOC_AMPDU, &ampdu) < 0)
1678 errx(-1, "cannot get AMPDU setting");
1679 if (d < 0) {
1680 d = -d;
1681 ampdu &= ~d;
1682 } else
1683 ampdu |= d;
1684 set80211(s, IEEE80211_IOC_AMPDU, ampdu, 0, NULL);
1685}
1686
1687static
1688DECL_CMD_FUNC(set80211ampdulimit, val, d)
1689{
1690 int v;
1691
1692 switch (atoi(val)) {
1693 case 8:
1694 case 8*1024:
1695 v = IEEE80211_HTCAP_MAXRXAMPDU_8K;
1696 break;
1697 case 16:
1698 case 16*1024:
1699 v = IEEE80211_HTCAP_MAXRXAMPDU_16K;
1700 break;
1701 case 32:
1702 case 32*1024:
1703 v = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1704 break;
1705 case 64:
1706 case 64*1024:
1707 v = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1708 break;
1709 default:
1710 errx(-1, "invalid A-MPDU limit %s", val);
1711 }
1712 set80211(s, IEEE80211_IOC_AMPDU_LIMIT, v, 0, NULL);
1713}
1714
1715static
1716DECL_CMD_FUNC(set80211ampdudensity, val, d)
1717{
1718 int v;
1719
46158ff5 1720 if (isanyarg(val) || iseq(val, "na"))
dc4301ae
RP
1721 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1722 else switch ((int)(atof(val)*4)) {
1723 case 0:
1724 v = IEEE80211_HTCAP_MPDUDENSITY_NA;
1725 break;
1726 case 1:
1727 v = IEEE80211_HTCAP_MPDUDENSITY_025;
1728 break;
1729 case 2:
1730 v = IEEE80211_HTCAP_MPDUDENSITY_05;
1731 break;
1732 case 4:
1733 v = IEEE80211_HTCAP_MPDUDENSITY_1;
1734 break;
1735 case 8:
1736 v = IEEE80211_HTCAP_MPDUDENSITY_2;
1737 break;
1738 case 16:
1739 v = IEEE80211_HTCAP_MPDUDENSITY_4;
1740 break;
1741 case 32:
1742 v = IEEE80211_HTCAP_MPDUDENSITY_8;
1743 break;
1744 case 64:
1745 v = IEEE80211_HTCAP_MPDUDENSITY_16;
1746 break;
1747 default:
1748 errx(-1, "invalid A-MPDU density %s", val);
1749 }
1750 set80211(s, IEEE80211_IOC_AMPDU_DENSITY, v, 0, NULL);
1751}
1752
1753static void
1754set80211amsdu(const char *val, int d, int s, const struct afswtch *rafp)
1755{
1756 int amsdu;
1757
1758 if (get80211val(s, IEEE80211_IOC_AMSDU, &amsdu) < 0)
1759 err(-1, "cannot get AMSDU setting");
1760 if (d < 0) {
1761 d = -d;
1762 amsdu &= ~d;
1763 } else
1764 amsdu |= d;
1765 set80211(s, IEEE80211_IOC_AMSDU, amsdu, 0, NULL);
1766}
1767
1768static
1769DECL_CMD_FUNC(set80211amsdulimit, val, d)
1770{
1771 set80211(s, IEEE80211_IOC_AMSDU_LIMIT, atoi(val), 0, NULL);
1772}
1773
1774static void
1775set80211puren(const char *val, int d, int s, const struct afswtch *rafp)
1776{
1777 set80211(s, IEEE80211_IOC_PUREN, d, 0, NULL);
1778}
1779
1780static void
1781set80211htcompat(const char *val, int d, int s, const struct afswtch *rafp)
1782{
1783 set80211(s, IEEE80211_IOC_HTCOMPAT, d, 0, NULL);
1784}
1785
1786static void
1787set80211htconf(const char *val, int d, int s, const struct afswtch *rafp)
1788{
1789 set80211(s, IEEE80211_IOC_HTCONF, d, 0, NULL);
1790 htconf = d;
1791}
1792
1793static void
1794set80211dwds(const char *val, int d, int s, const struct afswtch *rafp)
1795{
1796 set80211(s, IEEE80211_IOC_DWDS, d, 0, NULL);
1797}
1798
1799static void
1800set80211inact(const char *val, int d, int s, const struct afswtch *rafp)
1801{
1802 set80211(s, IEEE80211_IOC_INACTIVITY, d, 0, NULL);
1803}
1804
1805static void
1806set80211tsn(const char *val, int d, int s, const struct afswtch *rafp)
1807{
1808 set80211(s, IEEE80211_IOC_TSN, d, 0, NULL);
1809}
1810
1811static void
1812set80211dotd(const char *val, int d, int s, const struct afswtch *rafp)
1813{
1814 set80211(s, IEEE80211_IOC_DOTD, d, 0, NULL);
1815}
1816
1817static void
1818set80211smps(const char *val, int d, int s, const struct afswtch *rafp)
1819{
1820 set80211(s, IEEE80211_IOC_SMPS, d, 0, NULL);
1821}
1822
1823static void
1824set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
1825{
1826 set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
1827}
1828
1829static
1830DECL_CMD_FUNC(set80211tdmaslot, val, d)
1831{
1832 set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
1833}
1834
1835static
1836DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
1837{
1838 set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
1839}
1840
1841static
1842DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
1843{
1844 set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
1845}
1846
1847static
1848DECL_CMD_FUNC(set80211tdmabintval, val, d)
1849{
1850 set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
1851}
1852
1853static
1854DECL_CMD_FUNC(set80211meshttl, val, d)
1855{
1856 set80211(s, IEEE80211_IOC_MESH_TTL, atoi(val), 0, NULL);
1857}
1858
1859static
1860DECL_CMD_FUNC(set80211meshforward, val, d)
1861{
1862 set80211(s, IEEE80211_IOC_MESH_FWRD, atoi(val), 0, NULL);
1863}
1864
1865static
1866DECL_CMD_FUNC(set80211meshpeering, val, d)
1867{
1868 set80211(s, IEEE80211_IOC_MESH_AP, atoi(val), 0, NULL);
1869}
1870
1871static
1872DECL_CMD_FUNC(set80211meshmetric, val, d)
1873{
1874 char v[12];
46158ff5 1875
dc4301ae
RP
1876 memcpy(v, val, sizeof(v));
1877 set80211(s, IEEE80211_IOC_MESH_PR_METRIC, 0, 0, v);
1878}
1879
1880static
1881DECL_CMD_FUNC(set80211meshpath, val, d)
1882{
1883 char v[12];
46158ff5 1884
dc4301ae
RP
1885 memcpy(v, val, sizeof(v));
1886 set80211(s, IEEE80211_IOC_MESH_PR_PATH, 0, 0, v);
1887}
1888
1889static int
1890regdomain_sort(const void *a, const void *b)
1891{
1892#define CHAN_ALL \
1893 (IEEE80211_CHAN_ALLTURBO|IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)
1894 const struct ieee80211_channel *ca = a;
1895 const struct ieee80211_channel *cb = b;
1896
1897 return ca->ic_freq == cb->ic_freq ?
46158ff5 1898 ((int)ca->ic_flags & CHAN_ALL) - ((int)cb->ic_flags & CHAN_ALL) :
dc4301ae
RP
1899 ca->ic_freq - cb->ic_freq;
1900#undef CHAN_ALL
1901}
1902
1903static const struct ieee80211_channel *
1904chanlookup(const struct ieee80211_channel chans[], int nchans,
1905 int freq, int flags)
1906{
1907 int i;
1908
1909 flags &= IEEE80211_CHAN_ALLTURBO;
1910 for (i = 0; i < nchans; i++) {
1911 const struct ieee80211_channel *c = &chans[i];
1912 if (c->ic_freq == freq &&
46158ff5 1913 ((int)c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags)
dc4301ae
RP
1914 return c;
1915 }
1916 return NULL;
1917}
1918
1919static int
1920chanfind(const struct ieee80211_channel chans[], int nchans, int flags)
1921{
1922 int i;
1923
1924 for (i = 0; i < nchans; i++) {
1925 const struct ieee80211_channel *c = &chans[i];
46158ff5 1926 if (((int)c->ic_flags & flags) == flags)
dc4301ae
RP
1927 return 1;
1928 }
1929 return 0;
1930}
1931
1932/*
1933 * Check channel compatibility.
1934 */
1935static int
1936checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
1937{
1938 flags &= ~REQ_FLAGS;
1939 /*
1940 * Check if exact channel is in the calibration table;
1941 * everything below is to deal with channels that we
1942 * want to include but that are not explicitly listed.
1943 */
1944 if (flags & IEEE80211_CHAN_HT40) {
1945 /* NB: we use an HT40 channel center that matches HT20 */
1946 flags = (flags &~ IEEE80211_CHAN_HT40) | IEEE80211_CHAN_HT20;
1947 }
1948 if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, flags) != NULL)
1949 return 1;
1950 if (flags & IEEE80211_CHAN_GSM) {
1951 /*
1952 * XXX GSM frequency mapping is handled in the kernel
1953 * so we cannot find them in the calibration table;
1954 * just accept the channel and the kernel will reject
1955 * the channel list if it's wrong.
1956 */
1957 return 1;
1958 }
1959 /*
1960 * If this is a 1/2 or 1/4 width channel allow it if a full
1961 * width channel is present for this frequency, and the device
1962 * supports fractional channels on this band. This is a hack
1963 * that avoids bloating the calibration table; it may be better
1964 * by per-band attributes though (we are effectively calculating
1965 * this attribute by scanning the channel list ourself).
1966 */
1967 if ((flags & (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == 0)
1968 return 0;
1969 if (chanlookup(avail->ic_chans, avail->ic_nchans, freq,
1970 flags &~ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) == NULL)
1971 return 0;
1972 if (flags & IEEE80211_CHAN_HALF) {
1973 return chanfind(avail->ic_chans, avail->ic_nchans,
1974 IEEE80211_CHAN_HALF |
1975 (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1976 } else {
1977 return chanfind(avail->ic_chans, avail->ic_nchans,
1978 IEEE80211_CHAN_QUARTER |
1979 (flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ)));
1980 }
1981}
1982
1983static void
1984regdomain_addchans(struct ieee80211req_chaninfo *ci,
1985 const netband_head *bands,
1986 const struct ieee80211_regdomain *reg,
1987 uint32_t chanFlags,
1988 const struct ieee80211req_chaninfo *avail)
1989{
1990 const struct netband *nb;
1991 const struct freqband *b;
1992 struct ieee80211_channel *c, *prev;
1993 int freq, hi_adj, lo_adj, channelSep;
1994 uint32_t flags;
1995
1996 hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
1997 lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
1998 channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
1999 LIST_FOREACH(nb, bands, next) {
2000 b = nb->band;
2001 if (verbose) {
2002 printf("%s:", __func__);
2003 printb(" chanFlags", chanFlags, IEEE80211_CHAN_BITS);
2004 printb(" bandFlags", nb->flags | b->flags,
2005 IEEE80211_CHAN_BITS);
2006 putchar('\n');
2007 }
2008 prev = NULL;
2009 for (freq = b->freqStart + lo_adj;
2010 freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
2011 /*
2012 * Construct flags for the new channel. We take
2013 * the attributes from the band descriptions except
2014 * for HT40 which is enabled generically (i.e. +/-
2015 * extension channel) in the band description and
2016 * then constrained according by channel separation.
2017 */
2018 flags = nb->flags | b->flags;
2019 if (flags & IEEE80211_CHAN_HT) {
2020 /*
2021 * HT channels are generated specially; we're
2022 * called to add HT20, HT40+, and HT40- chan's
2023 * so we need to expand only band specs for
2024 * the HT channel type being added.
2025 */
2026 if ((chanFlags & IEEE80211_CHAN_HT20) &&
2027 (flags & IEEE80211_CHAN_HT20) == 0) {
2028 if (verbose)
2029 printf("%u: skip, not an "
2030 "HT20 channel\n", freq);
2031 continue;
2032 }
2033 if ((chanFlags & IEEE80211_CHAN_HT40) &&
2034 (flags & IEEE80211_CHAN_HT40) == 0) {
2035 if (verbose)
2036 printf("%u: skip, not an "
2037 "HT40 channel\n", freq);
2038 continue;
2039 }
2040 /*
2041 * DFS and HT40 don't mix. This should be
2042 * expressed in the regdomain database but
2043 * just in case enforce it here.
2044 */
2045 if ((chanFlags & IEEE80211_CHAN_HT40) &&
2046 (flags & IEEE80211_CHAN_DFS)) {
2047 if (verbose)
2048 printf("%u: skip, HT40+DFS "
2049 "not permitted\n", freq);
2050 continue;
2051 }
2052 /* NB: HT attribute comes from caller */
2053 flags &= ~IEEE80211_CHAN_HT;
2054 flags |= chanFlags & IEEE80211_CHAN_HT;
2055 }
2056 /*
2057 * Check if device can operate on this frequency.
2058 */
2059 if (!checkchan(avail, freq, flags)) {
2060 if (verbose) {
2061 printf("%u: skip, ", freq);
2062 printb("flags", flags,
2063 IEEE80211_CHAN_BITS);
2064 printf(" not available\n");
2065 }
2066 continue;
2067 }
2068 if ((flags & REQ_ECM) && !reg->ecm) {
2069 if (verbose)
2070 printf("%u: skip, ECM channel\n", freq);
2071 continue;
2072 }
2073 if ((flags & REQ_INDOOR) && reg->location == 'O') {
2074 if (verbose)
2075 printf("%u: skip, indoor channel\n",
2076 freq);
2077 continue;
2078 }
2079 if ((flags & REQ_OUTDOOR) && reg->location == 'I') {
2080 if (verbose)
2081 printf("%u: skip, outdoor channel\n",
2082 freq);
2083 continue;
2084 }
2085 if ((flags & IEEE80211_CHAN_HT40) &&
2086 prev != NULL && (freq - prev->ic_freq) < channelSep) {
2087 if (verbose)
2088 printf("%u: skip, only %u channel "
46158ff5 2089 "separation, need %d\n", freq,
dc4301ae
RP
2090 freq - prev->ic_freq, channelSep);
2091 continue;
2092 }
2093 if (ci->ic_nchans == IEEE80211_CHAN_MAX) {
2094 if (verbose)
2095 printf("%u: skip, channel table full\n",
2096 freq);
2097 break;
2098 }
2099 c = &ci->ic_chans[ci->ic_nchans++];
2100 memset(c, 0, sizeof(*c));
2101 c->ic_freq = freq;
2102 c->ic_flags = flags;
2103 if (c->ic_flags & IEEE80211_CHAN_DFS)
2104 c->ic_maxregpower = nb->maxPowerDFS;
2105 else
2106 c->ic_maxregpower = nb->maxPower;
2107 if (verbose) {
2108 printf("[%3d] add freq %u ",
2109 ci->ic_nchans-1, c->ic_freq);
2110 printb("flags", c->ic_flags, IEEE80211_CHAN_BITS);
2111 printf(" power %u\n", c->ic_maxregpower);
2112 }
2113 /* NB: kernel fills in other fields */
2114 prev = c;
2115 }
2116 }
2117}
2118
2119static void
2120regdomain_makechannels(
2121 struct ieee80211_regdomain_req *req,
2122 const struct ieee80211_devcaps_req *dc)
2123{
2124 struct regdata *rdp = getregdata();
2125 const struct country *cc;
2126 const struct ieee80211_regdomain *reg = &req->rd;
2127 struct ieee80211req_chaninfo *ci = &req->chaninfo;
2128 const struct regdomain *rd;
2129
2130 /*
2131 * Locate construction table for new channel list. We treat
2132 * the regdomain/SKU as definitive so a country can be in
2133 * multiple with different properties (e.g. US in FCC+FCC3).
2134 * If no regdomain is specified then we fallback on the country
2135 * code to find the associated regdomain since countries always
2136 * belong to at least one regdomain.
2137 */
2138 if (reg->regdomain == 0) {
2139 cc = lib80211_country_findbycc(rdp, reg->country);
2140 if (cc == NULL)
2141 errx(1, "internal error, country %d not found",
2142 reg->country);
2143 rd = cc->rd;
2144 } else
2145 rd = lib80211_regdomain_findbysku(rdp, reg->regdomain);
2146 if (rd == NULL)
2147 errx(1, "internal error, regdomain %d not found",
2148 reg->regdomain);
2149 if (rd->sku != SKU_DEBUG) {
2150 /*
2151 * regdomain_addchans incrememnts the channel count for
2152 * each channel it adds so initialize ic_nchans to zero.
2153 * Note that we know we have enough space to hold all possible
2154 * channels because the devcaps list size was used to
2155 * allocate our request.
2156 */
2157 ci->ic_nchans = 0;
2158 if (!LIST_EMPTY(&rd->bands_11b))
2159 regdomain_addchans(ci, &rd->bands_11b, reg,
2160 IEEE80211_CHAN_B, &dc->dc_chaninfo);
2161 if (!LIST_EMPTY(&rd->bands_11g))
2162 regdomain_addchans(ci, &rd->bands_11g, reg,
2163 IEEE80211_CHAN_G, &dc->dc_chaninfo);
2164 if (!LIST_EMPTY(&rd->bands_11a))
2165 regdomain_addchans(ci, &rd->bands_11a, reg,
2166 IEEE80211_CHAN_A, &dc->dc_chaninfo);
2167 if (!LIST_EMPTY(&rd->bands_11na) && dc->dc_htcaps != 0) {
2168 regdomain_addchans(ci, &rd->bands_11na, reg,
2169 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20,
2170 &dc->dc_chaninfo);
2171 if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2172 regdomain_addchans(ci, &rd->bands_11na, reg,
2173 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U,
2174 &dc->dc_chaninfo);
2175 regdomain_addchans(ci, &rd->bands_11na, reg,
2176 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D,
2177 &dc->dc_chaninfo);
2178 }
2179 }
2180 if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
2181 regdomain_addchans(ci, &rd->bands_11ng, reg,
2182 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
2183 &dc->dc_chaninfo);
2184 if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
2185 regdomain_addchans(ci, &rd->bands_11ng, reg,
2186 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U,
2187 &dc->dc_chaninfo);
2188 regdomain_addchans(ci, &rd->bands_11ng, reg,
2189 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D,
2190 &dc->dc_chaninfo);
2191 }
2192 }
2193 qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]),
2194 regdomain_sort);
2195 } else
2196 memcpy(ci, &dc->dc_chaninfo,
2197 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
2198}
2199
2200static void
2201list_countries(void)
2202{
2203 struct regdata *rdp = getregdata();
2204 const struct country *cp;
2205 const struct regdomain *dp;
2206 int i;
2207
2208 i = 0;
2209 printf("\nCountry codes:\n");
2210 LIST_FOREACH(cp, &rdp->countries, next) {
2211 printf("%2s %-15.15s%s", cp->isoname,
2212 cp->name, ((i+1)%4) == 0 ? "\n" : " ");
2213 i++;
2214 }
2215 i = 0;
2216 printf("\nRegulatory domains:\n");
2217 LIST_FOREACH(dp, &rdp->domains, next) {
2218 printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " ");
2219 i++;
2220 }
2221 printf("\n");
2222}
2223
2224static void
2225defaultcountry(const struct regdomain *rd)
2226{
2227 struct regdata *rdp = getregdata();
2228 const struct country *cc;
2229
2230 cc = lib80211_country_findbycc(rdp, rd->cc->code);
2231 if (cc == NULL)
2232 errx(1, "internal error, ISO country code %d not "
2233 "defined for regdomain %s", rd->cc->code, rd->name);
2234 regdomain.country = cc->code;
2235 regdomain.isocc[0] = cc->isoname[0];
2236 regdomain.isocc[1] = cc->isoname[1];
2237}
2238
2239static
2240DECL_CMD_FUNC(set80211regdomain, val, d)
2241{
2242 struct regdata *rdp = getregdata();
2243 const struct regdomain *rd;
2244
2245 rd = lib80211_regdomain_findbyname(rdp, val);
2246 if (rd == NULL) {
2247 char *eptr;
2248 long sku = strtol(val, &eptr, 0);
2249
2250 if (eptr != val)
2251 rd = lib80211_regdomain_findbysku(rdp, sku);
2252 if (eptr == val || rd == NULL)
2253 errx(1, "unknown regdomain %s", val);
2254 }
2255 getregdomain(s);
2256 regdomain.regdomain = rd->sku;
2257 if (regdomain.country == 0 && rd->cc != NULL) {
2258 /*
2259 * No country code setup and there's a default
2260 * one for this regdomain fill it in.
2261 */
2262 defaultcountry(rd);
2263 }
2264 callback_register(setregdomain_cb, &regdomain);
2265}
2266
2267static
2268DECL_CMD_FUNC(set80211country, val, d)
2269{
2270 struct regdata *rdp = getregdata();
2271 const struct country *cc;
2272
2273 cc = lib80211_country_findbyname(rdp, val);
2274 if (cc == NULL) {
2275 char *eptr;
2276 long code = strtol(val, &eptr, 0);
2277
2278 if (eptr != val)
2279 cc = lib80211_country_findbycc(rdp, code);
2280 if (eptr == val || cc == NULL)
2281 errx(1, "unknown ISO country code %s", val);
2282 }
2283 getregdomain(s);
2284 regdomain.regdomain = cc->rd->sku;
2285 regdomain.country = cc->code;
2286 regdomain.isocc[0] = cc->isoname[0];
2287 regdomain.isocc[1] = cc->isoname[1];
2288 callback_register(setregdomain_cb, &regdomain);
2289}
2290
2291static void
2292set80211location(const char *val, int d, int s, const struct afswtch *rafp)
2293{
2294 getregdomain(s);
2295 regdomain.location = d;
2296 callback_register(setregdomain_cb, &regdomain);
2297}
2298
2299static void
2300set80211ecm(const char *val, int d, int s, const struct afswtch *rafp)
2301{
2302 getregdomain(s);
2303 regdomain.ecm = d;
2304 callback_register(setregdomain_cb, &regdomain);
2305}
2306
2307static void
2308LINE_INIT(char c)
2309{
2310 spacer = c;
2311 if (c == '\t')
2312 col = 8;
2313 else
2314 col = 1;
2315}
2316
2317static void
2318LINE_BREAK(void)
2319{
2320 if (spacer != '\t') {
2321 printf("\n");
2322 spacer = '\t';
2323 }
2324 col = 8; /* 8-col tab */
2325}
2326
2327static void
2328LINE_CHECK(const char *fmt, ...)
2329{
2330 char buf[80];
2331 va_list ap;
2332 int n;
2333
2334 va_start(ap, fmt);
2335 n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap);
2336 va_end(ap);
2337 col += 1+n;
2338 if (col > MAXCOL) {
2339 LINE_BREAK();
2340 col += n;
2341 }
2342 buf[0] = spacer;
2343 printf("%s", buf);
2344 spacer = ' ';
2345}
2346
2347static int
2348getmaxrate(const uint8_t rates[15], uint8_t nrates)
2349{
2350 int i, maxrate = -1;
2351
2352 for (i = 0; i < nrates; i++) {
2353 int rate = rates[i] & IEEE80211_RATE_VAL;
2354 if (rate > maxrate)
2355 maxrate = rate;
2356 }
2357 return maxrate / 2;
2358}
2359
2360static const char *
2361getcaps(int capinfo)
2362{
2363 static char capstring[32];
2364 char *cp = capstring;
841ab66c
SZ
2365
2366 if (capinfo & IEEE80211_CAPINFO_ESS)
2367 *cp++ = 'E';
2368 if (capinfo & IEEE80211_CAPINFO_IBSS)
2369 *cp++ = 'I';
2370 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
2371 *cp++ = 'c';
2372 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
2373 *cp++ = 'C';
2374 if (capinfo & IEEE80211_CAPINFO_PRIVACY)
2375 *cp++ = 'P';
2376 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
2377 *cp++ = 'S';
2378 if (capinfo & IEEE80211_CAPINFO_PBCC)
2379 *cp++ = 'B';
2380 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
2381 *cp++ = 'A';
2382 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
2383 *cp++ = 's';
2384 if (capinfo & IEEE80211_CAPINFO_RSN)
2385 *cp++ = 'R';
2386 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
2387 *cp++ = 'D';
2388 *cp = '\0';
2389 return capstring;
2390}
2391
dc4301ae
RP
2392static const char *
2393getflags(int flags)
2394{
2395 static char flagstring[32];
2396 char *cp = flagstring;
2397
2398 if (flags & IEEE80211_NODE_AUTH)
2399 *cp++ = 'A';
2400 if (flags & IEEE80211_NODE_QOS)
2401 *cp++ = 'Q';
2402 if (flags & IEEE80211_NODE_ERP)
2403 *cp++ = 'E';
2404 if (flags & IEEE80211_NODE_PWR_MGT)
2405 *cp++ = 'P';
2406 if (flags & IEEE80211_NODE_HT) {
2407 *cp++ = 'H';
2408 if (flags & IEEE80211_NODE_HTCOMPAT)
2409 *cp++ = '+';
2410 }
2411 if (flags & IEEE80211_NODE_WPS)
2412 *cp++ = 'W';
2413 if (flags & IEEE80211_NODE_TSN)
2414 *cp++ = 'N';
2415 if (flags & IEEE80211_NODE_AMPDU_TX)
2416 *cp++ = 'T';
2417 if (flags & IEEE80211_NODE_AMPDU_RX)
2418 *cp++ = 'R';
2419 if (flags & IEEE80211_NODE_MIMO_PS) {
2420 *cp++ = 'M';
2421 if (flags & IEEE80211_NODE_MIMO_RTS)
2422 *cp++ = '+';
2423 }
2424 if (flags & IEEE80211_NODE_RIFS)
2425 *cp++ = 'I';
2426 if (flags & IEEE80211_NODE_SGI40) {
2427 *cp++ = 'S';
2428 if (flags & IEEE80211_NODE_SGI20)
2429 *cp++ = '+';
2430 } else if (flags & IEEE80211_NODE_SGI20)
2431 *cp++ = 's';
2432 if (flags & IEEE80211_NODE_AMSDU_TX)
2433 *cp++ = 't';
2434 if (flags & IEEE80211_NODE_AMSDU_RX)
2435 *cp++ = 'r';
2436 *cp = '\0';
2437 return flagstring;
2438}
2439
2440static void
2441printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
2442{
2443 printf("%s", tag);
2444 if (verbose) {
2445 maxlen -= strlen(tag)+2;
46158ff5 2446 if (2*ielen > (size_t)maxlen)
dc4301ae
RP
2447 maxlen--;
2448 printf("<");
2449 for (; ielen > 0; ie++, ielen--) {
2450 if (maxlen-- <= 0)
2451 break;
2452 printf("%02x", *ie);
2453 }
2454 if (ielen != 0)
2455 printf("-");
2456 printf(">");
2457 }
2458}
2459
2460#define LE_READ_2(p) \
2461 ((u_int16_t) \
2462 ((((const u_int8_t *)(p))[0] ) | \
2463 (((const u_int8_t *)(p))[1] << 8)))
2464#define LE_READ_4(p) \
2465 ((u_int32_t) \
2466 ((((const u_int8_t *)(p))[0] ) | \
2467 (((const u_int8_t *)(p))[1] << 8) | \
2468 (((const u_int8_t *)(p))[2] << 16) | \
2469 (((const u_int8_t *)(p))[3] << 24)))
2470
2471/*
2472 * NB: The decoding routines assume a properly formatted ie
2473 * which should be safe as the kernel only retains them
2474 * if they parse ok.
2475 */
2476
2477static void
2478printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2479{
2480#define MS(_v, _f) (((_v) & _f) >> _f##_S)
2481 static const char *acnames[] = { "BE", "BK", "VO", "VI" };
2482 const struct ieee80211_wme_param *wme =
2483 (const struct ieee80211_wme_param *) ie;
2484 int i;
2485
2486 printf("%s", tag);
2487 if (!verbose)
2488 return;
2489 printf("<qosinfo 0x%x", wme->param_qosInfo);
2490 ie += offsetof(struct ieee80211_wme_param, params_acParams);
2491 for (i = 0; i < WME_NUM_AC; i++) {
2492 const struct ieee80211_wme_acparams *ac =
2493 &wme->params_acParams[i];
2494
2495 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
2496 , acnames[i]
2497 , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
2498 , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
2499 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
2500 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
2501 , LE_READ_2(&ac->acp_txop)
2502 );
2503 }
2504 printf(">");
2505#undef MS
2506}
2507
2508static void
2509printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2510{
2511 printf("%s", tag);
2512 if (verbose) {
2513 const struct ieee80211_wme_info *wme =
2514 (const struct ieee80211_wme_info *) ie;
2515 printf("<version 0x%x info 0x%x>",
2516 wme->wme_version, wme->wme_info);
2517 }
2518}
2519
2520static void
2521printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2522{
2523 printf("%s", tag);
2524 if (verbose) {
2525 const struct ieee80211_ie_htcap *htcap =
2526 (const struct ieee80211_ie_htcap *) ie;
2527 const char *sep;
2528 int i, j;
2529
2530 printf("<cap 0x%x param 0x%x",
2531 LE_READ_2(&htcap->hc_cap), htcap->hc_param);
2532 printf(" mcsset[");
2533 sep = "";
2534 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2535 if (isset(htcap->hc_mcsset, i)) {
2536 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2537 if (isclr(htcap->hc_mcsset, j))
2538 break;
2539 j--;
2540 if (i == j)
2541 printf("%s%u", sep, i);
2542 else
2543 printf("%s%u-%u", sep, i, j);
2544 i += j-i;
2545 sep = ",";
2546 }
2547 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>",
2548 LE_READ_2(&htcap->hc_extcap),
2549 LE_READ_4(&htcap->hc_txbf),
2550 htcap->hc_antenna);
2551 }
2552}
2553
2554static void
2555printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2556{
2557 printf("%s", tag);
2558 if (verbose) {
2559 const struct ieee80211_ie_htinfo *htinfo =
2560 (const struct ieee80211_ie_htinfo *) ie;
2561 const char *sep;
2562 int i, j;
2563
2564 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel,
2565 htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3,
2566 LE_READ_2(&htinfo->hi_byte45));
2567 printf(" basicmcs[");
2568 sep = "";
2569 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++)
2570 if (isset(htinfo->hi_basicmcsset, i)) {
2571 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++)
2572 if (isclr(htinfo->hi_basicmcsset, j))
2573 break;
2574 j--;
2575 if (i == j)
2576 printf("%s%u", sep, i);
2577 else
2578 printf("%s%u-%u", sep, i, j);
2579 i += j-i;
2580 sep = ",";
2581 }
2582 printf("]>");
2583 }
2584}
2585
2586static void
2587printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2588{
2589
2590 printf("%s", tag);
2591 if (verbose) {
2592 const struct ieee80211_ath_ie *ath =
2593 (const struct ieee80211_ath_ie *)ie;
2594
2595 printf("<");
2596 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME)
2597 printf("DTURBO,");
2598 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION)
2599 printf("COMP,");
2600 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME)
2601 printf("FF,");
2602 if (ath->ath_capability & ATHEROS_CAP_XR)
2603 printf("XR,");
2604 if (ath->ath_capability & ATHEROS_CAP_AR)
2605 printf("AR,");
2606 if (ath->ath_capability & ATHEROS_CAP_BURST)
2607 printf("BURST,");
2608 if (ath->ath_capability & ATHEROS_CAP_WME)
2609 printf("WME,");
2610 if (ath->ath_capability & ATHEROS_CAP_BOOST)
2611 printf("BOOST,");
2612 printf("0x%x>", LE_READ_2(ath->ath_defkeyix));
2613 }
2614}
2615
2616
2617static void
2618printmeshconf(const char *tag, const uint8_t *ie, size_t ielen, int maxlen)
2619{
2620#define MATCHOUI(field, oui, string) \
2621do { \
2622 if (memcmp(field, oui, 4) == 0) \
2623 printf("%s", string); \
2624} while (0)
2625
2626 printf("%s", tag);
2627 if (verbose) {
2628 const struct ieee80211_meshconf_ie *mconf =
2629 (const struct ieee80211_meshconf_ie *)ie;
2630 printf("<PATH:");
2631 if (mconf->conf_pselid == IEEE80211_MESHCONF_PATH_HWMP)
2632 printf("HWMP");
2633 else
2634 printf("UNKNOWN");
2635 printf(" LINK:");
2636 if (mconf->conf_pmetid == IEEE80211_MESHCONF_METRIC_AIRTIME)
2637 printf("AIRTIME");
2638 else
2639 printf("UNKNOWN");
2640 printf(" CONGESTION:");
2641 if (mconf->conf_ccid == IEEE80211_MESHCONF_CC_DISABLED)
2642 printf("DISABLED");
2643 else
2644 printf("UNKNOWN");
2645 printf(" SYNC:");
2646 if (mconf->conf_syncid == IEEE80211_MESHCONF_SYNC_NEIGHOFF)
2647 printf("NEIGHOFF");
2648 else
2649 printf("UNKNOWN");
2650 printf(" AUTH:");
2651 if (mconf->conf_authid == IEEE80211_MESHCONF_AUTH_DISABLED)
2652 printf("DISABLED");
2653 else
2654 printf("UNKNOWN");
2655 printf(" FORM:0x%x CAPS:0x%x>", mconf->conf_form,
2656 mconf->conf_cap);
2657 }
2658#undef MATCHOUI
2659}
2660
2661static const char *
2662wpa_cipher(const u_int8_t *sel)
2663{
2664#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2665 u_int32_t w = LE_READ_4(sel);
2666
2667 switch (w) {
2668 case WPA_SEL(WPA_CSE_NULL):
2669 return "NONE";
2670 case WPA_SEL(WPA_CSE_WEP40):
2671 return "WEP40";
2672 case WPA_SEL(WPA_CSE_WEP104):
2673 return "WEP104";
2674 case WPA_SEL(WPA_CSE_TKIP):
2675 return "TKIP";
2676 case WPA_SEL(WPA_CSE_CCMP):
2677 return "AES-CCMP";
2678 }
2679 return "?"; /* NB: so 1<< is discarded */
2680#undef WPA_SEL
2681}
2682
2683static const char *
2684wpa_keymgmt(const u_int8_t *sel)
2685{
2686#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
2687 u_int32_t w = LE_READ_4(sel);
2688
2689 switch (w) {
2690 case WPA_SEL(WPA_ASE_8021X_UNSPEC):
2691 return "8021X-UNSPEC";
2692 case WPA_SEL(WPA_ASE_8021X_PSK):
2693 return "8021X-PSK";
2694 case WPA_SEL(WPA_ASE_NONE):
2695 return "NONE";
2696 }
2697 return "?";
2698#undef WPA_SEL
2699}
2700
841ab66c 2701static void
dc4301ae 2702printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
841ab66c 2703{
dc4301ae
RP
2704 u_int8_t len = ie[1];
2705
841ab66c
SZ
2706 printf("%s", tag);
2707 if (verbose) {
dc4301ae
RP
2708 const char *sep;
2709 int n;
2710
2711 ie += 6, len -= 4; /* NB: len is payload only */
2712
2713 printf("<v%u", LE_READ_2(ie));
2714 ie += 2, len -= 2;
2715
2716 printf(" mc:%s", wpa_cipher(ie));
2717 ie += 4, len -= 4;
2718
2719 /* unicast ciphers */
2720 n = LE_READ_2(ie);
2721 ie += 2, len -= 2;
2722 sep = " uc:";
2723 for (; n > 0; n--) {
2724 printf("%s%s", sep, wpa_cipher(ie));
2725 ie += 4, len -= 4;
2726 sep = "+";
2727 }
2728
2729 /* key management algorithms */
2730 n = LE_READ_2(ie);
2731 ie += 2, len -= 2;
2732 sep = " km:";
2733 for (; n > 0; n--) {
2734 printf("%s%s", sep, wpa_keymgmt(ie));
2735 ie += 4, len -= 4;
2736 sep = "+";
2737 }
2738
2739 if (len > 2) /* optional capabilities */
2740 printf(", caps 0x%x", LE_READ_2(ie));
2741 printf(">");
2742 }
2743}
2744
2745static const char *
2746rsn_cipher(const u_int8_t *sel)
2747{
2748#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2749 u_int32_t w = LE_READ_4(sel);
2750
2751 switch (w) {
2752 case RSN_SEL(RSN_CSE_NULL):
2753 return "NONE";
2754 case RSN_SEL(RSN_CSE_WEP40):
2755 return "WEP40";
2756 case RSN_SEL(RSN_CSE_WEP104):
2757 return "WEP104";
2758 case RSN_SEL(RSN_CSE_TKIP):
2759 return "TKIP";
2760 case RSN_SEL(RSN_CSE_CCMP):
2761 return "AES-CCMP";
2762 case RSN_SEL(RSN_CSE_WRAP):
2763 return "AES-OCB";
2764 }
2765 return "?";
2766#undef WPA_SEL
2767}
2768
2769static const char *
2770rsn_keymgmt(const u_int8_t *sel)
2771{
2772#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
2773 u_int32_t w = LE_READ_4(sel);
2774
2775 switch (w) {
2776 case RSN_SEL(RSN_ASE_8021X_UNSPEC):
2777 return "8021X-UNSPEC";
2778 case RSN_SEL(RSN_ASE_8021X_PSK):
2779 return "8021X-PSK";
2780 case RSN_SEL(RSN_ASE_NONE):
2781 return "NONE";
2782 }
2783 return "?";
2784#undef RSN_SEL
2785}
2786
2787static void
2788printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2789{
2790 printf("%s", tag);
2791 if (verbose) {
2792 const char *sep;
2793 int n;
2794
2795 ie += 2, ielen -= 2;
2796
2797 printf("<v%u", LE_READ_2(ie));
2798 ie += 2, ielen -= 2;
2799
2800 printf(" mc:%s", rsn_cipher(ie));
2801 ie += 4, ielen -= 4;
2802
2803 /* unicast ciphers */
2804 n = LE_READ_2(ie);
2805 ie += 2, ielen -= 2;
2806 sep = " uc:";
2807 for (; n > 0; n--) {
2808 printf("%s%s", sep, rsn_cipher(ie));
2809 ie += 4, ielen -= 4;
2810 sep = "+";
2811 }
2812
2813 /* key management algorithms */
2814 n = LE_READ_2(ie);
2815 ie += 2, ielen -= 2;
2816 sep = " km:";
2817 for (; n > 0; n--) {
2818 printf("%s%s", sep, rsn_keymgmt(ie));
2819 ie += 4, ielen -= 4;
2820 sep = "+";
2821 }
2822
2823 if (ielen > 2) /* optional capabilities */
2824 printf(", caps 0x%x", LE_READ_2(ie));
2825 /* XXXPMKID */
2826 printf(">");
2827 }
2828}
2829
dc4301ae
RP
2830#define BE_READ_2(p) \
2831 ((u_int16_t) \
2832 ((((const u_int8_t *)(p))[1] ) | \
2833 (((const u_int8_t *)(p))[0] << 8)))
2834
2835static void
2836printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
2837{
dc4301ae 2838 u_int8_t len = ie[1];
46158ff5
AL
2839 uint16_t tlv_type;
2840 uint16_t tlv_len;
2ecde614
AL
2841 uint16_t cfg_mthd;
2842 int n;
2843 int f;
dc4301ae
RP
2844
2845 printf("%s", tag);
2846 if (verbose) {
2847 static const char *dev_pass_id[] = {
2848 "D", /* Default (PIN) */
2849 "U", /* User-specified */
2850 "M", /* Machine-specified */
2851 "K", /* Rekey */
2852 "P", /* PushButton */
2853 "R" /* Registrar-specified */
2854 };
dc4301ae
RP
2855
2856 ie +=6, len -= 4; /* NB: len is payload only */
2857
2858 /* WPS IE in Beacon and Probe Resp frames have different fields */
841ab66c 2859 printf("<");
dc4301ae 2860 while (len) {
46158ff5
AL
2861 tlv_type = BE_READ_2(ie);
2862 tlv_len = BE_READ_2(ie + 2);
dc4301ae 2863
2ecde614
AL
2864 /* some devices broadcast invalid WPS frames */
2865 if (tlv_len > len) {
2866 printf("bad frame length tlv_type=0x%02x "
2867 "tlv_len=%d len=%d", tlv_type, tlv_len,
2868 len);
2869 break;
2870 }
2871
dc4301ae
RP
2872 ie += 4, len -= 4;
2873
2874 switch (tlv_type) {
2ecde614 2875 case IEEE80211_WPS_ATTR_VERSION:
dc4301ae 2876 printf("v:%d.%d", *ie >> 4, *ie & 0xf);
984263bc 2877 break;
2ecde614
AL
2878 case IEEE80211_WPS_ATTR_AP_SETUP_LOCKED:
2879 printf(" ap_setup:%s", *ie ? "locked" :
2880 "unlocked");
2881 break;
2882 case IEEE80211_WPS_ATTR_CONFIG_METHODS:
2883 case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
2884 if (tlv_type == IEEE80211_WPS_ATTR_SELECTED_REGISTRAR_CONFIG_METHODS)
2885 printf(" sel_reg_cfg_mthd:");
dc4301ae 2886 else
2ecde614
AL
2887 printf(" cfg_mthd:" );
2888 cfg_mthd = BE_READ_2(ie);
2889 f = 0;
2890 for (n = 15; n >= 0; n--) {
2891 if (f) {
2892 printf(",");
2893 f = 0;
2894 }
2895 switch (cfg_mthd & (1 << n)) {
2896 case 0:
2897 break;
2898 case IEEE80211_WPS_CONFIG_USBA:
2899 printf("usba");
2900 f++;
2901 break;
2902 case IEEE80211_WPS_CONFIG_ETHERNET:
2903 printf("ethernet");
2904 f++;
2905 break;
2906 case IEEE80211_WPS_CONFIG_LABEL:
2907 printf("label");
2908 f++;
2909 break;
2910 case IEEE80211_WPS_CONFIG_DISPLAY:
2911 if (!(cfg_mthd &
2912 (IEEE80211_WPS_CONFIG_VIRT_DISPLAY |
2913 IEEE80211_WPS_CONFIG_PHY_DISPLAY)))
2914 {
2915 printf("display");
2916 f++;
2917 }
2918 break;
2919 case IEEE80211_WPS_CONFIG_EXT_NFC_TOKEN:
2920 printf("ext_nfc_tokenk");
2921 f++;
2922 break;
2923 case IEEE80211_WPS_CONFIG_INT_NFC_TOKEN:
2924 printf("int_nfc_token");
2925 f++;
2926 break;
2927 case IEEE80211_WPS_CONFIG_NFC_INTERFACE:
2928 printf("nfc_interface");
2929 f++;
2930 break;
2931 case IEEE80211_WPS_CONFIG_PUSHBUTTON:
2932 if (!(cfg_mthd &
2933 (IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON |
2934 IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON))) {
2935 printf("push_button");
2936 f++;
2937 }
2938 break;
2939 case IEEE80211_WPS_CONFIG_KEYPAD:
2940 printf("keypad");
2941 f++;
2942 break;
2943 case IEEE80211_WPS_CONFIG_VIRT_PUSHBUTTON:
2944 printf("virtual_push_button");
2945 f++;
2946 break;
2947 case IEEE80211_WPS_CONFIG_PHY_PUSHBUTTON:
2948 printf("physical_push_button");
2949 f++;
2950 break;
2951 case IEEE80211_WPS_CONFIG_P2PS:
2952 printf("p2ps");
2953 f++;
2954 break;
2955 case IEEE80211_WPS_CONFIG_VIRT_DISPLAY:
2956 printf("virtual_display");
2957 f++;
2958 break;
2959 case IEEE80211_WPS_CONFIG_PHY_DISPLAY:
2960 printf("physical_display");
2961 f++;
2962 break;
2963 default:
2964 printf("unknown_wps_config<%04x>",
2965 cfg_mthd & (1 << n));
2966 f++;
2967 break;
2968 }
2969 }
dc4301ae 2970 break;
2ecde614
AL
2971 case IEEE80211_WPS_ATTR_DEV_NAME:
2972 printf(" device_name:<%.*s>", tlv_len, ie);
dc4301ae 2973 break;
2ecde614 2974 case IEEE80211_WPS_ATTR_DEV_PASSWORD_ID:
dc4301ae 2975 n = LE_READ_2(ie);
2ecde614 2976 if (n < (int)nitems(dev_pass_id))
dc4301ae
RP
2977 printf(" dpi:%s", dev_pass_id[n]);
2978 break;
2ecde614
AL
2979 case IEEE80211_WPS_ATTR_MANUFACTURER:
2980 printf(" manufacturer:<%.*s>", tlv_len, ie);
2981 break;
2982 case IEEE80211_WPS_ATTR_MODEL_NAME:
2983 printf(" model_name:<%.*s>", tlv_len, ie);
2984 break;
2985 case IEEE80211_WPS_ATTR_MODEL_NUMBER:
2986 printf(" model_number:<%.*s>", tlv_len, ie);
2987 break;
2988 case IEEE80211_WPS_ATTR_PRIMARY_DEV_TYPE:
2989 printf(" prim_dev:");
2990 for (n = 0; n < tlv_len; n++)
2991 printf("%02x", ie[n]);
2992 break;
2993 case IEEE80211_WPS_ATTR_RF_BANDS:
2994 printf(" rf:");
2995 f = 0;
2996 for (n = 7; n >= 0; n--) {
2997 if (f) {
2998 printf(",");
2999 f = 0;
3000 }
3001 switch (*ie & (1 << n)) {
3002 case 0:
3003 break;
3004 case IEEE80211_WPS_RF_BAND_24GHZ:
3005 printf("2.4Ghz");
3006 f++;
3007 break;
3008 case IEEE80211_WPS_RF_BAND_50GHZ:
3009 printf("5Ghz");
3010 f++;
3011 break;
3012 case IEEE80211_WPS_RF_BAND_600GHZ:
3013 printf("60Ghz");
3014 f++;
3015 break;
3016 default:
3017 printf("unknown<%02x>",
3018 *ie & (1 << n));
3019 f++;
3020 break;
3021 }
3022 }
3023 break;
3024 case IEEE80211_WPS_ATTR_RESPONSE_TYPE:
3025 printf(" resp_type:0x%02x", *ie);
3026 break;
3027 case IEEE80211_WPS_ATTR_SELECTED_REGISTRAR:
3028 printf(" sel:%s", *ie ? "T" : "F");
3029 break;
3030 case IEEE80211_WPS_ATTR_SERIAL_NUMBER:
3031 printf(" serial_number:<%.*s>", tlv_len, ie);
3032 break;
3033 case IEEE80211_WPS_ATTR_UUID_E:
dc4301ae 3034 printf(" uuid-e:");
2ecde614 3035 for (n = 0; n < (tlv_len - 1); n++)
dc4301ae
RP
3036 printf("%02x-", ie[n]);
3037 printf("%02x", ie[n]);
3038 break;
2ecde614
AL
3039 case IEEE80211_WPS_ATTR_VENDOR_EXT:
3040 printf(" vendor:");
3041 for (n = 0; n < tlv_len; n++)
3042 printf("%02x", ie[n]);
3043 break;
3044 case IEEE80211_WPS_ATTR_WPS_STATE:
3045 switch (*ie) {
3046 case IEEE80211_WPS_STATE_NOT_CONFIGURED:
3047 printf(" state:N");
3048 break;
3049 case IEEE80211_WPS_STATE_CONFIGURED:
3050 printf(" state:C");
3051 break;
3052 default:
3053 printf(" state:B<%02x>", *ie);
3054 break;
3055 }
3056 break;
3057 default:
3058 printf(" unknown_wps_attr:0x%x", tlv_type);
3059 break;
dc4301ae
RP
3060 }
3061 ie += tlv_len, len -= tlv_len;
984263bc 3062 }
841ab66c 3063 printf(">");
984263bc 3064 }
dc4301ae
RP
3065}
3066
3067static void
3068printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3069{
3070 printf("%s", tag);
3071 if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
3072 const struct ieee80211_tdma_param *tdma =
3073 (const struct ieee80211_tdma_param *) ie;
3074
3075 /* XXX tstamp */
3076 printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
3077 tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
3078 LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
3079 tdma->tdma_inuse[0]);
3080 }
841ab66c 3081}
984263bc 3082
841ab66c
SZ
3083/*
3084 * Copy the ssid string contents into buf, truncating to fit. If the
3085 * ssid is entirely printable then just copy intact. Otherwise convert
3086 * to hexadecimal. If the result is truncated then replace the last
3087 * three characters with "...".
3088 */
3089static int
3090copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
3091{
46158ff5 3092 const u_int8_t *p;
841ab66c 3093 size_t maxlen;
46158ff5 3094 size_t i;
841ab66c
SZ
3095
3096 if (essid_len > bufsize)
3097 maxlen = bufsize;
3098 else
3099 maxlen = essid_len;
3100 /* determine printable or not */
3101 for (i = 0, p = essid; i < maxlen; i++, p++) {
3102 if (*p < ' ' || *p > 0x7e)
3103 break;
3104 }
3105 if (i != maxlen) { /* not printable, print as hex */
3106 if (bufsize < 3)
3107 return 0;
3108 strlcpy(buf, "0x", bufsize);
3109 bufsize -= 2;
3110 p = essid;
3111 for (i = 0; i < maxlen && bufsize >= 2; i++) {
3112 sprintf(&buf[2+2*i], "%02x", p[i]);
3113 bufsize -= 2;
3114 }
3115 if (i != essid_len)
3116 memcpy(&buf[2+2*i-3], "...", 3);
3117 } else { /* printable, truncate as needed */
3118 memcpy(buf, essid, maxlen);
3119 if (maxlen != essid_len)
3120 memcpy(&buf[maxlen-3], "...", 3);
3121 }
3122 return maxlen;
3123}
3124
dc4301ae
RP
3125static void
3126printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
3127{
3128 char ssid[2*IEEE80211_NWID_LEN+1];
3129
3130 printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
3131}
3132
3133static void
46158ff5
AL
3134printrates(const char *tag, const u_int8_t *ie, size_t ielen,
3135 __unused int maxlen)
dc4301ae
RP
3136{
3137 const char *sep;
46158ff5 3138 size_t i;
dc4301ae
RP
3139
3140 printf("%s", tag);
3141 sep = "<";
3142 for (i = 2; i < ielen; i++) {
3143 printf("%s%s%d", sep,
3144 ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
3145 ie[i] & IEEE80211_RATE_VAL);
3146 sep = ",";
3147 }
3148 printf(">");
3149}
3150
3151static void
46158ff5
AL
3152printcountry(const char *tag, const u_int8_t *ie, size_t ielen,
3153 __unused int maxlen)
dc4301ae
RP
3154{
3155 const struct ieee80211_country_ie *cie =
3156 (const struct ieee80211_country_ie *) ie;
46158ff5 3157 size_t i, nbands, schan, nchan;
dc4301ae
RP
3158
3159 printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
3160 nbands = (cie->len - 3) / sizeof(cie->band[0]);
3161 for (i = 0; i < nbands; i++) {
3162 schan = cie->band[i].schan;
3163 nchan = cie->band[i].nchan;
3164 if (nchan != 1)
46158ff5 3165 printf(" %zu-%zu,%u", schan, schan + nchan-1,
dc4301ae
RP
3166 cie->band[i].maxtxpwr);
3167 else
46158ff5 3168 printf(" %zu,%u", schan, cie->band[i].maxtxpwr);
dc4301ae
RP
3169 }
3170 printf(">");
3171}
3172
46158ff5 3173/* unaligned little endian access */
841ab66c
SZ
3174#define LE_READ_4(p) \
3175 ((u_int32_t) \
3176 ((((const u_int8_t *)(p))[0] ) | \
3177 (((const u_int8_t *)(p))[1] << 8) | \
3178 (((const u_int8_t *)(p))[2] << 16) | \
3179 (((const u_int8_t *)(p))[3] << 24)))
3180
dc4301ae 3181static __inline int
841ab66c
SZ
3182iswpaoui(const u_int8_t *frm)
3183{
3184 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
3185}
3186
dc4301ae
RP
3187static __inline int
3188iswmeinfo(const u_int8_t *frm)
3189{
3190 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3191 frm[6] == WME_INFO_OUI_SUBTYPE;
3192}
3193
3194static __inline int
3195iswmeparam(const u_int8_t *frm)
841ab66c 3196{
dc4301ae
RP
3197 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
3198 frm[6] == WME_PARAM_OUI_SUBTYPE;
841ab66c
SZ
3199}
3200
dc4301ae 3201static __inline int
841ab66c
SZ
3202isatherosoui(const u_int8_t *frm)
3203{
3204 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
3205}
3206
dc4301ae
RP
3207static __inline int
3208istdmaoui(const uint8_t *frm)
3209{
3210 return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
3211}
3212
3213static __inline int
3214iswpsoui(const uint8_t *frm)
3215{
3216 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
3217}
3218
3219static const char *
3220iename(int elemid)
3221{
2ecde614 3222 static char iename_buf[64];
dc4301ae
RP
3223 switch (elemid) {
3224 case IEEE80211_ELEMID_FHPARMS: return " FHPARMS";
3225 case IEEE80211_ELEMID_CFPARMS: return " CFPARMS";
3226 case IEEE80211_ELEMID_TIM: return " TIM";
3227 case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
3228 case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
3229 case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
3230 case IEEE80211_ELEMID_PWRCAP: return " PWRCAP";
3231 case IEEE80211_ELEMID_TPCREQ: return " TPCREQ";
3232 case IEEE80211_ELEMID_TPCREP: return " TPCREP";
3233 case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
3234 case IEEE80211_ELEMID_CSA: return " CSA";
3235 case IEEE80211_ELEMID_MEASREQ: return " MEASREQ";
3236 case IEEE80211_ELEMID_MEASREP: return " MEASREP";
3237 case IEEE80211_ELEMID_QUIET: return " QUIET";
3238 case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS";
2ecde614
AL
3239 case IEEE80211_ELEMID_RESERVED_47:
3240 return " RESERVED_47";
3241 case IEEE80211_ELEMID_MOBILITY_DOMAIN:
3242 return " MOBILITY_DOMAIN";
3243 case IEEE80211_ELEMID_RRM_ENACAPS:
3244 return " RRM_ENCAPS";
3245 case IEEE80211_ELEMID_OVERLAP_BSS_SCAN_PARAM:
3246 return " OVERLAP_BSS";
dc4301ae
RP
3247 case IEEE80211_ELEMID_TPC: return " TPC";
3248 case IEEE80211_ELEMID_CCKM: return " CCKM";
2ecde614 3249 case IEEE80211_ELEMID_EXTCAP: return " EXTCAP";
dc4301ae 3250 }
2ecde614
AL
3251 snprintf(iename_buf, sizeof(iename_buf), " UNKNOWN_ELEMID_%d",
3252 elemid);
3253 return (const char *) iename_buf;
dc4301ae
RP
3254}
3255
841ab66c
SZ
3256static void
3257printies(const u_int8_t *vp, int ielen, int maxcols)
3258{
3259 while (ielen > 0) {
3260 switch (vp[0]) {
dc4301ae
RP
3261 case IEEE80211_ELEMID_SSID:
3262 if (verbose)
3263 printssid(" SSID", vp, 2+vp[1], maxcols);
3264 break;
3265 case IEEE80211_ELEMID_RATES:
3266 case IEEE80211_ELEMID_XRATES:
3267 if (verbose)
3268 printrates(vp[0] == IEEE80211_ELEMID_RATES ?
3269 " RATES" : " XRATES", vp, 2+vp[1], maxcols);
3270 break;
3271 case IEEE80211_ELEMID_DSPARMS:
3272 if (verbose)
3273 printf(" DSPARMS<%u>", vp[2]);
3274 break;
3275 case IEEE80211_ELEMID_COUNTRY:
3276 if (verbose)
3277 printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
3278 break;
3279 case IEEE80211_ELEMID_ERP:
3280 if (verbose)
3281 printf(" ERP<0x%x>", vp[2]);
3282 break;
841ab66c
SZ
3283 case IEEE80211_ELEMID_VENDOR:
3284 if (iswpaoui(vp))
dc4301ae
RP
3285 printwpaie(" WPA", vp, 2+vp[1], maxcols);
3286 else if (iswmeinfo(vp))
3287 printwmeinfo(" WME", vp, 2+vp[1], maxcols);
3288 else if (iswmeparam(vp))
3289 printwmeparam(" WME", vp, 2+vp[1], maxcols);
841ab66c 3290 else if (isatherosoui(vp))
dc4301ae
RP
3291 printathie(" ATH", vp, 2+vp[1], maxcols);
3292 else if (iswpsoui(vp))
3293 printwpsie(" WPS", vp, 2+vp[1], maxcols);
3294 else if (istdmaoui(vp))
3295 printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
3296 else if (verbose)
841ab66c
SZ
3297 printie(" VEN", vp, 2+vp[1], maxcols);
3298 break;
3299 case IEEE80211_ELEMID_RSN:
dc4301ae
RP
3300 printrsnie(" RSN", vp, 2+vp[1], maxcols);
3301 break;
3302 case IEEE80211_ELEMID_HTCAP:
3303 printhtcap(" HTCAP", vp, 2+vp[1], maxcols);
3304 break;
3305 case IEEE80211_ELEMID_HTINFO:
3306 if (verbose)
3307 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols);
3308 break;
3309 case IEEE80211_ELEMID_MESHID:
3310 if (verbose)
3311 printssid(" MESHID", vp, 2+vp[1], maxcols);
3312 break;
3313 case IEEE80211_ELEMID_MESHCONF:
3314 printmeshconf(" MESHCONF", vp, 2+vp[1], maxcols);
841ab66c
SZ
3315 break;
3316 default:
dc4301ae
RP
3317 if (verbose)
3318 printie(iename(vp[0]), vp, 2+vp[1], maxcols);
841ab66c
SZ
3319 break;
3320 }
3321 ielen -= 2+vp[1];
3322 vp += 2+vp[1];
3323 }
3324}
3325
dc4301ae
RP
3326static void
3327printmimo(const struct ieee80211_mimo_info *mi)
3328{
3329 /* NB: don't muddy display unless there's something to show */
3330 if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) {
3331 /* XXX ignore EVM for now */
3332 printf(" (rssi %d:%d:%d nf %d:%d:%d)",
3333 mi->rssi[0], mi->rssi[1], mi->rssi[2],
3334 mi->noise[0], mi->noise[1], mi->noise[2]);
3335 }
3336}
3337
841ab66c 3338static void
bd07b1c3 3339list_scan(int s, int long_ssids)
841ab66c
SZ
3340{
3341 uint8_t buf[24*1024];
841ab66c 3342 char ssid[IEEE80211_NWID_LEN+1];
dc4301ae 3343 const uint8_t *cp;
46158ff5 3344 size_t len, ssidmax, idlen;
841ab66c 3345
dc4301ae 3346 if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0)
841ab66c 3347 errx(1, "unable to get scan results");
841ab66c
SZ
3348 if (len < sizeof(struct ieee80211req_scan_result))
3349 return;
3350
dc4301ae
RP
3351 getchaninfo(s);
3352
bd07b1c3 3353 ssidmax = (verbose || long_ssids) ? IEEE80211_NWID_LEN - 1 : 14;
dc4301ae 3354 printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n"
46158ff5 3355 , (int)ssidmax, (int)ssidmax, "SSID/MESH ID"
841ab66c
SZ
3356 , "BSSID"
3357 , "CHAN"
3358 , "RATE"
dc4301ae 3359 , " S:N"
841ab66c
SZ
3360 , "INT"
3361 , "CAPS"
3362 );
3363 cp = buf;
3364 do {
dc4301ae
RP
3365 const struct ieee80211req_scan_result *sr;
3366 const uint8_t *vp, *idp;
3367
3368 sr = (const struct ieee80211req_scan_result *) cp;
3369 vp = cp + sr->isr_ie_off;
3370 if (sr->isr_meshid_len) {
3371 idp = vp + sr->isr_ssid_len;
3372 idlen = sr->isr_meshid_len;
3373 } else {
3374 idp = vp;
3375 idlen = sr->isr_ssid_len;
3376 }
3377 printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s"
46158ff5
AL
3378 , (int)ssidmax
3379 , copy_essid(ssid, ssidmax, idp, idlen)
3380 , ssid
841ab66c 3381 , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
dc4301ae 3382 , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
841ab66c 3383 , getmaxrate(sr->isr_rates, sr->isr_nrates)
dc4301ae 3384 , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise
841ab66c 3385 , sr->isr_intval
dc4301ae 3386 , getcaps(sr->isr_capinfo)
841ab66c 3387 );
dc4301ae
RP
3388 printies(vp + sr->isr_ssid_len + sr->isr_meshid_len,
3389 sr->isr_ie_len, 24);
841ab66c
SZ
3390 printf("\n");
3391 cp += sr->isr_len, len -= sr->isr_len;
3392 } while (len >= sizeof(struct ieee80211req_scan_result));
3393}
3394
841ab66c
SZ
3395static void
3396scan_and_wait(int s)
3397{
dc4301ae 3398 struct ieee80211_scan_req sr;
841ab66c
SZ
3399 struct ieee80211req ireq;
3400 int sroute;
3401
3402 sroute = socket(PF_ROUTE, SOCK_RAW, 0);
3403 if (sroute < 0) {
3404 perror("socket(PF_ROUTE,SOCK_RAW)");
3405 return;
3406 }
46158ff5 3407 memset(&ireq, 0, sizeof(ireq));
80d2947b 3408 strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
841ab66c 3409 ireq.i_type = IEEE80211_IOC_SCAN_REQ;
dc4301ae
RP
3410
3411 memset(&sr, 0, sizeof(sr));
3412 sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
3413 | IEEE80211_IOC_SCAN_NOPICK
3414 | IEEE80211_IOC_SCAN_ONCE;
3415 sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
3416 sr.sr_nssid = 0;
3417
3418 ireq.i_data = &sr;
3419 ireq.i_len = sizeof(sr);
841ab66c
SZ
3420 /* NB: only root can trigger a scan so ignore errors */
3421 if (ioctl(s, SIOCS80211, &ireq) >= 0) {
3422 char buf[2048];
3423 struct if_announcemsghdr *ifan;
3424 struct rt_msghdr *rtm;
3425
3426 do {
3427 if (read(sroute, buf, sizeof(buf)) < 0) {
3428 perror("read(PF_ROUTE)");
984263bc 3429 break;
841ab66c
SZ
3430 }
3431 rtm = (struct rt_msghdr *) buf;
3432 if (rtm->rtm_version != RTM_VERSION)
984263bc 3433 break;
841ab66c
SZ
3434 ifan = (struct if_announcemsghdr *) rtm;
3435 } while (rtm->rtm_type != RTM_IEEE80211 ||
3436 ifan->ifan_what != RTM_IEEE80211_SCAN);
3437 }
3438 close(sroute);
3439}
3440
3441static
3442DECL_CMD_FUNC(set80211scan, val, d)
3443{
3444 scan_and_wait(s);
bd07b1c3 3445 list_scan(s, 0);
841ab66c
SZ
3446}
3447
73144f38
SZ
3448static enum ieee80211_opmode get80211opmode(int s);
3449
dc4301ae
RP
3450static int
3451gettxseq(const struct ieee80211req_sta_info *si)
3452{
3453 int i, txseq;
3454
3455 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3456 return si->isi_txseqs[0];
3457 /* XXX not right but usually what folks want */
3458 txseq = 0;
3459 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3460 if (si->isi_txseqs[i] > txseq)
3461 txseq = si->isi_txseqs[i];
3462 return txseq;
3463}
3464
3465static int
3466getrxseq(const struct ieee80211req_sta_info *si)
3467{
3468 int i, rxseq;
3469
3470 if ((si->isi_state & IEEE80211_NODE_QOS) == 0)
3471 return si->isi_rxseqs[0];
3472 /* XXX not right but usually what folks want */
3473 rxseq = 0;
3474 for (i = 0; i < IEEE80211_TID_SIZE; i++)
3475 if (si->isi_rxseqs[i] > rxseq)
3476 rxseq = si->isi_rxseqs[i];
3477 return rxseq;
3478}
3479
841ab66c
SZ
3480static void
3481list_stations(int s)
3482{
73144f38
SZ
3483 union {
3484 struct ieee80211req_sta_req req;
3485 uint8_t buf[24*1024];
3486 } u;
3487 enum ieee80211_opmode opmode = get80211opmode(s);
dc4301ae 3488 const uint8_t *cp;
46158ff5 3489 size_t len;
841ab66c 3490
73144f38 3491 /* broadcast address =>'s get all stations */
46158ff5 3492 memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
73144f38
SZ
3493 if (opmode == IEEE80211_M_STA) {
3494 /*
3495 * Get information about the associated AP.
3496 */
46158ff5
AL
3497 get80211(s, IEEE80211_IOC_BSSID,
3498 u.req.is_u.macaddr, IEEE80211_ADDR_LEN);
dc4301ae
RP
3499 }
3500 if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0)
841ab66c 3501 errx(1, "unable to get station information");
841ab66c
SZ
3502 if (len < sizeof(struct ieee80211req_sta_info))
3503 return;
3504
dc4301ae
RP
3505 getchaninfo(s);
3506
46158ff5 3507 if (opmode == IEEE80211_M_MBSS) {
dc4301ae
RP
3508 printf("%-17.17s %4s %5s %5s %7s %4s %4s %4s %6s %6s\n"
3509 , "ADDR"
3510 , "CHAN"
3511 , "LOCAL"
3512 , "PEER"
3513 , "STATE"
3514 , "RATE"
3515 , "RSSI"
3516 , "IDLE"
3517 , "TXSEQ"
3518 , "RXSEQ"
3519 );
46158ff5 3520 } else {
dc4301ae
RP
3521 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %-7s\n"
3522 , "ADDR"
3523 , "AID"
3524 , "CHAN"
3525 , "RATE"
3526 , "RSSI"
3527 , "IDLE"
3528 , "TXSEQ"
3529 , "RXSEQ"
3530 , "CAPS"
3531 , "FLAG"
3532 );
46158ff5 3533 }
dc4301ae 3534 cp = (const uint8_t *) u.req.info;
841ab66c 3535 do {
dc4301ae 3536 const struct ieee80211req_sta_info *si;
841ab66c 3537
dc4301ae 3538 si = (const struct ieee80211req_sta_info *) cp;
73144f38
SZ
3539 if (si->isi_len < sizeof(*si))
3540 break;
46158ff5 3541 if (opmode == IEEE80211_M_MBSS) {
dc4301ae
RP
3542 printf("%s %4d %5x %5x %7.7s %3dM %4.1f %4d %6d %6d"
3543 , ether_ntoa((const struct ether_addr*)
3544 si->isi_macaddr)
3545 , ieee80211_mhz2ieee(si->isi_freq,
3546 si->isi_flags)
3547 , si->isi_localid
3548 , si->isi_peerid
3549 , mesh_linkstate_string(si->isi_peerstate)
3550 , si->isi_txmbps/2
3551 , si->isi_rssi/2.
3552 , si->isi_inact
3553 , gettxseq(si)
3554 , getrxseq(si)
3555 );
46158ff5 3556 } else {
dc4301ae
RP
3557 printf("%s %4u %4d %3dM %4.1f %4d %6d %6d %-4.4s %-7.7s"
3558 , ether_ntoa((const struct ether_addr*)
3559 si->isi_macaddr)
3560 , IEEE80211_AID(si->isi_associd)
3561 , ieee80211_mhz2ieee(si->isi_freq,
3562 si->isi_flags)
3563 , si->isi_txmbps/2
3564 , si->isi_rssi/2.
3565 , si->isi_inact
3566 , gettxseq(si)
3567 , getrxseq(si)
3568 , getcaps(si->isi_capinfo)
3569 , getflags(si->isi_state)
3570 );
46158ff5 3571 }
dc4301ae
RP
3572 printies(cp + si->isi_ie_off, si->isi_ie_len, 24);
3573 printmimo(&si->isi_mimo);
841ab66c
SZ
3574 printf("\n");
3575 cp += si->isi_len, len -= si->isi_len;
3576 } while (len >= sizeof(struct ieee80211req_sta_info));
3577}
3578
dc4301ae
RP
3579static const char *
3580mesh_linkstate_string(uint8_t state)
841ab66c 3581{
dc4301ae
RP
3582 static const char *state_names[] = {
3583 [0] = "IDLE",
3584 [1] = "OPEN-TX",
3585 [2] = "OPEN-RX",
3586 [3] = "CONF-RX",
3587 [4] = "ESTAB",
3588 [5] = "HOLDING",
3589 };
3590
b6b91ec7 3591 if (state >= nitems(state_names)) {
dc4301ae
RP
3592 static char buf[10];
3593 snprintf(buf, sizeof(buf), "#%u", state);
3594 return buf;
46158ff5 3595 } else {
dc4301ae 3596 return state_names[state];
46158ff5 3597 }
dc4301ae 3598}
841ab66c 3599
dc4301ae
RP
3600static const char *
3601get_chaninfo(const struct ieee80211_channel *c, int precise,
3602 char buf[], size_t bsize)
3603{
841ab66c
SZ
3604 buf[0] = '\0';
3605 if (IEEE80211_IS_CHAN_FHSS(c))
dc4301ae 3606 strlcat(buf, " FHSS", bsize);
841ab66c 3607 if (IEEE80211_IS_CHAN_A(c))
dc4301ae
RP
3608 strlcat(buf, " 11a", bsize);
3609 else if (IEEE80211_IS_CHAN_ANYG(c))
3610 strlcat(buf, " 11g", bsize);
841ab66c 3611 else if (IEEE80211_IS_CHAN_B(c))
dc4301ae
RP
3612 strlcat(buf, " 11b", bsize);
3613 if (IEEE80211_IS_CHAN_HALF(c))
3614 strlcat(buf, "/10MHz", bsize);
3615 if (IEEE80211_IS_CHAN_QUARTER(c))
3616 strlcat(buf, "/5MHz", bsize);
3617 if (IEEE80211_IS_CHAN_TURBO(c))
3618 strlcat(buf, " Turbo", bsize);
3619 if (precise) {
3620 if (IEEE80211_IS_CHAN_HT20(c))
3621 strlcat(buf, " ht/20", bsize);
3622 else if (IEEE80211_IS_CHAN_HT40D(c))
3623 strlcat(buf, " ht/40-", bsize);
3624 else if (IEEE80211_IS_CHAN_HT40U(c))
3625 strlcat(buf, " ht/40+", bsize);
3626 } else {
3627 if (IEEE80211_IS_CHAN_HT(c))
3628 strlcat(buf, " ht", bsize);
3629 }
3630 return buf;
841ab66c
SZ
3631}
3632
3633static void
dc4301ae 3634print_chaninfo(const struct ieee80211_channel *c, int verb)
841ab66c 3635{
dc4301ae
RP
3636 char buf[14];
3637
3638 printf("Channel %3u : %u%c MHz%-14.14s",
3639 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq,
3640 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
3641 get_chaninfo(c, verb, buf, sizeof(buf)));
3642}
3643
3644static int
3645chanpref(const struct ieee80211_channel *c)
3646{
3647 if (IEEE80211_IS_CHAN_HT40(c))
3648 return 40;
3649 if (IEEE80211_IS_CHAN_HT20(c))
3650 return 30;
3651 if (IEEE80211_IS_CHAN_HALF(c))
3652 return 10;
3653 if (IEEE80211_IS_CHAN_QUARTER(c))
3654 return 5;
3655 if (IEEE80211_IS_CHAN_TURBO(c))
3656 return 25;
3657 if (IEEE80211_IS_CHAN_A(c))
3658 return 20;
3659 if (IEEE80211_IS_CHAN_G(c))
3660 return 20;
3661 if (IEEE80211_IS_CHAN_B(c))
3662 return 15;
3663 if (IEEE80211_IS_CHAN_PUREG(c))
3664 return 15;
3665 return 0;
3666}
3667
3668static void
3669print_channels(int s, const struct ieee80211req_chaninfo *chans,
3670 int allchans, int verb)
3671{
3672 struct ieee80211req_chaninfo *achans;
3673 uint8_t reported[IEEE80211_CHAN_BYTES];
841ab66c 3674 const struct ieee80211_channel *c;
46158ff5 3675 size_t i, half;
841ab66c 3676
dc4301ae
RP
3677 achans = malloc(IEEE80211_CHANINFO_SPACE(chans));
3678 if (achans == NULL)
3679 errx(1, "no space for active channel list");
3680 achans->ic_nchans = 0;
3681 memset(reported, 0, sizeof(reported));
3682 if (!allchans) {
3683 struct ieee80211req_chanlist active;
3684
3685 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0)
3686 errx(1, "unable to get active channel list");
3687 for (i = 0; i < chans->ic_nchans; i++) {
3688 c = &chans->ic_chans[i];
3689 if (!isset(active.ic_channels, c->ic_ieee))
3690 continue;
3691 /*
3692 * Suppress compatible duplicates unless
3693 * verbose. The kernel gives us it's
3694 * complete channel list which has separate
3695 * entries for 11g/11b and 11a/turbo.
3696 */
3697 if (isset(reported, c->ic_ieee) && !verb) {
3698 /* XXX we assume duplicates are adjacent */
3699 achans->ic_chans[achans->ic_nchans-1] = *c;
3700 } else {
3701 achans->ic_chans[achans->ic_nchans++] = *c;
3702 setbit(reported, c->ic_ieee);
3703 }
3704 }
3705 } else {
3706 for (i = 0; i < chans->ic_nchans; i++) {
3707 c = &chans->ic_chans[i];
3708 /* suppress duplicates as above */
3709 if (isset(reported, c->ic_ieee) && !verb) {
3710 /* XXX we assume duplicates are adjacent */
3711 struct ieee80211_channel *a =
3712 &achans->ic_chans[achans->ic_nchans-1];
3713 if (chanpref(c) > chanpref(a))
3714 *a = *c;
3715 } else {
3716 achans->ic_chans[achans->ic_nchans++] = *c;
3717 setbit(reported, c->ic_ieee);
3718 }
3719 }
3720 }
3721 half = achans->ic_nchans / 2;
3722 if (achans->ic_nchans % 2)
3723 half++;
3724
3725 for (i = 0; i < achans->ic_nchans / 2; i++) {
3726 print_chaninfo(&achans->ic_chans[i], verb);
3727 print_chaninfo(&achans->ic_chans[half+i], verb);
3728 printf("\n");
3729 }
3730 if (achans->ic_nchans % 2) {
3731 print_chaninfo(&achans->ic_chans[i], verb);
3732 printf("\n");
3733 }
3734 free(achans);
3735}
3736
3737static void
3738list_channels(int s, int allchans)
3739{
3740 getchaninfo(s);
3741 print_channels(s, chaninfo, allchans, verbose);
3742}
3743
3744static void
3745print_txpow(const struct ieee80211_channel *c)
3746{
3747 printf("Channel %3u : %u MHz %3.1f reg %2d ",
3748 c->ic_ieee, c->ic_freq,
3749 c->ic_maxpower/2., c->ic_maxregpower);
3750}
3751
3752static void
3753print_txpow_verbose(const struct ieee80211_channel *c)
3754{
3755 print_chaninfo(c, 1);
3756 printf("min %4.1f dBm max %3.1f dBm reg %2d dBm",
3757 c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower);
3758 /* indicate where regulatory cap limits power use */
3759 if (c->ic_maxpower > 2*c->ic_maxregpower)
3760 printf(" <");
3761}
3762
3763static void
3764list_txpow(int s)
3765{
3766 struct ieee80211req_chaninfo *achans;
3767 uint8_t reported[IEEE80211_CHAN_BYTES];
3768 struct ieee80211_channel *c, *prev;
46158ff5 3769 size_t i, half;
dc4301ae
RP
3770
3771 getchaninfo(s);
3772 achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo));
3773 if (achans == NULL)
3774 errx(1, "no space for active channel list");
3775 achans->ic_nchans = 0;
3776 memset(reported, 0, sizeof(reported));
3777 for (i = 0; i < chaninfo->ic_nchans; i++) {
3778 c = &chaninfo->ic_chans[i];
3779 /* suppress duplicates as above */
3780 if (isset(reported, c->ic_ieee) && !verbose) {
3781 /* XXX we assume duplicates are adjacent */
3782 prev = &achans->ic_chans[achans->ic_nchans-1];
3783 /* display highest power on channel */
3784 if (c->ic_maxpower > prev->ic_maxpower)
3785 *prev = *c;
3786 } else {
3787 achans->ic_chans[achans->ic_nchans++] = *c;
3788 setbit(reported, c->ic_ieee);
3789 }
3790 }
3791 if (!verbose) {
3792 half = achans->ic_nchans / 2;
3793 if (achans->ic_nchans % 2)
3794 half++;
3795
3796 for (i = 0; i < achans->ic_nchans / 2; i++) {
3797 print_txpow(&achans->ic_chans[i]);
3798 print_txpow(&achans->ic_chans[half+i]);
3799 printf("\n");
3800 }
3801 if (achans->ic_nchans % 2) {
3802 print_txpow(&achans->ic_chans[i]);
3803 printf("\n");
3804 }
3805 } else {
3806 for (i = 0; i < achans->ic_nchans; i++) {
3807 print_txpow_verbose(&achans->ic_chans[i]);
3808 printf("\n");
3809 }
3810 }
3811 free(achans);
3812}
3813
3814static void
3815list_keys(int s)
3816{
3817}
3818
3819#define IEEE80211_C_BITS \
3820 "\20\1STA\002803ENCAP\7FF\10TURBOP\11IBSS\12PMGT" \
3821 "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
3822 "\21MONITOR\22DFS\23MBSS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
3823 "\37TXFRAG\40TDMA"
3824
3825static void
3826list_capabilities(int s)
3827{
3828 struct ieee80211_devcaps_req *dc;
3829
3830 if (verbose)
3831 dc = malloc(IEEE80211_DEVCAPS_SIZE(MAXCHAN));
3832 else
3833 dc = malloc(IEEE80211_DEVCAPS_SIZE(1));
3834 if (dc == NULL)
3835 errx(1, "no space for device capabilities");
3836 dc->dc_chaninfo.ic_nchans = verbose ? MAXCHAN : 1;
3837 getdevcaps(s, dc);
3838 printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS);
3839 if (dc->dc_cryptocaps != 0 || verbose) {
3840 putchar('\n');
3841 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS);
3842 }
3843 if (dc->dc_htcaps != 0 || verbose) {
3844 putchar('\n');
3845 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
3846 }
3847 putchar('\n');
3848 if (verbose) {
3849 chaninfo = &dc->dc_chaninfo; /* XXX */
3850 print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, verbose);
3851 }
3852 free(dc);
3853}
3854
3855static int
3856get80211wme(int s, int param, int ac, int *val)
3857{
3858 struct ieee80211req ireq;
3859
46158ff5 3860 memset(&ireq, 0, sizeof(ireq));
80d2947b 3861 strlcpy(ireq.i_name, name, sizeof(ireq.i_name));
dc4301ae
RP
3862 ireq.i_type = param;
3863 ireq.i_len = ac;
3864 if (ioctl(s, SIOCG80211, &ireq) < 0) {
3865 warn("cannot get WME parameter %d, ac %d%s",
3866 param, ac & IEEE80211_WMEPARAM_VAL,
3867 ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : "");
3868 return -1;
841ab66c 3869 }
dc4301ae
RP
3870 *val = ireq.i_val;
3871 return 0;
841ab66c
SZ
3872}
3873
3874static void
dc4301ae 3875list_wme_aci(int s, const char *tag, int ac)
841ab66c 3876{
dc4301ae
RP
3877 int val;
3878
3879 printf("\t%s", tag);
3880
3881 /* show WME BSS parameters */
3882 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1)
3883 printf(" cwmin %2u", val);
3884 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1)
3885 printf(" cwmax %2u", val);
3886 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1)
3887 printf(" aifs %2u", val);
3888 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1)
3889 printf(" txopLimit %3u", val);
3890 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) {
3891 if (val)
3892 printf(" acm");
3893 else if (verbose)
3894 printf(" -acm");
3895 }
3896 /* !BSS only */
3897 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3898 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) {
3899 if (!val)
3900 printf(" -ack");
3901 else if (verbose)
3902 printf(" ack");
3903 }
3904 }
3905 printf("\n");
841ab66c
SZ
3906}
3907
dc4301ae
RP
3908static void
3909list_wme(int s)
3910{
3911 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
3912 int ac;
984263bc 3913
dc4301ae
RP
3914 if (verbose) {
3915 /* display both BSS and local settings */
3916 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
3917 again:
3918 if (ac & IEEE80211_WMEPARAM_BSS)
3919 list_wme_aci(s, " ", ac);
3920 else
3921 list_wme_aci(s, acnames[ac], ac);
3922 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) {
3923 ac |= IEEE80211_WMEPARAM_BSS;
3924 goto again;
3925 } else
3926 ac &= ~IEEE80211_WMEPARAM_BSS;
3927 }
3928 } else {
3929 /* display only channel settings */
3930 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++)
3931 list_wme_aci(s, acnames[ac], ac);
3932 }
3933}
21152d39 3934
841ab66c 3935static void
dc4301ae 3936list_roam(int s)
841ab66c 3937{
dc4301ae
RP
3938 const struct ieee80211_roamparam *rp;
3939 int mode;
21152d39 3940
dc4301ae
RP
3941 getroam(s);
3942 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
3943 rp = &roamparams.params[mode];
3944 if (rp->rssi == 0 && rp->rate == 0)
3945 continue;
3946 if (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG) {
3947 if (rp->rssi & 1)
3948 LINE_CHECK("roam:%-7.7s rssi %2u.5dBm MCS %2u ",
3949 modename[mode], rp->rssi/2,
3950 rp->rate &~ IEEE80211_RATE_MCS);