ifieee80211.c revision 187801
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 187801 2009-01-27 23:42:14Z 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 void 1793regdomain_addchans(struct ieee80211req_chaninfo *ci, 1794 const netband_head *bands, 1795 const struct ieee80211_regdomain *reg, 1796 uint32_t chanFlags, 1797 const struct ieee80211req_chaninfo *avail) 1798{ 1799 const struct netband *nb; 1800 const struct freqband *b; 1801 struct ieee80211_channel *c, *prev; 1802 int freq, channelSep; 1803 1804 channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40; 1805 LIST_FOREACH(nb, bands, next) { 1806 b = nb->band; 1807 if (verbose) 1808 printf("%s: chanFlags 0x%x b %p\n", 1809 __func__, chanFlags, b); 1810 prev = NULL; 1811 for (freq = b->freqStart; freq <= b->freqEnd; freq += b->chanSep) { 1812 uint32_t flags = nb->flags | b->flags; 1813 1814 /* check if device can operate on this frequency */ 1815 if (chanlookup(avail->ic_chans, avail->ic_nchans, freq, chanFlags) == NULL) { 1816 if (verbose) 1817 printf("%u: skip, flags 0x%x not available\n", freq, chanFlags); 1818 continue; 1819 } 1820 /* 1821 * NB: don't enforce 1/2 and 1/4 rate channels being 1822 * specified in the device's calibration list for 1823 * 900MHz cards because most are not self-identifying. 1824 */ 1825 if ((flags & IEEE80211_CHAN_HALF) && 1826 ((chanFlags & IEEE80211_CHAN_HALF) == 0 && 1827 (flags & IEEE80211_CHAN_GSM) == 0)) { 1828 if (verbose) 1829 printf("%u: skip, device does not support half-rate channels\n", freq); 1830 continue; 1831 } 1832 if ((flags & IEEE80211_CHAN_QUARTER) && 1833 ((chanFlags & IEEE80211_CHAN_QUARTER) == 0 && 1834 (flags & IEEE80211_CHAN_GSM) == 0)) { 1835 if (verbose) 1836 printf("%u: skip, device does not support quarter-rate channels\n", freq); 1837 continue; 1838 } 1839 if ((flags & IEEE80211_CHAN_HT20) && 1840 (chanFlags & IEEE80211_CHAN_HT20) == 0) { 1841 if (verbose) 1842 printf("%u: skip, device does not support HT20 operation\n", freq); 1843 continue; 1844 } 1845 if ((flags & IEEE80211_CHAN_HT40) && 1846 (chanFlags & IEEE80211_CHAN_HT40) == 0) { 1847 if (verbose) 1848 printf("%u: skip, device does not support HT40 operation\n", freq); 1849 continue; 1850 } 1851 if ((flags & REQ_ECM) && !reg->ecm) { 1852 if (verbose) 1853 printf("%u: skip, ECM channel\n", freq); 1854 continue; 1855 } 1856 if ((flags & REQ_OUTDOOR) && reg->location == 'I') { 1857 if (verbose) 1858 printf("%u: skip, outdoor channel\n", freq); 1859 continue; 1860 } 1861 if ((flags & IEEE80211_CHAN_HT40) && 1862 prev != NULL && (freq - prev->ic_freq) < channelSep) { 1863 if (verbose) 1864 printf("%u: skip, only %u channel " 1865 "separation, need %d\n", freq, 1866 freq - prev->ic_freq, channelSep); 1867 continue; 1868 } 1869 if (ci->ic_nchans == IEEE80211_CHAN_MAX) { 1870 if (verbose) 1871 printf("%u: skip, channel table full\n", freq); 1872 break; 1873 } 1874 c = &ci->ic_chans[ci->ic_nchans++]; 1875 memset(c, 0, sizeof(*c)); 1876 c->ic_freq = freq; 1877 c->ic_flags = chanFlags | 1878 (flags &~ (REQ_FLAGS | IEEE80211_CHAN_HT40)); 1879 if (c->ic_flags & IEEE80211_CHAN_DFS) 1880 c->ic_maxregpower = nb->maxPowerDFS; 1881 else 1882 c->ic_maxregpower = nb->maxPower; 1883 if (verbose) 1884 printf("[%3d] add freq %u flags 0x%x power %u\n", 1885 ci->ic_nchans-1, c->ic_freq, c->ic_flags, 1886 c->ic_maxregpower); 1887 /* NB: kernel fills in other fields */ 1888 prev = c; 1889 } 1890 } 1891} 1892 1893static void 1894regdomain_makechannels( 1895 struct ieee80211_regdomain_req *req, 1896 const struct ieee80211_devcaps_req *dc) 1897{ 1898 struct regdata *rdp = getregdata(); 1899 const struct country *cc; 1900 const struct ieee80211_regdomain *reg = &req->rd; 1901 struct ieee80211req_chaninfo *ci = &req->chaninfo; 1902 const struct regdomain *rd; 1903 1904 /* 1905 * Locate construction table for new channel list. We treat 1906 * the regdomain/SKU as definitive so a country can be in 1907 * multiple with different properties (e.g. US in FCC+FCC3). 1908 * If no regdomain is specified then we fallback on the country 1909 * code to find the associated regdomain since countries always 1910 * belong to at least one regdomain. 1911 */ 1912 if (reg->regdomain == 0) { 1913 cc = lib80211_country_findbycc(rdp, reg->country); 1914 if (cc == NULL) 1915 errx(1, "internal error, country %d not found", 1916 reg->country); 1917 rd = cc->rd; 1918 } else 1919 rd = lib80211_regdomain_findbysku(rdp, reg->regdomain); 1920 if (rd == NULL) 1921 errx(1, "internal error, regdomain %d not found", 1922 reg->regdomain); 1923 if (rd->sku != SKU_DEBUG) { 1924 /* 1925 * regdomain_addchans incrememnts the channel count for 1926 * each channel it adds so initialize ic_nchans to zero. 1927 * Note that we know we have enough space to hold all possible 1928 * channels because the devcaps list size was used to 1929 * allocate our request. 1930 */ 1931 ci->ic_nchans = 0; 1932 if (!LIST_EMPTY(&rd->bands_11b)) 1933 regdomain_addchans(ci, &rd->bands_11b, reg, 1934 IEEE80211_CHAN_B, &dc->dc_chaninfo); 1935 if (!LIST_EMPTY(&rd->bands_11g)) { 1936 regdomain_addchans(ci, &rd->bands_11g, reg, 1937 IEEE80211_CHAN_G, &dc->dc_chaninfo); 1938 regdomain_addchans(ci, &rd->bands_11g, reg, 1939 IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, 1940 &dc->dc_chaninfo); 1941 regdomain_addchans(ci, &rd->bands_11g, reg, 1942 IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, 1943 &dc->dc_chaninfo); 1944 } 1945 if (!LIST_EMPTY(&rd->bands_11a)) { 1946 regdomain_addchans(ci, &rd->bands_11a, reg, 1947 IEEE80211_CHAN_A, &dc->dc_chaninfo); 1948 regdomain_addchans(ci, &rd->bands_11a, reg, 1949 IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, 1950 &dc->dc_chaninfo); 1951 regdomain_addchans(ci, &rd->bands_11a, reg, 1952 IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, 1953 &dc->dc_chaninfo); 1954 } 1955 if (!LIST_EMPTY(&rd->bands_11na)) { 1956 regdomain_addchans(ci, &rd->bands_11na, reg, 1957 IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, 1958 &dc->dc_chaninfo); 1959 regdomain_addchans(ci, &rd->bands_11na, reg, 1960 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, 1961 &dc->dc_chaninfo); 1962 regdomain_addchans(ci, &rd->bands_11na, reg, 1963 IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, 1964 &dc->dc_chaninfo); 1965 } 1966 if (!LIST_EMPTY(&rd->bands_11ng)) { 1967 regdomain_addchans(ci, &rd->bands_11ng, reg, 1968 IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, 1969 &dc->dc_chaninfo); 1970 regdomain_addchans(ci, &rd->bands_11ng, reg, 1971 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, 1972 &dc->dc_chaninfo); 1973 regdomain_addchans(ci, &rd->bands_11ng, reg, 1974 IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, 1975 &dc->dc_chaninfo); 1976 } 1977 qsort(ci->ic_chans, ci->ic_nchans, sizeof(ci->ic_chans[0]), 1978 regdomain_sort); 1979 } else 1980 memcpy(ci, &dc->dc_chaninfo, 1981 IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo)); 1982} 1983 1984static void 1985list_countries(void) 1986{ 1987 struct regdata *rdp = getregdata(); 1988 const struct country *cp; 1989 const struct regdomain *dp; 1990 int i; 1991 1992 i = 0; 1993 printf("\nCountry codes:\n"); 1994 LIST_FOREACH(cp, &rdp->countries, next) { 1995 printf("%2s %-15.15s%s", cp->isoname, 1996 cp->name, ((i+1)%4) == 0 ? "\n" : " "); 1997 i++; 1998 } 1999 i = 0; 2000 printf("\nRegulatory domains:\n"); 2001 LIST_FOREACH(dp, &rdp->domains, next) { 2002 printf("%-15.15s%s", dp->name, ((i+1)%4) == 0 ? "\n" : " "); 2003 i++; 2004 } 2005 printf("\n"); 2006} 2007 2008static void 2009defaultcountry(const struct regdomain *rd) 2010{ 2011 struct regdata *rdp = getregdata(); 2012 const struct country *cc; 2013 2014 cc = lib80211_country_findbycc(rdp, rd->cc->code); 2015 if (cc == NULL) 2016 errx(1, "internal error, ISO country code %d not " 2017 "defined for regdomain %s", rd->cc->code, rd->name); 2018 regdomain.country = cc->code; 2019 regdomain.isocc[0] = cc->isoname[0]; 2020 regdomain.isocc[1] = cc->isoname[1]; 2021} 2022 2023static 2024DECL_CMD_FUNC(set80211regdomain, val, d) 2025{ 2026 struct regdata *rdp = getregdata(); 2027 const struct regdomain *rd; 2028 2029 rd = lib80211_regdomain_findbyname(rdp, val); 2030 if (rd == NULL) { 2031 char *eptr; 2032 long sku = strtol(val, &eptr, 0); 2033 2034 if (eptr != val) 2035 rd = lib80211_regdomain_findbysku(rdp, sku); 2036 if (eptr == val || rd == NULL) 2037 errx(1, "unknown regdomain %s", val); 2038 } 2039 getregdomain(s); 2040 regdomain.regdomain = rd->sku; 2041 if (regdomain.country == 0 && rd->cc != NULL) { 2042 /* 2043 * No country code setup and there's a default 2044 * one for this regdomain fill it in. 2045 */ 2046 defaultcountry(rd); 2047 } 2048 callback_register(setregdomain_cb, ®domain); 2049} 2050 2051static 2052DECL_CMD_FUNC(set80211country, val, d) 2053{ 2054 struct regdata *rdp = getregdata(); 2055 const struct country *cc; 2056 2057 cc = lib80211_country_findbyname(rdp, val); 2058 if (cc == NULL) { 2059 char *eptr; 2060 long code = strtol(val, &eptr, 0); 2061 2062 if (eptr != val) 2063 cc = lib80211_country_findbycc(rdp, code); 2064 if (eptr == val || cc == NULL) 2065 errx(1, "unknown ISO country code %s", val); 2066 } 2067 getregdomain(s); 2068 regdomain.regdomain = cc->rd->sku; 2069 regdomain.country = cc->code; 2070 regdomain.isocc[0] = cc->isoname[0]; 2071 regdomain.isocc[1] = cc->isoname[1]; 2072 callback_register(setregdomain_cb, ®domain); 2073} 2074 2075static void 2076set80211location(const char *val, int d, int s, const struct afswtch *rafp) 2077{ 2078 getregdomain(s); 2079 regdomain.location = d; 2080 callback_register(setregdomain_cb, ®domain); 2081} 2082 2083static void 2084set80211ecm(const char *val, int d, int s, const struct afswtch *rafp) 2085{ 2086 getregdomain(s); 2087 regdomain.ecm = d; 2088 callback_register(setregdomain_cb, ®domain); 2089} 2090 2091static void 2092LINE_INIT(char c) 2093{ 2094 spacer = c; 2095 if (c == '\t') 2096 col = 8; 2097 else 2098 col = 1; 2099} 2100 2101static void 2102LINE_BREAK(void) 2103{ 2104 if (spacer != '\t') { 2105 printf("\n"); 2106 spacer = '\t'; 2107 } 2108 col = 8; /* 8-col tab */ 2109} 2110 2111static void 2112LINE_CHECK(const char *fmt, ...) 2113{ 2114 char buf[80]; 2115 va_list ap; 2116 int n; 2117 2118 va_start(ap, fmt); 2119 n = vsnprintf(buf+1, sizeof(buf)-1, fmt, ap); 2120 va_end(ap); 2121 col += 1+n; 2122 if (col > MAXCOL) { 2123 LINE_BREAK(); 2124 col += n; 2125 } 2126 buf[0] = spacer; 2127 printf("%s", buf); 2128 spacer = ' '; 2129} 2130 2131static int 2132getmaxrate(const uint8_t rates[15], uint8_t nrates) 2133{ 2134 int i, maxrate = -1; 2135 2136 for (i = 0; i < nrates; i++) { 2137 int rate = rates[i] & IEEE80211_RATE_VAL; 2138 if (rate > maxrate) 2139 maxrate = rate; 2140 } 2141 return maxrate / 2; 2142} 2143 2144static const char * 2145getcaps(int capinfo) 2146{ 2147 static char capstring[32]; 2148 char *cp = capstring; 2149 2150 if (capinfo & IEEE80211_CAPINFO_ESS) 2151 *cp++ = 'E'; 2152 if (capinfo & IEEE80211_CAPINFO_IBSS) 2153 *cp++ = 'I'; 2154 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE) 2155 *cp++ = 'c'; 2156 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ) 2157 *cp++ = 'C'; 2158 if (capinfo & IEEE80211_CAPINFO_PRIVACY) 2159 *cp++ = 'P'; 2160 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) 2161 *cp++ = 'S'; 2162 if (capinfo & IEEE80211_CAPINFO_PBCC) 2163 *cp++ = 'B'; 2164 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY) 2165 *cp++ = 'A'; 2166 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 2167 *cp++ = 's'; 2168 if (capinfo & IEEE80211_CAPINFO_RSN) 2169 *cp++ = 'R'; 2170 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM) 2171 *cp++ = 'D'; 2172 *cp = '\0'; 2173 return capstring; 2174} 2175 2176static const char * 2177getflags(int flags) 2178{ 2179 static char flagstring[32]; 2180 char *cp = flagstring; 2181 2182 if (flags & IEEE80211_NODE_AUTH) 2183 *cp++ = 'A'; 2184 if (flags & IEEE80211_NODE_QOS) 2185 *cp++ = 'Q'; 2186 if (flags & IEEE80211_NODE_ERP) 2187 *cp++ = 'E'; 2188 if (flags & IEEE80211_NODE_PWR_MGT) 2189 *cp++ = 'P'; 2190 if (flags & IEEE80211_NODE_HT) { 2191 *cp++ = 'H'; 2192 if (flags & IEEE80211_NODE_HTCOMPAT) 2193 *cp++ = '+'; 2194 } 2195 if (flags & IEEE80211_NODE_WPS) 2196 *cp++ = 'W'; 2197 if (flags & IEEE80211_NODE_TSN) 2198 *cp++ = 'N'; 2199 if (flags & IEEE80211_NODE_AMPDU_TX) 2200 *cp++ = 'T'; 2201 if (flags & IEEE80211_NODE_AMPDU_RX) 2202 *cp++ = 'R'; 2203 if (flags & IEEE80211_NODE_MIMO_PS) { 2204 *cp++ = 'M'; 2205 if (flags & IEEE80211_NODE_MIMO_RTS) 2206 *cp++ = '+'; 2207 } 2208 if (flags & IEEE80211_NODE_RIFS) 2209 *cp++ = 'I'; 2210 *cp = '\0'; 2211 return flagstring; 2212} 2213 2214static void 2215printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen) 2216{ 2217 printf("%s", tag); 2218 if (verbose) { 2219 maxlen -= strlen(tag)+2; 2220 if (2*ielen > maxlen) 2221 maxlen--; 2222 printf("<"); 2223 for (; ielen > 0; ie++, ielen--) { 2224 if (maxlen-- <= 0) 2225 break; 2226 printf("%02x", *ie); 2227 } 2228 if (ielen != 0) 2229 printf("-"); 2230 printf(">"); 2231 } 2232} 2233 2234#define LE_READ_2(p) \ 2235 ((u_int16_t) \ 2236 ((((const u_int8_t *)(p))[0] ) | \ 2237 (((const u_int8_t *)(p))[1] << 8))) 2238#define LE_READ_4(p) \ 2239 ((u_int32_t) \ 2240 ((((const u_int8_t *)(p))[0] ) | \ 2241 (((const u_int8_t *)(p))[1] << 8) | \ 2242 (((const u_int8_t *)(p))[2] << 16) | \ 2243 (((const u_int8_t *)(p))[3] << 24))) 2244 2245/* 2246 * NB: The decoding routines assume a properly formatted ie 2247 * which should be safe as the kernel only retains them 2248 * if they parse ok. 2249 */ 2250 2251static void 2252printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2253{ 2254#define MS(_v, _f) (((_v) & _f) >> _f##_S) 2255 static const char *acnames[] = { "BE", "BK", "VO", "VI" }; 2256 const struct ieee80211_wme_param *wme = 2257 (const struct ieee80211_wme_param *) ie; 2258 int i; 2259 2260 printf("%s", tag); 2261 if (!verbose) 2262 return; 2263 printf("<qosinfo 0x%x", wme->param_qosInfo); 2264 ie += offsetof(struct ieee80211_wme_param, params_acParams); 2265 for (i = 0; i < WME_NUM_AC; i++) { 2266 const struct ieee80211_wme_acparams *ac = 2267 &wme->params_acParams[i]; 2268 2269 printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]" 2270 , acnames[i] 2271 , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : "" 2272 , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN) 2273 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN) 2274 , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX) 2275 , LE_READ_2(&ac->acp_txop) 2276 ); 2277 } 2278 printf(">"); 2279#undef MS 2280} 2281 2282static void 2283printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2284{ 2285 printf("%s", tag); 2286 if (verbose) { 2287 const struct ieee80211_wme_info *wme = 2288 (const struct ieee80211_wme_info *) ie; 2289 printf("<version 0x%x info 0x%x>", 2290 wme->wme_version, wme->wme_info); 2291 } 2292} 2293 2294static void 2295printhtcap(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2296{ 2297 printf("%s", tag); 2298 if (verbose) { 2299 const struct ieee80211_ie_htcap *htcap = 2300 (const struct ieee80211_ie_htcap *) ie; 2301 const char *sep; 2302 int i, j; 2303 2304 printf("<cap 0x%x param 0x%x", 2305 LE_READ_2(&htcap->hc_cap), htcap->hc_param); 2306 printf(" mcsset["); 2307 sep = ""; 2308 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) 2309 if (isset(htcap->hc_mcsset, i)) { 2310 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++) 2311 if (isclr(htcap->hc_mcsset, j)) 2312 break; 2313 j--; 2314 if (i == j) 2315 printf("%s%u", sep, i); 2316 else 2317 printf("%s%u-%u", sep, i, j); 2318 i += j-i; 2319 sep = ","; 2320 } 2321 printf("] extcap 0x%x txbf 0x%x antenna 0x%x>", 2322 LE_READ_2(&htcap->hc_extcap), 2323 LE_READ_4(&htcap->hc_txbf), 2324 htcap->hc_antenna); 2325 } 2326} 2327 2328static void 2329printhtinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2330{ 2331 printf("%s", tag); 2332 if (verbose) { 2333 const struct ieee80211_ie_htinfo *htinfo = 2334 (const struct ieee80211_ie_htinfo *) ie; 2335 const char *sep; 2336 int i, j; 2337 2338 printf("<ctl %u, %x,%x,%x,%x", htinfo->hi_ctrlchannel, 2339 htinfo->hi_byte1, htinfo->hi_byte2, htinfo->hi_byte3, 2340 LE_READ_2(&htinfo->hi_byte45)); 2341 printf(" basicmcs["); 2342 sep = ""; 2343 for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) 2344 if (isset(htinfo->hi_basicmcsset, i)) { 2345 for (j = i+1; j < IEEE80211_HTRATE_MAXSIZE; j++) 2346 if (isclr(htinfo->hi_basicmcsset, j)) 2347 break; 2348 j--; 2349 if (i == j) 2350 printf("%s%u", sep, i); 2351 else 2352 printf("%s%u-%u", sep, i, j); 2353 i += j-i; 2354 sep = ","; 2355 } 2356 printf("]>"); 2357 } 2358} 2359 2360static void 2361printathie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2362{ 2363 2364 printf("%s", tag); 2365 if (verbose) { 2366 const struct ieee80211_ath_ie *ath = 2367 (const struct ieee80211_ath_ie *)ie; 2368 2369 printf("<"); 2370 if (ath->ath_capability & ATHEROS_CAP_TURBO_PRIME) 2371 printf("DTURBO,"); 2372 if (ath->ath_capability & ATHEROS_CAP_COMPRESSION) 2373 printf("COMP,"); 2374 if (ath->ath_capability & ATHEROS_CAP_FAST_FRAME) 2375 printf("FF,"); 2376 if (ath->ath_capability & ATHEROS_CAP_XR) 2377 printf("XR,"); 2378 if (ath->ath_capability & ATHEROS_CAP_AR) 2379 printf("AR,"); 2380 if (ath->ath_capability & ATHEROS_CAP_BURST) 2381 printf("BURST,"); 2382 if (ath->ath_capability & ATHEROS_CAP_WME) 2383 printf("WME,"); 2384 if (ath->ath_capability & ATHEROS_CAP_BOOST) 2385 printf("BOOST,"); 2386 printf("0x%x>", LE_READ_2(ath->ath_defkeyix)); 2387 } 2388} 2389 2390static const char * 2391wpa_cipher(const u_int8_t *sel) 2392{ 2393#define WPA_SEL(x) (((x)<<24)|WPA_OUI) 2394 u_int32_t w = LE_READ_4(sel); 2395 2396 switch (w) { 2397 case WPA_SEL(WPA_CSE_NULL): 2398 return "NONE"; 2399 case WPA_SEL(WPA_CSE_WEP40): 2400 return "WEP40"; 2401 case WPA_SEL(WPA_CSE_WEP104): 2402 return "WEP104"; 2403 case WPA_SEL(WPA_CSE_TKIP): 2404 return "TKIP"; 2405 case WPA_SEL(WPA_CSE_CCMP): 2406 return "AES-CCMP"; 2407 } 2408 return "?"; /* NB: so 1<< is discarded */ 2409#undef WPA_SEL 2410} 2411 2412static const char * 2413wpa_keymgmt(const u_int8_t *sel) 2414{ 2415#define WPA_SEL(x) (((x)<<24)|WPA_OUI) 2416 u_int32_t w = LE_READ_4(sel); 2417 2418 switch (w) { 2419 case WPA_SEL(WPA_ASE_8021X_UNSPEC): 2420 return "8021X-UNSPEC"; 2421 case WPA_SEL(WPA_ASE_8021X_PSK): 2422 return "8021X-PSK"; 2423 case WPA_SEL(WPA_ASE_NONE): 2424 return "NONE"; 2425 } 2426 return "?"; 2427#undef WPA_SEL 2428} 2429 2430static void 2431printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2432{ 2433 u_int8_t len = ie[1]; 2434 2435 printf("%s", tag); 2436 if (verbose) { 2437 const char *sep; 2438 int n; 2439 2440 ie += 6, len -= 4; /* NB: len is payload only */ 2441 2442 printf("<v%u", LE_READ_2(ie)); 2443 ie += 2, len -= 2; 2444 2445 printf(" mc:%s", wpa_cipher(ie)); 2446 ie += 4, len -= 4; 2447 2448 /* unicast ciphers */ 2449 n = LE_READ_2(ie); 2450 ie += 2, len -= 2; 2451 sep = " uc:"; 2452 for (; n > 0; n--) { 2453 printf("%s%s", sep, wpa_cipher(ie)); 2454 ie += 4, len -= 4; 2455 sep = "+"; 2456 } 2457 2458 /* key management algorithms */ 2459 n = LE_READ_2(ie); 2460 ie += 2, len -= 2; 2461 sep = " km:"; 2462 for (; n > 0; n--) { 2463 printf("%s%s", sep, wpa_keymgmt(ie)); 2464 ie += 4, len -= 4; 2465 sep = "+"; 2466 } 2467 2468 if (len > 2) /* optional capabilities */ 2469 printf(", caps 0x%x", LE_READ_2(ie)); 2470 printf(">"); 2471 } 2472} 2473 2474static const char * 2475rsn_cipher(const u_int8_t *sel) 2476{ 2477#define RSN_SEL(x) (((x)<<24)|RSN_OUI) 2478 u_int32_t w = LE_READ_4(sel); 2479 2480 switch (w) { 2481 case RSN_SEL(RSN_CSE_NULL): 2482 return "NONE"; 2483 case RSN_SEL(RSN_CSE_WEP40): 2484 return "WEP40"; 2485 case RSN_SEL(RSN_CSE_WEP104): 2486 return "WEP104"; 2487 case RSN_SEL(RSN_CSE_TKIP): 2488 return "TKIP"; 2489 case RSN_SEL(RSN_CSE_CCMP): 2490 return "AES-CCMP"; 2491 case RSN_SEL(RSN_CSE_WRAP): 2492 return "AES-OCB"; 2493 } 2494 return "?"; 2495#undef WPA_SEL 2496} 2497 2498static const char * 2499rsn_keymgmt(const u_int8_t *sel) 2500{ 2501#define RSN_SEL(x) (((x)<<24)|RSN_OUI) 2502 u_int32_t w = LE_READ_4(sel); 2503 2504 switch (w) { 2505 case RSN_SEL(RSN_ASE_8021X_UNSPEC): 2506 return "8021X-UNSPEC"; 2507 case RSN_SEL(RSN_ASE_8021X_PSK): 2508 return "8021X-PSK"; 2509 case RSN_SEL(RSN_ASE_NONE): 2510 return "NONE"; 2511 } 2512 return "?"; 2513#undef RSN_SEL 2514} 2515 2516static void 2517printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2518{ 2519 printf("%s", tag); 2520 if (verbose) { 2521 const char *sep; 2522 int n; 2523 2524 ie += 2, ielen -= 2; 2525 2526 printf("<v%u", LE_READ_2(ie)); 2527 ie += 2, ielen -= 2; 2528 2529 printf(" mc:%s", rsn_cipher(ie)); 2530 ie += 4, ielen -= 4; 2531 2532 /* unicast ciphers */ 2533 n = LE_READ_2(ie); 2534 ie += 2, ielen -= 2; 2535 sep = " uc:"; 2536 for (; n > 0; n--) { 2537 printf("%s%s", sep, rsn_cipher(ie)); 2538 ie += 4, ielen -= 4; 2539 sep = "+"; 2540 } 2541 2542 /* key management algorithms */ 2543 n = LE_READ_2(ie); 2544 ie += 2, ielen -= 2; 2545 sep = " km:"; 2546 for (; n > 0; n--) { 2547 printf("%s%s", sep, rsn_keymgmt(ie)); 2548 ie += 4, ielen -= 4; 2549 sep = "+"; 2550 } 2551 2552 if (ielen > 2) /* optional capabilities */ 2553 printf(", caps 0x%x", LE_READ_2(ie)); 2554 /* XXXPMKID */ 2555 printf(">"); 2556 } 2557} 2558 2559/* XXX move to a public include file */ 2560#define IEEE80211_WPS_DEV_PASS_ID 0x1012 2561#define IEEE80211_WPS_SELECTED_REG 0x1041 2562#define IEEE80211_WPS_SETUP_STATE 0x1044 2563#define IEEE80211_WPS_UUID_E 0x1047 2564#define IEEE80211_WPS_VERSION 0x104a 2565 2566#define BE_READ_2(p) \ 2567 ((u_int16_t) \ 2568 ((((const u_int8_t *)(p))[1] ) | \ 2569 (((const u_int8_t *)(p))[0] << 8))) 2570 2571static void 2572printwpsie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2573{ 2574#define N(a) (sizeof(a) / sizeof(a[0])) 2575 u_int8_t len = ie[1]; 2576 2577 printf("%s", tag); 2578 if (verbose) { 2579 static const char *dev_pass_id[] = { 2580 "D", /* Default (PIN) */ 2581 "U", /* User-specified */ 2582 "M", /* Machine-specified */ 2583 "K", /* Rekey */ 2584 "P", /* PushButton */ 2585 "R" /* Registrar-specified */ 2586 }; 2587 int n; 2588 2589 ie +=6, len -= 4; /* NB: len is payload only */ 2590 2591 /* WPS IE in Beacon and Probe Resp frames have different fields */ 2592 printf("<"); 2593 while (len) { 2594 uint16_t tlv_type = BE_READ_2(ie); 2595 uint16_t tlv_len = BE_READ_2(ie + 2); 2596 2597 ie += 4, len -= 4; 2598 2599 switch (tlv_type) { 2600 case IEEE80211_WPS_VERSION: 2601 printf("v:%d.%d", *ie >> 4, *ie & 0xf); 2602 break; 2603 case IEEE80211_WPS_SETUP_STATE: 2604 /* Only 1 and 2 are valid */ 2605 if (*ie == 0 || *ie >= 3) 2606 printf(" state:B"); 2607 else 2608 printf(" st:%s", *ie == 1 ? "N" : "C"); 2609 break; 2610 case IEEE80211_WPS_SELECTED_REG: 2611 printf(" sel:%s", *ie ? "T" : "F"); 2612 break; 2613 case IEEE80211_WPS_DEV_PASS_ID: 2614 n = LE_READ_2(ie); 2615 if (n < N(dev_pass_id)) 2616 printf(" dpi:%s", dev_pass_id[n]); 2617 break; 2618 case IEEE80211_WPS_UUID_E: 2619 printf(" uuid-e:"); 2620 for (n = 0; n < (tlv_len - 1); n++) 2621 printf("%02x-", ie[n]); 2622 printf("%02x", ie[n]); 2623 break; 2624 } 2625 ie += tlv_len, len -= tlv_len; 2626 } 2627 printf(">"); 2628 } 2629#undef N 2630} 2631 2632static void 2633printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2634{ 2635 printf("%s", tag); 2636 if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) { 2637 const struct ieee80211_tdma_param *tdma = 2638 (const struct ieee80211_tdma_param *) ie; 2639 2640 /* XXX tstamp */ 2641 printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>", 2642 tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt, 2643 LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval, 2644 tdma->tdma_inuse[0]); 2645 } 2646} 2647 2648/* 2649 * Copy the ssid string contents into buf, truncating to fit. If the 2650 * ssid is entirely printable then just copy intact. Otherwise convert 2651 * to hexadecimal. If the result is truncated then replace the last 2652 * three characters with "...". 2653 */ 2654static int 2655copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len) 2656{ 2657 const u_int8_t *p; 2658 size_t maxlen; 2659 int i; 2660 2661 if (essid_len > bufsize) 2662 maxlen = bufsize; 2663 else 2664 maxlen = essid_len; 2665 /* determine printable or not */ 2666 for (i = 0, p = essid; i < maxlen; i++, p++) { 2667 if (*p < ' ' || *p > 0x7e) 2668 break; 2669 } 2670 if (i != maxlen) { /* not printable, print as hex */ 2671 if (bufsize < 3) 2672 return 0; 2673 strlcpy(buf, "0x", bufsize); 2674 bufsize -= 2; 2675 p = essid; 2676 for (i = 0; i < maxlen && bufsize >= 2; i++) { 2677 sprintf(&buf[2+2*i], "%02x", p[i]); 2678 bufsize -= 2; 2679 } 2680 if (i != essid_len) 2681 memcpy(&buf[2+2*i-3], "...", 3); 2682 } else { /* printable, truncate as needed */ 2683 memcpy(buf, essid, maxlen); 2684 if (maxlen != essid_len) 2685 memcpy(&buf[maxlen-3], "...", 3); 2686 } 2687 return maxlen; 2688} 2689 2690static void 2691printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2692{ 2693 char ssid[2*IEEE80211_NWID_LEN+1]; 2694 2695 printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid); 2696} 2697 2698static void 2699printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2700{ 2701 const char *sep; 2702 int i; 2703 2704 printf("%s", tag); 2705 sep = "<"; 2706 for (i = 2; i < ielen; i++) { 2707 printf("%s%s%d", sep, 2708 ie[i] & IEEE80211_RATE_BASIC ? "B" : "", 2709 ie[i] & IEEE80211_RATE_VAL); 2710 sep = ","; 2711 } 2712 printf(">"); 2713} 2714 2715static void 2716printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen) 2717{ 2718 const struct ieee80211_country_ie *cie = 2719 (const struct ieee80211_country_ie *) ie; 2720 int i, nbands, schan, nchan; 2721 2722 printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]); 2723 nbands = (cie->len - 3) / sizeof(cie->band[0]); 2724 for (i = 0; i < nbands; i++) { 2725 schan = cie->band[i].schan; 2726 nchan = cie->band[i].nchan; 2727 if (nchan != 1) 2728 printf(" %u-%u,%u", schan, schan + nchan-1, 2729 cie->band[i].maxtxpwr); 2730 else 2731 printf(" %u,%u", schan, cie->band[i].maxtxpwr); 2732 } 2733 printf(">"); 2734} 2735 2736/* unaligned little endian access */ 2737#define LE_READ_4(p) \ 2738 ((u_int32_t) \ 2739 ((((const u_int8_t *)(p))[0] ) | \ 2740 (((const u_int8_t *)(p))[1] << 8) | \ 2741 (((const u_int8_t *)(p))[2] << 16) | \ 2742 (((const u_int8_t *)(p))[3] << 24))) 2743 2744static __inline int 2745iswpaoui(const u_int8_t *frm) 2746{ 2747 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); 2748} 2749 2750static __inline int 2751iswmeinfo(const u_int8_t *frm) 2752{ 2753 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 2754 frm[6] == WME_INFO_OUI_SUBTYPE; 2755} 2756 2757static __inline int 2758iswmeparam(const u_int8_t *frm) 2759{ 2760 return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) && 2761 frm[6] == WME_PARAM_OUI_SUBTYPE; 2762} 2763 2764static __inline int 2765isatherosoui(const u_int8_t *frm) 2766{ 2767 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); 2768} 2769 2770static __inline int 2771istdmaoui(const uint8_t *frm) 2772{ 2773 return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI); 2774} 2775 2776static __inline int 2777iswpsoui(const uint8_t *frm) 2778{ 2779 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI); 2780} 2781 2782static const char * 2783iename(int elemid) 2784{ 2785 switch (elemid) { 2786 case IEEE80211_ELEMID_FHPARMS: return " FHPARMS"; 2787 case IEEE80211_ELEMID_CFPARMS: return " CFPARMS"; 2788 case IEEE80211_ELEMID_TIM: return " TIM"; 2789 case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS"; 2790 case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE"; 2791 case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR"; 2792 case IEEE80211_ELEMID_PWRCAP: return " PWRCAP"; 2793 case IEEE80211_ELEMID_TPCREQ: return " TPCREQ"; 2794 case IEEE80211_ELEMID_TPCREP: return " TPCREP"; 2795 case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN"; 2796 case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA"; 2797 case IEEE80211_ELEMID_MEASREQ: return " MEASREQ"; 2798 case IEEE80211_ELEMID_MEASREP: return " MEASREP"; 2799 case IEEE80211_ELEMID_QUIET: return " QUIET"; 2800 case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS"; 2801 case IEEE80211_ELEMID_TPC: return " TPC"; 2802 case IEEE80211_ELEMID_CCKM: return " CCKM"; 2803 } 2804 return " ???"; 2805} 2806 2807static void 2808printies(const u_int8_t *vp, int ielen, int maxcols) 2809{ 2810 while (ielen > 0) { 2811 switch (vp[0]) { 2812 case IEEE80211_ELEMID_SSID: 2813 if (verbose) 2814 printssid(" SSID", vp, 2+vp[1], maxcols); 2815 break; 2816 case IEEE80211_ELEMID_RATES: 2817 case IEEE80211_ELEMID_XRATES: 2818 if (verbose) 2819 printrates(vp[0] == IEEE80211_ELEMID_RATES ? 2820 " RATES" : " XRATES", vp, 2+vp[1], maxcols); 2821 break; 2822 case IEEE80211_ELEMID_DSPARMS: 2823 if (verbose) 2824 printf(" DSPARMS<%u>", vp[2]); 2825 break; 2826 case IEEE80211_ELEMID_COUNTRY: 2827 if (verbose) 2828 printcountry(" COUNTRY", vp, 2+vp[1], maxcols); 2829 break; 2830 case IEEE80211_ELEMID_ERP: 2831 if (verbose) 2832 printf(" ERP<0x%x>", vp[2]); 2833 break; 2834 case IEEE80211_ELEMID_VENDOR: 2835 if (iswpaoui(vp)) 2836 printwpaie(" WPA", vp, 2+vp[1], maxcols); 2837 else if (iswmeinfo(vp)) 2838 printwmeinfo(" WME", vp, 2+vp[1], maxcols); 2839 else if (iswmeparam(vp)) 2840 printwmeparam(" WME", vp, 2+vp[1], maxcols); 2841 else if (isatherosoui(vp)) 2842 printathie(" ATH", vp, 2+vp[1], maxcols); 2843 else if (iswpsoui(vp)) 2844 printwpsie(" WPS", vp, 2+vp[1], maxcols); 2845 else if (istdmaoui(vp)) 2846 printtdmaie(" TDMA", vp, 2+vp[1], maxcols); 2847 else if (verbose) 2848 printie(" VEN", vp, 2+vp[1], maxcols); 2849 break; 2850 case IEEE80211_ELEMID_RSN: 2851 printrsnie(" RSN", vp, 2+vp[1], maxcols); 2852 break; 2853 case IEEE80211_ELEMID_HTCAP: 2854 printhtcap(" HTCAP", vp, 2+vp[1], maxcols); 2855 break; 2856 case IEEE80211_ELEMID_HTINFO: 2857 if (verbose) 2858 printhtinfo(" HTINFO", vp, 2+vp[1], maxcols); 2859 break; 2860 default: 2861 if (verbose) 2862 printie(iename(vp[0]), vp, 2+vp[1], maxcols); 2863 break; 2864 } 2865 ielen -= 2+vp[1]; 2866 vp += 2+vp[1]; 2867 } 2868} 2869 2870static void 2871printmimo(const struct ieee80211_mimo_info *mi) 2872{ 2873 /* NB: don't muddy display unless there's something to show */ 2874 if (mi->rssi[0] != 0 || mi->rssi[1] != 0 || mi->rssi[2] != 0) { 2875 /* XXX ignore EVM for now */ 2876 printf(" (rssi %d:%d:%d nf %d:%d:%d)", 2877 mi->rssi[0], mi->rssi[1], mi->rssi[2], 2878 mi->noise[0], mi->noise[1], mi->noise[2]); 2879 } 2880} 2881 2882static void 2883list_scan(int s) 2884{ 2885 uint8_t buf[24*1024]; 2886 char ssid[IEEE80211_NWID_LEN+1]; 2887 const uint8_t *cp; 2888 int len, ssidmax; 2889 2890 if (get80211len(s, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf), &len) < 0) 2891 errx(1, "unable to get scan results"); 2892 if (len < sizeof(struct ieee80211req_scan_result)) 2893 return; 2894 2895 getchaninfo(s); 2896 2897 ssidmax = verbose ? IEEE80211_NWID_LEN : 14; 2898 printf("%-*.*s %-17.17s %4s %4s %-7s %3s %4s\n" 2899 , ssidmax, ssidmax, "SSID" 2900 , "BSSID" 2901 , "CHAN" 2902 , "RATE" 2903 , " S:N" 2904 , "INT" 2905 , "CAPS" 2906 ); 2907 cp = buf; 2908 do { 2909 const struct ieee80211req_scan_result *sr; 2910 const uint8_t *vp; 2911 2912 sr = (const struct ieee80211req_scan_result *) cp; 2913 vp = cp + sr->isr_ie_off; 2914 printf("%-*.*s %s %3d %3dM %3d:%-3d %3d %-4.4s" 2915 , ssidmax 2916 , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len) 2917 , ssid 2918 , ether_ntoa((const struct ether_addr *) sr->isr_bssid) 2919 , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags) 2920 , getmaxrate(sr->isr_rates, sr->isr_nrates) 2921 , (sr->isr_rssi/2)+sr->isr_noise, sr->isr_noise 2922 , sr->isr_intval 2923 , getcaps(sr->isr_capinfo) 2924 ); 2925 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24); 2926 printf("\n"); 2927 cp += sr->isr_len, len -= sr->isr_len; 2928 } while (len >= sizeof(struct ieee80211req_scan_result)); 2929} 2930 2931#ifdef __FreeBSD__ 2932#include <net80211/ieee80211_freebsd.h> 2933#endif 2934#ifdef __NetBSD__ 2935#include <net80211/ieee80211_netbsd.h> 2936#endif 2937 2938static void 2939scan_and_wait(int s) 2940{ 2941 struct ieee80211_scan_req sr; 2942 struct ieee80211req ireq; 2943 int sroute; 2944 2945 sroute = socket(PF_ROUTE, SOCK_RAW, 0); 2946 if (sroute < 0) { 2947 perror("socket(PF_ROUTE,SOCK_RAW)"); 2948 return; 2949 } 2950 (void) memset(&ireq, 0, sizeof(ireq)); 2951 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 2952 ireq.i_type = IEEE80211_IOC_SCAN_REQ; 2953 2954 memset(&sr, 0, sizeof(sr)); 2955 sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE 2956 | IEEE80211_IOC_SCAN_NOPICK 2957 | IEEE80211_IOC_SCAN_ONCE; 2958 sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; 2959 sr.sr_nssid = 0; 2960 2961 ireq.i_data = &sr; 2962 ireq.i_len = sizeof(sr); 2963 /* NB: only root can trigger a scan so ignore errors */ 2964 if (ioctl(s, SIOCS80211, &ireq) >= 0) { 2965 char buf[2048]; 2966 struct if_announcemsghdr *ifan; 2967 struct rt_msghdr *rtm; 2968 2969 do { 2970 if (read(sroute, buf, sizeof(buf)) < 0) { 2971 perror("read(PF_ROUTE)"); 2972 break; 2973 } 2974 rtm = (struct rt_msghdr *) buf; 2975 if (rtm->rtm_version != RTM_VERSION) 2976 break; 2977 ifan = (struct if_announcemsghdr *) rtm; 2978 } while (rtm->rtm_type != RTM_IEEE80211 || 2979 ifan->ifan_what != RTM_IEEE80211_SCAN); 2980 } 2981 close(sroute); 2982} 2983 2984static 2985DECL_CMD_FUNC(set80211scan, val, d) 2986{ 2987 scan_and_wait(s); 2988 list_scan(s); 2989} 2990 2991static enum ieee80211_opmode get80211opmode(int s); 2992 2993static int 2994gettxseq(const struct ieee80211req_sta_info *si) 2995{ 2996#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ 2997 2998 int i, txseq; 2999 3000 if ((si->isi_state & IEEE80211_NODE_QOS) == 0) 3001 return si->isi_txseqs[0]; 3002 /* XXX not right but usually what folks want */ 3003 txseq = 0; 3004 for (i = 0; i < IEEE80211_TID_SIZE; i++) 3005 if (si->isi_txseqs[i] > txseq) 3006 txseq = si->isi_txseqs[i]; 3007 return txseq; 3008#undef IEEE80211_NODE_QOS 3009} 3010 3011static int 3012getrxseq(const struct ieee80211req_sta_info *si) 3013{ 3014#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ 3015 3016 int i, rxseq; 3017 3018 if ((si->isi_state & IEEE80211_NODE_QOS) == 0) 3019 return si->isi_rxseqs[0]; 3020 /* XXX not right but usually what folks want */ 3021 rxseq = 0; 3022 for (i = 0; i < IEEE80211_TID_SIZE; i++) 3023 if (si->isi_rxseqs[i] > rxseq) 3024 rxseq = si->isi_rxseqs[i]; 3025 return rxseq; 3026#undef IEEE80211_NODE_QOS 3027} 3028 3029static void 3030list_stations(int s) 3031{ 3032 union { 3033 struct ieee80211req_sta_req req; 3034 uint8_t buf[24*1024]; 3035 } u; 3036 enum ieee80211_opmode opmode = get80211opmode(s); 3037 const uint8_t *cp; 3038 int len; 3039 3040 /* broadcast address =>'s get all stations */ 3041 (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); 3042 if (opmode == IEEE80211_M_STA) { 3043 /* 3044 * Get information about the associated AP. 3045 */ 3046 (void) get80211(s, IEEE80211_IOC_BSSID, 3047 u.req.is_u.macaddr, IEEE80211_ADDR_LEN); 3048 } 3049 if (get80211len(s, IEEE80211_IOC_STA_INFO, &u, sizeof(u), &len) < 0) 3050 errx(1, "unable to get station information"); 3051 if (len < sizeof(struct ieee80211req_sta_info)) 3052 return; 3053 3054 getchaninfo(s); 3055 3056 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %4s\n" 3057 , "ADDR" 3058 , "AID" 3059 , "CHAN" 3060 , "RATE" 3061 , "RSSI" 3062 , "IDLE" 3063 , "TXSEQ" 3064 , "RXSEQ" 3065 , "CAPS" 3066 , "FLAG" 3067 ); 3068 cp = (const uint8_t *) u.req.info; 3069 do { 3070 const struct ieee80211req_sta_info *si; 3071 3072 si = (const struct ieee80211req_sta_info *) cp; 3073 if (si->isi_len < sizeof(*si)) 3074 break; 3075 printf("%s %4u %4d %3dM %3.1f %4d %6d %6d %-4.4s %-4.4s" 3076 , ether_ntoa((const struct ether_addr*) si->isi_macaddr) 3077 , IEEE80211_AID(si->isi_associd) 3078 , ieee80211_mhz2ieee(si->isi_freq, si->isi_flags) 3079 , si->isi_txmbps/2 3080 , si->isi_rssi/2. 3081 , si->isi_inact 3082 , gettxseq(si) 3083 , getrxseq(si) 3084 , getcaps(si->isi_capinfo) 3085 , getflags(si->isi_state) 3086 ); 3087 printies(cp + si->isi_ie_off, si->isi_ie_len, 24); 3088 printmimo(&si->isi_mimo); 3089 printf("\n"); 3090 cp += si->isi_len, len -= si->isi_len; 3091 } while (len >= sizeof(struct ieee80211req_sta_info)); 3092} 3093 3094static const char * 3095get_chaninfo(const struct ieee80211_channel *c, int precise, 3096 char buf[], size_t bsize) 3097{ 3098 buf[0] = '\0'; 3099 if (IEEE80211_IS_CHAN_FHSS(c)) 3100 strlcat(buf, " FHSS", bsize); 3101 if (IEEE80211_IS_CHAN_A(c)) { 3102 if (IEEE80211_IS_CHAN_HALF(c)) 3103 strlcat(buf, " 11a/10Mhz", bsize); 3104 else if (IEEE80211_IS_CHAN_QUARTER(c)) 3105 strlcat(buf, " 11a/5Mhz", bsize); 3106 else 3107 strlcat(buf, " 11a", bsize); 3108 } 3109 if (IEEE80211_IS_CHAN_ANYG(c)) { 3110 if (IEEE80211_IS_CHAN_HALF(c)) 3111 strlcat(buf, " 11g/10Mhz", bsize); 3112 else if (IEEE80211_IS_CHAN_QUARTER(c)) 3113 strlcat(buf, " 11g/5Mhz", bsize); 3114 else 3115 strlcat(buf, " 11g", bsize); 3116 } else if (IEEE80211_IS_CHAN_B(c)) 3117 strlcat(buf, " 11b", bsize); 3118 if (IEEE80211_IS_CHAN_TURBO(c)) 3119 strlcat(buf, " Turbo", bsize); 3120 if (precise) { 3121 if (IEEE80211_IS_CHAN_HT20(c)) 3122 strlcat(buf, " ht/20", bsize); 3123 else if (IEEE80211_IS_CHAN_HT40D(c)) 3124 strlcat(buf, " ht/40-", bsize); 3125 else if (IEEE80211_IS_CHAN_HT40U(c)) 3126 strlcat(buf, " ht/40+", bsize); 3127 } else { 3128 if (IEEE80211_IS_CHAN_HT(c)) 3129 strlcat(buf, " ht", bsize); 3130 } 3131 return buf; 3132} 3133 3134static void 3135print_chaninfo(const struct ieee80211_channel *c, int verb) 3136{ 3137 char buf[14]; 3138 3139 printf("Channel %3u : %u%c Mhz%-14.14s", 3140 ieee80211_mhz2ieee(c->ic_freq, c->ic_flags), c->ic_freq, 3141 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', 3142 get_chaninfo(c, verb, buf, sizeof(buf))); 3143} 3144 3145static void 3146print_channels(int s, const struct ieee80211req_chaninfo *chans, 3147 int allchans, int verb) 3148{ 3149 struct ieee80211req_chaninfo *achans; 3150 uint8_t reported[IEEE80211_CHAN_BYTES]; 3151 const struct ieee80211_channel *c; 3152 int i, half; 3153 3154 achans = malloc(IEEE80211_CHANINFO_SPACE(chans)); 3155 if (achans == NULL) 3156 errx(1, "no space for active channel list"); 3157 achans->ic_nchans = 0; 3158 memset(reported, 0, sizeof(reported)); 3159 if (!allchans) { 3160 struct ieee80211req_chanlist active; 3161 3162 if (get80211(s, IEEE80211_IOC_CHANLIST, &active, sizeof(active)) < 0) 3163 errx(1, "unable to get active channel list"); 3164 for (i = 0; i < chans->ic_nchans; i++) { 3165 c = &chans->ic_chans[i]; 3166 if (!isset(active.ic_channels, c->ic_ieee)) 3167 continue; 3168 /* 3169 * Suppress compatible duplicates unless 3170 * verbose. The kernel gives us it's 3171 * complete channel list which has separate 3172 * entries for 11g/11b and 11a/turbo. 3173 */ 3174 if (isset(reported, c->ic_ieee) && !verb) { 3175 /* XXX we assume duplicates are adjacent */ 3176 achans->ic_chans[achans->ic_nchans-1] = *c; 3177 } else { 3178 achans->ic_chans[achans->ic_nchans++] = *c; 3179 setbit(reported, c->ic_ieee); 3180 } 3181 } 3182 } else { 3183 for (i = 0; i < chans->ic_nchans; i++) { 3184 c = &chans->ic_chans[i]; 3185 /* suppress duplicates as above */ 3186 if (isset(reported, c->ic_ieee) && !verb) { 3187 /* XXX we assume duplicates are adjacent */ 3188 achans->ic_chans[achans->ic_nchans-1] = *c; 3189 } else { 3190 achans->ic_chans[achans->ic_nchans++] = *c; 3191 setbit(reported, c->ic_ieee); 3192 } 3193 } 3194 } 3195 half = achans->ic_nchans / 2; 3196 if (achans->ic_nchans % 2) 3197 half++; 3198 3199 for (i = 0; i < achans->ic_nchans / 2; i++) { 3200 print_chaninfo(&achans->ic_chans[i], verb); 3201 print_chaninfo(&achans->ic_chans[half+i], verb); 3202 printf("\n"); 3203 } 3204 if (achans->ic_nchans % 2) { 3205 print_chaninfo(&achans->ic_chans[i], verb); 3206 printf("\n"); 3207 } 3208 free(achans); 3209} 3210 3211static void 3212list_channels(int s, int allchans) 3213{ 3214 getchaninfo(s); 3215 print_channels(s, chaninfo, allchans, verbose); 3216} 3217 3218static void 3219print_txpow(const struct ieee80211_channel *c) 3220{ 3221 printf("Channel %3u : %u Mhz %3.1f reg %2d ", 3222 c->ic_ieee, c->ic_freq, 3223 c->ic_maxpower/2., c->ic_maxregpower); 3224} 3225 3226static void 3227print_txpow_verbose(const struct ieee80211_channel *c) 3228{ 3229 print_chaninfo(c, 1); 3230 printf("min %4.1f dBm max %3.1f dBm reg %2d dBm", 3231 c->ic_minpower/2., c->ic_maxpower/2., c->ic_maxregpower); 3232 /* indicate where regulatory cap limits power use */ 3233 if (c->ic_maxpower > 2*c->ic_maxregpower) 3234 printf(" <"); 3235} 3236 3237static void 3238list_txpow(int s) 3239{ 3240 struct ieee80211req_chaninfo *achans; 3241 uint8_t reported[IEEE80211_CHAN_BYTES]; 3242 struct ieee80211_channel *c, *prev; 3243 int i, half; 3244 3245 getchaninfo(s); 3246 achans = malloc(IEEE80211_CHANINFO_SPACE(chaninfo)); 3247 if (achans == NULL) 3248 errx(1, "no space for active channel list"); 3249 achans->ic_nchans = 0; 3250 memset(reported, 0, sizeof(reported)); 3251 for (i = 0; i < chaninfo->ic_nchans; i++) { 3252 c = &chaninfo->ic_chans[i]; 3253 /* suppress duplicates as above */ 3254 if (isset(reported, c->ic_ieee) && !verbose) { 3255 /* XXX we assume duplicates are adjacent */ 3256 prev = &achans->ic_chans[achans->ic_nchans-1]; 3257 /* display highest power on channel */ 3258 if (c->ic_maxpower > prev->ic_maxpower) 3259 *prev = *c; 3260 } else { 3261 achans->ic_chans[achans->ic_nchans++] = *c; 3262 setbit(reported, c->ic_ieee); 3263 } 3264 } 3265 if (!verbose) { 3266 half = achans->ic_nchans / 2; 3267 if (achans->ic_nchans % 2) 3268 half++; 3269 3270 for (i = 0; i < achans->ic_nchans / 2; i++) { 3271 print_txpow(&achans->ic_chans[i]); 3272 print_txpow(&achans->ic_chans[half+i]); 3273 printf("\n"); 3274 } 3275 if (achans->ic_nchans % 2) { 3276 print_txpow(&achans->ic_chans[i]); 3277 printf("\n"); 3278 } 3279 } else { 3280 for (i = 0; i < achans->ic_nchans; i++) { 3281 print_txpow_verbose(&achans->ic_chans[i]); 3282 printf("\n"); 3283 } 3284 } 3285 free(achans); 3286} 3287 3288static void 3289list_keys(int s) 3290{ 3291} 3292 3293#define IEEE80211_C_BITS \ 3294 "\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \ 3295 "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \ 3296 "\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \ 3297 "\37TXFRAG\40TDMA" 3298 3299static void 3300list_capabilities(int s) 3301{ 3302 struct ieee80211_devcaps_req *dc; 3303 3304 dc = malloc(IEEE80211_DEVCAPS_SIZE(1)); 3305 if (dc == NULL) 3306 errx(1, "no space for device capabilities"); 3307 dc->dc_chaninfo.ic_nchans = 1; 3308 getdevcaps(s, dc); 3309 printb("drivercaps", dc->dc_drivercaps, IEEE80211_C_BITS); 3310 if (dc->dc_cryptocaps != 0 || verbose) { 3311 putchar('\n'); 3312 printb("cryptocaps", dc->dc_cryptocaps, IEEE80211_CRYPTO_BITS); 3313 } 3314 if (dc->dc_htcaps != 0 || verbose) { 3315 putchar('\n'); 3316 printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS); 3317 } 3318 putchar('\n'); 3319 free(dc); 3320} 3321 3322static int 3323get80211wme(int s, int param, int ac, int *val) 3324{ 3325 struct ieee80211req ireq; 3326 3327 (void) memset(&ireq, 0, sizeof(ireq)); 3328 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 3329 ireq.i_type = param; 3330 ireq.i_len = ac; 3331 if (ioctl(s, SIOCG80211, &ireq) < 0) { 3332 warn("cannot get WME parameter %d, ac %d%s", 3333 param, ac & IEEE80211_WMEPARAM_VAL, 3334 ac & IEEE80211_WMEPARAM_BSS ? " (BSS)" : ""); 3335 return -1; 3336 } 3337 *val = ireq.i_val; 3338 return 0; 3339} 3340 3341static void 3342list_wme_aci(int s, const char *tag, int ac) 3343{ 3344 int val; 3345 3346 printf("\t%s", tag); 3347 3348 /* show WME BSS parameters */ 3349 if (get80211wme(s, IEEE80211_IOC_WME_CWMIN, ac, &val) != -1) 3350 printf(" cwmin %2u", val); 3351 if (get80211wme(s, IEEE80211_IOC_WME_CWMAX, ac, &val) != -1) 3352 printf(" cwmax %2u", val); 3353 if (get80211wme(s, IEEE80211_IOC_WME_AIFS, ac, &val) != -1) 3354 printf(" aifs %2u", val); 3355 if (get80211wme(s, IEEE80211_IOC_WME_TXOPLIMIT, ac, &val) != -1) 3356 printf(" txopLimit %3u", val); 3357 if (get80211wme(s, IEEE80211_IOC_WME_ACM, ac, &val) != -1) { 3358 if (val) 3359 printf(" acm"); 3360 else if (verbose) 3361 printf(" -acm"); 3362 } 3363 /* !BSS only */ 3364 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { 3365 if (get80211wme(s, IEEE80211_IOC_WME_ACKPOLICY, ac, &val) != -1) { 3366 if (!val) 3367 printf(" -ack"); 3368 else if (verbose) 3369 printf(" ack"); 3370 } 3371 } 3372 printf("\n"); 3373} 3374 3375static void 3376list_wme(int s) 3377{ 3378 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; 3379 int ac; 3380 3381 if (verbose) { 3382 /* display both BSS and local settings */ 3383 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { 3384 again: 3385 if (ac & IEEE80211_WMEPARAM_BSS) 3386 list_wme_aci(s, " ", ac); 3387 else 3388 list_wme_aci(s, acnames[ac], ac); 3389 if ((ac & IEEE80211_WMEPARAM_BSS) == 0) { 3390 ac |= IEEE80211_WMEPARAM_BSS; 3391 goto again; 3392 } else 3393 ac &= ~IEEE80211_WMEPARAM_BSS; 3394 } 3395 } else { 3396 /* display only channel settings */ 3397 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) 3398 list_wme_aci(s, acnames[ac], ac); 3399 } 3400} 3401 3402static void 3403list_roam(int s) 3404{ 3405 const struct ieee80211_roamparam *rp; 3406 int mode; 3407 3408 getroam(s); 3409 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { 3410 rp = &roamparams.params[mode]; 3411 if (rp->rssi == 0 && rp->rate == 0) 3412 continue; 3413 if (rp->rssi & 1) 3414 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm rate %2u Mb/s", 3415 modename[mode], rp->rssi/2, rp->rate/2); 3416 else 3417 LINE_CHECK("roam:%-6.6s rssi %4udBm rate %2u Mb/s", 3418 modename[mode], rp->rssi/2, rp->rate/2); 3419 } 3420 for (; mode < IEEE80211_MODE_MAX; mode++) { 3421 rp = &roamparams.params[mode]; 3422 if (rp->rssi == 0 && rp->rate == 0) 3423 continue; 3424 if (rp->rssi & 1) 3425 LINE_CHECK("roam:%-6.6s rssi %2u.5dBm MCS %2u ", 3426 modename[mode], rp->rssi/2, rp->rate &~ 0x80); 3427 else 3428 LINE_CHECK("roam:%-6.6s rssi %4udBm MCS %2u ", 3429 modename[mode], rp->rssi/2, rp->rate &~ 0x80); 3430 } 3431} 3432 3433static void 3434list_txparams(int s) 3435{ 3436 const struct ieee80211_txparam *tp; 3437 int mode; 3438 3439 gettxparams(s); 3440 for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_11NA; mode++) { 3441 tp = &txparams.params[mode]; 3442 if (tp->mgmtrate == 0 && tp->mcastrate == 0) 3443 continue; 3444 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) 3445 LINE_CHECK("%-6.6s ucast NONE mgmt %2u Mb/s " 3446 "mcast %2u Mb/s maxretry %u", 3447 modename[mode], tp->mgmtrate/2, 3448 tp->mcastrate/2, tp->maxretry); 3449 else 3450 LINE_CHECK("%-6.6s ucast %2u Mb/s mgmt %2u Mb/s " 3451 "mcast %2u Mb/s maxretry %u", 3452 modename[mode], tp->ucastrate/2, tp->mgmtrate/2, 3453 tp->mcastrate/2, tp->maxretry); 3454 } 3455 for (; mode < IEEE80211_MODE_MAX; mode++) { 3456 tp = &txparams.params[mode]; 3457 if (tp->mgmtrate == 0 && tp->mcastrate == 0) 3458 continue; 3459 if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) 3460 LINE_CHECK("%-6.6s ucast NONE mgmt %2u MCS " 3461 "mcast %2u MCS maxretry %u", 3462 modename[mode], tp->mgmtrate &~ 0x80, 3463 tp->mcastrate &~ 0x80, tp->maxretry); 3464 else 3465 LINE_CHECK("%-6.6s ucast %2u MCS mgmt %2u MCS " 3466 "mcast %2u MCS maxretry %u", 3467 modename[mode], tp->ucastrate &~ 0x80, 3468 tp->mgmtrate &~ 0x80, 3469 tp->mcastrate &~ 0x80, tp->maxretry); 3470 } 3471} 3472 3473static void 3474printpolicy(int policy) 3475{ 3476 switch (policy) { 3477 case IEEE80211_MACCMD_POLICY_OPEN: 3478 printf("policy: open\n"); 3479 break; 3480 case IEEE80211_MACCMD_POLICY_ALLOW: 3481 printf("policy: allow\n"); 3482 break; 3483 case IEEE80211_MACCMD_POLICY_DENY: 3484 printf("policy: deny\n"); 3485 break; 3486 case IEEE80211_MACCMD_POLICY_RADIUS: 3487 printf("policy: radius\n"); 3488 break; 3489 default: 3490 printf("policy: unknown (%u)\n", policy); 3491 break; 3492 } 3493} 3494 3495static void 3496list_mac(int s) 3497{ 3498 struct ieee80211req ireq; 3499 struct ieee80211req_maclist *acllist; 3500 int i, nacls, policy, len; 3501 uint8_t *data; 3502 char c; 3503 3504 (void) memset(&ireq, 0, sizeof(ireq)); 3505 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); /* XXX ?? */ 3506 ireq.i_type = IEEE80211_IOC_MACCMD; 3507 ireq.i_val = IEEE80211_MACCMD_POLICY; 3508 if (ioctl(s, SIOCG80211, &ireq) < 0) { 3509 if (errno == EINVAL) { 3510 printf("No acl policy loaded\n"); 3511 return; 3512 } 3513 err(1, "unable to get mac policy"); 3514 } 3515 policy = ireq.i_val; 3516 if (policy == IEEE80211_MACCMD_POLICY_OPEN) { 3517 c = '*'; 3518 } else if (policy == IEEE80211_MACCMD_POLICY_ALLOW) { 3519 c = '+'; 3520 } else if (policy == IEEE80211_MACCMD_POLICY_DENY) { 3521 c = '-'; 3522 } else if (policy == IEEE80211_MACCMD_POLICY_RADIUS) { 3523 c = 'r'; /* NB: should never have entries */ 3524 } else { 3525 printf("policy: unknown (%u)\n", policy); 3526 c = '?'; 3527 } 3528 if (verbose || c == '?') 3529 printpolicy(policy); 3530 3531 ireq.i_val = IEEE80211_MACCMD_LIST; 3532 ireq.i_len = 0; 3533 if (ioctl(s, SIOCG80211, &ireq) < 0) 3534 err(1, "unable to get mac acl list size"); 3535 if (ireq.i_len == 0) { /* NB: no acls */ 3536 if (!(verbose || c == '?')) 3537 printpolicy(policy); 3538 return; 3539 } 3540 len = ireq.i_len; 3541 3542 data = malloc(len); 3543 if (data == NULL) 3544 err(1, "out of memory for acl list"); 3545 3546 ireq.i_data = data; 3547 if (ioctl(s, SIOCG80211, &ireq) < 0) 3548 err(1, "unable to get mac acl list"); 3549 nacls = len / sizeof(*acllist); 3550 acllist = (struct ieee80211req_maclist *) data; 3551 for (i = 0; i < nacls; i++) 3552 printf("%c%s\n", c, ether_ntoa( 3553 (const struct ether_addr *) acllist[i].ml_macaddr)); 3554 free(data); 3555} 3556 3557static void 3558print_regdomain(const struct ieee80211_regdomain *reg, int verb) 3559{ 3560 if ((reg->regdomain != 0 && 3561 reg->regdomain != reg->country) || verb) { 3562 const struct regdomain *rd = 3563 lib80211_regdomain_findbysku(getregdata(), reg->regdomain); 3564 if (rd == NULL) 3565 LINE_CHECK("regdomain %d", reg->regdomain); 3566 else 3567 LINE_CHECK("regdomain %s", rd->name); 3568 } 3569 if (reg->country != 0 || verb) { 3570 const struct country *cc = 3571 lib80211_country_findbycc(getregdata(), reg->country); 3572 if (cc == NULL) 3573 LINE_CHECK("country %d", reg->country); 3574 else 3575 LINE_CHECK("country %s", cc->isoname); 3576 } 3577 if (reg->location == 'I') 3578 LINE_CHECK("indoor"); 3579 else if (reg->location == 'O') 3580 LINE_CHECK("outdoor"); 3581 else if (verb) 3582 LINE_CHECK("anywhere"); 3583 if (reg->ecm) 3584 LINE_CHECK("ecm"); 3585 else if (verb) 3586 LINE_CHECK("-ecm"); 3587} 3588 3589static void 3590list_regdomain(int s, int channelsalso) 3591{ 3592 getregdomain(s); 3593 if (channelsalso) { 3594 getchaninfo(s); 3595 spacer = ':'; 3596 print_regdomain(®domain, 1); 3597 LINE_BREAK(); 3598 print_channels(s, chaninfo, 1/*allchans*/, 1/*verbose*/); 3599 } else 3600 print_regdomain(®domain, verbose); 3601} 3602 3603static 3604DECL_CMD_FUNC(set80211list, arg, d) 3605{ 3606#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 3607 3608 LINE_INIT('\t'); 3609 3610 if (iseq(arg, "sta")) 3611 list_stations(s); 3612 else if (iseq(arg, "scan") || iseq(arg, "ap")) 3613 list_scan(s); 3614 else if (iseq(arg, "chan") || iseq(arg, "freq")) 3615 list_channels(s, 1); 3616 else if (iseq(arg, "active")) 3617 list_channels(s, 0); 3618 else if (iseq(arg, "keys")) 3619 list_keys(s); 3620 else if (iseq(arg, "caps")) 3621 list_capabilities(s); 3622 else if (iseq(arg, "wme") || iseq(arg, "wmm")) 3623 list_wme(s); 3624 else if (iseq(arg, "mac")) 3625 list_mac(s); 3626 else if (iseq(arg, "txpow")) 3627 list_txpow(s); 3628 else if (iseq(arg, "roam")) 3629 list_roam(s); 3630 else if (iseq(arg, "txparam") || iseq(arg, "txparm")) 3631 list_txparams(s); 3632 else if (iseq(arg, "regdomain")) 3633 list_regdomain(s, 1); 3634 else if (iseq(arg, "countries")) 3635 list_countries(); 3636 else 3637 errx(1, "Don't know how to list %s for %s", arg, name); 3638 LINE_BREAK(); 3639#undef iseq 3640} 3641 3642static enum ieee80211_opmode 3643get80211opmode(int s) 3644{ 3645 struct ifmediareq ifmr; 3646 3647 (void) memset(&ifmr, 0, sizeof(ifmr)); 3648 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); 3649 3650 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { 3651 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { 3652 if (ifmr.ifm_current & IFM_FLAG0) 3653 return IEEE80211_M_AHDEMO; 3654 else 3655 return IEEE80211_M_IBSS; 3656 } 3657 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) 3658 return IEEE80211_M_HOSTAP; 3659 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) 3660 return IEEE80211_M_MONITOR; 3661 } 3662 return IEEE80211_M_STA; 3663} 3664 3665#if 0 3666static void 3667printcipher(int s, struct ieee80211req *ireq, int keylenop) 3668{ 3669 switch (ireq->i_val) { 3670 case IEEE80211_CIPHER_WEP: 3671 ireq->i_type = keylenop; 3672 if (ioctl(s, SIOCG80211, ireq) != -1) 3673 printf("WEP-%s", 3674 ireq->i_len <= 5 ? "40" : 3675 ireq->i_len <= 13 ? "104" : "128"); 3676 else 3677 printf("WEP"); 3678 break; 3679 case IEEE80211_CIPHER_TKIP: 3680 printf("TKIP"); 3681 break; 3682 case IEEE80211_CIPHER_AES_OCB: 3683 printf("AES-OCB"); 3684 break; 3685 case IEEE80211_CIPHER_AES_CCM: 3686 printf("AES-CCM"); 3687 break; 3688 case IEEE80211_CIPHER_CKIP: 3689 printf("CKIP"); 3690 break; 3691 case IEEE80211_CIPHER_NONE: 3692 printf("NONE"); 3693 break; 3694 default: 3695 printf("UNKNOWN (0x%x)", ireq->i_val); 3696 break; 3697 } 3698} 3699#endif 3700 3701static void 3702printkey(const struct ieee80211req_key *ik) 3703{ 3704 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; 3705 int keylen = ik->ik_keylen; 3706 int printcontents; 3707 3708 printcontents = printkeys && 3709 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); 3710 if (printcontents) 3711 LINE_BREAK(); 3712 switch (ik->ik_type) { 3713 case IEEE80211_CIPHER_WEP: 3714 /* compatibility */ 3715 LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1, 3716 keylen <= 5 ? "40-bit" : 3717 keylen <= 13 ? "104-bit" : "128-bit"); 3718 break; 3719 case IEEE80211_CIPHER_TKIP: 3720 if (keylen > 128/8) 3721 keylen -= 128/8; /* ignore MIC for now */ 3722 LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3723 break; 3724 case IEEE80211_CIPHER_AES_OCB: 3725 LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3726 break; 3727 case IEEE80211_CIPHER_AES_CCM: 3728 LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3729 break; 3730 case IEEE80211_CIPHER_CKIP: 3731 LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3732 break; 3733 case IEEE80211_CIPHER_NONE: 3734 LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen); 3735 break; 3736 default: 3737 LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit", 3738 ik->ik_type, ik->ik_keyix+1, 8*keylen); 3739 break; 3740 } 3741 if (printcontents) { 3742 int i; 3743 3744 printf(" <"); 3745 for (i = 0; i < keylen; i++) 3746 printf("%02x", ik->ik_keydata[i]); 3747 printf(">"); 3748 if (ik->ik_type != IEEE80211_CIPHER_WEP && 3749 (ik->ik_keyrsc != 0 || verbose)) 3750 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc); 3751 if (ik->ik_type != IEEE80211_CIPHER_WEP && 3752 (ik->ik_keytsc != 0 || verbose)) 3753 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc); 3754 if (ik->ik_flags != 0 && verbose) { 3755 const char *sep = " "; 3756 3757 if (ik->ik_flags & IEEE80211_KEY_XMIT) 3758 printf("%stx", sep), sep = "+"; 3759 if (ik->ik_flags & IEEE80211_KEY_RECV) 3760 printf("%srx", sep), sep = "+"; 3761 if (ik->ik_flags & IEEE80211_KEY_DEFAULT) 3762 printf("%sdef", sep), sep = "+"; 3763 } 3764 LINE_BREAK(); 3765 } 3766} 3767 3768static void 3769printrate(const char *tag, int v, int defrate, int defmcs) 3770{ 3771 if (v == 11) 3772 LINE_CHECK("%s 5.5", tag); 3773 else if (v & 0x80) { 3774 if (v != defmcs) 3775 LINE_CHECK("%s %d", tag, v &~ 0x80); 3776 } else { 3777 if (v != defrate) 3778 LINE_CHECK("%s %d", tag, v/2); 3779 } 3780} 3781 3782static int 3783getssid(int s, int ix, void *data, size_t len, int *plen) 3784{ 3785 struct ieee80211req ireq; 3786 3787 (void) memset(&ireq, 0, sizeof(ireq)); 3788 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 3789 ireq.i_type = IEEE80211_IOC_SSID; 3790 ireq.i_val = ix; 3791 ireq.i_data = data; 3792 ireq.i_len = len; 3793 if (ioctl(s, SIOCG80211, &ireq) < 0) 3794 return -1; 3795 *plen = ireq.i_len; 3796 return 0; 3797} 3798 3799static void 3800ieee80211_status(int s) 3801{ 3802 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 3803 enum ieee80211_opmode opmode = get80211opmode(s); 3804 int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode; 3805 uint8_t data[32]; 3806 const struct ieee80211_channel *c; 3807 const struct ieee80211_roamparam *rp; 3808 const struct ieee80211_txparam *tp; 3809 3810 if (getssid(s, -1, data, sizeof(data), &len) < 0) { 3811 /* If we can't get the SSID, this isn't an 802.11 device. */ 3812 return; 3813 } 3814 3815 /* 3816 * Invalidate cached state so printing status for multiple 3817 * if's doesn't reuse the first interfaces' cached state. 3818 */ 3819 gotcurchan = 0; 3820 gotroam = 0; 3821 gottxparams = 0; 3822 gothtconf = 0; 3823 gotregdomain = 0; 3824 3825 if (get80211val(s, IEEE80211_IOC_NUMSSIDS, &num) < 0) 3826 num = 0; 3827 printf("\tssid "); 3828 if (num > 1) { 3829 for (i = 0; i < num; i++) { 3830 if (getssid(s, i, data, sizeof(data), &len) >= 0 && len > 0) { 3831 printf(" %d:", i + 1); 3832 print_string(data, len); 3833 } 3834 } 3835 } else 3836 print_string(data, len); 3837 3838 c = getcurchan(s); 3839 if (c->ic_freq != IEEE80211_CHAN_ANY) { 3840 char buf[14]; 3841 printf(" channel %d (%u Mhz%s)", c->ic_ieee, c->ic_freq, 3842 get_chaninfo(c, 1, buf, sizeof(buf))); 3843 } else if (verbose) 3844 printf(" channel UNDEF"); 3845 3846 if (get80211(s, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 && 3847 (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose)) 3848 printf(" bssid %s", ether_ntoa((struct ether_addr *)data)); 3849 3850 if (get80211len(s, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) { 3851 printf("\n\tstationname "); 3852 print_string(data, len); 3853 } 3854 3855 spacer = ' '; /* force first break */ 3856 LINE_BREAK(); 3857 3858 list_regdomain(s, 0); 3859 3860 wpa = 0; 3861 if (get80211val(s, IEEE80211_IOC_AUTHMODE, &val) != -1) { 3862 switch (val) { 3863 case IEEE80211_AUTH_NONE: 3864 LINE_CHECK("authmode NONE"); 3865 break; 3866 case IEEE80211_AUTH_OPEN: 3867 LINE_CHECK("authmode OPEN"); 3868 break; 3869 case IEEE80211_AUTH_SHARED: 3870 LINE_CHECK("authmode SHARED"); 3871 break; 3872 case IEEE80211_AUTH_8021X: 3873 LINE_CHECK("authmode 802.1x"); 3874 break; 3875 case IEEE80211_AUTH_WPA: 3876 if (get80211val(s, IEEE80211_IOC_WPA, &wpa) < 0) 3877 wpa = 1; /* default to WPA1 */ 3878 switch (wpa) { 3879 case 2: 3880 LINE_CHECK("authmode WPA2/802.11i"); 3881 break; 3882 case 3: 3883 LINE_CHECK("authmode WPA1+WPA2/802.11i"); 3884 break; 3885 default: 3886 LINE_CHECK("authmode WPA"); 3887 break; 3888 } 3889 break; 3890 case IEEE80211_AUTH_AUTO: 3891 LINE_CHECK("authmode AUTO"); 3892 break; 3893 default: 3894 LINE_CHECK("authmode UNKNOWN (0x%x)", val); 3895 break; 3896 } 3897 } 3898 3899 if (wpa || verbose) { 3900 if (get80211val(s, IEEE80211_IOC_WPS, &val) != -1) { 3901 if (val) 3902 LINE_CHECK("wps"); 3903 else if (verbose) 3904 LINE_CHECK("-wps"); 3905 } 3906 if (get80211val(s, IEEE80211_IOC_TSN, &val) != -1) { 3907 if (val) 3908 LINE_CHECK("tsn"); 3909 else if (verbose) 3910 LINE_CHECK("-tsn"); 3911 } 3912 if (ioctl(s, IEEE80211_IOC_COUNTERMEASURES, &val) != -1) { 3913 if (val) 3914 LINE_CHECK("countermeasures"); 3915 else if (verbose) 3916 LINE_CHECK("-countermeasures"); 3917 } 3918#if 0 3919 /* XXX not interesting with WPA done in user space */ 3920 ireq.i_type = IEEE80211_IOC_KEYMGTALGS; 3921 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3922 } 3923 3924 ireq.i_type = IEEE80211_IOC_MCASTCIPHER; 3925 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3926 LINE_CHECK("mcastcipher "); 3927 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); 3928 spacer = ' '; 3929 } 3930 3931 ireq.i_type = IEEE80211_IOC_UCASTCIPHER; 3932 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3933 LINE_CHECK("ucastcipher "); 3934 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); 3935 } 3936 3937 if (wpa & 2) { 3938 ireq.i_type = IEEE80211_IOC_RSNCAPS; 3939 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3940 LINE_CHECK("RSN caps 0x%x", ireq.i_val); 3941 spacer = ' '; 3942 } 3943 } 3944 3945 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; 3946 if (ioctl(s, SIOCG80211, &ireq) != -1) { 3947 } 3948#endif 3949 } 3950 3951 if (get80211val(s, IEEE80211_IOC_WEP, &wepmode) != -1 && 3952 wepmode != IEEE80211_WEP_NOSUP) { 3953 int firstkey; 3954 3955 switch (wepmode) { 3956 case IEEE80211_WEP_OFF: 3957 LINE_CHECK("privacy OFF"); 3958 break; 3959 case IEEE80211_WEP_ON: 3960 LINE_CHECK("privacy ON"); 3961 break; 3962 case IEEE80211_WEP_MIXED: 3963 LINE_CHECK("privacy MIXED"); 3964 break; 3965 default: 3966 LINE_CHECK("privacy UNKNOWN (0x%x)", wepmode); 3967 break; 3968 } 3969 3970 /* 3971 * If we get here then we've got WEP support so we need 3972 * to print WEP status. 3973 */ 3974 3975 if (get80211val(s, IEEE80211_IOC_WEPTXKEY, &val) < 0) { 3976 warn("WEP support, but no tx key!"); 3977 goto end; 3978 } 3979 if (val != -1) 3980 LINE_CHECK("deftxkey %d", val+1); 3981 else if (wepmode != IEEE80211_WEP_OFF || verbose) 3982 LINE_CHECK("deftxkey UNDEF"); 3983 3984 if (get80211val(s, IEEE80211_IOC_NUMWEPKEYS, &num) < 0) { 3985 warn("WEP support, but no NUMWEPKEYS support!"); 3986 goto end; 3987 } 3988 3989 firstkey = 1; 3990 for (i = 0; i < num; i++) { 3991 struct ieee80211req_key ik; 3992 3993 memset(&ik, 0, sizeof(ik)); 3994 ik.ik_keyix = i; 3995 if (get80211(s, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) { 3996 warn("WEP support, but can get keys!"); 3997 goto end; 3998 } 3999 if (ik.ik_keylen != 0) { 4000 if (verbose) 4001 LINE_BREAK(); 4002 printkey(&ik); 4003 firstkey = 0; 4004 } 4005 } 4006end: 4007 ; 4008 } 4009 4010 if (get80211val(s, IEEE80211_IOC_POWERSAVE, &val) != -1 && 4011 val != IEEE80211_POWERSAVE_NOSUP ) { 4012 if (val != IEEE80211_POWERSAVE_OFF || verbose) { 4013 switch (val) { 4014 case IEEE80211_POWERSAVE_OFF: 4015 LINE_CHECK("powersavemode OFF"); 4016 break; 4017 case IEEE80211_POWERSAVE_CAM: 4018 LINE_CHECK("powersavemode CAM"); 4019 break; 4020 case IEEE80211_POWERSAVE_PSP: 4021 LINE_CHECK("powersavemode PSP"); 4022 break; 4023 case IEEE80211_POWERSAVE_PSP_CAM: 4024 LINE_CHECK("powersavemode PSP-CAM"); 4025 break; 4026 } 4027 if (get80211val(s, IEEE80211_IOC_POWERSAVESLEEP, &val) != -1) 4028 LINE_CHECK("powersavesleep %d", val); 4029 } 4030 } 4031 4032 if (get80211val(s, IEEE80211_IOC_TXPOWER, &val) != -1) { 4033 if (val & 1) 4034 LINE_CHECK("txpower %d.5", val/2); 4035 else 4036 LINE_CHECK("txpower %d", val/2); 4037 } 4038 if (verbose) { 4039 if (get80211val(s, IEEE80211_IOC_TXPOWMAX, &val) != -1) 4040 LINE_CHECK("txpowmax %.1f", val/2.); 4041 } 4042 4043 if (get80211val(s, IEEE80211_IOC_DOTD, &val) != -1) { 4044 if (val) 4045 LINE_CHECK("dotd"); 4046 else if (verbose) 4047 LINE_CHECK("-dotd"); 4048 } 4049 4050 if (get80211val(s, IEEE80211_IOC_RTSTHRESHOLD, &val) != -1) { 4051 if (val != IEEE80211_RTS_MAX || verbose) 4052 LINE_CHECK("rtsthreshold %d", val); 4053 } 4054 4055 if (get80211val(s, IEEE80211_IOC_FRAGTHRESHOLD, &val) != -1) { 4056 if (val != IEEE80211_FRAG_MAX || verbose) 4057 LINE_CHECK("fragthreshold %d", val); 4058 } 4059 if (opmode == IEEE80211_M_STA || verbose) { 4060 if (get80211val(s, IEEE80211_IOC_BMISSTHRESHOLD, &val) != -1) { 4061 if (val != IEEE80211_HWBMISS_MAX || verbose) 4062 LINE_CHECK("bmiss %d", val); 4063 } 4064 } 4065 4066 if (!verbose) { 4067 gettxparams(s); 4068 tp = &txparams.params[chan2mode(c)]; 4069 printrate("ucastrate", tp->ucastrate, 4070 IEEE80211_FIXED_RATE_NONE, IEEE80211_FIXED_RATE_NONE); 4071 printrate("mcastrate", tp->mcastrate, 2*1, 0x80|0); 4072 printrate("mgmtrate", tp->mgmtrate, 2*1, 0x80|0); 4073 if (tp->maxretry != 6) /* XXX */ 4074 LINE_CHECK("maxretry %d", tp->maxretry); 4075 } else { 4076 LINE_BREAK(); 4077 list_txparams(s); 4078 } 4079 4080 bgscaninterval = -1; 4081 (void) get80211val(s, IEEE80211_IOC_BGSCAN_INTERVAL, &bgscaninterval); 4082 4083 if (get80211val(s, IEEE80211_IOC_SCANVALID, &val) != -1) { 4084 if (val != bgscaninterval || verbose) 4085 LINE_CHECK("scanvalid %u", val); 4086 } 4087 4088 bgscan = 0; 4089 if (get80211val(s, IEEE80211_IOC_BGSCAN, &bgscan) != -1) { 4090 if (bgscan) 4091 LINE_CHECK("bgscan"); 4092 else if (verbose) 4093 LINE_CHECK("-bgscan"); 4094 } 4095 if (bgscan || verbose) { 4096 if (bgscaninterval != -1) 4097 LINE_CHECK("bgscanintvl %u", bgscaninterval); 4098 if (get80211val(s, IEEE80211_IOC_BGSCAN_IDLE, &val) != -1) 4099 LINE_CHECK("bgscanidle %u", val); 4100 if (!verbose) { 4101 getroam(s); 4102 rp = &roamparams.params[chan2mode(c)]; 4103 if (rp->rssi & 1) 4104 LINE_CHECK("roam:rssi %u.5", rp->rssi/2); 4105 else 4106 LINE_CHECK("roam:rssi %u", rp->rssi/2); 4107 LINE_CHECK("roam:rate %u", rp->rate/2); 4108 } else { 4109 LINE_BREAK(); 4110 list_roam(s); 4111 } 4112 } 4113 4114 if (IEEE80211_IS_CHAN_ANYG(c) || verbose) { 4115 if (get80211val(s, IEEE80211_IOC_PUREG, &val) != -1) { 4116 if (val) 4117 LINE_CHECK("pureg"); 4118 else if (verbose) 4119 LINE_CHECK("-pureg"); 4120 } 4121 if (get80211val(s, IEEE80211_IOC_PROTMODE, &val) != -1) { 4122 switch (val) { 4123 case IEEE80211_PROTMODE_OFF: 4124 LINE_CHECK("protmode OFF"); 4125 break; 4126 case IEEE80211_PROTMODE_CTS: 4127 LINE_CHECK("protmode CTS"); 4128 break; 4129 case IEEE80211_PROTMODE_RTSCTS: 4130 LINE_CHECK("protmode RTSCTS"); 4131 break; 4132 default: 4133 LINE_CHECK("protmode UNKNOWN (0x%x)", val); 4134 break; 4135 } 4136 } 4137 } 4138 4139 if (IEEE80211_IS_CHAN_HT(c) || verbose) { 4140 gethtconf(s); 4141 switch (htconf & 3) { 4142 case 0: 4143 case 2: 4144 LINE_CHECK("-ht"); 4145 break; 4146 case 1: 4147 LINE_CHECK("ht20"); 4148 break; 4149 case 3: 4150 if (verbose) 4151 LINE_CHECK("ht"); 4152 break; 4153 } 4154 if (get80211val(s, IEEE80211_IOC_HTCOMPAT, &val) != -1) { 4155 if (!val) 4156 LINE_CHECK("-htcompat"); 4157 else if (verbose) 4158 LINE_CHECK("htcompat"); 4159 } 4160 if (get80211val(s, IEEE80211_IOC_AMPDU, &val) != -1) { 4161 switch (val) { 4162 case 0: 4163 LINE_CHECK("-ampdu"); 4164 break; 4165 case 1: 4166 LINE_CHECK("ampdutx -ampdurx"); 4167 break; 4168 case 2: 4169 LINE_CHECK("-ampdutx ampdurx"); 4170 break; 4171 case 3: 4172 if (verbose) 4173 LINE_CHECK("ampdu"); 4174 break; 4175 } 4176 } 4177 if (get80211val(s, IEEE80211_IOC_AMPDU_LIMIT, &val) != -1) { 4178 switch (val) { 4179 case IEEE80211_HTCAP_MAXRXAMPDU_8K: 4180 LINE_CHECK("ampdulimit 8k"); 4181 break; 4182 case IEEE80211_HTCAP_MAXRXAMPDU_16K: 4183 LINE_CHECK("ampdulimit 16k"); 4184 break; 4185 case IEEE80211_HTCAP_MAXRXAMPDU_32K: 4186 LINE_CHECK("ampdulimit 32k"); 4187 break; 4188 case IEEE80211_HTCAP_MAXRXAMPDU_64K: 4189 LINE_CHECK("ampdulimit 64k"); 4190 break; 4191 } 4192 } 4193 if (get80211val(s, IEEE80211_IOC_AMPDU_DENSITY, &val) != -1) { 4194 switch (val) { 4195 case IEEE80211_HTCAP_MPDUDENSITY_NA: 4196 if (verbose) 4197 LINE_CHECK("ampdudensity NA"); 4198 break; 4199 case IEEE80211_HTCAP_MPDUDENSITY_025: 4200 LINE_CHECK("ampdudensity .25"); 4201 break; 4202 case IEEE80211_HTCAP_MPDUDENSITY_05: 4203 LINE_CHECK("ampdudensity .5"); 4204 break; 4205 case IEEE80211_HTCAP_MPDUDENSITY_1: 4206 LINE_CHECK("ampdudensity 1"); 4207 break; 4208 case IEEE80211_HTCAP_MPDUDENSITY_2: 4209 LINE_CHECK("ampdudensity 2"); 4210 break; 4211 case IEEE80211_HTCAP_MPDUDENSITY_4: 4212 LINE_CHECK("ampdudensity 4"); 4213 break; 4214 case IEEE80211_HTCAP_MPDUDENSITY_8: 4215 LINE_CHECK("ampdudensity 8"); 4216 break; 4217 case IEEE80211_HTCAP_MPDUDENSITY_16: 4218 LINE_CHECK("ampdudensity 16"); 4219 break; 4220 } 4221 } 4222 if (get80211val(s, IEEE80211_IOC_AMSDU, &val) != -1) { 4223 switch (val) { 4224 case 0: 4225 LINE_CHECK("-amsdu"); 4226 break; 4227 case 1: 4228 LINE_CHECK("amsdutx -amsdurx"); 4229 break; 4230 case 2: 4231 LINE_CHECK("-amsdutx amsdurx"); 4232 break; 4233 case 3: 4234 if (verbose) 4235 LINE_CHECK("amsdu"); 4236 break; 4237 } 4238 } 4239 /* XXX amsdu limit */ 4240 if (get80211val(s, IEEE80211_IOC_SHORTGI, &val) != -1) { 4241 if (val) 4242 LINE_CHECK("shortgi"); 4243 else if (verbose) 4244 LINE_CHECK("-shortgi"); 4245 } 4246 if (get80211val(s, IEEE80211_IOC_HTPROTMODE, &val) != -1) { 4247 if (val == IEEE80211_PROTMODE_OFF) 4248 LINE_CHECK("htprotmode OFF"); 4249 else if (val != IEEE80211_PROTMODE_RTSCTS) 4250 LINE_CHECK("htprotmode UNKNOWN (0x%x)", val); 4251 else if (verbose) 4252 LINE_CHECK("htprotmode RTSCTS"); 4253 } 4254 if (get80211val(s, IEEE80211_IOC_PUREN, &val) != -1) { 4255 if (val) 4256 LINE_CHECK("puren"); 4257 else if (verbose) 4258 LINE_CHECK("-puren"); 4259 } 4260 if (get80211val(s, IEEE80211_IOC_SMPS, &val) != -1) { 4261 if (val == IEEE80211_HTCAP_SMPS_DYNAMIC) 4262 LINE_CHECK("smpsdyn"); 4263 else if (val == IEEE80211_HTCAP_SMPS_ENA) 4264 LINE_CHECK("smps"); 4265 else if (verbose) 4266 LINE_CHECK("-smps"); 4267 } 4268 if (get80211val(s, IEEE80211_IOC_RIFS, &val) != -1) { 4269 if (val) 4270 LINE_CHECK("rifs"); 4271 else if (verbose) 4272 LINE_CHECK("-rifs"); 4273 } 4274 } 4275 4276 if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) { 4277 if (wme) 4278 LINE_CHECK("wme"); 4279 else if (verbose) 4280 LINE_CHECK("-wme"); 4281 } else 4282 wme = 0; 4283 4284 if (get80211val(s, IEEE80211_IOC_BURST, &val) != -1) { 4285 if (val) 4286 LINE_CHECK("burst"); 4287 else if (verbose) 4288 LINE_CHECK("-burst"); 4289 } 4290 4291 if (get80211val(s, IEEE80211_IOC_FF, &val) != -1) { 4292 if (val) 4293 LINE_CHECK("ff"); 4294 else if (verbose) 4295 LINE_CHECK("-ff"); 4296 } 4297 if (get80211val(s, IEEE80211_IOC_TURBOP, &val) != -1) { 4298 if (val) 4299 LINE_CHECK("dturbo"); 4300 else if (verbose) 4301 LINE_CHECK("-dturbo"); 4302 } 4303 if (get80211val(s, IEEE80211_IOC_DWDS, &val) != -1) { 4304 if (val) 4305 LINE_CHECK("dwds"); 4306 else if (verbose) 4307 LINE_CHECK("-dwds"); 4308 } 4309 4310 if (opmode == IEEE80211_M_HOSTAP) { 4311 if (get80211val(s, IEEE80211_IOC_HIDESSID, &val) != -1) { 4312 if (val) 4313 LINE_CHECK("hidessid"); 4314 else if (verbose) 4315 LINE_CHECK("-hidessid"); 4316 } 4317 if (get80211val(s, IEEE80211_IOC_APBRIDGE, &val) != -1) { 4318 if (!val) 4319 LINE_CHECK("-apbridge"); 4320 else if (verbose) 4321 LINE_CHECK("apbridge"); 4322 } 4323 if (get80211val(s, IEEE80211_IOC_DTIM_PERIOD, &val) != -1) 4324 LINE_CHECK("dtimperiod %u", val); 4325 4326 if (get80211val(s, IEEE80211_IOC_DOTH, &val) != -1) { 4327 if (!val) 4328 LINE_CHECK("-doth"); 4329 else if (verbose) 4330 LINE_CHECK("doth"); 4331 } 4332 if (get80211val(s, IEEE80211_IOC_DFS, &val) != -1) { 4333 if (!val) 4334 LINE_CHECK("-dfs"); 4335 else if (verbose) 4336 LINE_CHECK("dfs"); 4337 } 4338 if (get80211val(s, IEEE80211_IOC_INACTIVITY, &val) != -1) { 4339 if (!val) 4340 LINE_CHECK("-inact"); 4341 else if (verbose) 4342 LINE_CHECK("inact"); 4343 } 4344 } else { 4345 if (get80211val(s, IEEE80211_IOC_ROAMING, &val) != -1) { 4346 if (val != IEEE80211_ROAMING_AUTO || verbose) { 4347 switch (val) { 4348 case IEEE80211_ROAMING_DEVICE: 4349 LINE_CHECK("roaming DEVICE"); 4350 break; 4351 case IEEE80211_ROAMING_AUTO: 4352 LINE_CHECK("roaming AUTO"); 4353 break; 4354 case IEEE80211_ROAMING_MANUAL: 4355 LINE_CHECK("roaming MANUAL"); 4356 break; 4357 default: 4358 LINE_CHECK("roaming UNKNOWN (0x%x)", 4359 val); 4360 break; 4361 } 4362 } 4363 } 4364 } 4365 4366 if (opmode == IEEE80211_M_AHDEMO) { 4367 if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1) 4368 LINE_CHECK("tdmaslot %u", val); 4369 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1) 4370 LINE_CHECK("tdmaslotcnt %u", val); 4371 if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1) 4372 LINE_CHECK("tdmaslotlen %u", val); 4373 if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1) 4374 LINE_CHECK("tdmabintval %u", val); 4375 } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) { 4376 /* XXX default define not visible */ 4377 if (val != 100 || verbose) 4378 LINE_CHECK("bintval %u", val); 4379 } 4380 4381 if (wme && verbose) { 4382 LINE_BREAK(); 4383 list_wme(s); 4384 } 4385 LINE_BREAK(); 4386} 4387 4388static int 4389get80211(int s, int type, void *data, int len) 4390{ 4391 struct ieee80211req ireq; 4392 4393 (void) memset(&ireq, 0, sizeof(ireq)); 4394 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4395 ireq.i_type = type; 4396 ireq.i_data = data; 4397 ireq.i_len = len; 4398 return ioctl(s, SIOCG80211, &ireq); 4399} 4400 4401static int 4402get80211len(int s, int type, void *data, int len, int *plen) 4403{ 4404 struct ieee80211req ireq; 4405 4406 (void) memset(&ireq, 0, sizeof(ireq)); 4407 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4408 ireq.i_type = type; 4409 ireq.i_len = len; 4410 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ 4411 ireq.i_data = data; 4412 if (ioctl(s, SIOCG80211, &ireq) < 0) 4413 return -1; 4414 *plen = ireq.i_len; 4415 return 0; 4416} 4417 4418static int 4419get80211val(int s, int type, int *val) 4420{ 4421 struct ieee80211req ireq; 4422 4423 (void) memset(&ireq, 0, sizeof(ireq)); 4424 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 4425 ireq.i_type = type; 4426 if (ioctl(s, SIOCG80211, &ireq) < 0) 4427 return -1; 4428 *val = ireq.i_val; 4429 return 0; 4430} 4431 4432static void 4433set80211(int s, int type, int val, int len, void *data) 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 ireq.i_val = val; 4441 ireq.i_len = len; 4442 assert(ireq.i_len == len); /* NB: check for 16-bit truncation */ 4443 ireq.i_data = data; 4444 if (ioctl(s, SIOCS80211, &ireq) < 0) 4445 err(1, "SIOCS80211"); 4446} 4447 4448static const char * 4449get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) 4450{ 4451 int len; 4452 int hexstr; 4453 u_int8_t *p; 4454 4455 len = *lenp; 4456 p = buf; 4457 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x'); 4458 if (hexstr) 4459 val += 2; 4460 for (;;) { 4461 if (*val == '\0') 4462 break; 4463 if (sep != NULL && strchr(sep, *val) != NULL) { 4464 val++; 4465 break; 4466 } 4467 if (hexstr) { 4468 if (!isxdigit((u_char)val[0])) { 4469 warnx("bad hexadecimal digits"); 4470 return NULL; 4471 } 4472 if (!isxdigit((u_char)val[1])) { 4473 warnx("odd count hexadecimal digits"); 4474 return NULL; 4475 } 4476 } 4477 if (p >= buf + len) { 4478 if (hexstr) 4479 warnx("hexadecimal digits too long"); 4480 else 4481 warnx("string too long"); 4482 return NULL; 4483 } 4484 if (hexstr) { 4485#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10) 4486 *p++ = (tohex((u_char)val[0]) << 4) | 4487 tohex((u_char)val[1]); 4488#undef tohex 4489 val += 2; 4490 } else 4491 *p++ = *val++; 4492 } 4493 len = p - buf; 4494 /* The string "-" is treated as the empty string. */ 4495 if (!hexstr && len == 1 && buf[0] == '-') { 4496 len = 0; 4497 memset(buf, 0, *lenp); 4498 } else if (len < *lenp) 4499 memset(p, 0, *lenp - len); 4500 *lenp = len; 4501 return val; 4502} 4503 4504static void 4505print_string(const u_int8_t *buf, int len) 4506{ 4507 int i; 4508 int hasspc; 4509 4510 i = 0; 4511 hasspc = 0; 4512 for (; i < len; i++) { 4513 if (!isprint(buf[i]) && buf[i] != '\0') 4514 break; 4515 if (isspace(buf[i])) 4516 hasspc++; 4517 } 4518 if (i == len) { 4519 if (hasspc || len == 0 || buf[0] == '\0') 4520 printf("\"%.*s\"", len, buf); 4521 else 4522 printf("%.*s", len, buf); 4523 } else { 4524 printf("0x"); 4525 for (i = 0; i < len; i++) 4526 printf("%02x", buf[i]); 4527 } 4528} 4529 4530/* 4531 * Virtual AP cloning support. 4532 */ 4533static struct ieee80211_clone_params params = { 4534 .icp_opmode = IEEE80211_M_STA, /* default to station mode */ 4535}; 4536 4537static void 4538wlan_create(int s, struct ifreq *ifr) 4539{ 4540 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 4541 4542 if (params.icp_parent[0] == '\0') 4543 errx(1, "must specify a parent when creating a wlan device"); 4544 if (params.icp_opmode == IEEE80211_M_WDS && 4545 memcmp(params.icp_bssid, zerobssid, sizeof(zerobssid)) == 0) 4546 errx(1, "no bssid specified for WDS (use wlanbssid)"); 4547 ifr->ifr_data = (caddr_t) ¶ms; 4548 if (ioctl(s, SIOCIFCREATE2, ifr) < 0) 4549 err(1, "SIOCIFCREATE2"); 4550} 4551 4552static 4553DECL_CMD_FUNC(set80211clone_wlandev, arg, d) 4554{ 4555 strlcpy(params.icp_parent, arg, IFNAMSIZ); 4556 clone_setcallback(wlan_create); 4557} 4558 4559static 4560DECL_CMD_FUNC(set80211clone_wlanbssid, arg, d) 4561{ 4562 const struct ether_addr *ea; 4563 4564 ea = ether_aton(arg); 4565 if (ea == NULL) 4566 errx(1, "%s: cannot parse bssid", arg); 4567 memcpy(params.icp_bssid, ea->octet, IEEE80211_ADDR_LEN); 4568 clone_setcallback(wlan_create); 4569} 4570 4571static 4572DECL_CMD_FUNC(set80211clone_wlanaddr, arg, d) 4573{ 4574 const struct ether_addr *ea; 4575 4576 ea = ether_aton(arg); 4577 if (ea == NULL) 4578 errx(1, "%s: cannot parse addres", arg); 4579 memcpy(params.icp_macaddr, ea->octet, IEEE80211_ADDR_LEN); 4580 params.icp_flags |= IEEE80211_CLONE_MACADDR; 4581 clone_setcallback(wlan_create); 4582} 4583 4584static 4585DECL_CMD_FUNC(set80211clone_wlanmode, arg, d) 4586{ 4587#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 4588 if (iseq(arg, "sta")) 4589 params.icp_opmode = IEEE80211_M_STA; 4590 else if (iseq(arg, "ahdemo") || iseq(arg, "adhoc-demo")) 4591 params.icp_opmode = IEEE80211_M_AHDEMO; 4592 else if (iseq(arg, "ibss") || iseq(arg, "adhoc")) 4593 params.icp_opmode = IEEE80211_M_IBSS; 4594 else if (iseq(arg, "ap") || iseq(arg, "host")) 4595 params.icp_opmode = IEEE80211_M_HOSTAP; 4596 else if (iseq(arg, "wds")) 4597 params.icp_opmode = IEEE80211_M_WDS; 4598 else if (iseq(arg, "monitor")) 4599 params.icp_opmode = IEEE80211_M_MONITOR; 4600 else if (iseq(arg, "tdma")) { 4601 params.icp_opmode = IEEE80211_M_AHDEMO; 4602 params.icp_flags |= IEEE80211_CLONE_TDMA; 4603 } else 4604 errx(1, "Don't know to create %s for %s", arg, name); 4605 clone_setcallback(wlan_create); 4606#undef iseq 4607} 4608 4609static void 4610set80211clone_beacons(const char *val, int d, int s, const struct afswtch *rafp) 4611{ 4612 /* NB: inverted sense */ 4613 if (d) 4614 params.icp_flags &= ~IEEE80211_CLONE_NOBEACONS; 4615 else 4616 params.icp_flags |= IEEE80211_CLONE_NOBEACONS; 4617 clone_setcallback(wlan_create); 4618} 4619 4620static void 4621set80211clone_bssid(const char *val, int d, int s, const struct afswtch *rafp) 4622{ 4623 if (d) 4624 params.icp_flags |= IEEE80211_CLONE_BSSID; 4625 else 4626 params.icp_flags &= ~IEEE80211_CLONE_BSSID; 4627 clone_setcallback(wlan_create); 4628} 4629 4630static void 4631set80211clone_wdslegacy(const char *val, int d, int s, const struct afswtch *rafp) 4632{ 4633 if (d) 4634 params.icp_flags |= IEEE80211_CLONE_WDSLEGACY; 4635 else 4636 params.icp_flags &= ~IEEE80211_CLONE_WDSLEGACY; 4637 clone_setcallback(wlan_create); 4638} 4639 4640static struct cmd ieee80211_cmds[] = { 4641 DEF_CMD_ARG("ssid", set80211ssid), 4642 DEF_CMD_ARG("nwid", set80211ssid), 4643 DEF_CMD_ARG("stationname", set80211stationname), 4644 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */ 4645 DEF_CMD_ARG("channel", set80211channel), 4646 DEF_CMD_ARG("authmode", set80211authmode), 4647 DEF_CMD_ARG("powersavemode", set80211powersavemode), 4648 DEF_CMD("powersave", 1, set80211powersave), 4649 DEF_CMD("-powersave", 0, set80211powersave), 4650 DEF_CMD_ARG("powersavesleep", set80211powersavesleep), 4651 DEF_CMD_ARG("wepmode", set80211wepmode), 4652 DEF_CMD("wep", 1, set80211wep), 4653 DEF_CMD("-wep", 0, set80211wep), 4654 DEF_CMD_ARG("deftxkey", set80211weptxkey), 4655 DEF_CMD_ARG("weptxkey", set80211weptxkey), 4656 DEF_CMD_ARG("wepkey", set80211wepkey), 4657 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */ 4658 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */ 4659 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold), 4660 DEF_CMD_ARG("protmode", set80211protmode), 4661 DEF_CMD_ARG("txpower", set80211txpower), 4662 DEF_CMD_ARG("roaming", set80211roaming), 4663 DEF_CMD("wme", 1, set80211wme), 4664 DEF_CMD("-wme", 0, set80211wme), 4665 DEF_CMD("wmm", 1, set80211wme), 4666 DEF_CMD("-wmm", 0, set80211wme), 4667 DEF_CMD("hidessid", 1, set80211hidessid), 4668 DEF_CMD("-hidessid", 0, set80211hidessid), 4669 DEF_CMD("apbridge", 1, set80211apbridge), 4670 DEF_CMD("-apbridge", 0, set80211apbridge), 4671 DEF_CMD_ARG("chanlist", set80211chanlist), 4672 DEF_CMD_ARG("bssid", set80211bssid), 4673 DEF_CMD_ARG("ap", set80211bssid), 4674 DEF_CMD("scan", 0, set80211scan), 4675 DEF_CMD_ARG("list", set80211list), 4676 DEF_CMD_ARG2("cwmin", set80211cwmin), 4677 DEF_CMD_ARG2("cwmax", set80211cwmax), 4678 DEF_CMD_ARG2("aifs", set80211aifs), 4679 DEF_CMD_ARG2("txoplimit", set80211txoplimit), 4680 DEF_CMD_ARG("acm", set80211acm), 4681 DEF_CMD_ARG("-acm", set80211noacm), 4682 DEF_CMD_ARG("ack", set80211ackpolicy), 4683 DEF_CMD_ARG("-ack", set80211noackpolicy), 4684 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin), 4685 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax), 4686 DEF_CMD_ARG2("bss:aifs", set80211bssaifs), 4687 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit), 4688 DEF_CMD_ARG("dtimperiod", set80211dtimperiod), 4689 DEF_CMD_ARG("bintval", set80211bintval), 4690 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), 4691 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), 4692 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), 4693 DEF_CMD("mac:radius", IEEE80211_MACCMD_POLICY_RADIUS, set80211maccmd), 4694 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), 4695 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), 4696 DEF_CMD_ARG("mac:add", set80211addmac), 4697 DEF_CMD_ARG("mac:del", set80211delmac), 4698 DEF_CMD_ARG("mac:kick", set80211kickmac), 4699 DEF_CMD("pureg", 1, set80211pureg), 4700 DEF_CMD("-pureg", 0, set80211pureg), 4701 DEF_CMD("ff", 1, set80211fastframes), 4702 DEF_CMD("-ff", 0, set80211fastframes), 4703 DEF_CMD("dturbo", 1, set80211dturbo), 4704 DEF_CMD("-dturbo", 0, set80211dturbo), 4705 DEF_CMD("bgscan", 1, set80211bgscan), 4706 DEF_CMD("-bgscan", 0, set80211bgscan), 4707 DEF_CMD_ARG("bgscanidle", set80211bgscanidle), 4708 DEF_CMD_ARG("bgscanintvl", set80211bgscanintvl), 4709 DEF_CMD_ARG("scanvalid", set80211scanvalid), 4710 DEF_CMD_ARG("roam:rssi", set80211roamrssi), 4711 DEF_CMD_ARG("roam:rate", set80211roamrate), 4712 DEF_CMD_ARG("mcastrate", set80211mcastrate), 4713 DEF_CMD_ARG("ucastrate", set80211ucastrate), 4714 DEF_CMD_ARG("mgtrate", set80211mgtrate), 4715 DEF_CMD_ARG("mgmtrate", set80211mgtrate), 4716 DEF_CMD_ARG("maxretry", set80211maxretry), 4717 DEF_CMD_ARG("fragthreshold", set80211fragthreshold), 4718 DEF_CMD("burst", 1, set80211burst), 4719 DEF_CMD("-burst", 0, set80211burst), 4720 DEF_CMD_ARG("bmiss", set80211bmissthreshold), 4721 DEF_CMD_ARG("bmissthreshold", set80211bmissthreshold), 4722 DEF_CMD("shortgi", 1, set80211shortgi), 4723 DEF_CMD("-shortgi", 0, set80211shortgi), 4724 DEF_CMD("ampdurx", 2, set80211ampdu), 4725 DEF_CMD("-ampdurx", -2, set80211ampdu), 4726 DEF_CMD("ampdutx", 1, set80211ampdu), 4727 DEF_CMD("-ampdutx", -1, set80211ampdu), 4728 DEF_CMD("ampdu", 3, set80211ampdu), /* NB: tx+rx */ 4729 DEF_CMD("-ampdu", -3, set80211ampdu), 4730 DEF_CMD_ARG("ampdulimit", set80211ampdulimit), 4731 DEF_CMD_ARG("ampdudensity", set80211ampdudensity), 4732 DEF_CMD("amsdurx", 2, set80211amsdu), 4733 DEF_CMD("-amsdurx", -2, set80211amsdu), 4734 DEF_CMD("amsdutx", 1, set80211amsdu), 4735 DEF_CMD("-amsdutx", -1, set80211amsdu), 4736 DEF_CMD("amsdu", 3, set80211amsdu), /* NB: tx+rx */ 4737 DEF_CMD("-amsdu", -3, set80211amsdu), 4738 DEF_CMD_ARG("amsdulimit", set80211amsdulimit), 4739 DEF_CMD("puren", 1, set80211puren), 4740 DEF_CMD("-puren", 0, set80211puren), 4741 DEF_CMD("doth", 1, set80211doth), 4742 DEF_CMD("-doth", 0, set80211doth), 4743 DEF_CMD("dfs", 1, set80211dfs), 4744 DEF_CMD("-dfs", 0, set80211dfs), 4745 DEF_CMD("htcompat", 1, set80211htcompat), 4746 DEF_CMD("-htcompat", 0, set80211htcompat), 4747 DEF_CMD("dwds", 1, set80211dwds), 4748 DEF_CMD("-dwds", 0, set80211dwds), 4749 DEF_CMD("inact", 1, set80211inact), 4750 DEF_CMD("-inact", 0, set80211inact), 4751 DEF_CMD("tsn", 1, set80211tsn), 4752 DEF_CMD("-tsn", 0, set80211tsn), 4753 DEF_CMD_ARG("regdomain", set80211regdomain), 4754 DEF_CMD_ARG("country", set80211country), 4755 DEF_CMD("indoor", 'I', set80211location), 4756 DEF_CMD("-indoor", 'O', set80211location), 4757 DEF_CMD("outdoor", 'O', set80211location), 4758 DEF_CMD("-outdoor", 'I', set80211location), 4759 DEF_CMD("anywhere", ' ', set80211location), 4760 DEF_CMD("ecm", 1, set80211ecm), 4761 DEF_CMD("-ecm", 0, set80211ecm), 4762 DEF_CMD("dotd", 1, set80211dotd), 4763 DEF_CMD("-dotd", 0, set80211dotd), 4764 DEF_CMD_ARG("htprotmode", set80211htprotmode), 4765 DEF_CMD("ht20", 1, set80211htconf), 4766 DEF_CMD("-ht20", 0, set80211htconf), 4767 DEF_CMD("ht40", 3, set80211htconf), /* NB: 20+40 */ 4768 DEF_CMD("-ht40", 0, set80211htconf), 4769 DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */ 4770 DEF_CMD("-ht", 0, set80211htconf), 4771 DEF_CMD("rifs", 1, set80211rifs), 4772 DEF_CMD("-rifs", 0, set80211rifs), 4773 DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps), 4774 DEF_CMD("smpsdyn", IEEE80211_HTCAP_SMPS_DYNAMIC, set80211smps), 4775 DEF_CMD("-smps", IEEE80211_HTCAP_SMPS_OFF, set80211smps), 4776 /* XXX for testing */ 4777 DEF_CMD_ARG("chanswitch", set80211chanswitch), 4778 4779 DEF_CMD_ARG("tdmaslot", set80211tdmaslot), 4780 DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt), 4781 DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen), 4782 DEF_CMD_ARG("tdmabintval", set80211tdmabintval), 4783 4784 /* vap cloning support */ 4785 DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr), 4786 DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid), 4787 DEF_CLONE_CMD_ARG("wlandev", set80211clone_wlandev), 4788 DEF_CLONE_CMD_ARG("wlanmode", set80211clone_wlanmode), 4789 DEF_CLONE_CMD("beacons", 1, set80211clone_beacons), 4790 DEF_CLONE_CMD("-beacons", 0, set80211clone_beacons), 4791 DEF_CLONE_CMD("bssid", 1, set80211clone_bssid), 4792 DEF_CLONE_CMD("-bssid", 0, set80211clone_bssid), 4793 DEF_CLONE_CMD("wdslegacy", 1, set80211clone_wdslegacy), 4794 DEF_CLONE_CMD("-wdslegacy", 0, set80211clone_wdslegacy), 4795}; 4796static struct afswtch af_ieee80211 = { 4797 .af_name = "af_ieee80211", 4798 .af_af = AF_UNSPEC, 4799 .af_other_status = ieee80211_status, 4800}; 4801 4802static __constructor void 4803ieee80211_ctor(void) 4804{ 4805#define N(a) (sizeof(a) / sizeof(a[0])) 4806 int i; 4807 4808 for (i = 0; i < N(ieee80211_cmds); i++) 4809 cmd_register(&ieee80211_cmds[i]); 4810 af_register(&af_ieee80211); 4811#undef N 4812} 4813