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