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