ifieee80211.c revision 187843
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 187843 2009-01-28 19:20:12Z 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 strlcat(buf, " 11a", bsize); 3124 else if (IEEE80211_IS_CHAN_ANYG(c)) 3125 strlcat(buf, " 11g", bsize); 3126 else if (IEEE80211_IS_CHAN_B(c)) 3127 strlcat(buf, " 11b", bsize); 3128 if (IEEE80211_IS_CHAN_HALF(c)) 3129 strlcat(buf, "/10Mhz", bsize); 3130 if (IEEE80211_IS_CHAN_QUARTER(c)) 3131 strlcat(buf, "/5Mhz", bsize); 3132 if (IEEE80211_IS_CHAN_TURBO(c)) 3133 strlcat(buf, " Turbo", bsize); 3134 if (precise) { 3135 if (IEEE80211_IS_CHAN_HT20(c)) 3136 strlcat(buf, " ht/20", bsize); 3137 else if (IEEE80211_IS_CHAN_HT40D(c)) 3138 strlcat(buf, " ht/40-", bsize); 3139 else if (IEEE80211_IS_CHAN_HT40U(c)) 3140 strlcat(buf, " ht/40+", bsize); 3141 } else { 3142 if (IEEE80211_IS_CHAN_HT(c)) 3143 strlcat(buf, " ht", bsize); 3144 } 3145 return buf; 3146} 3147 3148static void 3149print_chaninfo(const struct ieee80211_channel *c, int verb) 3150{ 3151 char buf[14]; 3152 3153 printf("Channel %3u : %u%c Mhz%-14.14s", 3154 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq, 3155 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', 3156 get_chaninfo(c, verb, buf, sizeof(buf))); 3157} 3158 3159static void 3160print_channels(int s, const struct ieee80211req_chaninfo *chans, 3161 int allchans, int verb) 3162{ 3163 struct ieee80211req_chaninfo *achans; 3164 uint8_t reported[IEEE80211_CHAN_BYTES]; 3165 const struct ieee80211_channel *c; 3166 int i, half; 3167 3168 achans = malloc(IEEE80211_CHANINFO_SPACE(chans)); 3169 if (achans == NULL) 3170 errx(1, "no space for active channel list"); 3171 achans->ic_nchans = 0; 3172 memset(reported, 0, sizeof(reported)); 3173 if (!allchans) { 3174 struct ieee80211req_chanlist active; 3175 3176 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0) 3177 errx(1, "unable to get active channel list"); 3178 for (i = 0; i < chans->ic_nchans; i++) { 3179 c = &chans->ic_chans[i]; 3180 if (!isset(active.ic_channels, c->ic_ieee)) 3181 continue; 3182 /* 3183 * Suppress compatible duplicates unless 3184 * verbose. The kernel gives us it's 3185 * complete channel list which has separate 3186 * entries for 11g/11b and 11a/turbo. 3187 */ 3188 if (isset(reported, c->ic_ieee) && !verb) { 3189 /* XXX we assume duplicates are adjacent */ 3190 achans->ic_chans[achans->ic_nchans-1] = *c; 3191 } else { 3192 achans->ic_chans[achans->ic_nchans++] = *c; 3193 setbit(reported, c->ic_ieee); 3194 } 3195 } 3196 } else { 3197 for (i = 0; i < chans->ic_nchans; i++) { 3198 c = &chans->ic_chans[i]; 3199 /* suppress duplicates as above */ 3200 if (isset(reported, c->ic_ieee) && !verb) { 3201 /* XXX we assume duplicates are adjacent */ 3202 achans->ic_chans[achans->ic_nchans-1] = *c; 3203 } else { 3204 achans->ic_chans[achans->ic_nchans++] = *c; 3205 setbit(reported, c->ic_ieee); 3206 } 3207 } 3208 } 3209 half = achans->ic_nchans / 2; 3210 if (achans->ic_nchans % 2) 3211 half++; 3212 3213 for (i = 0; i < achans->ic_nchans / 2; i++) { 3214 print_chaninfo(&achans->ic_chans[i], verb); 3215 print_chaninfo(&achans->ic_chans[half+i], verb); 3216 printf("\n"); 3217 } 3218 if (achans->ic_nchans % 2) { 3219 print_chaninfo(&achans->ic_chans[i], verb); 3220 printf("\n"); 3221 } 3222 free(achans); 3223} 3224 3225static void 3226list_channels(int s, int allchans) 3227{ 3228 getchaninfo(s); 3229 print_channels(s, chaninfo, allchans, verbose); 3230} 3231 3232static void 3233print_txpow(const struct ieee80211_channel *c) 3234{ 3235 printf("Channel %3u : %u Mhz %3.1f reg %2d ", 3236 c->ic_ieee, c->ic_freq, 3237 c->ic_maxpower/2., c->ic_maxregpower); 3238} 3239 3240static void 3241print_txpow_verbose(const struct ieee80211_channel *c) 3242{ 3243 print_chaninfo(c, 1); 3244 printf("min %4.1f dBm max %3.1f dBm reg %2d dBm", 3245 c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower); 3246 /* indicate where regulatory cap limits power use */ 3247 if (c->ic_maxpower > 2*c->ic_maxregpower) 3248 printf(" <"); 3249} 3250 3251static void 3252list_txpow(int s) 3253{ 3254 struct ieee80211req_chaninfo *achans; 3255 uint8_t reported[IEEE80211_CHAN_BYTES]; 3256 struct ieee80211_channel *c, *prev; 3257 int i, half; 3258 3259 getchaninfo(s); 3260 achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo)); 3261 if (achans == NULL) 3262 errx(1, "no space for active channel list"); 3263 achans->ic_nchans = 0; 3264 memset(reported, 0, sizeof(reported)); 3265 for (i = 0; i < chaninfo->ic_nchans; i++) { 3266 c = &chaninfo->ic_chans[i]; 3267 /* suppress duplicates as above */ 3268 if (isset(reported, c->ic_ieee) && !verbose) { 3269 /* XXX we assume duplicates are adjacent */ 3270 prev = &achans->ic_chans[achans->ic_nchans-1]; 3271 /* display highest power on channel */ 3272 if (c->ic_maxpower > prev->ic_maxpower) 3273 *prev = *c; 3274 } else { 3275 achans->ic_chans[achans->ic_nchans++] = *c; 3276 setbit(reported, c->ic_ieee); 3277 } 3278 } 3279 if (!verbose) { 3280 half = achans->ic_nchans / 2; 3281 if (achans->ic_nchans % 2) 3282 half++; 3283 3284 for (i = 0; i < achans->ic_nchans / 2; i++) { 3285 print_txpow(&achans->ic_chans[i]); 3286 print_txpow(&achans->ic_chans[half+i]); 3287 printf("\n"); 3288 } 3289 if (achans->ic_nchans % 2) { 3290 print_txpow(&achans->ic_chans[i]); 3291 printf("\n"); 3292 } 3293 } else { 3294 for (i = 0; i < achans->ic_nchans; i++) { 3295 print_txpow_verbose(&achans->ic_chans[i]); 3296 printf("\n"); 3297 } 3298 } 3299 free(achans); 3300} 3301 3302static void 3303list_keys(int s) 3304{ 3305} 3306 3307#define IEEE80211_C_BITS \ 3308 "\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \ 3309 "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \ 3310 "\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \ 3311 "\37TXFRAG\40TDMA" 3312 3313static void 3314list_capabilities(int s) 3315{ 3316 struct ieee80211_devcaps_req *dc; 3317 3318 dc = malloc(IEEE80211_DEVCAPS_SIZE(1)); 3319 if (dc == NULL) 3320 errx(1, "no space for device capabilities"); 3321 dc->dc_chaninfo.ic_nchans = 1; 3322 getdevcaps(s, dc); 3323 printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS); 3324 if (dc->dc_cryptocaps != 0 || verbose) { 3325 putchar('\n'); 3326 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS); 3327 } 3328 if (dc->dc_htcaps != 0 || verbose) { 3329 putchar('\n'); 3330 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS); 3331 } 3332 putchar('\n'); 3333 free(dc); 3334} 3335 3336static int 3337get80211wme(int s, int param, int ac, int *val) 3338{ 3339 struct ieee80211req ireq; 3340 3341 (void) memset(&ireq, 0, sizeof(ireq)); 3342 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 3343 ireq.i_type = param; 3344 ireq.i_len = ac; 3345 if (ioctl(s, SIOCG80211, &ireq) < 0) { 3346 warn("cannot get WME parameter %d, ac %d%s", 3347 param, ac & IEEE80211_WMEPARAM_VAL, 3348 ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : ""); 3349 return -1; 3350 } 3351 *val = ireq.i_val; 3352 return 0; 3353} 3354 3355static void 3356list_wme_aci(int s, const char *tag, int ac) 3357{ 3358 int val; 3359 3360 printf("\t%s", tag); 3361 3362 /* show WME BSS parameters */ 3363 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1) 3364 printf(" cwmin %2u", val); 3365 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1) 3366 printf(" cwmax %2u", val); 3367 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1) 3368 printf(" aifs %2u", val); 3369 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1) 3370 printf(" txopLimit %3u", val); 3371 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) { 3372 if (val) 3373 printf(" acm"); 3374 else if (verbose) 3375 printf(" -acm"); 3376 } 3377 /* !BSS only */ 3378 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { 3379 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) { 3380 if (!val) 3381 printf(" -ack"); 3382 else if (verbose) 3383 printf(" ack"); 3384 } 3385 } 3386 printf("\n"); 3387} 3388 3389static void 3390list_wme(int s) 3391{ 3392 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; 3393 int ac; 3394 3395 if (verbose) { 3396 /* display both BSS and local settings */ 3397 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { 3398 again: 3399 if (ac & IEEE80211_WMEPARAM_BSS) 3400 list_wme_aci(s, " ", ac); 3401 else 3402 list_wme_aci(s, acnames[ac], ac); 3403 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { 3404 ac |= IEEE80211_WMEPARAM_BSS; 3405 goto again; 3406 } else 3407 ac &= ~IEEE80211_WMEPARAM_BSS; 3408 } 3409 } else { 3410 /* display only channel settings */ 3411 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) 3412 list_wme_aci(s, acnames[ac], ac); 3413 } 3414} 3415 3416static void 3417list_roam(int s) 3418{ 3419 const struct ieee80211_roamparam *rp; 3420 int mode; 3421 3422 getroam(s); 3423 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { 3424 rp = &roamparams.params[mode]; 3425 if (rp->rssi == 0 && rp->rate == 0) 3426 continue; 3427 if (rp->rssi & 1) 3428 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s", 3429 modename[mode], rp->rssi/2, rp->rate/2); 3430 else 3431 LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s", 3432 modename[mode], rp->rssi/2, rp->rate/2); 3433 } 3434 for (; mode < IEEE80211_MODE_MAX; mode++) { 3435 rp = &roamparams.params[mode]; 3436 if (rp->rssi == 0 && rp->rate == 0) 3437 continue; 3438 if (rp->rssi & 1) 3439 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ", 3440 modename[mode], rp->rssi/2, rp->rate &~ 0x80); 3441 else 3442 LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ", 3443 modename[mode], rp->rssi/2, rp->rate &~ 0x80); 3444 } 3445} 3446 3447static void 3448list_txparams(int s) 3449{ 3450 const struct ieee80211_txparam *tp; 3451 int mode; 3452 3453 gettxparams(s); 3454 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { 3455 tp = &txparams.params[mode]; 3456 if (tp->mgmtrate == 0 && tp->mcastrate == 0) 3457 continue; 3458 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) 3459 LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s " 3460 "mcast %2u Mb/s maxretry %u", 3461 modename[mode], tp->mgmtrate/2, 3462 tp->mcastrate/2, tp->maxretry); 3463 else 3464 LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s " 3465 "mcast %2u Mb/s maxretry %u", 3466 modename[mode], tp->ucastrate/2, tp->mgmtrate/2, 3467 tp->mcastrate/2, tp->maxretry); 3468 } 3469 for (; mode < IEEE80211_MODE_MAX; mode++) { 3470 tp = &txparams.params[mode]; 3471 if (tp->mgmtrate == 0 && tp->mcastrate == 0) 3472 continue; 3473 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) 3474 LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS " 3475 "mcast %2u MCS maxretry %u", 3476 modename[mode], tp->mgmtrate &~ 0x80, 3477 tp->mcastrate &~ 0x80, tp->maxretry); 3478 else 3479 LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS " 3480 "mcast %2u MCS maxretry %u", 3481 modename[mode], tp->ucastrate &~ 0x80, 3482 tp->mgmtrate &~ 0x80, 3483 tp->mcastrate &~ 0x80, tp->maxretry); 3484 } 3485} 3486 3487static void 3488printpolicy(int policy) 3489{ 3490 switch (policy) { 3491 case IEEE80211_MACCMD_POLICY_OPEN: 3492 printf("policy: open\n"); 3493 break; 3494 case IEEE80211_MACCMD_POLICY_ALLOW: 3495 printf("policy: allow\n"); 3496 break; 3497 case IEEE80211_MACCMD_POLICY_DENY: 3498 printf("policy: deny\n"); 3499 break; 3500 case IEEE80211_MACCMD_POLICY_RADIUS: 3501 printf("policy: radius\n"); 3502 break; 3503 default: 3504 printf("policy: unknown (%u)\n", policy); 3505 break; 3506 } 3507} 3508 3509static void 3510list_mac(int s) 3511{ 3512 struct ieee80211req ireq; 3513 struct ieee80211req_maclist *acllist; 3514 int i, nacls, policy, len; 3515 uint8_t *data; 3516 char c; 3517 3518 (void) memset(&ireq, 0, sizeof(ireq)); 3519 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */ 3520 ireq.i_type = IEEE80211_IOC_MACCMD; 3521 ireq.i_val = IEEE80211_MACCMD_POLICY; 3522 if (ioctl(s, SIOCG80211, &ireq) < 0) { 3523 if (errno == EINVAL) { 3524 printf("No acl policy loaded\n"); 3525 return; 3526 } 3527 err(1, "unable to get mac policy"); 3528 } 3529 policy = ireq.i_val; 3530 if (policy == IEEE80211_MACCMD_POLICY_OPEN) { 3531 c = '*'; 3532 } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) { 3533 c = '+'; 3534 } else if (policy == IEEE80211_MACCMD_POLICY_DENY) { 3535 c = '-'; 3536 } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) { 3537 c = 'r'; /* NB: should never have entries */ 3538 } else { 3539 printf("policy: unknown (%u)\n", policy); 3540 c = '?'; 3541 } 3542 if (verbose || c == '?') 3543 printpolicy(policy); 3544 3545 ireq.i_val = IEEE80211_MACCMD_LIST; 3546 ireq.i_len = 0; 3547 if (ioctl(s, SIOCG80211, &ireq) < 0) 3548 err(1, "unable to get mac acl list size"); 3549 if (ireq.i_len == 0) { /* NB: no acls */ 3550 if (!(verbose || c == '?')) 3551 printpolicy(policy); 3552 return; 3553 } 3554 len = ireq.i_len; 3555 3556 data = malloc(len); 3557 if (data == NULL) 3558 err(1, "out of memory for acl list"); 3559 3560 ireq.i_data = data; 3561 if (ioctl(s, SIOCG80211, &ireq) < 0) 3562 err(1, "unable to get mac acl list"); 3563 nacls = len / sizeof(*acllist); 3564 acllist = (struct ieee80211req_maclist *) data; 3565 for (i = 0; i < nacls; i++) 3566 printf("%c%s\n", c, ether_ntoa( 3567 (const struct ether_addr *) acllist[i].ml_macaddr)); 3568 free(data); 3569} 3570 3571static void 3572print_regdomain(const struct ieee80211_regdomain *reg, int verb) 3573{ 3574 if ((reg->regdomain != 0 && 3575 reg->regdomain != reg->country) || verb) { 3576 const struct regdomain *rd = 3577 lib80211_regdomain_findbysku(getregdata(), reg->regdomain); 3578 if (rd == NULL) 3579 LINE_CHECK("regdomain %d", reg->regdomain); 3580 else 3581 LINE_CHECK("regdomain %s", rd->name); 3582 } 3583 if (reg->country != 0 || verb) { 3584 const struct country *cc = 3585 lib80211_country_findbycc(getregdata(), reg->country); 3586 if (cc == NULL) 3587 LINE_CHECK("country %d", reg->country); 3588 else 3589 LINE_CHECK("country %s", cc->isoname); 3590 } 3591 if (reg->location == 'I') 3592 LINE_CHECK("indoor"); 3593 else if (reg->location == 'O') 3594 LINE_CHECK("outdoor"); 3595 else if (verb) 3596 LINE_CHECK("anywhere"); 3597 if (reg->ecm) 3598 LINE_CHECK("ecm"); 3599 else if (verb) 3600 LINE_CHECK("-ecm"); 3601} 3602 3603static void 3604list_regdomain(int s, int channelsalso) 3605{ 3606 getregdomain(s); 3607 if (channelsalso) { 3608 getchaninfo(s); 3609 spacer = ':'; 3610 print_regdomain(®domain, 1); 3611 LINE_BREAK(); 3612 print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/); 3613 } else 3614 print_regdomain(®domain, verbose); 3615} 3616 3617static 3618DECL_CMD_FUNC(set80211list, arg, d) 3619{ 3620#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 3621 3622 LINE_INIT('\t'); 3623 3624 if (iseq(arg, "sta")) 3625 list_stations(s); 3626 else if (iseq(arg, "scan") || iseq(arg, "ap")) 3627 list_scan(s); 3628 else if (iseq(arg, "chan") || iseq(arg, "freq")) 3629 list_channels(s, 1); 3630 else if (iseq(arg, "active")) 3631 list_channels(s, 0); 3632 else if (iseq(arg, "keys")) 3633 list_keys(s); 3634 else if (iseq(arg, "caps")) 3635 list_capabilities(s); 3636 else if (iseq(arg, "wme") || iseq(arg, "wmm")) 3637 list_wme(s); 3638 else if (iseq(arg, "mac")) 3639 list_mac(s); 3640 else if (iseq(arg, "txpow")) 3641 list_txpow(s); 3642 else if (iseq(arg, "roam")) 3643 list_roam(s); 3644 else if (iseq(arg, "txparam") || iseq(arg, "txparm")) 3645 list_txparams(s); 3646 else if (iseq(arg, "regdomain")) 3647 list_regdomain(s, 1); 3648 else if (iseq(arg, "countries")) 3649 list_countries(); 3650 else 3651 errx(1, "Don't know how to list %s for %s", arg, name); 3652 LINE_BREAK(); 3653#undef iseq 3654} 3655 3656static enum ieee80211_opmode 3657get80211opmode(int s) 3658{ 3659 struct ifmediareq ifmr; 3660 3661 (void) memset(&ifmr, 0, sizeof(ifmr)); 3662 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); 3663 3664 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { 3665 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { 3666 if (ifmr.ifm_current & IFM_FLAG0) 3667 return IEEE80211_M_AHDEMO; 3668 else 3669 return IEEE80211_M_IBSS; 3670 } 3671 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) 3672 return IEEE80211_M_HOSTAP; 3673 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) 3674 return IEEE80211_M_MONITOR; 3675 } 3676 return IEEE80211_M_STA; 3677} 3678 3679#if 0 3680static void 3681printcipher(int s, struct ieee80211req *ireq, int keylenop) 3682{ 3683 switch (ireq->i_val) { 3684 case IEEE80211_CIPHER_WEP: 3685 ireq->i_type = keylenop; 3686 if (ioctl(s, SIOCG80211, ireq) != -1) 3687 printf("WEP-%s", 3688 ireq->i_len <= 5 ? "40" : 3689 ireq->i_len <= 13 ? "104" : "128"); 3690 else 3691 printf("WEP"); 3692 break; 3693 case IEEE80211_CIPHER_TKIP: 3694 printf("TKIP"); 3695 break; 3696 case IEEE80211_CIPHER_AES_OCB: 3697 printf("AES-OCB"); 3698 break; 3699 case IEEE80211_CIPHER_AES_CCM: 3700 printf("AES-CCM"); 3701 break; 3702 case IEEE80211_CIPHER_CKIP: 3703 printf("CKIP"); 3704 break; 3705 case IEEE80211_CIPHER_NONE: 3706 printf("NONE"); 3707 break; 3708 default: 3709 printf("UNKNOWN (0x%x)", ireq->i_val); 3710 break; 3711 } 3712} 3713#endif 3714 3715static void 3716printkey(const struct ieee80211req_key *ik) 3717{ 3718 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; 3719 int keylen = ik->ik_keylen; 3720 int printcontents; 3721 3722 printcontents = printkeys && 3723 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); 3724 if (printcontents) 3725 LINE_BREAK(); 3726 switch (ik->ik_type) { 3727 case IEEE80211_CIPHER_WEP: 3728 /* compatibility */ 3729 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1, 3730 keylen <= 5 ? "40-bit" : 3731 keylen <= 13 ? "104-bit" : "128-bit"); 3732 break; 3733 case IEEE80211_CIPHER_TKIP: 3734 if (keylen > 128/8) 3735 keylen -= 128/8; /* ignore MIC for now */ 3736 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3737 break; 3738 case IEEE80211_CIPHER_AES_OCB: 3739 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3740 break; 3741 case IEEE80211_CIPHER_AES_CCM: 3742 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3743 break; 3744 case IEEE80211_CIPHER_CKIP: 3745 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3746 break; 3747 case IEEE80211_CIPHER_NONE: 3748 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3749 break; 3750 default: 3751 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit", 3752 ik->ik_type, ik->ik_keyix+1, 8*keylen); 3753 break; 3754 } 3755 if (printcontents) { 3756 int i; 3757 3758 printf(" <"); 3759 for (i = 0; i < keylen; i++) 3760 printf("%02x", ik->ik_keydata[i]); 3761 printf(">"); 3762 if (ik->ik_type != IEEE80211_CIPHER_WEP && 3763 (ik->ik_keyrsc != 0 || verbose)) 3764 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc); 3765 if (ik->ik_type != IEEE80211_CIPHER_WEP && 3766 (ik->ik_keytsc != 0 || verbose)) 3767 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc); 3768 if (ik->ik_flags != 0 && verbose) { 3769 const char *sep = " "; 3770 3771 if (ik->ik_flags & IEEE80211_KEY_XMIT) 3772 printf("%stx", sep), sep = "+"; 3773 if (ik->ik_flags & IEEE80211_KEY_RECV) 3774 printf("%srx", sep), sep = "+"; 3775 if (ik->ik_flags & IEEE80211_KEY_DEFAULT) 3776 printf("%sdef", sep), sep = "+"; 3777 } 3778 LINE_BREAK(); 3779 } 3780} 3781 3782static void 3783printrate(const char *tag, int v, int defrate, int defmcs) 3784{ 3785 if (v == 11) 3786 LINE_CHECK("%s 5.5", tag); 3787 else if (v & 0x80) { 3788 if (v != defmcs) 3789 LINE_CHECK("%s %d", tag, v &~ 0x80); 3790 } else { 3791 if (v != defrate) 3792 LINE_CHECK("%s %d", tag, v/2); 3793 } 3794} 3795 3796static int 3797getssid(int s, int ix, void *data, size_t len, int *plen) 3798{ 3799 struct ieee80211req ireq; 3800 3801 (void) memset(&ireq, 0, sizeof(ireq)); 3802 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 3803 ireq.i_type = IEEE80211_IOC_SSID; 3804 ireq.i_val = ix; 3805 ireq.i_data = data; 3806 ireq.i_len = len; 3807 if (ioctl(s, SIOCG80211, &ireq) < 0) 3808 return -1; 3809 *plen = ireq.i_len; 3810 return 0; 3811} 3812 3813static void 3814ieee80211_status(int s) 3815{ 3816 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 3817 enum ieee80211_opmode opmode = get80211opmode(s); 3818 int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode; 3819 uint8_t data[32]; 3820 const struct ieee80211_channel *c; 3821 const struct ieee80211_roamparam *rp; 3822 const struct ieee80211_txparam *tp; 3823 3824 if (getssid(s, -1, data, sizeof(data), &len) < 0) { 3825 /* If we can't get the SSID, this isn't an 802.11 device. */ 3826 return; 3827 } 3828 3829 /* 3830 * Invalidate cached state so printing status for multiple 3831 * if's doesn't reuse the first interfaces' cached state. 3832 */ 3833 gotcurchan = 0; 3834 gotroam = 0; 3835 gottxparams = 0; 3836 gothtconf = 0; 3837 gotregdomain = 0; 3838 3839 if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0) 3840 num = 0; 3841 printf("\tssid "); 3842 if (num > 1) { 3843 for (i = 0; i < num; i++) { 3844 if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) { 3845 printf(" %d:", i + 1); 3846 print_string(data, len); 3847 } 3848 } 3849 } else 3850 print_string(data, len); 3851 3852 c = getcurchan(s); 3853 if (c->ic_freq != IEEE80211_CHAN_ANY) { 3854 char buf[14]; 3855 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq, 3856 get_chaninfo(c, 1, buf, sizeof(buf))); 3857 } else if (verbose) 3858 printf(" channel UNDEF"); 3859 3860 if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 && 3861 (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose)) 3862 printf(" bssid %s", ether_ntoa((struct ether_addr *)data)); 3863 3864 if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) { 3865 printf("\n\tstationname "); 3866 print_string(data, len); 3867 } 3868 3869 spacer = ' '; /* force first break */ 3870 LINE_BREAK(); 3871 3872 list_regdomain(s, 0); 3873 3874 wpa = 0; 3875 if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) { 3876 switch (val) { 3877 case IEEE80211_AUTH_NONE: 3878 LINE_CHECK("authmode NONE"); 3879 break; 3880 case IEEE80211_AUTH_OPEN: 3881 LINE_CHECK("authmode OPEN"); 3882 break; 3883 case IEEE80211_AUTH_SHARED: 3884 LINE_CHECK("authmode SHARED"); 3885 break; 3886 case IEEE80211_AUTH_8021X: 3887 LINE_CHECK("authmode 802.1x"); 3888 break; 3889 case IEEE80211_AUTH_WPA: 3890 if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0) 3891 wpa = 1; /* default to WPA1 */ 3892 switch (wpa) { 3893 case 2: 3894 LINE_CHECK("authmode WPA2/802.11i"); 3895 break; 3896 case 3: 3897 LINE_CHECK("authmode WPA1+WPA2/802.11i"); 3898 break; 3899 default: 3900 LINE_CHECK("authmode WPA"); 3901 break; 3902 } 3903 break; 3904 case IEEE80211_AUTH_AUTO: 3905 LINE_CHECK("authmode AUTO"); 3906 break; 3907 default: 3908 LINE_CHECK("authmode UNKNOWN (0x%x)", val); 3909 break; 3910 } 3911 } 3912 3913 if (wpa || verbose) { 3914 if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) { 3915 if (val) 3916 LINE_CHECK("wps"); 3917 else if (verbose) 3918 LINE_CHECK("-wps"); 3919 } 3920 if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) { 3921 if (val) 3922 LINE_CHECK("tsn"); 3923 else if (verbose) 3924 LINE_CHECK("-tsn"); 3925 } 3926 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) { 3927 if (val) 3928 LINE_CHECK("countermeasures"); 3929 else if (verbose) 3930 LINE_CHECK("-countermeasures"); 3931 } 3932#if 0 3933 /* XXX not interesting with WPA done in user space */ 3934 ireq.i_type = IEEE80211_IOC_KEYMGTALGS; 3935 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3936 } 3937 3938 ireq.i_type = IEEE80211_IOC_MCASTCIPHER; 3939 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3940 LINE_CHECK("mcastcipher "); 3941 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); 3942 spacer = ' '; 3943 } 3944 3945 ireq.i_type = IEEE80211_IOC_UCASTCIPHER; 3946 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3947 LINE_CHECK("ucastcipher "); 3948 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); 3949 } 3950 3951 if (wpa & 2) { 3952 ireq.i_type = IEEE80211_IOC_RSNCAPS; 3953 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3954 LINE_CHECK("RSN caps 0x%x", ireq.i_val); 3955 spacer = ' '; 3956 } 3957 } 3958 3959 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; 3960 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3961 } 3962#endif 3963 } 3964 3965 if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 && 3966 wepmode != IEEE80211_WEP_NOSUP) { 3967 int firstkey; 3968 3969 switch (wepmode) { 3970 case IEEE80211_WEP_OFF: 3971 LINE_CHECK("privacy OFF"); 3972 break; 3973 case IEEE80211_WEP_ON: 3974 LINE_CHECK("privacy ON"); 3975 break; 3976 case IEEE80211_WEP_MIXED: 3977 LINE_CHECK("privacy MIXED"); 3978 break; 3979 default: 3980 LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode); 3981 break; 3982 } 3983 3984 /* 3985 * If we get here then we've got WEP support so we need 3986 * to print WEP status. 3987 */ 3988 3989 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) { 3990 warn("WEP support, but no tx key!"); 3991 goto end; 3992 } 3993 if (val != -1) 3994 LINE_CHECK("deftxkey %d", val+1); 3995 else if (wepmode != IEEE80211_WEP_OFF || verbose) 3996 LINE_CHECK("deftxkey UNDEF"); 3997 3998 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) { 3999 warn("WEP support, but no NUMWEPKEYS support!"); 4000 goto end; 4001 } 4002 4003 firstkey = 1; 4004 for (i = 0; i < num; i++) { 4005 struct ieee80211req_key ik; 4006 4007 memset(&ik, 0, sizeof(ik)); 4008 ik.ik_keyix = i; 4009 if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) { 4010 warn("WEP support, but can get keys!"); 4011 goto end; 4012 } 4013 if (ik.ik_keylen != 0) { 4014 if (verbose) 4015 LINE_BREAK(); 4016 printkey(&ik); 4017 firstkey = 0; 4018 } 4019 } 4020end: 4021 ; 4022 } 4023 4024 if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 && 4025 val != IEEE80211_POWERSAVE_NOSUP ) { 4026 if (val != IEEE80211_POWERSAVE_OFF || verbose) { 4027 switch (val) { 4028 case IEEE80211_POWERSAVE_OFF: 4029 LINE_CHECK("powersavemode OFF"); 4030 break; 4031 case IEEE80211_POWERSAVE_CAM: 4032 LINE_CHECK("powersavemode CAM"); 4033 break; 4034 case IEEE80211_POWERSAVE_PSP: 4035 LINE_CHECK("powersavemode PSP"); 4036 break; 4037 case IEEE80211_POWERSAVE_PSP_CAM: 4038 LINE_CHECK("powersavemode PSP-CAM"); 4039 break; 4040 } 4041 if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1) 4042 LINE_CHECK("powersavesleep %d", val); 4043 } 4044 } 4045 4046 if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) { 4047 if (val & 1) 4048 LINE_CHECK("txpower %d.5", val/2); 4049 else 4050 LINE_CHECK("txpower %d", val/2); 4051 } 4052 if (verbose) { 4053 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1) 4054 LINE_CHECK("txpowmax %.1f", val/2.); 4055 } 4056 4057 if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) { 4058 if (val) 4059 LINE_CHECK("dotd"); 4060 else if (verbose) 4061 LINE_CHECK("-dotd"); 4062 } 4063 4064 if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) { 4065 if (val != IEEE80211_RTS_MAX || verbose) 4066 LINE_CHECK("rtsthreshold %d", val); 4067 } 4068 4069 if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) { 4070 if (val != IEEE80211_FRAG_MAX || verbose) 4071 LINE_CHECK("fragthreshold %d", val); 4072 } 4073 if (opmode == IEEE80211_M_STA || verbose) { 4074 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) { 4075 if (val != IEEE80211_HWBMISS_MAX || verbose) 4076 LINE_CHECK("bmiss %d", val); 4077 } 4078 } 4079 4080 if (!verbose) { 4081 gettxparams(s); 4082 tp = &txparams.params[chan2mode(c)]; 4083 printrate("ucastrate", tp->ucastrate, 4084 IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE); 4085 printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0); 4086 printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0); 4087 if (tp->maxretry != 6) /* XXX */ 4088 LINE_CHECK("maxretry %d", tp->maxretry); 4089 } else { 4090 LINE_BREAK(); 4091 list_txparams(s); 4092 } 4093 4094 bgscaninterval = -1; 4095 (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval); 4096 4097 if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) { 4098 if (val != bgscaninterval || verbose) 4099 LINE_CHECK("scanvalid %u", val); 4100 } 4101 4102 bgscan = 0; 4103 if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) { 4104 if (bgscan) 4105 LINE_CHECK("bgscan"); 4106 else if (verbose) 4107 LINE_CHECK("-bgscan"); 4108 } 4109 if (bgscan || verbose) { 4110 if (bgscaninterval != -1) 4111 LINE_CHECK("bgscanintvl %u", bgscaninterval); 4112 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1) 4113 LINE_CHECK("bgscanidle %u", val); 4114 if (!verbose) { 4115 getroam(s); 4116 rp = &roamparams.params[chan2mode(c)]; 4117 if (rp->rssi & 1) 4118 LINE_CHECK("roam:rssi %u.5", rp->rssi/2); 4119 else 4120 LINE_CHECK("roam:rssi %u", rp->rssi/2); 4121 LINE_CHECK("roam:rate %u", rp->rate/2); 4122 } else { 4123 LINE_BREAK(); 4124 list_roam(s); 4125 } 4126 } 4127 4128 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) { 4129 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) { 4130 if (val) 4131 LINE_CHECK("pureg"); 4132 else if (verbose) 4133 LINE_CHECK("-pureg"); 4134 } 4135 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) { 4136 switch (val) { 4137 case IEEE80211_PROTMODE_OFF: 4138 LINE_CHECK("protmode OFF"); 4139 break; 4140 case IEEE80211_PROTMODE_CTS: 4141 LINE_CHECK("protmode CTS"); 4142 break; 4143 case IEEE80211_PROTMODE_RTSCTS: 4144 LINE_CHECK("protmode RTSCTS"); 4145 break; 4146 default: 4147 LINE_CHECK("protmode UNKNOWN (0x%x)", val); 4148 break; 4149 } 4150 } 4151 } 4152 4153 if (IEEE80211_IS_CHAN_HT(c) || verbose) { 4154 gethtconf(s); 4155 switch (htconf & 3) { 4156 case 0: 4157 case 2: 4158 LINE_CHECK("-ht"); 4159 break; 4160 case 1: 4161 LINE_CHECK("ht20"); 4162 break; 4163 case 3: 4164 if (verbose) 4165 LINE_CHECK("ht"); 4166 break; 4167 } 4168 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) { 4169 if (!val) 4170 LINE_CHECK("-htcompat"); 4171 else if (verbose) 4172 LINE_CHECK("htcompat"); 4173 } 4174 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) { 4175 switch (val) { 4176 case 0: 4177 LINE_CHECK("-ampdu"); 4178 break; 4179 case 1: 4180 LINE_CHECK("ampdutx -ampdurx"); 4181 break; 4182 case 2: 4183 LINE_CHECK("-ampdutx ampdurx"); 4184 break; 4185 case 3: 4186 if (verbose) 4187 LINE_CHECK("ampdu"); 4188 break; 4189 } 4190 } 4191 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) { 4192 switch (val) { 4193 case IEEE80211_HTCAP_MAXRXAMPDU_8K: 4194 LINE_CHECK("ampdulimit 8k"); 4195 break; 4196 case IEEE80211_HTCAP_MAXRXAMPDU_16K: 4197 LINE_CHECK("ampdulimit 16k"); 4198 break; 4199 case IEEE80211_HTCAP_MAXRXAMPDU_32K: 4200 LINE_CHECK("ampdulimit 32k"); 4201 break; 4202 case IEEE80211_HTCAP_MAXRXAMPDU_64K: 4203 LINE_CHECK("ampdulimit 64k"); 4204 break; 4205 } 4206 } 4207 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) { 4208 switch (val) { 4209 case IEEE80211_HTCAP_MPDUDENSITY_NA: 4210 if (verbose) 4211 LINE_CHECK("ampdudensity NA"); 4212 break; 4213 case IEEE80211_HTCAP_MPDUDENSITY_025: 4214 LINE_CHECK("ampdudensity .25"); 4215 break; 4216 case IEEE80211_HTCAP_MPDUDENSITY_05: 4217 LINE_CHECK("ampdudensity .5"); 4218 break; 4219 case IEEE80211_HTCAP_MPDUDENSITY_1: 4220 LINE_CHECK("ampdudensity 1"); 4221 break; 4222 case IEEE80211_HTCAP_MPDUDENSITY_2: 4223 LINE_CHECK("ampdudensity 2"); 4224 break; 4225 case IEEE80211_HTCAP_MPDUDENSITY_4: 4226 LINE_CHECK("ampdudensity 4"); 4227 break; 4228 case IEEE80211_HTCAP_MPDUDENSITY_8: 4229 LINE_CHECK("ampdudensity 8"); 4230 break; 4231 case IEEE80211_HTCAP_MPDUDENSITY_16: 4232 LINE_CHECK("ampdudensity 16"); 4233 break; 4234 } 4235 } 4236 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) { 4237 switch (val) { 4238 case 0: 4239 LINE_CHECK("-amsdu"); 4240 break; 4241 case 1: 4242 LINE_CHECK("amsdutx -amsdurx"); 4243 break; 4244 case 2: 4245 LINE_CHECK("-amsdutx amsdurx"); 4246 break; 4247 case 3: 4248 if (verbose) 4249 LINE_CHECK("amsdu"); 4250 break; 4251 } 4252 } 4253 /* XXX amsdu limit */ 4254 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) { 4255 if (val) 4256 LINE_CHECK("shortgi"); 4257 else if (verbose) 4258 LINE_CHECK("-shortgi"); 4259 } 4260 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) { 4261 if (val == IEEE80211_PROTMODE_OFF) 4262 LINE_CHECK("htprotmode OFF"); 4263 else if (val != IEEE80211_PROTMODE_RTSCTS) 4264 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val); 4265 else if (verbose) 4266 LINE_CHECK("htprotmode RTSCTS"); 4267 } 4268 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) { 4269 if (val) 4270 LINE_CHECK("puren"); 4271 else if (verbose) 4272 LINE_CHECK("-puren"); 4273 } 4274 if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) { 4275 if (val == IEEE80211_HTCAP_SMPS_DYNAMIC) 4276 LINE_CHECK("smpsdyn"); 4277 else if (val == IEEE80211_HTCAP_SMPS_ENA) 4278 LINE_CHECK("smps"); 4279 else if (verbose) 4280 LINE_CHECK("-smps"); 4281 } 4282 if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) { 4283 if (val) 4284 LINE_CHECK("rifs"); 4285 else if (verbose) 4286 LINE_CHECK("-rifs"); 4287 } 4288 } 4289 4290 if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) { 4291 if (wme) 4292 LINE_CHECK("wme"); 4293 else if (verbose) 4294 LINE_CHECK("-wme"); 4295 } else 4296 wme = 0; 4297 4298 if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) { 4299 if (val) 4300 LINE_CHECK("burst"); 4301 else if (verbose) 4302 LINE_CHECK("-burst"); 4303 } 4304 4305 if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) { 4306 if (val) 4307 LINE_CHECK("ff"); 4308 else if (verbose) 4309 LINE_CHECK("-ff"); 4310 } 4311 if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) { 4312 if (val) 4313 LINE_CHECK("dturbo"); 4314 else if (verbose) 4315 LINE_CHECK("-dturbo"); 4316 } 4317 if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) { 4318 if (val) 4319 LINE_CHECK("dwds"); 4320 else if (verbose) 4321 LINE_CHECK("-dwds"); 4322 } 4323 4324 if (opmode == IEEE80211_M_HOSTAP) { 4325 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) { 4326 if (val) 4327 LINE_CHECK("hidessid"); 4328 else if (verbose) 4329 LINE_CHECK("-hidessid"); 4330 } 4331 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) { 4332 if (!val) 4333 LINE_CHECK("-apbridge"); 4334 else if (verbose) 4335 LINE_CHECK("apbridge"); 4336 } 4337 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1) 4338 LINE_CHECK("dtimperiod %u", val); 4339 4340 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) { 4341 if (!val) 4342 LINE_CHECK("-doth"); 4343 else if (verbose) 4344 LINE_CHECK("doth"); 4345 } 4346 if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) { 4347 if (!val) 4348 LINE_CHECK("-dfs"); 4349 else if (verbose) 4350 LINE_CHECK("dfs"); 4351 } 4352 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) { 4353 if (!val) 4354 LINE_CHECK("-inact"); 4355 else if (verbose) 4356 LINE_CHECK("inact"); 4357 } 4358 } else { 4359 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) { 4360 if (val != IEEE80211_ROAMING_AUTO || verbose) { 4361 switch (val) { 4362 case IEEE80211_ROAMING_DEVICE: 4363 LINE_CHECK("roaming DEVICE"); 4364 break; 4365 case IEEE80211_ROAMING_AUTO: 4366 LINE_CHECK("roaming AUTO"); 4367 break; 4368 case IEEE80211_ROAMING_MANUAL: 4369 LINE_CHECK("roaming MANUAL"); 4370 break; 4371 default: 4372 LINE_CHECK("roaming UNKNOWN (0x%x)", 4373 val); 4374 break; 4375 } 4376 } 4377 } 4378 } 4379 4380 if (opmode == IEEE80211_M_AHDEMO) { 4381 if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1) 4382 LINE_CHECK("tdmaslot %u", val); 4383 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1) 4384 LINE_CHECK("tdmaslotcnt %u", val); 4385 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1) 4386 LINE_CHECK("tdmaslotlen %u", val); 4387 if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1) 4388 LINE_CHECK("tdmabintval %u", val); 4389 } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) { 4390 /* XXX default define not visible */ 4391 if (val != 100 || verbose) 4392 LINE_CHECK("bintval %u", val); 4393 } 4394 4395 if (wme && verbose) { 4396 LINE_BREAK(); 4397 list_wme(s); 4398 } 4399 LINE_BREAK(); 4400} 4401 4402static int 4403get80211(int s, int type, void *data, int len) 4404{ 4405 struct ieee80211req ireq; 4406 4407 (void) memset(&ireq, 0, sizeof(ireq)); 4408 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4409 ireq.i_type = type; 4410 ireq.i_data = data; 4411 ireq.i_len = len; 4412 return ioctl(s, SIOCG80211, &ireq); 4413} 4414 4415static int 4416get80211len(int s, int type, void *data, int len, int *plen) 4417{ 4418 struct ieee80211req ireq; 4419 4420 (void) memset(&ireq, 0, sizeof(ireq)); 4421 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4422 ireq.i_type = type; 4423 ireq.i_len = len; 4424 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ 4425 ireq.i_data = data; 4426 if (ioctl(s, SIOCG80211, &ireq) < 0) 4427 return -1; 4428 *plen = ireq.i_len; 4429 return 0; 4430} 4431 4432static int 4433get80211val(int s, int type, int *val) 4434{ 4435 struct ieee80211req ireq; 4436 4437 (void) memset(&ireq, 0, sizeof(ireq)); 4438 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4439 ireq.i_type = type; 4440 if (ioctl(s, SIOCG80211, &ireq) < 0) 4441 return -1; 4442 *val = ireq.i_val; 4443 return 0; 4444} 4445 4446static void 4447set80211(int s, int type, int val, int len, void *data) 4448{ 4449 struct ieee80211req ireq; 4450 4451 (void) memset(&ireq, 0, sizeof(ireq)); 4452 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4453 ireq.i_type = type; 4454 ireq.i_val = val; 4455 ireq.i_len = len; 4456 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ 4457 ireq.i_data = data; 4458 if (ioctl(s, SIOCS80211, &ireq) < 0) 4459 err(1, "SIOCS80211"); 4460} 4461 4462static const char * 4463get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) 4464{ 4465 int len; 4466 int hexstr; 4467 u_int8_t *p; 4468 4469 len = *lenp; 4470 p = buf; 4471 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x'); 4472 if (hexstr) 4473 val += 2; 4474 for (;;) { 4475 if (*val == '\0') 4476 break; 4477 if (sep != NULL && strchr(sep, *val) != NULL) { 4478 val++; 4479 break; 4480 } 4481 if (hexstr) { 4482 if (!isxdigit((u_char)val[0])) { 4483 warnx("bad hexadecimal digits"); 4484 return NULL; 4485 } 4486 if (!isxdigit((u_char)val[1])) { 4487 warnx("odd count hexadecimal digits"); 4488 return NULL; 4489 } 4490 } 4491 if (p >= buf + len) { 4492 if (hexstr) 4493 warnx("hexadecimal digits too long"); 4494 else 4495 warnx("string too long"); 4496 return NULL; 4497 } 4498 if (hexstr) { 4499#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10) 4500 *p++ = (tohex((u_char)val[0]) << 4) | 4501 tohex((u_char)val[1]); 4502#undef tohex 4503 val += 2; 4504 } else 4505 *p++ = *val++; 4506 } 4507 len = p - buf; 4508 /* The string "-" is treated as the empty string. */ 4509 if (!hexstr && len == 1 && buf[0] == '-') { 4510 len = 0; 4511 memset(buf, 0, *lenp); 4512 } else if (len < *lenp) 4513 memset(p, 0, *lenp - len); 4514 *lenp = len; 4515 return val; 4516} 4517 4518static void 4519print_string(const u_int8_t *buf, int len) 4520{ 4521 int i; 4522 int hasspc; 4523 4524 i = 0; 4525 hasspc = 0; 4526 for (; i < len; i++) { 4527 if (!isprint(buf[i]) && buf[i] != '\0') 4528 break; 4529 if (isspace(buf[i])) 4530 hasspc++; 4531 } 4532 if (i == len) { 4533 if (hasspc || len == 0 || buf[0] == '\0') 4534 printf("\"%.*s\"", len, buf); 4535 else 4536 printf("%.*s", len, buf); 4537 } else { 4538 printf("0x"); 4539 for (i = 0; i < len; i++) 4540 printf("%02x", buf[i]); 4541 } 4542} 4543 4544/* 4545 * Virtual AP cloning support. 4546 */ 4547static struct ieee80211_clone_params params = { 4548 .icp_opmode = IEEE80211_M_STA, /* default to station mode */ 4549}; 4550 4551static void 4552wlan_create(int s, struct ifreq *ifr) 4553{ 4554 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 4555 4556 if (params.icp_parent[0] == '\0') 4557 errx(1, "must specify a parent when creating a wlan device"); 4558 if (params.icp_opmode == IEEE80211_M_WDS && 4559 memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0) 4560 errx(1, "no bssid specified for WDS (use wlanbssid)"); 4561 ifr->ifr_data = (caddr_t) ¶ms; 4562 if (ioctl(s, SIOCIFCREATE2, ifr) < 0) 4563 err(1, "SIOCIFCREATE2"); 4564} 4565 4566static 4567DECL_CMD_FUNC(set80211clone_wlandev, arg, d) 4568{ 4569 strlcpy(params.icp_parent, arg, IFNAMSIZ); 4570 clone_setcallback(wlan_create); 4571} 4572 4573static 4574DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d) 4575{ 4576 const struct ether_addr *ea; 4577 4578 ea = ether_aton(arg); 4579 if (ea == NULL) 4580 errx(1, "%s: cannot parse bssid", arg); 4581 memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN); 4582 clone_setcallback(wlan_create); 4583} 4584 4585static 4586DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d) 4587{ 4588 const struct ether_addr *ea; 4589 4590 ea = ether_aton(arg); 4591 if (ea == NULL) 4592 errx(1, "%s: cannot parse addres", arg); 4593 memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN); 4594 params.icp_flags |= IEEE80211_CLONE_MACADDR; 4595 clone_setcallback(wlan_create); 4596} 4597 4598static 4599DECL_CMD_FUNC(set80211clone_wlanmode, arg, d) 4600{ 4601#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 4602 if (iseq(arg, "sta")) 4603 params.icp_opmode = IEEE80211_M_STA; 4604 else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo")) 4605 params.icp_opmode = IEEE80211_M_AHDEMO; 4606 else if (iseq(arg, "ibss") || iseq(arg, "adhoc")) 4607 params.icp_opmode = IEEE80211_M_IBSS; 4608 else if (iseq(arg, "ap") || iseq(arg, "host")) 4609 params.icp_opmode = IEEE80211_M_HOSTAP; 4610 else if (iseq(arg, "wds")) 4611 params.icp_opmode = IEEE80211_M_WDS; 4612 else if (iseq(arg, "monitor")) 4613 params.icp_opmode = IEEE80211_M_MONITOR; 4614 else if (iseq(arg, "tdma")) { 4615 params.icp_opmode = IEEE80211_M_AHDEMO; 4616 params.icp_flags |= IEEE80211_CLONE_TDMA; 4617 } else 4618 errx(1, "Don't know to create %s for %s", arg, name); 4619 clone_setcallback(wlan_create); 4620#undef iseq 4621} 4622 4623static void 4624set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp) 4625{ 4626 /* NB: inverted sense */ 4627 if (d) 4628 params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS; 4629 else 4630 params.icp_flags |= IEEE80211_CLONE_NOBEACONS; 4631 clone_setcallback(wlan_create); 4632} 4633 4634static void 4635set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp) 4636{ 4637 if (d) 4638 params.icp_flags |= IEEE80211_CLONE_BSSID; 4639 else 4640 params.icp_flags &= ~IEEE80211_CLONE_BSSID; 4641 clone_setcallback(wlan_create); 4642} 4643 4644static void 4645set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp) 4646{ 4647 if (d) 4648 params.icp_flags |= IEEE80211_CLONE_WDSLEGACY; 4649 else 4650 params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY; 4651 clone_setcallback(wlan_create); 4652} 4653 4654static struct cmd ieee80211_cmds[] = { 4655 DEF_CMD_ARG("ssid", set80211ssid), 4656 DEF_CMD_ARG("nwid", set80211ssid), 4657 DEF_CMD_ARG("stationname", set80211stationname), 4658 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */ 4659 DEF_CMD_ARG("channel", set80211channel), 4660 DEF_CMD_ARG("authmode", set80211authmode), 4661 DEF_CMD_ARG("powersavemode", set80211powersavemode), 4662 DEF_CMD("powersave", 1, set80211powersave), 4663 DEF_CMD("-powersave", 0, set80211powersave), 4664 DEF_CMD_ARG("powersavesleep", set80211powersavesleep), 4665 DEF_CMD_ARG("wepmode", set80211wepmode), 4666 DEF_CMD("wep", 1, set80211wep), 4667 DEF_CMD("-wep", 0, set80211wep), 4668 DEF_CMD_ARG("deftxkey", set80211weptxkey), 4669 DEF_CMD_ARG("weptxkey", set80211weptxkey), 4670 DEF_CMD_ARG("wepkey", set80211wepkey), 4671 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */ 4672 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */ 4673 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold), 4674 DEF_CMD_ARG("protmode", set80211protmode), 4675 DEF_CMD_ARG("txpower", set80211txpower), 4676 DEF_CMD_ARG("roaming", set80211roaming), 4677 DEF_CMD("wme", 1, set80211wme), 4678 DEF_CMD("-wme", 0, set80211wme), 4679 DEF_CMD("wmm", 1, set80211wme), 4680 DEF_CMD("-wmm", 0, set80211wme), 4681 DEF_CMD("hidessid", 1, set80211hidessid), 4682 DEF_CMD("-hidessid", 0, set80211hidessid), 4683 DEF_CMD("apbridge", 1, set80211apbridge), 4684 DEF_CMD("-apbridge", 0, set80211apbridge), 4685 DEF_CMD_ARG("chanlist", set80211chanlist), 4686 DEF_CMD_ARG("bssid", set80211bssid), 4687 DEF_CMD_ARG("ap", set80211bssid), 4688 DEF_CMD("scan", 0, set80211scan), 4689 DEF_CMD_ARG("list", set80211list), 4690 DEF_CMD_ARG2("cwmin", set80211cwmin), 4691 DEF_CMD_ARG2("cwmax", set80211cwmax), 4692 DEF_CMD_ARG2("aifs", set80211aifs), 4693 DEF_CMD_ARG2("txoplimit", set80211txoplimit), 4694 DEF_CMD_ARG("acm", set80211acm), 4695 DEF_CMD_ARG("-acm", set80211noacm), 4696 DEF_CMD_ARG("ack", set80211ackpolicy), 4697 DEF_CMD_ARG("-ack", set80211noackpolicy), 4698 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin), 4699 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax), 4700 DEF_CMD_ARG2("bss:aifs", set80211bssaifs), 4701 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit), 4702 DEF_CMD_ARG("dtimperiod", set80211dtimperiod), 4703 DEF_CMD_ARG("bintval", set80211bintval), 4704 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), 4705 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), 4706 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), 4707 DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd), 4708 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), 4709 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), 4710 DEF_CMD_ARG("mac:add", set80211addmac), 4711 DEF_CMD_ARG("mac:del", set80211delmac), 4712 DEF_CMD_ARG("mac:kick", set80211kickmac), 4713 DEF_CMD("pureg", 1, set80211pureg), 4714 DEF_CMD("-pureg", 0, set80211pureg), 4715 DEF_CMD("ff", 1, set80211fastframes), 4716 DEF_CMD("-ff", 0, set80211fastframes), 4717 DEF_CMD("dturbo", 1, set80211dturbo), 4718 DEF_CMD("-dturbo", 0, set80211dturbo), 4719 DEF_CMD("bgscan", 1, set80211bgscan), 4720 DEF_CMD("-bgscan", 0, set80211bgscan), 4721 DEF_CMD_ARG("bgscanidle", set80211bgscanidle), 4722 DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl), 4723 DEF_CMD_ARG("scanvalid", set80211scanvalid), 4724 DEF_CMD_ARG("roam:rssi", set80211roamrssi), 4725 DEF_CMD_ARG("roam:rate", set80211roamrate), 4726 DEF_CMD_ARG("mcastrate", set80211mcastrate), 4727 DEF_CMD_ARG("ucastrate", set80211ucastrate), 4728 DEF_CMD_ARG("mgtrate", set80211mgtrate), 4729 DEF_CMD_ARG("mgmtrate", set80211mgtrate), 4730 DEF_CMD_ARG("maxretry", set80211maxretry), 4731 DEF_CMD_ARG("fragthreshold", set80211fragthreshold), 4732 DEF_CMD("burst", 1, set80211burst), 4733 DEF_CMD("-burst", 0, set80211burst), 4734 DEF_CMD_ARG("bmiss", set80211bmissthreshold), 4735 DEF_CMD_ARG("bmissthreshold", set80211bmissthreshold), 4736 DEF_CMD("shortgi", 1, set80211shortgi), 4737 DEF_CMD("-shortgi", 0, set80211shortgi), 4738 DEF_CMD("ampdurx", 2, set80211ampdu), 4739 DEF_CMD("-ampdurx", -2, set80211ampdu), 4740 DEF_CMD("ampdutx", 1, set80211ampdu), 4741 DEF_CMD("-ampdutx", -1, set80211ampdu), 4742 DEF_CMD("ampdu", 3, set80211ampdu), /* NB: tx+rx */ 4743 DEF_CMD("-ampdu", -3, set80211ampdu), 4744 DEF_CMD_ARG("ampdulimit", set80211ampdulimit), 4745 DEF_CMD_ARG("ampdudensity", set80211ampdudensity), 4746 DEF_CMD("amsdurx", 2, set80211amsdu), 4747 DEF_CMD("-amsdurx", -2, set80211amsdu), 4748 DEF_CMD("amsdutx", 1, set80211amsdu), 4749 DEF_CMD("-amsdutx", -1, set80211amsdu), 4750 DEF_CMD("amsdu", 3, set80211amsdu), /* NB: tx+rx */ 4751 DEF_CMD("-amsdu", -3, set80211amsdu), 4752 DEF_CMD_ARG("amsdulimit", set80211amsdulimit), 4753 DEF_CMD("puren", 1, set80211puren), 4754 DEF_CMD("-puren", 0, set80211puren), 4755 DEF_CMD("doth", 1, set80211doth), 4756 DEF_CMD("-doth", 0, set80211doth), 4757 DEF_CMD("dfs", 1, set80211dfs), 4758 DEF_CMD("-dfs", 0, set80211dfs), 4759 DEF_CMD("htcompat", 1, set80211htcompat), 4760 DEF_CMD("-htcompat", 0, set80211htcompat), 4761 DEF_CMD("dwds", 1, set80211dwds), 4762 DEF_CMD("-dwds", 0, set80211dwds), 4763 DEF_CMD("inact", 1, set80211inact), 4764 DEF_CMD("-inact", 0, set80211inact), 4765 DEF_CMD("tsn", 1, set80211tsn), 4766 DEF_CMD("-tsn", 0, set80211tsn), 4767 DEF_CMD_ARG("regdomain", set80211regdomain), 4768 DEF_CMD_ARG("country", set80211country), 4769 DEF_CMD("indoor", 'I', set80211location), 4770 DEF_CMD("-indoor", 'O', set80211location), 4771 DEF_CMD("outdoor", 'O', set80211location), 4772 DEF_CMD("-outdoor", 'I', set80211location), 4773 DEF_CMD("anywhere", ' ', set80211location), 4774 DEF_CMD("ecm", 1, set80211ecm), 4775 DEF_CMD("-ecm", 0, set80211ecm), 4776 DEF_CMD("dotd", 1, set80211dotd), 4777 DEF_CMD("-dotd", 0, set80211dotd), 4778 DEF_CMD_ARG("htprotmode", set80211htprotmode), 4779 DEF_CMD("ht20", 1, set80211htconf), 4780 DEF_CMD("-ht20", 0, set80211htconf), 4781 DEF_CMD("ht40", 3, set80211htconf), /* NB: 20+40 */ 4782 DEF_CMD("-ht40", 0, set80211htconf), 4783 DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */ 4784 DEF_CMD("-ht", 0, set80211htconf), 4785 DEF_CMD("rifs", 1, set80211rifs), 4786 DEF_CMD("-rifs", 0, set80211rifs), 4787 DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps), 4788 DEF_CMD("smpsdyn", IEEE80211_HTCAP_SMPS_DYNAMIC, set80211smps), 4789 DEF_CMD("-smps", IEEE80211_HTCAP_SMPS_OFF, set80211smps), 4790 /* XXX for testing */ 4791 DEF_CMD_ARG("chanswitch", set80211chanswitch), 4792 4793 DEF_CMD_ARG("tdmaslot", set80211tdmaslot), 4794 DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt), 4795 DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen), 4796 DEF_CMD_ARG("tdmabintval", set80211tdmabintval), 4797 4798 /* vap cloning support */ 4799 DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr), 4800 DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid), 4801 DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev), 4802 DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode), 4803 DEF_CLONE_CMD("beacons", 1, set80211clone_beacons), 4804 DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons), 4805 DEF_CLONE_CMD("bssid", 1, set80211clone_bssid), 4806 DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid), 4807 DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy), 4808 DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy), 4809}; 4810static struct afswtch af_ieee80211 = { 4811 .af_name = "af_ieee80211", 4812 .af_af = AF_UNSPEC, 4813 .af_other_status = ieee80211_status, 4814}; 4815 4816static __constructor void 4817ieee80211_ctor(void) 4818{ 4819#define N(a) (sizeof(a) / sizeof(a[0])) 4820 int i; 4821 4822 for (i = 0; i < N(ieee80211_cmds); i++) 4823 cmd_register(&ieee80211_cmds[i]); 4824 af_register(&af_ieee80211); 4825#undef N 4826} 4827