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