ifieee80211.c revision 148621
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 148621 2005-08-01 16:34: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, ac, d) 535{ 536 set80211(s, IEEE80211_IOC_WME_ACM, 1, getac(ac), NULL); 537} 538static 539DECL_CMD_FUNC(set80211noacm, ac, d) 540{ 541 set80211(s, IEEE80211_IOC_WME_ACM, 0, getac(ac), NULL); 542} 543 544static 545DECL_CMD_FUNC(set80211ackpolicy, ac, d) 546{ 547 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 1, getac(ac), NULL); 548} 549static 550DECL_CMD_FUNC(set80211noackpolicy, ac, d) 551{ 552 set80211(s, IEEE80211_IOC_WME_ACKPOLICY, 0, getac(ac), NULL); 553} 554 555static 556DECL_CMD_FUNC2(set80211bsscwmin, ac, val) 557{ 558 set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), 559 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 560} 561 562static 563DECL_CMD_FUNC2(set80211bsscwmax, ac, val) 564{ 565 set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), 566 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 567} 568 569static 570DECL_CMD_FUNC2(set80211bssaifs, ac, val) 571{ 572 set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), 573 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 574} 575 576static 577DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val) 578{ 579 set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), 580 getac(ac)|IEEE80211_WMEPARAM_BSS, NULL); 581} 582 583static 584DECL_CMD_FUNC(set80211dtimperiod, val, d) 585{ 586 set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL); 587} 588 589static 590DECL_CMD_FUNC(set80211bintval, val, d) 591{ 592 set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL); 593} 594 595static void 596set80211macmac(int s, int op, const char *val) 597{ 598 char *temp; 599 struct sockaddr_dl sdl; 600 601 temp = malloc(strlen(val) + 1); 602 if (temp == NULL) 603 errx(1, "malloc failed"); 604 temp[0] = ':'; 605 strcpy(temp + 1, val); 606 sdl.sdl_len = sizeof(sdl); 607 link_addr(temp, &sdl); 608 free(temp); 609 if (sdl.sdl_alen != IEEE80211_ADDR_LEN) 610 errx(1, "malformed link-level address"); 611 set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl)); 612} 613 614static 615DECL_CMD_FUNC(set80211addmac, val, d) 616{ 617 set80211macmac(s, IEEE80211_IOC_ADDMAC, val); 618} 619 620static 621DECL_CMD_FUNC(set80211delmac, val, d) 622{ 623 set80211macmac(s, IEEE80211_IOC_DELMAC, val); 624} 625 626static 627DECL_CMD_FUNC(set80211maccmd, val, d) 628{ 629 set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL); 630} 631 632static void 633set80211pureg(const char *val, int d, int s, const struct afswtch *rafp) 634{ 635 set80211(s, IEEE80211_IOC_PUREG, d, 0, NULL); 636} 637 638static 639DECL_CMD_FUNC(set80211fragthreshold, val, d) 640{ 641 set80211(s, IEEE80211_IOC_FRAGTHRESHOLD, 642 isundefarg(val) ? IEEE80211_FRAG_MAX : atoi(val), 0, NULL); 643} 644 645static int 646getmaxrate(uint8_t rates[15], uint8_t nrates) 647{ 648 int i, maxrate = -1; 649 650 for (i = 0; i < nrates; i++) { 651 int rate = rates[i] & IEEE80211_RATE_VAL; 652 if (rate > maxrate) 653 maxrate = rate; 654 } 655 return maxrate / 2; 656} 657 658static const char * 659getcaps(int capinfo) 660{ 661 static char capstring[32]; 662 char *cp = capstring; 663 664 if (capinfo & IEEE80211_CAPINFO_ESS) 665 *cp++ = 'E'; 666 if (capinfo & IEEE80211_CAPINFO_IBSS) 667 *cp++ = 'I'; 668 if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE) 669 *cp++ = 'c'; 670 if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ) 671 *cp++ = 'C'; 672 if (capinfo & IEEE80211_CAPINFO_PRIVACY) 673 *cp++ = 'P'; 674 if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) 675 *cp++ = 'S'; 676 if (capinfo & IEEE80211_CAPINFO_PBCC) 677 *cp++ = 'B'; 678 if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY) 679 *cp++ = 'A'; 680 if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 681 *cp++ = 's'; 682 if (capinfo & IEEE80211_CAPINFO_RSN) 683 *cp++ = 'R'; 684 if (capinfo & IEEE80211_CAPINFO_DSSSOFDM) 685 *cp++ = 'D'; 686 *cp = '\0'; 687 return capstring; 688} 689 690static void 691printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen) 692{ 693 printf("%s", tag); 694 if (verbose) { 695 maxlen -= strlen(tag)+2; 696 if (2*ielen > maxlen) 697 maxlen--; 698 printf("<"); 699 for (; ielen > 0; ie++, ielen--) { 700 if (maxlen-- <= 0) 701 break; 702 printf("%02x", *ie); 703 } 704 if (ielen != 0) 705 printf("-"); 706 printf(">"); 707 } 708} 709 710/* 711 * Copy the ssid string contents into buf, truncating to fit. If the 712 * ssid is entirely printable then just copy intact. Otherwise convert 713 * to hexadecimal. If the result is truncated then replace the last 714 * three characters with "...". 715 */ 716static int 717copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len) 718{ 719 const u_int8_t *p; 720 size_t maxlen; 721 int i; 722 723 if (essid_len > bufsize) 724 maxlen = bufsize; 725 else 726 maxlen = essid_len; 727 /* determine printable or not */ 728 for (i = 0, p = essid; i < maxlen; i++, p++) { 729 if (*p < ' ' || *p > 0x7e) 730 break; 731 } 732 if (i != maxlen) { /* not printable, print as hex */ 733 if (bufsize < 3) 734 return 0; 735 strlcpy(buf, "0x", bufsize); 736 bufsize -= 2; 737 p = essid; 738 for (i = 0; i < maxlen && bufsize >= 2; i++) { 739 sprintf(&buf[2+2*i], "%02x", p[i]); 740 bufsize -= 2; 741 } 742 if (i != essid_len) 743 memcpy(&buf[2+2*i-3], "...", 3); 744 } else { /* printable, truncate as needed */ 745 memcpy(buf, essid, maxlen); 746 if (maxlen != essid_len) 747 memcpy(&buf[maxlen-3], "...", 3); 748 } 749 return maxlen; 750} 751 752/* unalligned little endian access */ 753#define LE_READ_4(p) \ 754 ((u_int32_t) \ 755 ((((const u_int8_t *)(p))[0] ) | \ 756 (((const u_int8_t *)(p))[1] << 8) | \ 757 (((const u_int8_t *)(p))[2] << 16) | \ 758 (((const u_int8_t *)(p))[3] << 24))) 759 760static int __inline 761iswpaoui(const u_int8_t *frm) 762{ 763 return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); 764} 765 766static int __inline 767iswmeoui(const u_int8_t *frm) 768{ 769 return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI); 770} 771 772static int __inline 773isatherosoui(const u_int8_t *frm) 774{ 775 return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); 776} 777 778static void 779printies(const u_int8_t *vp, int ielen, int maxcols) 780{ 781 while (ielen > 0) { 782 switch (vp[0]) { 783 case IEEE80211_ELEMID_VENDOR: 784 if (iswpaoui(vp)) 785 printie(" WPA", vp, 2+vp[1], maxcols); 786 else if (iswmeoui(vp)) 787 printie(" WME", vp, 2+vp[1], maxcols); 788 else if (isatherosoui(vp)) 789 printie(" ATH", vp, 2+vp[1], maxcols); 790 else 791 printie(" VEN", vp, 2+vp[1], maxcols); 792 break; 793 case IEEE80211_ELEMID_RSN: 794 printie(" RSN", vp, 2+vp[1], maxcols); 795 break; 796 default: 797 printie(" ???", vp, 2+vp[1], maxcols); 798 break; 799 } 800 ielen -= 2+vp[1]; 801 vp += 2+vp[1]; 802 } 803} 804 805static void 806list_scan(int s) 807{ 808 uint8_t buf[24*1024]; 809 struct ieee80211req ireq; 810 char ssid[14]; 811 uint8_t *cp; 812 int len; 813 814 (void) memset(&ireq, 0, sizeof(ireq)); 815 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 816 ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; 817 ireq.i_data = buf; 818 ireq.i_len = sizeof(buf); 819 if (ioctl(s, SIOCG80211, &ireq) < 0) 820 errx(1, "unable to get scan results"); 821 len = ireq.i_len; 822 if (len < sizeof(struct ieee80211req_scan_result)) 823 return; 824 825 printf("%-14.14s %-17.17s %4s %4s %-5s %3s %4s\n" 826 , "SSID" 827 , "BSSID" 828 , "CHAN" 829 , "RATE" 830 , "S:N" 831 , "INT" 832 , "CAPS" 833 ); 834 cp = buf; 835 do { 836 struct ieee80211req_scan_result *sr; 837 uint8_t *vp; 838 839 sr = (struct ieee80211req_scan_result *) cp; 840 vp = (u_int8_t *)(sr+1); 841 printf("%-14.*s %s %3d %3dM %2d:%-2d %3d %-4.4s" 842 , copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len) 843 , ssid 844 , ether_ntoa((const struct ether_addr *) sr->isr_bssid) 845 , ieee80211_mhz2ieee(sr->isr_freq) 846 , getmaxrate(sr->isr_rates, sr->isr_nrates) 847 , sr->isr_rssi, sr->isr_noise 848 , sr->isr_intval 849 , getcaps(sr->isr_capinfo) 850 ); 851 printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);; 852 printf("\n"); 853 cp += sr->isr_len, len -= sr->isr_len; 854 } while (len >= sizeof(struct ieee80211req_scan_result)); 855} 856 857#include <net80211/ieee80211_freebsd.h> 858 859static void 860scan_and_wait(int s) 861{ 862 struct ieee80211req ireq; 863 int sroute; 864 865 sroute = socket(PF_ROUTE, SOCK_RAW, 0); 866 if (sroute < 0) { 867 perror("socket(PF_ROUTE,SOCK_RAW)"); 868 return; 869 } 870 (void) memset(&ireq, 0, sizeof(ireq)); 871 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 872 ireq.i_type = IEEE80211_IOC_SCAN_REQ; 873 /* NB: only root can trigger a scan so ignore errors */ 874 if (ioctl(s, SIOCS80211, &ireq) >= 0) { 875 char buf[2048]; 876 struct if_announcemsghdr *ifan; 877 struct rt_msghdr *rtm; 878 879 do { 880 if (read(sroute, buf, sizeof(buf)) < 0) { 881 perror("read(PF_ROUTE)"); 882 break; 883 } 884 rtm = (struct rt_msghdr *) buf; 885 if (rtm->rtm_version != RTM_VERSION) 886 break; 887 ifan = (struct if_announcemsghdr *) rtm; 888 } while (rtm->rtm_type != RTM_IEEE80211 || 889 ifan->ifan_what != RTM_IEEE80211_SCAN); 890 } 891 close(sroute); 892} 893 894static 895DECL_CMD_FUNC(set80211scan, val, d) 896{ 897 scan_and_wait(s); 898 list_scan(s); 899} 900 901static void 902list_stations(int s) 903{ 904 uint8_t buf[24*1024]; 905 struct ieee80211req ireq; 906 uint8_t *cp; 907 int len; 908 909 (void) memset(&ireq, 0, sizeof(ireq)); 910 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 911 ireq.i_type = IEEE80211_IOC_STA_INFO; 912 ireq.i_data = buf; 913 ireq.i_len = sizeof(buf); 914 if (ioctl(s, SIOCG80211, &ireq) < 0) 915 errx(1, "unable to get station information"); 916 len = ireq.i_len; 917 if (len < sizeof(struct ieee80211req_sta_info)) 918 return; 919 920 printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n" 921 , "ADDR" 922 , "AID" 923 , "CHAN" 924 , "RATE" 925 , "RSSI" 926 , "IDLE" 927 , "TXSEQ" 928 , "RXSEQ" 929 , "CAPS" 930 , "ERP" 931 ); 932 cp = buf; 933 do { 934 struct ieee80211req_sta_info *si; 935 uint8_t *vp; 936 937 si = (struct ieee80211req_sta_info *) cp; 938 vp = (u_int8_t *)(si+1); 939 printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x" 940 , ether_ntoa((const struct ether_addr*) si->isi_macaddr) 941 , IEEE80211_AID(si->isi_associd) 942 , ieee80211_mhz2ieee(si->isi_freq) 943 , (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2 944 , si->isi_rssi 945 , si->isi_inact 946 , si->isi_txseqs[0] 947 , si->isi_rxseqs[0] 948 , getcaps(si->isi_capinfo) 949 , si->isi_erp 950 ); 951 printies(vp, si->isi_ie_len, 24); 952 printf("\n"); 953 cp += si->isi_len, len -= si->isi_len; 954 } while (len >= sizeof(struct ieee80211req_sta_info)); 955} 956 957static void 958print_chaninfo(const struct ieee80211_channel *c) 959{ 960#define IEEE80211_IS_CHAN_PASSIVE(_c) \ 961 (((_c)->ic_flags & IEEE80211_CHAN_PASSIVE)) 962 char buf[14]; 963 964 buf[0] = '\0'; 965 if (IEEE80211_IS_CHAN_FHSS(c)) 966 strlcat(buf, " FHSS", sizeof(buf)); 967 if (IEEE80211_IS_CHAN_A(c)) 968 strlcat(buf, " 11a", sizeof(buf)); 969 /* XXX 11g schizophrenia */ 970 if (IEEE80211_IS_CHAN_G(c) || 971 IEEE80211_IS_CHAN_PUREG(c)) 972 strlcat(buf, " 11g", sizeof(buf)); 973 else if (IEEE80211_IS_CHAN_B(c)) 974 strlcat(buf, " 11b", sizeof(buf)); 975 if (IEEE80211_IS_CHAN_T(c)) 976 strlcat(buf, " Turbo", sizeof(buf)); 977 printf("Channel %3u : %u%c Mhz%-14.14s", 978 ieee80211_mhz2ieee(c->ic_freq), c->ic_freq, 979 IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf); 980#undef IEEE80211_IS_CHAN_PASSIVE 981} 982 983static void 984list_channels(int s, int allchans) 985{ 986 struct ieee80211req ireq; 987 struct ieee80211req_chaninfo chans; 988 struct ieee80211req_chaninfo achans; 989 const struct ieee80211_channel *c; 990 int i, half; 991 992 (void) memset(&ireq, 0, sizeof(ireq)); 993 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 994 ireq.i_type = IEEE80211_IOC_CHANINFO; 995 ireq.i_data = &chans; 996 ireq.i_len = sizeof(chans); 997 if (ioctl(s, SIOCG80211, &ireq) < 0) 998 errx(1, "unable to get channel information"); 999 if (!allchans) { 1000 struct ieee80211req_chanlist active; 1001 1002 ireq.i_type = IEEE80211_IOC_CHANLIST; 1003 ireq.i_data = &active; 1004 ireq.i_len = sizeof(active); 1005 if (ioctl(s, SIOCG80211, &ireq) < 0) 1006 errx(1, "unable to get active channel list"); 1007 memset(&achans, 0, sizeof(achans)); 1008 for (i = 0; i < chans.ic_nchans; i++) { 1009 c = &chans.ic_chans[i]; 1010 if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans) 1011 achans.ic_chans[achans.ic_nchans++] = *c; 1012 } 1013 } else 1014 achans = chans; 1015 half = achans.ic_nchans / 2; 1016 if (achans.ic_nchans % 2) 1017 half++; 1018 for (i = 0; i < achans.ic_nchans / 2; i++) { 1019 print_chaninfo(&achans.ic_chans[i]); 1020 print_chaninfo(&achans.ic_chans[half+i]); 1021 printf("\n"); 1022 } 1023 if (achans.ic_nchans % 2) { 1024 print_chaninfo(&achans.ic_chans[i]); 1025 printf("\n"); 1026 } 1027} 1028 1029static void 1030list_keys(int s) 1031{ 1032} 1033 1034#define IEEE80211_C_BITS \ 1035"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \ 1036"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \ 1037"\31WPA2\32BURST\33WME" 1038 1039static void 1040list_capabilities(int s) 1041{ 1042 struct ieee80211req ireq; 1043 u_int32_t caps; 1044 1045 (void) memset(&ireq, 0, sizeof(ireq)); 1046 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1047 ireq.i_type = IEEE80211_IOC_DRIVER_CAPS; 1048 if (ioctl(s, SIOCG80211, &ireq) < 0) 1049 errx(1, "unable to get driver capabilities"); 1050 caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len); 1051 printb(name, caps, IEEE80211_C_BITS); 1052 putchar('\n'); 1053} 1054 1055static void 1056list_wme(int s) 1057{ 1058 static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" }; 1059 struct ieee80211req ireq; 1060 int ac; 1061 1062 (void) memset(&ireq, 0, sizeof(ireq)); 1063 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1064 ireq.i_len = 0; 1065 for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) { 1066again: 1067 if (ireq.i_len & IEEE80211_WMEPARAM_BSS) 1068 printf("\t%s", " "); 1069 else 1070 printf("\t%s", acnames[ac]); 1071 1072 ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac; 1073 1074 /* show WME BSS parameters */ 1075 ireq.i_type = IEEE80211_IOC_WME_CWMIN; 1076 if (ioctl(s, SIOCG80211, &ireq) != -1) 1077 printf(" cwmin %2u", ireq.i_val); 1078 ireq.i_type = IEEE80211_IOC_WME_CWMAX; 1079 if (ioctl(s, SIOCG80211, &ireq) != -1) 1080 printf(" cwmax %2u", ireq.i_val); 1081 ireq.i_type = IEEE80211_IOC_WME_AIFS; 1082 if (ioctl(s, SIOCG80211, &ireq) != -1) 1083 printf(" aifs %2u", ireq.i_val); 1084 ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT; 1085 if (ioctl(s, SIOCG80211, &ireq) != -1) 1086 printf(" txopLimit %3u", ireq.i_val); 1087 ireq.i_type = IEEE80211_IOC_WME_ACM; 1088 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1089 if (ireq.i_val) 1090 printf(" acm"); 1091 else if (verbose) 1092 printf(" -acm"); 1093 } 1094 /* !BSS only */ 1095 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { 1096 ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY; 1097 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1098 if (!ireq.i_val) 1099 printf(" -ack"); 1100 else if (verbose) 1101 printf(" ack"); 1102 } 1103 } 1104 printf("\n"); 1105 if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) { 1106 ireq.i_len |= IEEE80211_WMEPARAM_BSS; 1107 goto again; 1108 } else 1109 ireq.i_len &= ~IEEE80211_WMEPARAM_BSS; 1110 } 1111} 1112 1113static 1114DECL_CMD_FUNC(set80211list, arg, d) 1115{ 1116#define iseq(a,b) (strncasecmp(a,b,sizeof(b)-1) == 0) 1117 1118 if (iseq(arg, "sta")) 1119 list_stations(s); 1120 else if (iseq(arg, "scan") || iseq(arg, "ap")) 1121 list_scan(s); 1122 else if (iseq(arg, "chan") || iseq(arg, "freq")) 1123 list_channels(s, 1); 1124 else if (iseq(arg, "active")) 1125 list_channels(s, 0); 1126 else if (iseq(arg, "keys")) 1127 list_keys(s); 1128 else if (iseq(arg, "caps")) 1129 list_capabilities(s); 1130 else if (iseq(arg, "wme")) 1131 list_wme(s); 1132 else 1133 errx(1, "Don't know how to list %s for %s", arg, name); 1134#undef iseq 1135} 1136 1137static enum ieee80211_opmode 1138get80211opmode(int s) 1139{ 1140 struct ifmediareq ifmr; 1141 1142 (void) memset(&ifmr, 0, sizeof(ifmr)); 1143 (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); 1144 1145 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { 1146 if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) 1147 return IEEE80211_M_IBSS; /* XXX ahdemo */ 1148 if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) 1149 return IEEE80211_M_HOSTAP; 1150 if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) 1151 return IEEE80211_M_MONITOR; 1152 } 1153 return IEEE80211_M_STA; 1154} 1155 1156static const struct ieee80211_channel * 1157getchaninfo(int s, int chan) 1158{ 1159 struct ieee80211req ireq; 1160 static struct ieee80211req_chaninfo chans; 1161 static struct ieee80211_channel undef; 1162 const struct ieee80211_channel *c; 1163 int i, freq; 1164 1165 (void) memset(&ireq, 0, sizeof(ireq)); 1166 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1167 ireq.i_type = IEEE80211_IOC_CHANINFO; 1168 ireq.i_data = &chans; 1169 ireq.i_len = sizeof(chans); 1170 if (ioctl(s, SIOCG80211, &ireq) < 0) 1171 errx(1, "unable to get channel information"); 1172 freq = ieee80211_ieee2mhz(chan); 1173 for (i = 0; i < chans.ic_nchans; i++) { 1174 c = &chans.ic_chans[i]; 1175 if (c->ic_freq == freq) 1176 return c; 1177 } 1178 return &undef; 1179} 1180 1181#if 0 1182static void 1183printcipher(int s, struct ieee80211req *ireq, int keylenop) 1184{ 1185 switch (ireq->i_val) { 1186 case IEEE80211_CIPHER_WEP: 1187 ireq->i_type = keylenop; 1188 if (ioctl(s, SIOCG80211, ireq) != -1) 1189 printf("WEP-%s", 1190 ireq->i_len <= 5 ? "40" : 1191 ireq->i_len <= 13 ? "104" : "128"); 1192 else 1193 printf("WEP"); 1194 break; 1195 case IEEE80211_CIPHER_TKIP: 1196 printf("TKIP"); 1197 break; 1198 case IEEE80211_CIPHER_AES_OCB: 1199 printf("AES-OCB"); 1200 break; 1201 case IEEE80211_CIPHER_AES_CCM: 1202 printf("AES-CCM"); 1203 break; 1204 case IEEE80211_CIPHER_CKIP: 1205 printf("CKIP"); 1206 break; 1207 case IEEE80211_CIPHER_NONE: 1208 printf("NONE"); 1209 break; 1210 default: 1211 printf("UNKNOWN (0x%x)", ireq->i_val); 1212 break; 1213 } 1214} 1215#endif 1216 1217#define MAXCOL 78 1218int col; 1219char spacer; 1220 1221#define LINE_BREAK() do { \ 1222 if (spacer != '\t') { \ 1223 printf("\n"); \ 1224 spacer = '\t'; \ 1225 } \ 1226 col = 8; /* 8-col tab */ \ 1227} while (0) 1228#define LINE_CHECK(fmt, ...) do { \ 1229 col += sizeof(fmt)-2; \ 1230 if (col > MAXCOL) { \ 1231 LINE_BREAK(); \ 1232 col += sizeof(fmt)-2; \ 1233 } \ 1234 printf(fmt, __VA_ARGS__); \ 1235 spacer = ' '; \ 1236} while (0) 1237 1238static void 1239printkey(const struct ieee80211req_key *ik) 1240{ 1241 static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; 1242 int keylen = ik->ik_keylen; 1243 int printcontents; 1244 1245 printcontents = printkeys && 1246 (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); 1247 if (printcontents) 1248 LINE_BREAK(); 1249 switch (ik->ik_type) { 1250 case IEEE80211_CIPHER_WEP: 1251 /* compatibility */ 1252 LINE_CHECK("%cwepkey %u:%s", spacer, ik->ik_keyix+1, 1253 keylen <= 5 ? "40-bit" : 1254 keylen <= 13 ? "104-bit" : "128-bit"); 1255 break; 1256 case IEEE80211_CIPHER_TKIP: 1257 if (keylen > 128/8) 1258 keylen -= 128/8; /* ignore MIC for now */ 1259 LINE_CHECK("%cTKIP %u:%u-bit", 1260 spacer, ik->ik_keyix+1, 8*keylen); 1261 break; 1262 case IEEE80211_CIPHER_AES_OCB: 1263 LINE_CHECK("%cAES-OCB %u:%u-bit", 1264 spacer, ik->ik_keyix+1, 8*keylen); 1265 break; 1266 case IEEE80211_CIPHER_AES_CCM: 1267 LINE_CHECK("%cAES-CCM %u:%u-bit", 1268 spacer, ik->ik_keyix+1, 8*keylen); 1269 break; 1270 case IEEE80211_CIPHER_CKIP: 1271 LINE_CHECK("%cCKIP %u:%u-bit", 1272 spacer, ik->ik_keyix+1, 8*keylen); 1273 break; 1274 case IEEE80211_CIPHER_NONE: 1275 LINE_CHECK("%cNULL %u:%u-bit", 1276 spacer, ik->ik_keyix+1, 8*keylen); 1277 break; 1278 default: 1279 LINE_CHECK("%cUNKNOWN (0x%x) %u:%u-bit", spacer, 1280 ik->ik_type, ik->ik_keyix+1, 8*keylen); 1281 break; 1282 } 1283 if (printcontents) { 1284 int i; 1285 1286 printf(" <"); 1287 for (i = 0; i < keylen; i++) 1288 printf("%02x", ik->ik_keydata[i]); 1289 printf(">"); 1290 if (ik->ik_type != IEEE80211_CIPHER_WEP && 1291 (ik->ik_keyrsc != 0 || verbose)) 1292 printf(" rsc %ju", (uintmax_t)ik->ik_keyrsc); 1293 if (ik->ik_type != IEEE80211_CIPHER_WEP && 1294 (ik->ik_keytsc != 0 || verbose)) 1295 printf(" tsc %ju", (uintmax_t)ik->ik_keytsc); 1296 if (ik->ik_flags != 0 && verbose) { 1297 const char *sep = " "; 1298 1299 if (ik->ik_flags & IEEE80211_KEY_XMIT) 1300 printf("%stx", sep), sep = "+"; 1301 if (ik->ik_flags & IEEE80211_KEY_RECV) 1302 printf("%srx", sep), sep = "+"; 1303 if (ik->ik_flags & IEEE80211_KEY_DEFAULT) 1304 printf("%sdef", sep), sep = "+"; 1305 } 1306 LINE_BREAK(); 1307 } 1308} 1309 1310static void 1311ieee80211_status(int s) 1312{ 1313 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 1314 enum ieee80211_opmode opmode = get80211opmode(s); 1315 int i, num, wpa, wme; 1316 struct ieee80211req ireq; 1317 u_int8_t data[32]; 1318 const struct ieee80211_channel *c; 1319 1320 (void) memset(&ireq, 0, sizeof(ireq)); 1321 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1322 ireq.i_data = &data; 1323 1324 wpa = 0; /* unknown/not set */ 1325 1326 ireq.i_type = IEEE80211_IOC_SSID; 1327 ireq.i_val = -1; 1328 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1329 /* If we can't get the SSID, the this isn't an 802.11 device. */ 1330 return; 1331 } 1332 num = 0; 1333 ireq.i_type = IEEE80211_IOC_NUMSSIDS; 1334 if (ioctl(s, SIOCG80211, &ireq) >= 0) 1335 num = ireq.i_val; 1336 printf("\tssid "); 1337 if (num > 1) { 1338 ireq.i_type = IEEE80211_IOC_SSID; 1339 for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) { 1340 if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) { 1341 printf(" %d:", ireq.i_val + 1); 1342 print_string(data, ireq.i_len); 1343 } 1344 } 1345 } else 1346 print_string(data, ireq.i_len); 1347 1348 ireq.i_type = IEEE80211_IOC_CHANNEL; 1349 if (ioctl(s, SIOCG80211, &ireq) < 0) 1350 goto end; 1351 c = getchaninfo(s, ireq.i_val); 1352 if (ireq.i_val != -1) { 1353 printf(" channel %d", ireq.i_val); 1354 if (verbose) 1355 printf(" (%u)", c->ic_freq); 1356 } else if (verbose) 1357 printf(" channel UNDEF"); 1358 1359 ireq.i_type = IEEE80211_IOC_BSSID; 1360 ireq.i_len = IEEE80211_ADDR_LEN; 1361 if (ioctl(s, SIOCG80211, &ireq) >= 0 && 1362 memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0) 1363 printf(" bssid %s", ether_ntoa(ireq.i_data)); 1364 1365 ireq.i_type = IEEE80211_IOC_STATIONNAME; 1366 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1367 printf("\n\tstationname "); 1368 print_string(data, ireq.i_len); 1369 } 1370 1371 spacer = ' '; /* force first break */ 1372 LINE_BREAK(); 1373 1374 ireq.i_type = IEEE80211_IOC_AUTHMODE; 1375 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1376 switch (ireq.i_val) { 1377 case IEEE80211_AUTH_NONE: 1378 LINE_CHECK("%cauthmode NONE", spacer); 1379 break; 1380 case IEEE80211_AUTH_OPEN: 1381 LINE_CHECK("%cauthmode OPEN", spacer); 1382 break; 1383 case IEEE80211_AUTH_SHARED: 1384 LINE_CHECK("%cauthmode SHARED", spacer); 1385 break; 1386 case IEEE80211_AUTH_8021X: 1387 LINE_CHECK("%cauthmode 802.1x", spacer); 1388 break; 1389 case IEEE80211_AUTH_WPA: 1390 ireq.i_type = IEEE80211_IOC_WPA; 1391 if (ioctl(s, SIOCG80211, &ireq) != -1) 1392 wpa = ireq.i_val; 1393 if (!wpa) 1394 wpa = 1; /* default to WPA1 */ 1395 switch (wpa) { 1396 case 2: 1397 LINE_CHECK("%cauthmode WPA2/802.11i", 1398 spacer); 1399 break; 1400 case 3: 1401 LINE_CHECK("%cauthmode WPA1+WPA2/802.11i", 1402 spacer); 1403 break; 1404 default: 1405 LINE_CHECK("%cauthmode WPA", spacer); 1406 break; 1407 } 1408 break; 1409 case IEEE80211_AUTH_AUTO: 1410 LINE_CHECK("%cauthmode AUTO", spacer); 1411 break; 1412 default: 1413 LINE_CHECK("%cauthmode UNKNOWN (0x%x)", 1414 spacer, ireq.i_val); 1415 break; 1416 } 1417 } 1418 1419 ireq.i_type = IEEE80211_IOC_WEP; 1420 if (ioctl(s, SIOCG80211, &ireq) != -1 && 1421 ireq.i_val != IEEE80211_WEP_NOSUP) { 1422 int firstkey, wepmode; 1423 1424 wepmode = ireq.i_val; 1425 switch (wepmode) { 1426 case IEEE80211_WEP_OFF: 1427 LINE_CHECK("%cprivacy OFF", spacer); 1428 break; 1429 case IEEE80211_WEP_ON: 1430 LINE_CHECK("%cprivacy ON", spacer); 1431 break; 1432 case IEEE80211_WEP_MIXED: 1433 LINE_CHECK("%cprivacy MIXED", spacer); 1434 break; 1435 default: 1436 LINE_CHECK("%cprivacy UNKNOWN (0x%x)", 1437 spacer, wepmode); 1438 break; 1439 } 1440 1441 /* 1442 * If we get here then we've got WEP support so we need 1443 * to print WEP status. 1444 */ 1445 1446 ireq.i_type = IEEE80211_IOC_WEPTXKEY; 1447 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1448 warn("WEP support, but no tx key!"); 1449 goto end; 1450 } 1451 if (ireq.i_val != -1) 1452 LINE_CHECK("%cdeftxkey %d", spacer, ireq.i_val+1); 1453 else if (wepmode != IEEE80211_WEP_OFF || verbose) 1454 LINE_CHECK("%cdeftxkey UNDEF", spacer); 1455 1456 ireq.i_type = IEEE80211_IOC_NUMWEPKEYS; 1457 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1458 warn("WEP support, but no NUMWEPKEYS support!"); 1459 goto end; 1460 } 1461 num = ireq.i_val; 1462 1463 firstkey = 1; 1464 for (i = 0; i < num; i++) { 1465 struct ieee80211req_key ik; 1466 1467 memset(&ik, 0, sizeof(ik)); 1468 ik.ik_keyix = i; 1469 ireq.i_type = IEEE80211_IOC_WPAKEY; 1470 ireq.i_data = &ik; 1471 ireq.i_len = sizeof(ik); 1472 if (ioctl(s, SIOCG80211, &ireq) < 0) { 1473 warn("WEP support, but can get keys!"); 1474 goto end; 1475 } 1476 if (ik.ik_keylen != 0) { 1477 if (verbose) 1478 LINE_BREAK(); 1479 printkey(&ik); 1480 firstkey = 0; 1481 } 1482 } 1483 } 1484 1485 ireq.i_type = IEEE80211_IOC_POWERSAVE; 1486 if (ioctl(s, SIOCG80211, &ireq) != -1 && 1487 ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) { 1488 if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) { 1489 switch (ireq.i_val) { 1490 case IEEE80211_POWERSAVE_OFF: 1491 LINE_CHECK("%cpowersavemode OFF", 1492 spacer); 1493 break; 1494 case IEEE80211_POWERSAVE_CAM: 1495 LINE_CHECK("%cpowersavemode CAM", 1496 spacer); 1497 break; 1498 case IEEE80211_POWERSAVE_PSP: 1499 LINE_CHECK("%cpowersavemode PSP", 1500 spacer); 1501 break; 1502 case IEEE80211_POWERSAVE_PSP_CAM: 1503 LINE_CHECK("%cpowersavemode PSP-CAM", 1504 spacer); 1505 break; 1506 } 1507 ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP; 1508 if (ioctl(s, SIOCG80211, &ireq) != -1) 1509 LINE_CHECK("%cpowersavesleep %d", 1510 spacer, ireq.i_val); 1511 } 1512 } 1513 1514 ireq.i_type = IEEE80211_IOC_TXPOWMAX; 1515 if (ioctl(s, SIOCG80211, &ireq) != -1) 1516 LINE_CHECK("%ctxpowmax %d", spacer, ireq.i_val); 1517 1518 if (verbose) { 1519 ireq.i_type = IEEE80211_IOC_TXPOWER; 1520 if (ioctl(s, SIOCG80211, &ireq) != -1) 1521 LINE_CHECK("%ctxpower %d", spacer, ireq.i_val); 1522 } 1523 1524 ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD; 1525 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1526 if (ireq.i_val != IEEE80211_RTS_MAX || verbose) 1527 LINE_CHECK("%crtsthreshold %d", spacer, ireq.i_val); 1528 } 1529 1530 ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD; 1531 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1532 if (ireq.i_val != IEEE80211_FRAG_MAX || verbose) 1533 LINE_CHECK("%cfragthreshold %d", spacer, ireq.i_val); 1534 } 1535 1536 if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) { 1537 ireq.i_type = IEEE80211_IOC_PUREG; 1538 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1539 if (ireq.i_val) 1540 LINE_CHECK("%cpureg", spacer); 1541 else if (verbose) 1542 LINE_CHECK("%c-pureg", spacer); 1543 } 1544 ireq.i_type = IEEE80211_IOC_PROTMODE; 1545 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1546 switch (ireq.i_val) { 1547 case IEEE80211_PROTMODE_OFF: 1548 LINE_CHECK("%cprotmode OFF", spacer); 1549 break; 1550 case IEEE80211_PROTMODE_CTS: 1551 LINE_CHECK("%cprotmode CTS", spacer); 1552 break; 1553 case IEEE80211_PROTMODE_RTSCTS: 1554 LINE_CHECK("%cprotmode RTSCTS", spacer); 1555 break; 1556 default: 1557 LINE_CHECK("%cprotmode UNKNOWN (0x%x)", 1558 spacer, ireq.i_val); 1559 break; 1560 } 1561 } 1562 } 1563 1564 ireq.i_type = IEEE80211_IOC_WME; 1565 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1566 wme = ireq.i_val; 1567 if (wme) 1568 LINE_CHECK("%cwme", spacer); 1569 else if (verbose) 1570 LINE_CHECK("%c-wme", spacer); 1571 } else 1572 wme = 0; 1573 1574 if (opmode == IEEE80211_M_HOSTAP) { 1575 ireq.i_type = IEEE80211_IOC_HIDESSID; 1576 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1577 if (ireq.i_val) 1578 LINE_CHECK("%cssid HIDE", spacer); 1579 else if (verbose) 1580 LINE_CHECK("%cssid SHOW", spacer); 1581 } 1582 1583 ireq.i_type = IEEE80211_IOC_APBRIDGE; 1584 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1585 if (!ireq.i_val) 1586 LINE_CHECK("%c-apbridge", spacer); 1587 else if (verbose) 1588 LINE_CHECK("%capbridge", spacer); 1589 } 1590 1591 ireq.i_type = IEEE80211_IOC_DTIM_PERIOD; 1592 if (ioctl(s, SIOCG80211, &ireq) != -1) 1593 LINE_CHECK("%cdtimperiod %u", spacer, ireq.i_val); 1594 } else { 1595 ireq.i_type = IEEE80211_IOC_ROAMING; 1596 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1597 if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) { 1598 switch (ireq.i_val) { 1599 case IEEE80211_ROAMING_DEVICE: 1600 LINE_CHECK("%croaming DEVICE", spacer); 1601 break; 1602 case IEEE80211_ROAMING_AUTO: 1603 LINE_CHECK("%croaming AUTO", spacer); 1604 break; 1605 case IEEE80211_ROAMING_MANUAL: 1606 LINE_CHECK("%croaming MANUAL", spacer); 1607 break; 1608 default: 1609 LINE_CHECK("%croaming UNKNOWN (0x%x)", 1610 spacer, ireq.i_val); 1611 break; 1612 } 1613 } 1614 } 1615 } 1616 ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL; 1617 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1618 if (ireq.i_val) 1619 LINE_CHECK("%cbintval %u", spacer, ireq.i_val); 1620 else if (verbose) 1621 LINE_CHECK("%cbintval %u", spacer, ireq.i_val); 1622 } 1623 1624 if (wme && verbose) { 1625 LINE_BREAK(); 1626 list_wme(s); 1627 } 1628 1629 if (wpa) { 1630 ireq.i_type = IEEE80211_IOC_COUNTERMEASURES; 1631 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1632 if (ireq.i_val) 1633 LINE_CHECK("%ccountermeasures", spacer); 1634 else if (verbose) 1635 LINE_CHECK("%c-countermeasures", spacer); 1636 } 1637#if 0 1638 /* XXX not interesting with WPA done in user space */ 1639 ireq.i_type = IEEE80211_IOC_KEYMGTALGS; 1640 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1641 } 1642 1643 ireq.i_type = IEEE80211_IOC_MCASTCIPHER; 1644 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1645 printf("%cmcastcipher ", spacer); 1646 printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN); 1647 spacer = ' '; 1648 } 1649 1650 ireq.i_type = IEEE80211_IOC_UCASTCIPHER; 1651 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1652 printf("%cucastcipher ", spacer); 1653 printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN); 1654 } 1655 1656 if (wpa & 2) { 1657 ireq.i_type = IEEE80211_IOC_RSNCAPS; 1658 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1659 printf("%cRSN caps 0x%x", spacer, ireq.i_val); 1660 spacer = ' '; 1661 } 1662 } 1663 1664 ireq.i_type = IEEE80211_IOC_UCASTCIPHERS; 1665 if (ioctl(s, SIOCG80211, &ireq) != -1) { 1666 } 1667#endif 1668 LINE_BREAK(); 1669 } 1670 LINE_BREAK(); 1671 1672end: 1673 return; 1674} 1675 1676static void 1677set80211(int s, int type, int val, int len, u_int8_t *data) 1678{ 1679 struct ieee80211req ireq; 1680 1681 (void) memset(&ireq, 0, sizeof(ireq)); 1682 (void) strncpy(ireq.i_name, name, sizeof(ireq.i_name)); 1683 ireq.i_type = type; 1684 ireq.i_val = val; 1685 ireq.i_len = len; 1686 ireq.i_data = data; 1687 if (ioctl(s, SIOCS80211, &ireq) < 0) 1688 err(1, "SIOCS80211"); 1689} 1690 1691static const char * 1692get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp) 1693{ 1694 int len; 1695 int hexstr; 1696 u_int8_t *p; 1697 1698 len = *lenp; 1699 p = buf; 1700 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x'); 1701 if (hexstr) 1702 val += 2; 1703 for (;;) { 1704 if (*val == '\0') 1705 break; 1706 if (sep != NULL && strchr(sep, *val) != NULL) { 1707 val++; 1708 break; 1709 } 1710 if (hexstr) { 1711 if (!isxdigit((u_char)val[0])) { 1712 warnx("bad hexadecimal digits"); 1713 return NULL; 1714 } 1715 if (!isxdigit((u_char)val[1])) { 1716 warnx("odd count hexadecimal digits"); 1717 return NULL; 1718 } 1719 } 1720 if (p >= buf + len) { 1721 if (hexstr) 1722 warnx("hexadecimal digits too long"); 1723 else 1724 warnx("string too long"); 1725 return NULL; 1726 } 1727 if (hexstr) { 1728#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10) 1729 *p++ = (tohex((u_char)val[0]) << 4) | 1730 tohex((u_char)val[1]); 1731#undef tohex 1732 val += 2; 1733 } else 1734 *p++ = *val++; 1735 } 1736 len = p - buf; 1737 /* The string "-" is treated as the empty string. */ 1738 if (!hexstr && len == 1 && buf[0] == '-') 1739 len = 0; 1740 if (len < *lenp) 1741 memset(p, 0, *lenp - len); 1742 *lenp = len; 1743 return val; 1744} 1745 1746static void 1747print_string(const u_int8_t *buf, int len) 1748{ 1749 int i; 1750 int hasspc; 1751 1752 i = 0; 1753 hasspc = 0; 1754 for (; i < len; i++) { 1755 if (!isprint(buf[i]) && buf[i] != '\0') 1756 break; 1757 if (isspace(buf[i])) 1758 hasspc++; 1759 } 1760 if (i == len) { 1761 if (hasspc || len == 0 || buf[0] == '\0') 1762 printf("\"%.*s\"", len, buf); 1763 else 1764 printf("%.*s", len, buf); 1765 } else { 1766 printf("0x"); 1767 for (i = 0; i < len; i++) 1768 printf("%02x", buf[i]); 1769 } 1770} 1771 1772static struct cmd ieee80211_cmds[] = { 1773 DEF_CMD_ARG("ssid", set80211ssid), 1774 DEF_CMD_ARG("nwid", set80211ssid), 1775 DEF_CMD_ARG("stationname", set80211stationname), 1776 DEF_CMD_ARG("station", set80211stationname), /* BSD/OS */ 1777 DEF_CMD_ARG("channel", set80211channel), 1778 DEF_CMD_ARG("authmode", set80211authmode), 1779 DEF_CMD_ARG("powersavemode", set80211powersavemode), 1780 DEF_CMD("powersave", 1, set80211powersave), 1781 DEF_CMD("-powersave", 0, set80211powersave), 1782 DEF_CMD_ARG("powersavesleep", set80211powersavesleep), 1783 DEF_CMD_ARG("wepmode", set80211wepmode), 1784 DEF_CMD("wep", 1, set80211wep), 1785 DEF_CMD("-wep", 0, set80211wep), 1786 DEF_CMD_ARG("deftxkey", set80211weptxkey), 1787 DEF_CMD_ARG("weptxkey", set80211weptxkey), 1788 DEF_CMD_ARG("wepkey", set80211wepkey), 1789 DEF_CMD_ARG("nwkey", set80211nwkey), /* NetBSD */ 1790 DEF_CMD("-nwkey", 0, set80211wep), /* NetBSD */ 1791 DEF_CMD_ARG("rtsthreshold", set80211rtsthreshold), 1792 DEF_CMD_ARG("protmode", set80211protmode), 1793 DEF_CMD_ARG("txpower", set80211txpower), 1794 DEF_CMD_ARG("roaming", set80211roaming), 1795 DEF_CMD("wme", 1, set80211wme), 1796 DEF_CMD("-wme", 0, set80211wme), 1797 DEF_CMD("hidessid", 1, set80211hidessid), 1798 DEF_CMD("-hidessid", 0, set80211hidessid), 1799 DEF_CMD("apbridge", 1, set80211apbridge), 1800 DEF_CMD("-apbridge", 0, set80211apbridge), 1801 DEF_CMD_ARG("chanlist", set80211chanlist), 1802 DEF_CMD_ARG("bssid", set80211bssid), 1803 DEF_CMD_ARG("ap", set80211bssid), 1804 DEF_CMD("scan", 0, set80211scan), 1805 DEF_CMD_ARG("list", set80211list), 1806 DEF_CMD_ARG2("cwmin", set80211cwmin), 1807 DEF_CMD_ARG2("cwmax", set80211cwmax), 1808 DEF_CMD_ARG2("aifs", set80211aifs), 1809 DEF_CMD_ARG2("txoplimit", set80211txoplimit), 1810 DEF_CMD_ARG("acm", set80211acm), 1811 DEF_CMD_ARG("-acm", set80211noacm), 1812 DEF_CMD_ARG("ack", set80211ackpolicy), 1813 DEF_CMD_ARG("-ack", set80211noackpolicy), 1814 DEF_CMD_ARG2("bss:cwmin", set80211bsscwmin), 1815 DEF_CMD_ARG2("bss:cwmax", set80211bsscwmax), 1816 DEF_CMD_ARG2("bss:aifs", set80211bssaifs), 1817 DEF_CMD_ARG2("bss:txoplimit", set80211bsstxoplimit), 1818 DEF_CMD_ARG("dtimperiod", set80211dtimperiod), 1819 DEF_CMD_ARG("bintval", set80211bintval), 1820 DEF_CMD("mac:open", IEEE80211_MACCMD_POLICY_OPEN, set80211maccmd), 1821 DEF_CMD("mac:allow", IEEE80211_MACCMD_POLICY_ALLOW, set80211maccmd), 1822 DEF_CMD("mac:deny", IEEE80211_MACCMD_POLICY_DENY, set80211maccmd), 1823 DEF_CMD("mac:flush", IEEE80211_MACCMD_FLUSH, set80211maccmd), 1824 DEF_CMD("mac:detach", IEEE80211_MACCMD_DETACH, set80211maccmd), 1825 DEF_CMD_ARG("mac:add", set80211addmac), 1826 DEF_CMD_ARG("mac:del", set80211delmac), 1827#if 0 1828 DEF_CMD_ARG("mac:kick", set80211kickmac), 1829#endif 1830 DEF_CMD("pureg", 1, set80211pureg), 1831 DEF_CMD("-pureg", 0, set80211pureg), 1832 DEF_CMD_ARG("fragthreshold", set80211fragthreshold), 1833}; 1834static struct afswtch af_ieee80211 = { 1835 .af_name = "af_ieee80211", 1836 .af_af = AF_UNSPEC, 1837 .af_other_status = ieee80211_status, 1838}; 1839 1840static __constructor void 1841ieee80211_ctor(void) 1842{ 1843#define N(a) (sizeof(a) / sizeof(a[0])) 1844 int i; 1845 1846 for (i = 0; i < N(ieee80211_cmds); i++) 1847 cmd_register(&ieee80211_cmds[i]); 1848 af_register(&af_ieee80211); 1849#undef N 1850} 1851