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