ifieee80211.c revision 148553
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 148553 2005-07-30 03:28:41Z 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.h> 81#include <net80211/ieee80211_crypto.h> 82#include <net80211/ieee80211_ioctl.h> 83 84#include <ctype.h> 85#include <err.h> 86#include <errno.h> 87#include <fcntl.h> 88#include <inttypes.h> 89#include <stdio.h> 90#include <stdlib.h> 91#include <string.h> 92#include <unistd.h> 93 94#include "ifconfig.h" 95 96static void set80211(int s, int type, int val, int len, u_int8_t *data); 97static const char *get_string(const char *val, const char *sep, 98 u_int8_t *buf, int *lenp); 99static void print_string(const u_int8_t *buf, int len); 100 101static int 102isanyarg(const char *arg) 103{ 104 return (strcmp(arg, "-") == 0 || 105 strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0); 106} 107 108static void 109set80211ssid(const char *val, int d, int s, const struct afswtch *rafp) 110{ 111 int ssid; 112 int len; 113 u_int8_t data[33]; 114 115 ssid = 0; 116 len = strlen(val); 117 if (len > 2 && isdigit(val[0]) && val[1] == ':') { 118 ssid = atoi(val)-1; 119 val += 2; 120 } 121 122 bzero(data, sizeof(data)); 123 len = sizeof(data); 124 get_string(val, NULL, data, &len); 125 126 set80211(s, IEEE80211_IOC_SSID, ssid, len, data); 127} 128 129static void 130set80211stationname(const char *val, int d, int s, const struct afswtch *rafp) 131{ 132 int len; 133 u_int8_t data[33]; 134 135 bzero(data, sizeof(data)); 136 len = sizeof(data); 137 get_string(val, NULL, data, &len); 138 139 set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data); 140} 141 142/* 143 * Convert IEEE channel number to MHz frequency. 144 */ 145static u_int 146ieee80211_ieee2mhz(u_int chan) 147{ 148 if (chan == 14) 149 return 2484; 150 if (chan < 14) /* 0-13 */ 151 return 2407 + chan*5; 152 if (chan < 27) /* 15-26 */ 153 return 2512 + ((chan-15)*20); 154 return 5000 + (chan*5); 155} 156 157/* 158 * Convert MHz frequency to IEEE channel number. 159 */ 160static u_int 161ieee80211_mhz2ieee(u_int freq) 162{ 163 if (freq == 2484) 164 return 14; 165 if (freq < 2484) 166 return (freq - 2407) / 5; 167 if (freq < 5000) 168 return 15 + ((freq - 2512) / 20); 169 return (freq - 5000) / 5; 170} 171 172static void 173set80211channel(const char *val, int d, int s, const struct afswtch *rafp) 174{ 175 if (!isanyarg(val)) { 176 int v = atoi(val); 177 if (v > 255) /* treat as frequency */ 178 v = ieee80211_mhz2ieee(v); 179 set80211(s, IEEE80211_IOC_CHANNEL, v, 0, NULL); 180 } else 181 set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL); 182} 183 184static void 185set80211authmode(const char *val, int d, int s, const struct afswtch *rafp) 186{ 187 int mode; 188 189 if (strcasecmp(val, "none") == 0) { 190 mode = IEEE80211_AUTH_NONE; 191 } else if (strcasecmp(val, "open") == 0) { 192 mode = IEEE80211_AUTH_OPEN; 193 } else if (strcasecmp(val, "shared") == 0) { 194 mode = IEEE80211_AUTH_SHARED; 195 } else if (strcasecmp(val, "8021x") == 0) { 196 mode = IEEE80211_AUTH_8021X; 197 } else if (strcasecmp(val, "wpa") == 0) { 198 mode = IEEE80211_AUTH_WPA; 199 } else { 200 err(1, "unknown authmode"); 201 } 202 203 set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL); 204} 205 206static void 207set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp) 208{ 209 int mode; 210 211 if (strcasecmp(val, "off") == 0) { 212 mode = IEEE80211_POWERSAVE_OFF; 213 } else if (strcasecmp(val, "on") == 0) { 214 mode = IEEE80211_POWERSAVE_ON; 215 } else if (strcasecmp(val, "cam") == 0) { 216 mode = IEEE80211_POWERSAVE_CAM; 217 } else if (strcasecmp(val, "psp") == 0) { 218 mode = IEEE80211_POWERSAVE_PSP; 219 } else if (strcasecmp(val, "psp-cam") == 0) { 220 mode = IEEE80211_POWERSAVE_PSP_CAM; 221 } else { 222 err(1, "unknown powersavemode"); 223 } 224 225 set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL); 226} 227 228static void 229set80211powersave(const char *val, int d, int s, const struct afswtch *rafp) 230{ 231 if (d == 0) 232 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF, 233 0, NULL); 234 else 235 set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_ON, 236 0, NULL); 237} 238 239static void 240set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp) 241{ 242 set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL); 243} 244 245static void 246set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp) 247{ 248 int mode; 249 250 if (strcasecmp(val, "off") == 0) { 251 mode = IEEE80211_WEP_OFF; 252 } else if (strcasecmp(val, "on") == 0) { 253 mode = IEEE80211_WEP_ON; 254 } else if (strcasecmp(val, "mixed") == 0) { 255 mode = IEEE80211_WEP_MIXED; 256 } else { 257 err(1, "unknown wep mode"); 258 } 259 260 set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL); 261} 262 263static void 264set80211wep(const char *val, int d, int s, const struct afswtch *rafp) 265{ 266 set80211(s, IEEE80211_IOC_WEP, d, 0, NULL); 267} 268 269static int 270isundefarg(const char *arg) 271{ 272 return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0); 273} 274 275static void 276set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp) 277{ 278 if (isundefarg(val)) 279 set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL); 280 else 281 set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL); 282} 283 284static void 285set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp) 286{ 287 int key = 0; 288 int len; 289 u_int8_t data[IEEE80211_KEYBUF_SIZE]; 290 291 if (isdigit(val[0]) && val[1] == ':') { 292 key = atoi(val)-1; 293 val += 2; 294 } 295 296 bzero(data, sizeof(data)); 297 len = sizeof(data); 298 get_string(val, NULL, data, &len); 299 300 set80211(s, IEEE80211_IOC_WEPKEY, key, len, data); 301} 302 303/* 304 * This function is purly a NetBSD compatability interface. The NetBSD 305 * iterface is too inflexable, but it's there so we'll support it since 306 * it's not all that hard. 307 */ 308static void 309set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp) 310{ 311 int txkey; 312 int i, len; 313 u_int8_t data[IEEE80211_KEYBUF_SIZE]; 314 315 set80211(s, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL); 316 317 if (isdigit(val[0]) && val[1] == ':') { 318 txkey = val[0]-'0'-1; 319 val += 2; 320 321 for (i = 0; i < 4; i++) { 322 bzero(data, sizeof(data)); 323 len = sizeof(data); 324 val = get_string(val, ",", data, &len); 325 326 set80211(s, IEEE80211_IOC_WEPKEY, i, len, data); 327 } 328 } else { 329 bzero(data, sizeof(data)); 330 len = sizeof(data); 331 get_string(val, NULL, data, &len); 332 txkey = 0; 333 334 set80211(s, IEEE80211_IOC_WEPKEY, 0, len, data); 335 336 bzero(data, sizeof(data)); 337 for (i = 1; i < 4; i++) 338 set80211(s, IEEE80211_IOC_WEPKEY, i, 0, data); 339 } 340 341 set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL); 342} 343 344static void 345set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp) 346{ 347 set80211(s, IEEE80211_IOC_RTSTHRESHOLD, 348 isundefarg(val) ? IEEE80211_RTS_MAX : atoi(val), 0, NULL); 349} 350 351static void 352set80211protmode(const char *val, int d, int s, const struct afswtch *rafp) 353{ 354 int mode; 355 356 if (strcasecmp(val, "off") == 0) { 357 mode = IEEE80211_PROTMODE_OFF; 358 } else if (strcasecmp(val, "cts") == 0) { 359 mode = IEEE80211_PROTMODE_CTS; 360 } else if (strcasecmp(val, "rtscts") == 0) { 361 mode = IEEE80211_PROTMODE_RTSCTS; 362 } else { 363 err(1, "unknown protection mode"); 364 } 365 366 set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL); 367} 368 369static void 370set80211txpower(const char *val, int d, int s, const struct afswtch *rafp) 371{ 372 set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL); 373} 374 375#define IEEE80211_ROAMING_DEVICE 0 376#define IEEE80211_ROAMING_AUTO 1 377#define IEEE80211_ROAMING_MANUAL 2 378 379static void 380set80211roaming(const char *val, int d, int s, const struct afswtch *rafp) 381{ 382 int mode; 383 384 if (strcasecmp(val, "device") == 0) { 385 mode = IEEE80211_ROAMING_DEVICE; 386 } else if (strcasecmp(val, "auto") == 0) { 387 mode = IEEE80211_ROAMING_AUTO; 388 } else if (strcasecmp(val, "manual") == 0) { 389 mode = IEEE80211_ROAMING_MANUAL; 390 } else { 391 err(1, "unknown roaming mode"); 392 } 393 set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL); 394} 395 396static void 397set80211wme(const char *val, int d, int s, const struct afswtch *rafp) 398{ 399 set80211(s, IEEE80211_IOC_WME, d, 0, NULL); 400} 401 402static void 403set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp) 404{ 405 set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL); 406} 407 408static void 409set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp) 410{ 411 set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL); 412} 413 414static void 415set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp) 416{ 417 struct ieee80211req_chanlist chanlist; 418#define MAXCHAN (sizeof(chanlist.ic_channels)*NBBY) 419 char *temp, *cp, *tp; 420 421 temp = malloc(strlen(val) + 1); 422 if (temp == NULL) 423 errx(1, "malloc failed"); 424 strcpy(temp, val); 425 memset(&chanlist, 0, sizeof(chanlist)); 426 cp = temp; 427 for (;;) { 428 int first, last, f; 429 430 tp = strchr(cp, ','); 431 if (tp != NULL) 432 *tp++ = '\0'; 433 switch (sscanf(cp, "%u-%u", &first, &last)) { 434 case 1: 435 if (first > MAXCHAN) 436 errx(-1, "channel %u out of range, max %zu", 437 first, MAXCHAN); 438 setbit(chanlist.ic_channels, first); 439 break; 440 case 2: 441 if (first > MAXCHAN) 442 errx(-1, "channel %u out of range, max %zu", 443 first, MAXCHAN); 444 if (last > MAXCHAN) 445 errx(-1, "channel %u out of range, max %zu", 446 last, MAXCHAN); 447 if (first > last) 448 errx(-1, "void channel range, %u > %u", 449 first, last); 450 for (f = first; f <= last; f++) 451 setbit(chanlist.ic_channels, f); 452 break; 453 } 454 if (tp == NULL) 455 break; 456 while (isspace(*tp)) 457 tp++; 458 if (!isdigit(*tp)) 459 break; 460 cp = tp; 461 } 462 set80211(s, IEEE80211_IOC_CHANLIST, 0, 463 sizeof(chanlist), (uint8_t *) &chanlist); 464#undef MAXCHAN 465} 466 467static void 468set80211bssid(const char *val, int d, int s, const struct afswtch *rafp) 469{ 470 471 if (!isanyarg(val)) { 472 char *temp; 473 struct sockaddr_dl sdl; 474 475 temp = malloc(strlen(val) + 1); 476 if (temp == NULL) 477 errx(1, "malloc failed"); 478 temp[0] = ':'; 479 strcpy(temp + 1, val); 480 sdl.sdl_len = sizeof(sdl); 481 link_addr(temp, &sdl); 482 free(temp); 483 if (sdl.sdl_alen != IEEE80211_ADDR_LEN) 484 errx(1, "malformed link-level address"); 485 set80211(s, IEEE80211_IOC_BSSID, 0, 486 IEEE80211_ADDR_LEN, LLADDR(&sdl)); 487 } else { 488 uint8_t zerobssid[IEEE80211_ADDR_LEN]; 489 memset(zerobssid, 0, sizeof(zerobssid)); 490 set80211(s, IEEE80211_IOC_BSSID, 0, 491 IEEE80211_ADDR_LEN, zerobssid); 492 } 493} 494 495static int 496getac(const char *ac) 497{ 498 if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0) 499 return WME_AC_BE; 500 if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0) 501 return WME_AC_BK; 502 if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0) 503 return WME_AC_VI; 504 if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0) 505 return WME_AC_VO; 506 errx(1, "unknown wme access class %s", ac); 507} 508 509static 510DECL_CMD_FUNC2(set80211cwmin, ac, val) 511{ 512 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL); 513} 514 515static 516DECL_CMD_FUNC2(set80211cwmax, ac, val) 517{ 518 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL); 519} 520 521static 522DECL_CMD_FUNC2(set80211aifs, ac, val) 523{ 524 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL); 525} 526 527static 528DECL_CMD_FUNC2(set80211txoplimit, ac, val) 529{ 530 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL); 531} 532 533static 534DECL_CMD_FUNC(set80211acm, val, d) 535{ 536 set80211(s, IEEE80211_IOC_WME_ACM, d, WME_AC_BE, NULL); 537} 538 539static 540DECL_CMD_FUNC(set80211ackpolicy, val, d) 541{ 542 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, d, WME_AC_BE, NULL); 543} 544 545static 546DECL_CMD_FUNC2(set80211bsscwmin, ac, val) 547{ 548 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), 549 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 550} 551 552static 553DECL_CMD_FUNC2(set80211bsscwmax, ac, val) 554{ 555 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), 556 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 557} 558 559static 560DECL_CMD_FUNC2(set80211bssaifs, ac, val) 561{ 562 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), 563 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 564} 565 566static 567DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val) 568{ 569 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), 570 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 571} 572 573static 574DECL_CMD_FUNC(set80211dtimperiod, val, d) 575{ 576 set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL); 577} 578 579static 580DECL_CMD_FUNC(set80211bintval, val, d) 581{ 582 set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL); 583} 584 585static void 586set80211macmac(int s, int op, const char *val) 587{ 588 char *temp; 589 struct sockaddr_dl sdl; 590 591 temp = malloc(strlen(val) + 1); 592 if (temp == NULL) 593 errx(1, "malloc failed"); 594 temp[0] = ':'; 595 strcpy(temp + 1, val); 596 sdl.sdl_len = sizeof(sdl); 597 link_addr(temp, &sdl); 598 free(temp); 599 if (sdl.sdl_alen != IEEE80211_ADDR_LEN) 600 errx(1, "malformed link-level address"); 601 set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl)); 602} 603 604static 605DECL_CMD_FUNC(set80211addmac, val, d) 606{ 607 set80211macmac(s, IEEE80211_IOC_ADDMAC, val); 608} 609 610static 611DECL_CMD_FUNC(set80211delmac, val, d) 612{ 613 set80211macmac(s, IEEE80211_IOC_DELMAC, val); 614} 615 616static 617DECL_CMD_FUNC(set80211maccmd, val, d) 618{ 619 set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL); 620} 621 622static void 623set80211pureg(const char *val, int d, int s, const struct afswtch *rafp) 624{ 625 set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL); 626} 627 628static 629DECL_CMD_FUNC(set80211fragthreshold, val, d) 630{ 631 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD, 632 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL); 633} 634 635static 636DECL_CMD_FUNC(set80211fragthreshold, val, d) 637{ 638 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD, 639 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL); 640} 641 642static int 643getmaxrate(uint8_t rates[15], uint8_t nrates) 644{ 645 int i, maxrate = -1; 646 647 for (i = 0; i < nrates; i++) { 648 int rate = rates[i] & IEEE80211_RATE_VAL; 649 if (rate > maxrate) 650 maxrate = rate; 651 } 652 return maxrate / 2; 653} 654 655static const char * 656getcaps(int capinfo) 657{ 658 static char capstring[32]; 659 char *cp = capstring; 660 661 if (capinfo & IEEE80211_CAPINFO_ESS) 662 *cp++ = 'E'; 663 if (capinfo & IEEE80211_CAPINFO_IBSS) 664 *cp++ = 'I'; 665 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE) 666 *cp++ = 'c'; 667 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ) 668 *cp++ = 'C'; 669 if (capinfo & IEEE80211_CAPINFO_PRIVACY) 670 *cp++ = 'P'; 671 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) 672 *cp++ = 'S'; 673 if (capinfo & IEEE80211_CAPINFO_PBCC) 674 *cp++ = 'B'; 675 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY) 676 *cp++ = 'A'; 677 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 678 *cp++ = 's'; 679 if (capinfo & IEEE80211_CAPINFO_RSN) 680 *cp++ = 'R'; 681 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM) 682 *cp++ = 'D'; 683 *cp = '\0'; 684 return capstring; 685} 686 687static void 688printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen) 689{ 690 printf("%s", tag); 691 if (verbose) { 692 maxlen -= strlen(tag)+2; 693 if (2*ielen > maxlen) 694 maxlen--; 695 printf("<"); 696 for (; ielen > 0; ie++, ielen--) { 697 if (maxlen-- <= 0) 698 break; 699 printf("%02x", *ie); 700 } 701 if (ielen != 0) 702 printf("-"); 703 printf(">"); 704 } 705} 706 707/* 708 * Copy the ssid string contents into buf, truncating to fit. If the 709 * ssid is entirely printable then just copy intact. Otherwise convert 710 * to hexadecimal. If the result is truncated then replace the last 711 * three characters with "...". 712 */ 713static int 714copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len) 715{ 716 const u_int8_t *p; 717 size_t maxlen; 718 int i; 719 720 if (essid_len > bufsize) 721 maxlen = bufsize; 722 else 723 maxlen = essid_len; 724 /* determine printable or not */ 725 for (i = 0, p = essid; i < maxlen; i++, p++) { 726 if (*p < ' ' || *p > 0x7e) 727 break; 728 } 729 if (i != maxlen) { /* not printable, print as hex */ 730 if (bufsize < 3) 731 return 0; 732 strlcpy(buf, "0x", bufsize); 733 bufsize -= 2; 734 p = essid; 735 for (i = 0; i < maxlen && bufsize >= 2; i++) { 736 sprintf(&buf[2+2*i], "%02x", p[i]); 737 bufsize -= 2; 738 } 739 if (i != essid_len) 740 memcpy(&buf[2+2*i-3], "...", 3); 741 } else { /* printable, truncate as needed */ 742 memcpy(buf, essid, maxlen); 743 if (maxlen != essid_len) 744 memcpy(&buf[maxlen-3], "...", 3); 745 } 746 return maxlen; 747} 748 749/* unalligned little endian access */ 750#define LE_READ_4(p) \ 751 ((u_int32_t) \ 752 ((((const u_int8_t *)(p))[0] ) | \ 753 (((const u_int8_t *)(p))[1] << 8) | \ 754 (((const u_int8_t *)(p))[2] << 16) | \ 755 (((const u_int8_t *)(p))[3] << 24))) 756 757static int __inline 758iswpaoui(const u_int8_t *frm) 759{ 760 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); 761} 762 763static int __inline 764iswmeoui(const u_int8_t *frm) 765{ 766 return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); 767} 768 769static int __inline 770isatherosoui(const u_int8_t *frm) 771{ 772 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); 773} 774 775static void 776printies(const u_int8_t *vp, int ielen, int maxcols) 777{ 778 while (ielen > 0) { 779 switch (vp[0]) { 780 case IEEE80211_ELEMID_VENDOR: 781 if (iswpaoui(vp)) 782 printie(" WPA", vp, 2+vp[1], maxcols); 783 else if (iswmeoui(vp)) 784 printie(" WME", vp, 2+vp[1], maxcols); 785 else if (isatherosoui(vp)) 786 printie(" ATH", vp, 2+vp[1], maxcols); 787 else 788 printie(" VEN", vp, 2+vp[1], maxcols); 789 break; 790 case IEEE80211_ELEMID_RSN: 791 printie(" RSN", vp, 2+vp[1], maxcols); 792 break; 793 default: 794 printie(" ???", vp, 2+vp[1], maxcols); 795 break; 796 } 797 ielen -= 2+vp[1]; 798 vp += 2+vp[1]; 799 } 800} 801 802static void 803list_scan(int s) 804{ 805 uint8_t buf[24*1024]; 806 struct ieee80211req ireq; 807 char ssid[14]; 808 uint8_t *cp; 809 int len; 810 811 (void) memset(&ireq, 0, sizeof(ireq)); 812 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 813 ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; 814 ireq.i_data = buf; 815 ireq.i_len = sizeof(buf); 816 if (ioctl(s, SIOCG80211, &ireq) < 0) 817 errx(1, "unable to get scan results"); 818 len = ireq.i_len; 819 if (len < sizeof(struct ieee80211req_scan_result)) 820 return; 821 822 printf("%-14.14s %-17.17s %4s %4s %-5s %3s %4s\n" 823 , "SSID" 824 , "BSSID" 825 , "CHAN" 826 , "RATE" 827 , "S:N" 828 , "INT" 829 , "CAPS" 830 ); 831 cp = buf; 832 do { 833 struct ieee80211req_scan_result *sr; 834 uint8_t *vp; 835 836 sr = (struct ieee80211req_scan_result *) cp; 837 vp = (u_int8_t *)(sr+1); 838 printf("%-14.*s %s %3d %3dM %2d:%-2d %3d %-4.4s" 839 , copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len) 840 , ssid 841 , ether_ntoa((const struct ether_addr *) sr->isr_bssid) 842 , ieee80211_mhz2ieee(sr->isr_freq) 843 , getmaxrate(sr->isr_rates, sr->isr_nrates) 844 , sr->isr_rssi, sr->isr_noise 845 , sr->isr_intval 846 , getcaps(sr->isr_capinfo) 847 ); 848 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);; 849 printf("\n"); 850 cp += sr->isr_len, len -= sr->isr_len; 851 } while (len >= sizeof(struct ieee80211req_scan_result)); 852} 853 854#include <net80211/ieee80211_freebsd.h> 855 856static void 857scan_and_wait(int s) 858{ 859 struct ieee80211req ireq; 860 int sroute; 861 862 sroute = socket(PF_ROUTE, SOCK_RAW, 0); 863 if (sroute < 0) { 864 perror("socket(PF_ROUTE,SOCK_RAW)"); 865 return; 866 } 867 (void) memset(&ireq, 0, sizeof(ireq)); 868 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 869 ireq.i_type = IEEE80211_IOC_SCAN_REQ; 870 /* NB: only root can trigger a scan so ignore errors */ 871 if (ioctl(s, SIOCS80211, &ireq) >= 0) { 872 char buf[2048]; 873 struct if_announcemsghdr *ifan; 874 struct rt_msghdr *rtm; 875 876 do { 877 if (read(sroute, buf, sizeof(buf)) < 0) { 878 perror("read(PF_ROUTE)"); 879 break; 880 } 881 rtm = (struct rt_msghdr *) buf; 882 if (rtm->rtm_version != RTM_VERSION) 883 break; 884 ifan = (struct if_announcemsghdr *) rtm; 885 } while (rtm->rtm_type != RTM_IEEE80211 || 886 ifan->ifan_what != RTM_IEEE80211_SCAN); 887 } 888 close(sroute); 889} 890 891static 892DECL_CMD_FUNC(set80211scan, val, d) 893{ 894 scan_and_wait(s); 895 list_scan(s); 896} 897 898static void 899list_stations(int s) 900{ 901 uint8_t buf[24*1024]; 902 struct ieee80211req ireq; 903 uint8_t *cp; 904 int len; 905 906 (void) memset(&ireq, 0, sizeof(ireq)); 907 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 908 ireq.i_type = IEEE80211_IOC_STA_INFO; 909 ireq.i_data = buf; 910 ireq.i_len = sizeof(buf); 911 if (ioctl(s, SIOCG80211, &ireq) < 0) 912 errx(1, "unable to get station information"); 913 len = ireq.i_len; 914 if (len < sizeof(struct ieee80211req_sta_info)) 915 return; 916 917 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n" 918 , "ADDR" 919 , "AID" 920 , "CHAN" 921 , "RATE" 922 , "RSSI" 923 , "IDLE" 924 , "TXSEQ" 925 , "RXSEQ" 926 , "CAPS" 927 , "ERP" 928 ); 929 cp = buf; 930 do { 931 struct ieee80211req_sta_info *si; 932 uint8_t *vp; 933 934 si = (struct ieee80211req_sta_info *) cp; 935 vp = (u_int8_t *)(si+1); 936 printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x" 937 , ether_ntoa((const struct ether_addr*) si->isi_macaddr) 938 , IEEE80211_AID(si->isi_associd) 939 , ieee80211_mhz2ieee(si->isi_freq) 940 , (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2 941 , si->isi_rssi 942 , si->isi_inact 943 , si->isi_txseqs[0] 944 , si->isi_rxseqs[0] 945 , getcaps(si->isi_capinfo) 946 , si->isi_erp 947 ); 948 printies(vp, si->isi_ie_len, 24); 949 printf("\n"); 950 cp += si->isi_len, len -= si->isi_len; 951 } while (len >= sizeof(struct ieee80211req_sta_info)); 952} 953 954static void 955print_chaninfo(const struct ieee80211_channel *c) 956{ 957#define IEEE80211_IS_CHAN_PASSIVE(_c) \ 958 (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE)) 959 char buf[14]; 960 961 buf[0] = '\0'; 962 if (IEEE80211_IS_CHAN_FHSS(c)) 963 strlcat(buf, " FHSS", sizeof(buf)); 964 if (IEEE80211_IS_CHAN_A(c)) 965 strlcat(buf, " 11a", sizeof(buf)); 966 /* XXX 11g schizophrenia */ 967 if (IEEE80211_IS_CHAN_G(c) || 968 IEEE80211_IS_CHAN_PUREG(c)) 969 strlcat(buf, " 11g", sizeof(buf)); 970 else if (IEEE80211_IS_CHAN_B(c)) 971 strlcat(buf, " 11b", sizeof(buf)); 972 if (IEEE80211_IS_CHAN_T(c)) 973 strlcat(buf, " Turbo", sizeof(buf)); 974 printf("Channel %3u : %u%c Mhz%-14.14s", 975 ieee80211_mhz2ieee(c->ic_freq), c->ic_freq, 976 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf); 977#undef IEEE80211_IS_CHAN_PASSIVE 978} 979 980static void 981list_channels(int s, int allchans) 982{ 983 struct ieee80211req ireq; 984 struct ieee80211req_chaninfo chans; 985 struct ieee80211req_chaninfo achans; 986 const struct ieee80211_channel *c; 987 int i, half; 988 989 (void) memset(&ireq, 0, sizeof(ireq)); 990 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 991 ireq.i_type = IEEE80211_IOC_CHANINFO; 992 ireq.i_data = &chans; 993 ireq.i_len = sizeof(chans); 994 if (ioctl(s, SIOCG80211, &ireq) < 0) 995 errx(1, "unable to get channel information"); 996 if (!allchans) { 997 struct ieee80211req_chanlist active; 998 999 ireq.i_type = IEEE80211_IOC_CHANLIST; 1000 ireq.i_data = &active; 1001 ireq.i_len = sizeof(active); 1002 if (ioctl(s, SIOCG80211, &ireq) < 0) 1003 errx(1, "unable to get active channel list"); 1004 memset(&achans, 0, sizeof(achans)); 1005 for (i = 0; i < chans.ic_nchans; i++) { 1006 c = &chans.ic_chans[i]; 1007 if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans) 1008 achans.ic_chans[achans.ic_nchans++] = *c; 1009 } 1010 } else 1011 achans = chans; 1012 half = achans.ic_nchans / 2; 1013 if (achans.ic_nchans % 2) 1014 half++; 1015 for (i = 0; i < achans.ic_nchans / 2; i++) { 1016 print_chaninfo(&achans.ic_chans[i]); 1017 print_chaninfo(&achans.ic_chans[half+i]); 1018 printf("\n"); 1019 } 1020 if (achans.ic_nchans % 2) { 1021 print_chaninfo(&achans.ic_chans[i]); 1022 printf("\n"); 1023 } 1024} 1025 1026static void 1027list_keys(int s) 1028{ 1029} 1030 1031#define IEEE80211_C_BITS \ 1032"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \ 1033"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \ 1034"\31WPA2\32BURST\33WME" 1035 1036static void 1037list_capabilities(int s) 1038{ 1039 struct ieee80211req ireq; 1040 u_int32_t caps; 1041 1042 (void) memset(&ireq, 0, sizeof(ireq)); 1043 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1044 ireq.i_type = IEEE80211_IOC_DRIVER_CAPS; 1045 if (ioctl(s, SIOCG80211, &ireq) < 0) 1046 errx(1, "unable to get driver capabilities"); 1047 caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len); 1048 printb(name, caps, IEEE80211_C_BITS); 1049 putchar('\n'); 1050} 1051 1052static void 1053list_wme(int s) 1054{ 1055 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; 1056 struct ieee80211req ireq; 1057 int ac; 1058 1059 (void) memset(&ireq, 0, sizeof(ireq)); 1060 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1061 ireq.i_len = 0; 1062 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { 1063again: 1064 if (ireq.i_len & IEEE80211_WMEPARAM_BSS) 1065 printf("\t%s", " "); 1066 else 1067 printf("\t%s", acnames[ac]); 1068 1069 ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac; 1070 1071 /* show WME BSS parameters */ 1072 ireq.i_type = IEEE80211_IOC_WME_CWMIN; 1073 if (ioctl(s, SIOCG80211, &ireq) != -1) 1074 printf(" cwmin %2u", ireq.i_val); 1075 ireq.i_type = IEEE80211_IOC_WME_CWMAX; 1076 if (ioctl(s, SIOCG80211, &ireq) != -1) 1077 printf(" cwmax %2u", ireq.i_val); 1078 ireq.i_type = IEEE80211_IOC_WME_AIFS; 1079 if (ioctl(s, SIOCG80211, &ireq) != -1) 1080 printf(" aifs %2u", ireq.i_val); 1081 ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT; 1082 if (ioctl(s, SIOCG80211, &ireq) != -1) 1083 printf(" txopLimit %3u", ireq.i_val); 1084 ireq.i_type = IEEE80211_IOC_WME_ACM; 1085 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1086 if (ireq.i_val) 1087 printf(" acm"); 1088 else if (verbose) 1089 printf(" -acm"); 1090 } 1091 /* !BSS only */ 1092 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { 1093 ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY; 1094 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1095 if (!ireq.i_val) 1096 printf(" -ack"); 1097 else if (verbose) 1098 printf(" ack"); 1099 } 1100 } 1101 printf("\n"); 1102 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { 1103 ireq.i_len |= IEEE80211_WMEPARAM_BSS; 1104 goto again; 1105 } else 1106 ireq.i_len &= ~IEEE80211_WMEPARAM_BSS; 1107 } 1108} 1109 1110static 1111DECL_CMD_FUNC(set80211list, arg, d) 1112{ 1113#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 1114 1115 if (iseq(arg, "sta")) 1116 list_stations(s); 1117 else if (iseq(arg, "scan") || iseq(arg, "ap")) 1118 list_scan(s); 1119 else if (iseq(arg, "chan") || iseq(arg, "freq")) 1120 list_channels(s, 1); 1121 else if (iseq(arg, "active")) 1122 list_channels(s, 0); 1123 else if (iseq(arg, "keys")) 1124 list_keys(s); 1125 else if (iseq(arg, "caps")) 1126 list_capabilities(s); 1127 else if (iseq(arg, "wme")) 1128 list_wme(s); 1129 else 1130 errx(1, "Don't know how to list %s for %s", arg, name); 1131#undef iseq 1132} 1133 1134static enum ieee80211_opmode 1135get80211opmode(int s) 1136{ 1137 struct ifmediareq ifmr; 1138 1139 (void) memset(&ifmr, 0, sizeof(ifmr)); 1140 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); 1141 1142 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { 1143 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) 1144 return IEEE80211_M_IBSS; /* XXX ahdemo */ 1145 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) 1146 return IEEE80211_M_HOSTAP; 1147 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) 1148 return IEEE80211_M_MONITOR; 1149 } 1150 return IEEE80211_M_STA; 1151} 1152 1153static const struct ieee80211_channel * 1154getchaninfo(int s, int chan) 1155{ 1156 struct ieee80211req ireq; 1157 static struct ieee80211req_chaninfo chans; 1158 static struct ieee80211_channel undef; 1159 const struct ieee80211_channel *c; 1160 int i, freq; 1161 1162 (void) memset(&ireq, 0, sizeof(ireq)); 1163 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1164 ireq.i_type = IEEE80211_IOC_CHANINFO; 1165 ireq.i_data = &chans; 1166 ireq.i_len = sizeof(chans); 1167 if (ioctl(s, SIOCG80211, &ireq) < 0) 1168 errx(1, "unable to get channel information"); 1169 freq = ieee80211_ieee2mhz(chan); 1170 for (i = 0; i < chans.ic_nchans; i++) { 1171 c = &chans.ic_chans[i]; 1172 if (c->ic_freq == freq) 1173 return c; 1174 } 1175 return &undef; 1176} 1177 1178#if 0 1179static void 1180printcipher(int s, struct ieee80211req *ireq, int keylenop) 1181{ 1182 switch (ireq->i_val) { 1183 case IEEE80211_CIPHER_WEP: 1184 ireq->i_type = keylenop; 1185 if (ioctl(s, SIOCG80211, ireq) != -1) 1186 printf("WEP-%s", 1187 ireq->i_len <= 5 ? "40" : 1188 ireq->i_len <= 13 ? "104" : "128"); 1189 else 1190 printf("WEP"); 1191 break; 1192 case IEEE80211_CIPHER_TKIP: 1193 printf("TKIP"); 1194 break; 1195 case IEEE80211_CIPHER_AES_OCB: 1196 printf("AES-OCB"); 1197 break; 1198 case IEEE80211_CIPHER_AES_CCM: 1199 printf("AES-CCM"); 1200 break; 1201 case IEEE80211_CIPHER_CKIP: 1202 printf("CKIP"); 1203 break; 1204 case IEEE80211_CIPHER_NONE: 1205 printf("NONE"); 1206 break; 1207 default: 1208 printf("UNKNOWN (0x%x)", ireq->i_val); 1209 break; 1210 } 1211} 1212#endif 1213 1214#define MAXCOL 78 1215int col; 1216char spacer; 1217 1218#define LINE_BREAK() do { \ 1219 if (spacer != '\t') { \ 1220 printf("\n"); \ 1221 spacer = '\t'; \ 1222 } \ 1223 col = 8; /* 8-col tab */ \ 1224} while (0) 1225#define LINE_CHECK(fmt, ...) do { \ 1226 col += sizeof(fmt)-2; \ 1227 if (col > MAXCOL) { \ 1228 LINE_BREAK(); \ 1229 col += sizeof(fmt)-2; \ 1230 } \ 1231 printf(fmt, __VA_ARGS__); \ 1232 spacer = ' '; \ 1233} while (0) 1234 1235static void 1236printkey(const struct ieee80211req_key *ik) 1237{ 1238 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; 1239 int keylen = ik->ik_keylen; 1240 int printcontents; 1241 1242 printcontents = printkeys && 1243 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); 1244 if (printcontents) 1245 LINE_BREAK(); 1246 switch (ik->ik_type) { 1247 case IEEE80211_CIPHER_WEP: 1248 /* compatibility */ 1249 LINE_CHECK("%cwepkey %u:%s", spacer, ik->ik_keyix+1, 1250 keylen <= 5 ? "40-bit" : 1251 keylen <= 13 ? "104-bit" : "128-bit"); 1252 break; 1253 case IEEE80211_CIPHER_TKIP: 1254 if (keylen > 128/8) 1255 keylen -= 128/8; /* ignore MIC for now */ 1256 LINE_CHECK("%cTKIP %u:%u-bit", 1257 spacer, ik->ik_keyix+1, 8*keylen); 1258 break; 1259 case IEEE80211_CIPHER_AES_OCB: 1260 LINE_CHECK("%cAES-OCB %u:%u-bit", 1261 spacer, ik->ik_keyix+1, 8*keylen); 1262 break; 1263 case IEEE80211_CIPHER_AES_CCM: 1264 LINE_CHECK("%cAES-CCM %u:%u-bit", 1265 spacer, ik->ik_keyix+1, 8*keylen); 1266 break; 1267 case IEEE80211_CIPHER_CKIP: 1268 LINE_CHECK("%cCKIP %u:%u-bit", 1269 spacer, ik->ik_keyix+1, 8*keylen); 1270 break; 1271 case IEEE80211_CIPHER_NONE: 1272 LINE_CHECK("%cNULL %u:%u-bit", 1273 spacer, ik->ik_keyix+1, 8*keylen); 1274 break; 1275 default: 1276 LINE_CHECK("%cUNKNOWN (0x%x) %u:%u-bit", spacer, 1277 ik->ik_type, ik->ik_keyix+1, 8*keylen); 1278 break; 1279 } 1280 if (printcontents) { 1281 int i; 1282 1283 printf(" <"); 1284 for (i = 0; i < keylen; i++) 1285 printf("%02x", ik->ik_keydata[i]); 1286 printf(">"); 1287 if (ik->ik_type != IEEE80211_CIPHER_WEP && 1288 (ik->ik_keyrsc != 0 || verbose)) 1289 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc); 1290 if (ik->ik_type != IEEE80211_CIPHER_WEP && 1291 (ik->ik_keytsc != 0 || verbose)) 1292 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc); 1293 if (ik->ik_flags != 0 && verbose) { 1294 const char *sep = " "; 1295 1296 if (ik->ik_flags & IEEE80211_KEY_XMIT) 1297 printf("%stx", sep), sep = "+"; 1298 if (ik->ik_flags & IEEE80211_KEY_RECV) 1299 printf("%srx", sep), sep = "+"; 1300 if (ik->ik_flags & IEEE80211_KEY_DEFAULT) 1301 printf("%sdef", sep), sep = "+"; 1302 } 1303 LINE_BREAK(); 1304 } 1305} 1306 1307static void 1308ieee80211_status(int s) 1309{ 1310 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 1311 enum ieee80211_opmode opmode = get80211opmode(s); 1312 int i, num, wpa, wme; 1313 struct ieee80211req ireq; 1314 u_int8_t data[32]; 1315 const struct ieee80211_channel *c; 1316 1317 (void) memset(&ireq, 0, sizeof(ireq)); 1318 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1319 ireq.i_data = &data; 1320 1321 wpa = 0; /* unknown/not set */ 1322 1323 ireq.i_type = IEEE80211_IOC_SSID; 1324 ireq.i_val = -1; 1325 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1326 /* If we can't get the SSID, the this isn't an 802.11 device. */ 1327 return; 1328 } 1329 num = 0; 1330 ireq.i_type = IEEE80211_IOC_NUMSSIDS; 1331 if (ioctl(s, SIOCG80211, &ireq) >= 0) 1332 num = ireq.i_val; 1333 printf("\tssid "); 1334 if (num > 1) { 1335 ireq.i_type = IEEE80211_IOC_SSID; 1336 for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) { 1337 if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) { 1338 printf(" %d:", ireq.i_val + 1); 1339 print_string(data, ireq.i_len); 1340 } 1341 } 1342 } else 1343 print_string(data, ireq.i_len); 1344 1345 ireq.i_type = IEEE80211_IOC_CHANNEL; 1346 if (ioctl(s, SIOCG80211, &ireq) < 0) 1347 goto end; 1348 c = getchaninfo(s, ireq.i_val); 1349 if (ireq.i_val != -1) { 1350 printf(" channel %d", ireq.i_val); 1351 if (verbose) 1352 printf(" (%u)", c->ic_freq); 1353 } else if (verbose) 1354 printf(" channel UNDEF"); 1355 1356 ireq.i_type = IEEE80211_IOC_BSSID; 1357 ireq.i_len = IEEE80211_ADDR_LEN; 1358 if (ioctl(s, SIOCG80211, &ireq) >= 0 && 1359 memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0) 1360 printf(" bssid %s", ether_ntoa(ireq.i_data)); 1361 1362 ireq.i_type = IEEE80211_IOC_STATIONNAME; 1363 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1364 printf("\n\tstationname "); 1365 print_string(data, ireq.i_len); 1366 } 1367 1368 spacer = ' '; /* force first break */ 1369 LINE_BREAK(); 1370 1371 ireq.i_type = IEEE80211_IOC_AUTHMODE; 1372 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1373 switch (ireq.i_val) { 1374 case IEEE80211_AUTH_NONE: 1375 LINE_CHECK("%cauthmode NONE", spacer); 1376 break; 1377 case IEEE80211_AUTH_OPEN: 1378 LINE_CHECK("%cauthmode OPEN", spacer); 1379 break; 1380 case IEEE80211_AUTH_SHARED: 1381 LINE_CHECK("%cauthmode SHARED", spacer); 1382 break; 1383 case IEEE80211_AUTH_8021X: 1384 LINE_CHECK("%cauthmode 802.1x", spacer); 1385 break; 1386 case IEEE80211_AUTH_WPA: 1387 ireq.i_type = IEEE80211_IOC_WPA; 1388 if (ioctl(s, SIOCG80211, &ireq) != -1) 1389 wpa = ireq.i_val; 1390 if (!wpa) 1391 wpa = 1; /* default to WPA1 */ 1392 switch (wpa) { 1393 case 2: 1394 LINE_CHECK("%cauthmode WPA2/802.11i", 1395 spacer); 1396 break; 1397 case 3: 1398 LINE_CHECK("%cauthmode WPA1+WPA2/802.11i", 1399 spacer); 1400 break; 1401 default: 1402 LINE_CHECK("%cauthmode WPA", spacer); 1403 break; 1404 } 1405 break; 1406 case IEEE80211_AUTH_AUTO: 1407 LINE_CHECK("%cauthmode AUTO", spacer); 1408 break; 1409 default: 1410 LINE_CHECK("%cauthmode UNKNOWN (0x%x)", 1411 spacer, ireq.i_val); 1412 break; 1413 } 1414 } 1415 1416 ireq.i_type = IEEE80211_IOC_WEP; 1417 if (ioctl(s, SIOCG80211, &ireq) != -1 && 1418 ireq.i_val != IEEE80211_WEP_NOSUP) { 1419 int firstkey, wepmode; 1420 1421 wepmode = ireq.i_val; 1422 switch (wepmode) { 1423 case IEEE80211_WEP_OFF: 1424 LINE_CHECK("%cprivacy OFF", spacer); 1425 break; 1426 case IEEE80211_WEP_ON: 1427 LINE_CHECK("%cprivacy ON", spacer); 1428 break; 1429 case IEEE80211_WEP_MIXED: 1430 LINE_CHECK("%cprivacy MIXED", spacer); 1431 break; 1432 default: 1433 LINE_CHECK("%cprivacy UNKNOWN (0x%x)", 1434 spacer, wepmode); 1435 break; 1436 } 1437 1438 /* 1439 * If we get here then we've got WEP support so we need 1440 * to print WEP status. 1441 */ 1442 1443 ireq.i_type = IEEE80211_IOC_WEPTXKEY; 1444 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1445 warn("WEP support, but no tx key!"); 1446 goto end; 1447 } 1448 if (ireq.i_val != -1) 1449 LINE_CHECK("%cdeftxkey %d", spacer, ireq.i_val+1); 1450 else if (wepmode != IEEE80211_WEP_OFF || verbose) 1451 LINE_CHECK("%cdeftxkey UNDEF", spacer); 1452 1453 ireq.i_type = IEEE80211_IOC_NUMWEPKEYS; 1454 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1455 warn("WEP support, but no NUMWEPKEYS support!"); 1456 goto end; 1457 } 1458 num = ireq.i_val; 1459 1460 firstkey = 1; 1461 for (i = 0; i < num; i++) { 1462 struct ieee80211req_key ik; 1463 1464 memset(&ik, 0, sizeof(ik)); 1465 ik.ik_keyix = i; 1466 ireq.i_type = IEEE80211_IOC_WPAKEY; 1467 ireq.i_data = &ik; 1468 ireq.i_len = sizeof(ik); 1469 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1470 warn("WEP support, but can get keys!"); 1471 goto end; 1472 } 1473 if (ik.ik_keylen != 0) { 1474 if (verbose) 1475 LINE_BREAK(); 1476 printkey(&ik); 1477 firstkey = 0; 1478 } 1479 } 1480 } 1481 1482 ireq.i_type = IEEE80211_IOC_POWERSAVE; 1483 if (ioctl(s, SIOCG80211, &ireq) != -1 && 1484 ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) { 1485 if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) { 1486 switch (ireq.i_val) { 1487 case IEEE80211_POWERSAVE_OFF: 1488 LINE_CHECK("%cpowersavemode OFF", 1489 spacer); 1490 break; 1491 case IEEE80211_POWERSAVE_CAM: 1492 LINE_CHECK("%cpowersavemode CAM", 1493 spacer); 1494 break; 1495 case IEEE80211_POWERSAVE_PSP: 1496 LINE_CHECK("%cpowersavemode PSP", 1497 spacer); 1498 break; 1499 case IEEE80211_POWERSAVE_PSP_CAM: 1500 LINE_CHECK("%cpowersavemode PSP-CAM", 1501 spacer); 1502 break; 1503 } 1504 ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; 1505 if (ioctl(s, SIOCG80211, &ireq) != -1) 1506 LINE_CHECK("%cpowersavesleep %d", 1507 spacer, ireq.i_val); 1508 } 1509 } 1510 1511 ireq.i_type = IEEE80211_IOC_TXPOWMAX; 1512 if (ioctl(s, SIOCG80211, &ireq) != -1) 1513 LINE_CHECK("%ctxpowmax %d", spacer, ireq.i_val); 1514 1515 if (verbose) { 1516 ireq.i_type = IEEE80211_IOC_TXPOWER; 1517 if (ioctl(s, SIOCG80211, &ireq) != -1) 1518 LINE_CHECK("%ctxpower %d", spacer, ireq.i_val); 1519 } 1520 1521 ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD; 1522 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1523 if (ireq.i_val != IEEE80211_RTS_MAX || verbose) 1524 LINE_CHECK("%crtsthreshold %d", spacer, ireq.i_val); 1525 } 1526 1527 ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD; 1528 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1529 if (ireq.i_val != IEEE80211_FRAG_MAX || verbose) 1530 LINE_CHECK("%cfragthreshold %d", spacer, ireq.i_val); 1531 } 1532 1533 if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) { 1534 ireq.i_type = IEEE80211_IOC_PUREG; 1535 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1536 if (ireq.i_val) 1537 LINE_CHECK("%cpureg", spacer); 1538 else if (verbose) 1539 LINE_CHECK("%c-pureg", spacer); 1540 } 1541 ireq.i_type = IEEE80211_IOC_PROTMODE; 1542 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1543 switch (ireq.i_val) { 1544 case IEEE80211_PROTMODE_OFF: 1545 LINE_CHECK("%cprotmode OFF", spacer); 1546 break; 1547 case IEEE80211_PROTMODE_CTS: 1548 LINE_CHECK("%cprotmode CTS", spacer); 1549 break; 1550 case IEEE80211_PROTMODE_RTSCTS: 1551 LINE_CHECK("%cprotmode RTSCTS", spacer); 1552 break; 1553 default: 1554 LINE_CHECK("%cprotmode UNKNOWN (0x%x)", 1555 spacer, ireq.i_val); 1556 break; 1557 } 1558 } 1559 } 1560 1561 ireq.i_type = IEEE80211_IOC_WME; 1562 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1563 wme = ireq.i_val; 1564 if (wme) 1565 LINE_CHECK("%cwme", spacer); 1566 else if (verbose) 1567 LINE_CHECK("%c-wme", spacer); 1568 } else 1569 wme = 0; 1570 1571 if (opmode == IEEE80211_M_HOSTAP) { 1572 ireq.i_type = IEEE80211_IOC_HIDESSID; 1573 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1574 if (ireq.i_val) 1575 LINE_CHECK("%cssid HIDE", spacer); 1576 else if (verbose) 1577 LINE_CHECK("%cssid SHOW", spacer); 1578 } 1579 1580 ireq.i_type = IEEE80211_IOC_APBRIDGE; 1581 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1582 if (!ireq.i_val) 1583 LINE_CHECK("%c-apbridge", spacer); 1584 else if (verbose) 1585 LINE_CHECK("%capbridge", spacer); 1586 } 1587 1588 ireq.i_type = IEEE80211_IOC_DTIM_PERIOD; 1589 if (ioctl(s, SIOCG80211, &ireq) != -1) 1590 LINE_CHECK("%cdtimperiod %u", spacer, ireq.i_val); 1591 } else { 1592 ireq.i_type = IEEE80211_IOC_ROAMING; 1593 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1594 if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) { 1595 switch (ireq.i_val) { 1596 case IEEE80211_ROAMING_DEVICE: 1597 LINE_CHECK("%croaming DEVICE", spacer); 1598 break; 1599 case IEEE80211_ROAMING_AUTO: 1600 LINE_CHECK("%croaming AUTO", spacer); 1601 break; 1602 case IEEE80211_ROAMING_MANUAL: 1603 LINE_CHECK("%croaming MANUAL", spacer); 1604 break; 1605 default: 1606 LINE_CHECK("%croaming UNKNOWN (0x%x)", 1607 spacer, ireq.i_val); 1608 break; 1609 } 1610 } 1611 } 1612 } 1613 ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL; 1614 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1615 if (ireq.i_val) 1616 LINE_CHECK("%cbintval %u", spacer, ireq.i_val); 1617 else if (verbose) 1618 LINE_CHECK("%cbintval %u", spacer, ireq.i_val); 1619 } 1620 1621 if (wme && verbose) { 1622 LINE_BREAK(); 1623 list_wme(s); 1624 } 1625 1626 if (wpa) { 1627 ireq.i_type = IEEE80211_IOC_COUNTERMEASURES; 1628 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1629 if (ireq.i_val) 1630 LINE_CHECK("%ccountermeasures", spacer); 1631 else if (verbose) 1632 LINE_CHECK("%c-countermeasures", spacer); 1633 } 1634#if 0 1635 /* XXX not interesting with WPA done in user space */ 1636 ireq.i_type = IEEE80211_IOC_KEYMGTALGS; 1637 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1638 } 1639 1640 ireq.i_type = IEEE80211_IOC_MCASTCIPHER; 1641 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1642 printf("%cmcastcipher ", spacer); 1643 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); 1644 spacer = ' '; 1645 } 1646 1647 ireq.i_type = IEEE80211_IOC_UCASTCIPHER; 1648 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1649 printf("%cucastcipher ", spacer); 1650 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); 1651 } 1652 1653 if (wpa & 2) { 1654 ireq.i_type = IEEE80211_IOC_RSNCAPS; 1655 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1656 printf("%cRSN caps 0x%x", spacer, ireq.i_val); 1657 spacer = ' '; 1658 } 1659 } 1660 1661 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; 1662 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1663 } 1664#endif 1665 LINE_BREAK(); 1666 } 1667 LINE_BREAK(); 1668 1669end: 1670 return; 1671} 1672 1673static void 1674set80211(int s, int type, int val, int len, u_int8_t *data) 1675{ 1676 struct ieee80211req ireq; 1677 1678 (void) memset(&ireq, 0, sizeof(ireq)); 1679 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1680 ireq.i_type = type; 1681 ireq.i_val = val; 1682 ireq.i_len = len; 1683 ireq.i_data = data; 1684 if (ioctl(s, SIOCS80211, &ireq) < 0) 1685 err(1, "SIOCS80211"); 1686} 1687 1688static const char * 1689get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) 1690{ 1691 int len; 1692 int hexstr; 1693 u_int8_t *p; 1694 1695 len = *lenp; 1696 p = buf; 1697 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x'); 1698 if (hexstr) 1699 val += 2; 1700 for (;;) { 1701 if (*val == '\0') 1702 break; 1703 if (sep != NULL && strchr(sep, *val) != NULL) { 1704 val++; 1705 break; 1706 } 1707 if (hexstr) { 1708 if (!isxdigit((u_char)val[0])) { 1709 warnx("bad hexadecimal digits"); 1710 return NULL; 1711 } 1712 if (!isxdigit((u_char)val[1])) { 1713 warnx("odd count hexadecimal digits"); 1714 return NULL; 1715 } 1716 } 1717 if (p >= buf + len) { 1718 if (hexstr) 1719 warnx("hexadecimal digits too long"); 1720 else 1721 warnx("string too long"); 1722 return NULL; 1723 } 1724 if (hexstr) { 1725#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10) 1726 *p++ = (tohex((u_char)val[0]) << 4) | 1727 tohex((u_char)val[1]); 1728#undef tohex 1729 val += 2; 1730 } else 1731 *p++ = *val++; 1732 } 1733 len = p - buf; 1734 /* The string "-" is treated as the empty string. */ 1735 if (!hexstr && len == 1 && buf[0] == '-') 1736 len = 0; 1737 if (len < *lenp) 1738 memset(p, 0, *lenp - len); 1739 *lenp = len; 1740 return val; 1741} 1742 1743static void 1744print_string(const u_int8_t *buf, int len) 1745{ 1746 int i; 1747 int hasspc; 1748 1749 i = 0; 1750 hasspc = 0; 1751 for (; i < len; i++) { 1752 if (!isprint(buf[i]) && buf[i] != '\0') 1753 break; 1754 if (isspace(buf[i])) 1755 hasspc++; 1756 } 1757 if (i == len) { 1758 if (hasspc || len == 0 || buf[0] == '\0') 1759 printf("\"%.*s\"", len, buf); 1760 else 1761 printf("%.*s", len, buf); 1762 } else { 1763 printf("0x"); 1764 for (i = 0; i < len; i++) 1765 printf("%02x", buf[i]); 1766 } 1767} 1768 1769static struct cmd ieee80211_cmds[] = { 1770 DEF_CMD_ARG("ssid", set80211ssid), 1771 DEF_CMD_ARG("nwid", set80211ssid), 1772 DEF_CMD_ARG("stationname", set80211stationname), 1773 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */ 1774 DEF_CMD_ARG("channel", set80211channel), 1775 DEF_CMD_ARG("authmode", set80211authmode), 1776 DEF_CMD_ARG("powersavemode", set80211powersavemode), 1777 DEF_CMD("powersave", 1, set80211powersave), 1778 DEF_CMD("-powersave", 0, set80211powersave), 1779 DEF_CMD_ARG("powersavesleep", set80211powersavesleep), 1780 DEF_CMD_ARG("wepmode", set80211wepmode), 1781 DEF_CMD("wep", 1, set80211wep), 1782 DEF_CMD("-wep", 0, set80211wep), 1783 DEF_CMD_ARG("deftxkey", set80211weptxkey), 1784 DEF_CMD_ARG("weptxkey", set80211weptxkey), 1785 DEF_CMD_ARG("wepkey", set80211wepkey), 1786 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */ 1787 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */ 1788 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold), 1789 DEF_CMD_ARG("protmode", set80211protmode), 1790 DEF_CMD_ARG("txpower", set80211txpower), 1791 DEF_CMD_ARG("roaming", set80211roaming), 1792 DEF_CMD("wme", 1, set80211wme), 1793 DEF_CMD("-wme", 0, set80211wme), 1794 DEF_CMD("hidessid", 1, set80211hidessid), 1795 DEF_CMD("-hidessid", 0, set80211hidessid), 1796 DEF_CMD("apbridge", 1, set80211apbridge), 1797 DEF_CMD("-apbridge", 0, set80211apbridge), 1798 DEF_CMD_ARG("chanlist", set80211chanlist), 1799 DEF_CMD_ARG("bssid", set80211bssid), 1800 DEF_CMD_ARG("ap", set80211bssid), 1801 DEF_CMD("scan", 0, set80211scan), 1802 DEF_CMD_ARG("list", set80211list), 1803 DEF_CMD_ARG2("cwmin", set80211cwmin), 1804 DEF_CMD_ARG2("cwmax", set80211cwmax), 1805 DEF_CMD_ARG2("aifs", set80211aifs), 1806 DEF_CMD_ARG2("txoplimit", set80211txoplimit), 1807 DEF_CMD("acm", 1, set80211acm), 1808 DEF_CMD("-acm", 0, set80211acm), 1809 DEF_CMD("ack", 1, set80211ackpolicy), 1810 DEF_CMD("-ack", 0, set80211ackpolicy), 1811 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin), 1812 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax), 1813 DEF_CMD_ARG2("bss:aifs", set80211bssaifs), 1814 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit), 1815 DEF_CMD_ARG("dtimperiod", set80211dtimperiod), 1816 DEF_CMD_ARG("bintval", set80211bintval), 1817 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), 1818 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), 1819 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), 1820 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), 1821 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), 1822 DEF_CMD_ARG("mac:add", set80211addmac), 1823 DEF_CMD_ARG("mac:del", set80211delmac), 1824#if 0 1825 DEF_CMD_ARG("mac:kick", set80211kickmac), 1826#endif 1827 DEF_CMD("pureg", 1, set80211pureg), 1828 DEF_CMD("-pureg", 0, set80211pureg), 1829 DEF_CMD_ARG("fragthreshold", set80211fragthreshold), 1830}; 1831static struct afswtch af_ieee80211 = { 1832 .af_name = "af_ieee80211", 1833 .af_af = AF_UNSPEC, 1834 .af_other_status = ieee80211_status, 1835}; 1836 1837static __constructor void 1838ieee80211_ctor(void) 1839{ 1840#define N(a) (sizeof(a) / sizeof(a[0])) 1841 int i; 1842 1843 for (i = 0; i < N(ieee80211_cmds); i++) 1844 cmd_register(&ieee80211_cmds[i]); 1845 af_register(&af_ieee80211); 1846#undef N 1847} 1848