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