mirror of
https://github.com/proski/madwifi
synced 2024-11-29 01:33:22 +03:00
80c58504d7
The need to use software instead of hardware for beacon timers in AP+STA mode (aka nosbeacon) is now just determined in software, as we always knew whether or not to enable this. The confusing bssid and -bssid parameters are now deprecated. The "uniquebssid" flag is equivalent to "bssid" and can be used to force IEEE80211_CLONE_BSSID flag. If this is not specified, then the BSSID used will be the next unused BSSID in the sequence, which could very well be the parent device's MAC address. "uniquebssid" equates directly to IEEE80211_CLONE_BSSID" flag therefore. git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3476 0192ed92-7a03-0410-a25b-9323aeb14dbd
1037 lines
25 KiB
C
1037 lines
25 KiB
C
/*-
|
|
* Copyright (c) 2005 Sam Leffler, Errno Consulting
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
|
* redistribution must be conditioned upon including a substantially
|
|
* similar Disclaimer requirement for further binary redistribution.
|
|
* 3. Neither the names of the above-listed copyright holders nor the names
|
|
* of any contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of the
|
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
|
* Software Foundation.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* wlanconfig athX create wlandev wifiX
|
|
* wlanmode station | adhoc | ibss | ap | monitor [bssid | -bssid]
|
|
* wlanconfig athX destroy
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <ctype.h>
|
|
#include <getopt.h>
|
|
#include <err.h>
|
|
|
|
#include <include/compat.h>
|
|
|
|
#include "wireless_copy.h"
|
|
#include "net80211/ieee80211.h"
|
|
#include "net80211/ieee80211_crypto.h"
|
|
#include "net80211/ieee80211_ioctl.h"
|
|
|
|
/*
|
|
* These are taken from ieee80211_node.h
|
|
*/
|
|
#define IEEE80211_NODE_TURBOP 0x0001 /* Turbo prime enable */
|
|
#define IEEE80211_NODE_COMP 0x0002 /* Compresssion enable */
|
|
#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
|
|
#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
|
|
#define IEEE80211_NODE_AR 0x0010 /* AR capable */
|
|
#define IEEE80211_NODE_BOOST 0x0080
|
|
|
|
#define streq(a,b) (strncasecmp(a, b, sizeof(b) - 1) == 0)
|
|
|
|
#undef ARRAY_SIZE
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
|
|
static int if_split_name(const char *, char **, unsigned int *);
|
|
static void vap_create(struct ifreq *);
|
|
static void vap_destroy(const char *);
|
|
static void list_stations(const char *);
|
|
static void list_scan(const char *);
|
|
static void list_channels(const char *, int);
|
|
static void list_keys(const char *);
|
|
static void list_capabilities(const char *);
|
|
static void list_wme(const char *);
|
|
static void ieee80211_status(const char *);
|
|
|
|
static void usage(void);
|
|
static int getopmode(const char *);
|
|
static int getflag(const char *);
|
|
static int get80211param(const char *, int, void *, size_t);
|
|
static int get80211priv(const char *, int, void *, size_t);
|
|
static const char *getstamode(u_int8_t);
|
|
|
|
size_t strlcat(char *, const char *, size_t);
|
|
|
|
static int verbose = 0;
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
const char *ifname, *cmd;
|
|
unsigned char bnounit = 0;
|
|
char *if_base = NULL;
|
|
unsigned int unit_res = -1;
|
|
int res = 0;
|
|
|
|
if (argc < 2 ||
|
|
strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0)
|
|
usage();
|
|
|
|
ifname = argv[1];
|
|
|
|
if (argc == 2) {
|
|
ieee80211_status(ifname);
|
|
return 0;
|
|
}
|
|
|
|
cmd = argv[2];
|
|
if (streq(cmd, "create")) {
|
|
struct ieee80211_clone_params cp;
|
|
struct ifreq ifr;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
memset(&cp, 0, sizeof(cp));
|
|
strncpy(cp.icp_name, ifname, IFNAMSIZ);
|
|
/* NB: station mode is the default */
|
|
cp.icp_opmode = IEEE80211_M_STA;
|
|
/* NB: default is to request a unique bssid/mac */
|
|
cp.icp_flags = IEEE80211_CLONE_BSSID;
|
|
|
|
while (argc > 3) {
|
|
if (strcmp(argv[3], "wlanmode") == 0) {
|
|
if (argc < 5)
|
|
usage();
|
|
cp.icp_opmode = (u_int16_t) getopmode(argv[4]);
|
|
argc--;
|
|
argv++;
|
|
} else if (strcmp(argv[3], "wlandev") == 0) {
|
|
if (argc < 5)
|
|
usage();
|
|
strncpy(ifr.ifr_name, argv[4], IFNAMSIZ);
|
|
argc--;
|
|
argv++;
|
|
} else if (strcmp(argv[3], "nounit") == 0) {
|
|
bnounit = 1;
|
|
} else {
|
|
int flag = getflag(argv[3]);
|
|
if (flag < 0)
|
|
cp.icp_flags &= ~(-flag);
|
|
else
|
|
cp.icp_flags |= flag;
|
|
}
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (ifr.ifr_name[0] == '\0')
|
|
errx(1, "no device specified with wlandev");
|
|
|
|
res = if_split_name(cp.icp_name, &if_base, &unit_res);
|
|
if (res < 0) {
|
|
err(1, "if_split_name() - malloc");
|
|
} else if ((res == 0) && (bnounit == 0)) {
|
|
/* user gave a string only and using a unit */
|
|
snprintf(cp.icp_name + strlen(if_base),
|
|
IFNAMSIZ - strlen(if_base), "%%d");
|
|
}
|
|
|
|
free(if_base);
|
|
if_base = NULL;
|
|
|
|
ifr.ifr_data = (void *) &cp;
|
|
vap_create(&ifr);
|
|
printf("%s\n", ifr.ifr_name);
|
|
} else if (streq(cmd, "destroy")) {
|
|
vap_destroy(ifname);
|
|
} else if (streq(cmd, "list")) {
|
|
if (argc > 3) {
|
|
const char *arg = argv[3];
|
|
|
|
if (streq(arg, "sta"))
|
|
list_stations(ifname);
|
|
else if (streq(arg, "scan") || streq(arg, "ap"))
|
|
list_scan(ifname);
|
|
else if (streq(arg, "chan") || streq(arg, "freq"))
|
|
list_channels(ifname, 1);
|
|
else if (streq(arg, "active"))
|
|
list_channels(ifname, 0);
|
|
else if (streq(arg, "keys"))
|
|
list_keys(ifname);
|
|
else if (streq(arg, "caps"))
|
|
list_capabilities(ifname);
|
|
else if (streq(arg, "wme"))
|
|
list_wme(ifname);
|
|
else
|
|
err(1, "unknown 'list' option: %s", arg);
|
|
} else /* NB: for compatibility */
|
|
list_stations(ifname);
|
|
} else
|
|
usage();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// if_split_name - takes a name and splits it into the longest non-numeric only string
|
|
// including the first character plus the value of the longest numeric
|
|
// string including the last character
|
|
// returns : < 0 on error
|
|
// 0 on finding only a string
|
|
// 1 on finding a numeric/unit at the end of the string
|
|
//
|
|
// NOTE Allocates memory.
|
|
static int if_split_name(const char *if_name, char **if_base_name, unsigned int *unit) {
|
|
int status = -1;
|
|
const char *p = NULL;
|
|
const char *l = NULL;
|
|
|
|
/*
|
|
* Look for the unit from end to start
|
|
*/
|
|
l = if_name + strlen(if_name) - 1;
|
|
for (p = l; p >= if_name; --p) {
|
|
if (!isdigit(*p))
|
|
break;
|
|
}
|
|
|
|
if (p < l) {
|
|
if (unit != NULL)
|
|
*unit = atoi(p + 1);
|
|
status = 1;
|
|
} else
|
|
status = 0;
|
|
|
|
/*
|
|
* Wherever the unit began, one index before it is the ending of the string
|
|
*/
|
|
if (if_base_name != NULL) {
|
|
*if_base_name = malloc(p - if_name + 2);
|
|
if (*if_base_name != NULL) {
|
|
memset(*if_base_name, 0, p - if_name + 2);
|
|
strncpy(*if_base_name, if_name, p - if_name + 1);
|
|
} else
|
|
status = -1;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
vap_create(struct ifreq *ifr)
|
|
{
|
|
int s;
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (s < 0)
|
|
err(1, "socket(SOCK_DGRAM)");
|
|
|
|
if (ioctl(s, SIOC80211IFCREATE, ifr) < 0)
|
|
err(1, "ioctl");
|
|
|
|
close(s);
|
|
}
|
|
|
|
static void
|
|
vap_destroy(const char *ifname)
|
|
{
|
|
struct ifreq ifr;
|
|
int s;
|
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (s < 0)
|
|
err(1, "socket(SOCK_DGRAM)");
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
|
if (ioctl(s, SIOC80211IFDESTROY, &ifr) < 0)
|
|
err(1, "ioctl");
|
|
close(s);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "usage: wlanconfig athX create [nounit] wlandev wifiY\n");
|
|
fprintf(stderr, " wlanmode [sta|adhoc|ap|monitor|wds|ahdemo] [uniquebssid]\n");
|
|
fprintf(stderr, "usage: wlanconfig athX destroy\n");
|
|
fprintf(stderr, "usage: wlanconfig athX list [active|ap|caps|chan|freq|keys|scan|sta|wme]\n");
|
|
exit(-1);
|
|
}
|
|
|
|
static int
|
|
getopmode(const char *s)
|
|
{
|
|
if (streq(s, "sta") || streq(s, "managed"))
|
|
return IEEE80211_M_STA;
|
|
if (streq(s, "ibss") || streq(s, "adhoc") || streq(s, "ad-hoc"))
|
|
return IEEE80211_M_IBSS;
|
|
if (streq(s, "mon"))
|
|
return IEEE80211_M_MONITOR;
|
|
if (streq(s, "ap") || streq(s, "hostap") || streq(s, "master"))
|
|
return IEEE80211_M_HOSTAP;
|
|
if (streq(s, "wds"))
|
|
return IEEE80211_M_WDS;
|
|
if (streq(s, "ahdemo"))
|
|
return IEEE80211_M_AHDEMO;
|
|
|
|
errx(1, "unknown operating mode %s", s);
|
|
/*NOTREACHED*/
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
getflag(const char *s)
|
|
{
|
|
const char *cp;
|
|
int flag = 0;
|
|
|
|
cp = (s[0] == '-' ? s + 1 : s);
|
|
if (strcmp(cp, "bssid") == 0) {
|
|
printf("WARNING: the -bssid and bssid flags are deprecated.\n");
|
|
flag = IEEE80211_CLONE_BSSID;
|
|
}
|
|
if (strcmp(cp, "uniquebssid") == 0)
|
|
flag = IEEE80211_CLONE_BSSID;
|
|
if (strcmp(cp, "nosbeacon") == 0) {
|
|
printf("WARNING: the nosbeacon flag has been deprecated.\n");
|
|
return 0; /* deprecated but skip */
|
|
}
|
|
if (flag == 0)
|
|
errx(1, "unknown create option %s", s);
|
|
return (s[0] == '-' ? -flag : flag);
|
|
}
|
|
|
|
/*
|
|
* Convert MHz frequency to IEEE channel number.
|
|
*/
|
|
static u_int
|
|
ieee80211_mhz2ieee(u_int freq)
|
|
{
|
|
if (freq == 2484)
|
|
return 14;
|
|
if (freq < 2484)
|
|
return (freq - 2407) / 5;
|
|
if (freq < 5000)
|
|
return 15 + ((freq - 2512) / 20);
|
|
return (freq - 5000) / 5;
|
|
}
|
|
|
|
/*
|
|
* Convert RSSI to dBm.
|
|
*/
|
|
static u_int
|
|
rssi2dbm(u_int rssi)
|
|
{
|
|
return rssi - 95;
|
|
}
|
|
|
|
static int
|
|
getmaxrate(uint8_t rates[15], uint8_t nrates)
|
|
{
|
|
int i, maxrate = -1;
|
|
|
|
for (i = 0; i < nrates; i++) {
|
|
int rate = rates[i] & IEEE80211_RATE_VAL;
|
|
if (rate > maxrate)
|
|
maxrate = rate;
|
|
}
|
|
return maxrate / 2;
|
|
}
|
|
|
|
static const char *
|
|
getcaps(int capinfo)
|
|
{
|
|
static char capstring[32];
|
|
char *cp = capstring;
|
|
|
|
if (capinfo & IEEE80211_CAPINFO_ESS)
|
|
*cp++ = 'E';
|
|
if (capinfo & IEEE80211_CAPINFO_IBSS)
|
|
*cp++ = 'I';
|
|
if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
|
|
*cp++ = 'c';
|
|
if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
|
|
*cp++ = 'C';
|
|
if (capinfo & IEEE80211_CAPINFO_PRIVACY)
|
|
*cp++ = 'P';
|
|
if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
|
|
*cp++ = 'S';
|
|
if (capinfo & IEEE80211_CAPINFO_PBCC)
|
|
*cp++ = 'B';
|
|
if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
|
|
*cp++ = 'A';
|
|
if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
|
|
*cp++ = 's';
|
|
if (capinfo & IEEE80211_CAPINFO_RSN)
|
|
*cp++ = 'R';
|
|
if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
|
|
*cp++ = 'D';
|
|
*cp = '\0';
|
|
return capstring;
|
|
}
|
|
|
|
static const char *
|
|
getathcaps(int capinfo)
|
|
{
|
|
static char capstring[32];
|
|
char *cp = capstring;
|
|
|
|
if (capinfo & IEEE80211_NODE_TURBOP)
|
|
*cp++ = 'D';
|
|
if (capinfo & IEEE80211_NODE_COMP)
|
|
*cp++ = 'C';
|
|
if (capinfo & IEEE80211_NODE_FF)
|
|
*cp++ = 'F';
|
|
if (capinfo & IEEE80211_NODE_XR)
|
|
*cp++ = 'X';
|
|
if (capinfo & IEEE80211_NODE_AR)
|
|
*cp++ = 'A';
|
|
if (capinfo & IEEE80211_NODE_BOOST)
|
|
*cp++ = 'T';
|
|
*cp = '\0';
|
|
return capstring;
|
|
}
|
|
|
|
static const char *
|
|
getstamode(u_int8_t opmode)
|
|
{
|
|
if (opmode == IEEE80211_STA_OPMODE_XR)
|
|
return "XR";
|
|
|
|
return "Normal";
|
|
}
|
|
|
|
static void
|
|
printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
|
|
{
|
|
printf("%s", tag);
|
|
if (verbose) {
|
|
maxlen -= strlen(tag)+2;
|
|
if (2*ielen > maxlen)
|
|
maxlen--;
|
|
printf("<");
|
|
for (; ielen > 0; ie++, ielen--) {
|
|
if (maxlen-- <= 0)
|
|
break;
|
|
printf("%02x", *ie);
|
|
}
|
|
if (ielen != 0)
|
|
printf("-");
|
|
printf(">");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy the ssid string contents into buf, truncating to fit. If the
|
|
* ssid is entirely printable then just copy intact. Otherwise convert
|
|
* to hexadecimal. If the result is truncated then replace the last
|
|
* three characters with "...".
|
|
*/
|
|
static int
|
|
copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
|
|
{
|
|
const u_int8_t *p;
|
|
int maxlen;
|
|
int i;
|
|
|
|
if (essid_len > bufsize)
|
|
maxlen = bufsize;
|
|
else
|
|
maxlen = essid_len;
|
|
/* determine printable or not */
|
|
for (i = 0, p = essid; i < maxlen; i++, p++) {
|
|
if (*p < ' ' || *p > 0x7e)
|
|
break;
|
|
}
|
|
if (i != maxlen) { /* not printable, print as hex */
|
|
if (bufsize < 3)
|
|
return 0;
|
|
#if 0
|
|
strlcpy(buf, "0x", bufsize);
|
|
#else
|
|
strncpy(buf, "0x", bufsize);
|
|
#endif
|
|
bufsize -= 2;
|
|
p = essid;
|
|
for (i = 0; i < maxlen && bufsize >= 2; i++) {
|
|
sprintf(&buf[2 + 2 * i], "%02x", *p++);
|
|
bufsize -= 2;
|
|
}
|
|
maxlen = 2 + 2 * i;
|
|
} else { /* printable, truncate as needed */
|
|
memcpy(buf, essid, maxlen);
|
|
}
|
|
if (maxlen != essid_len)
|
|
memcpy(buf+maxlen - 3, "...", 3);
|
|
return maxlen;
|
|
}
|
|
|
|
/* unaligned little endian access */
|
|
#define LE_READ_4(p) \
|
|
((u_int32_t) \
|
|
((((const u_int8_t *)(p))[0] ) | \
|
|
(((const u_int8_t *)(p))[1] << 8) | \
|
|
(((const u_int8_t *)(p))[2] << 16) | \
|
|
(((const u_int8_t *)(p))[3] << 24)))
|
|
|
|
static __inline int
|
|
iswpaoui(const u_int8_t *frm)
|
|
{
|
|
return frm[1] > 3 && LE_READ_4(frm + 2) == ((WPA_OUI_TYPE << 24) | WPA_OUI);
|
|
}
|
|
|
|
static __inline int
|
|
iswmeoui(const u_int8_t *frm)
|
|
{
|
|
return frm[1] > 3 && LE_READ_4(frm + 2) == ((WME_OUI_TYPE << 24) | WME_OUI);
|
|
}
|
|
|
|
static __inline int
|
|
isatherosoui(const u_int8_t *frm)
|
|
{
|
|
return frm[1] > 3 && LE_READ_4(frm + 2) == ((ATH_OUI_TYPE << 24) | ATH_OUI);
|
|
}
|
|
|
|
static void
|
|
printies(const u_int8_t *vp, int ielen, int maxcols)
|
|
{
|
|
while (ielen > 0) {
|
|
switch (vp[0]) {
|
|
case IEEE80211_ELEMID_VENDOR:
|
|
if (iswpaoui(vp))
|
|
printie(" WPA", vp, 2 + vp[1], maxcols);
|
|
else if (iswmeoui(vp))
|
|
printie(" WME", vp, 2 + vp[1], maxcols);
|
|
else if (isatherosoui(vp))
|
|
printie(" ATH", vp, 2 + vp[1], maxcols);
|
|
else
|
|
printie(" VEN", vp, 2 + vp[1], maxcols);
|
|
break;
|
|
case IEEE80211_ELEMID_RSN:
|
|
printie(" RSN", vp, 2 + vp[1], maxcols);
|
|
break;
|
|
default:
|
|
printie(" ???", vp, 2 + vp[1], maxcols);
|
|
break;
|
|
}
|
|
ielen -= 2 + vp[1];
|
|
vp += 2 + vp[1];
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
ieee80211_ntoa(const uint8_t mac[IEEE80211_ADDR_LEN])
|
|
{
|
|
static char a[18];
|
|
int i;
|
|
|
|
i = snprintf(a, sizeof(a), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
return (i < 17 ? NULL : a);
|
|
}
|
|
|
|
static void
|
|
list_stations(const char *ifname)
|
|
{
|
|
uint8_t buf[24*1024];
|
|
struct iwreq iwr;
|
|
uint8_t *cp;
|
|
int s, len;
|
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (s < 0)
|
|
err(1, "socket(SOCK_DGRAM)");
|
|
|
|
(void) memset(&iwr, 0, sizeof(iwr));
|
|
(void) strncpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));
|
|
iwr.u.data.pointer = (void *) buf;
|
|
iwr.u.data.length = sizeof(buf);
|
|
if (ioctl(s, IEEE80211_IOCTL_STA_INFO, &iwr) < 0)
|
|
errx(1, "unable to get station information");
|
|
len = iwr.u.data.length;
|
|
if (len < sizeof(struct ieee80211req_sta_info))
|
|
return;
|
|
close(s);
|
|
|
|
printf("%-17.17s %4s %4s %4s %4s %4s %5s %6s %7s %6s %7s %4s %5s %3s %8s %8s\n",
|
|
"ADDR",
|
|
"AID",
|
|
"CHAN",
|
|
"RATE",
|
|
"RSSI",
|
|
"DBM",
|
|
"IDLE",
|
|
"TXSEQ",
|
|
"TXFRAG",
|
|
"RXSEQ",
|
|
"RXFRAG",
|
|
"CAPS",
|
|
"ACAPS",
|
|
"ERP",
|
|
"STATE",
|
|
"MODE");
|
|
cp = buf;
|
|
do {
|
|
struct ieee80211req_sta_info *si;
|
|
uint8_t *vp;
|
|
|
|
si = (struct ieee80211req_sta_info *) cp;
|
|
vp = (u_int8_t *)(si+1);
|
|
printf("%s %4u %4d %3dM %4d %4d %5d %6d %7d %6d %7d %-4.4s %-5.5s %3x %8x %8s",
|
|
ieee80211_ntoa(si->isi_macaddr),
|
|
IEEE80211_AID(si->isi_associd),
|
|
ieee80211_mhz2ieee(si->isi_freq),
|
|
(si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2,
|
|
si->isi_rssi,
|
|
rssi2dbm(si->isi_rssi),
|
|
si->isi_inact,
|
|
(si->isi_txseqs[0] & IEEE80211_SEQ_SEQ_MASK)
|
|
>> IEEE80211_SEQ_SEQ_SHIFT,
|
|
si->isi_txseqs[0] & IEEE80211_SEQ_FRAG_MASK,
|
|
(si->isi_rxseqs[0] & IEEE80211_SEQ_SEQ_MASK)
|
|
>> IEEE80211_SEQ_SEQ_SHIFT,
|
|
si->isi_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK,
|
|
getcaps(si->isi_capinfo),
|
|
getathcaps(si->isi_athflags),
|
|
si->isi_erp,
|
|
si->isi_state,
|
|
getstamode(si->isi_opmode));
|
|
printies(vp, si->isi_ie_len, 24);
|
|
printf("\n");
|
|
if (si->isi_uapsd) {
|
|
printf(" UAPSD QoSInfo: 0x%02x, ",
|
|
si->isi_uapsd);
|
|
printf("(VO,VI,BE,BK) = (%d,%d,%d,%d), MaxSpLimit = %s\n",
|
|
WME_UAPSD_AC_ENABLED(WME_AC_VO, si->isi_uapsd) ? 1 : 0,
|
|
WME_UAPSD_AC_ENABLED(WME_AC_VI, si->isi_uapsd) ? 1 : 0,
|
|
WME_UAPSD_AC_ENABLED(WME_AC_BE, si->isi_uapsd) ? 1 : 0,
|
|
WME_UAPSD_AC_ENABLED(WME_AC_BK, si->isi_uapsd) ? 1 : 0,
|
|
WME_UAPSD_MAXSP(si->isi_uapsd) == 1 ? "2" :
|
|
WME_UAPSD_MAXSP(si->isi_uapsd) == 2 ? "4" :
|
|
WME_UAPSD_MAXSP(si->isi_uapsd) == 3 ? "6" : "NoLimit");
|
|
}
|
|
cp += si->isi_len;
|
|
len -= si->isi_len;
|
|
} while (len >= sizeof(struct ieee80211req_sta_info));
|
|
}
|
|
|
|
/* unaligned little endian access */
|
|
#define LE_READ_4(p) \
|
|
((u_int32_t) \
|
|
((((const u_int8_t *)(p))[0] ) | \
|
|
(((const u_int8_t *)(p))[1] << 8) | \
|
|
(((const u_int8_t *)(p))[2] << 16) | \
|
|
(((const u_int8_t *)(p))[3] << 24)))
|
|
|
|
static void
|
|
list_scan(const char *ifname)
|
|
{
|
|
uint8_t buf[24 * 1024];
|
|
char ssid[14];
|
|
uint8_t *cp;
|
|
int len;
|
|
|
|
len = get80211priv(ifname, IEEE80211_IOCTL_SCAN_RESULTS,
|
|
buf, sizeof(buf));
|
|
if (len == -1)
|
|
errx(1, "unable to get scan results");
|
|
if (len < sizeof(struct ieee80211req_scan_result))
|
|
return;
|
|
|
|
printf("%-14.14s %-17.17s %4s %4s %-5s %3s %4s\n",
|
|
"SSID",
|
|
"BSSID",
|
|
"CHAN",
|
|
"RATE",
|
|
"S:N",
|
|
"INT",
|
|
"CAPS");
|
|
cp = buf;
|
|
do {
|
|
struct ieee80211req_scan_result *sr;
|
|
uint8_t *vp;
|
|
|
|
sr = (struct ieee80211req_scan_result *) cp;
|
|
vp = (u_int8_t *)(sr+1);
|
|
printf("%-14.*s %s %3d %3dM %2d:%-2d %3d %-4.4s",
|
|
copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len),
|
|
ssid,
|
|
ieee80211_ntoa(sr->isr_bssid),
|
|
ieee80211_mhz2ieee(sr->isr_freq),
|
|
getmaxrate(sr->isr_rates, sr->isr_nrates),
|
|
(int8_t) sr->isr_rssi, sr->isr_noise,
|
|
sr->isr_intval,
|
|
getcaps(sr->isr_capinfo));
|
|
printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
|
|
printf("\n");
|
|
cp += sr->isr_len, len -= sr->isr_len;
|
|
} while (len >= sizeof(struct ieee80211req_scan_result));
|
|
}
|
|
|
|
static void
|
|
print_chaninfo(const struct ieee80211_channel *c)
|
|
{
|
|
char buf[14];
|
|
|
|
buf[0] = '\0';
|
|
if (IEEE80211_IS_CHAN_FHSS(c))
|
|
strlcat(buf, " FHSS", sizeof(buf));
|
|
if (IEEE80211_IS_CHAN_A(c))
|
|
strlcat(buf, " 11a", sizeof(buf));
|
|
/* XXX 11g schizophrenia */
|
|
if (IEEE80211_IS_CHAN_G(c) ||
|
|
IEEE80211_IS_CHAN_PUREG(c))
|
|
strlcat(buf, " 11g", sizeof(buf));
|
|
else if (IEEE80211_IS_CHAN_B(c))
|
|
strlcat(buf, " 11b", sizeof(buf));
|
|
if (IEEE80211_IS_CHAN_STURBO(c))
|
|
strlcat(buf, " Static", sizeof(buf));
|
|
if (IEEE80211_IS_CHAN_DTURBO(c))
|
|
strlcat(buf, " Dynamic", sizeof(buf));
|
|
if (IEEE80211_IS_CHAN_HALF(c))
|
|
strlcat(buf, " Half", sizeof(buf));
|
|
if (IEEE80211_IS_CHAN_QUARTER(c))
|
|
strlcat(buf, " Quarter", sizeof(buf));
|
|
printf("Channel %3u : %u%c%c Mhz%-14.14s",
|
|
c->ic_ieee, c->ic_freq,
|
|
IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ',
|
|
IEEE80211_IS_CHAN_RADAR(c) ? '!' : ' ',
|
|
buf);
|
|
}
|
|
|
|
static void
|
|
list_channels(const char *ifname, int allchans)
|
|
{
|
|
struct ieee80211req_chaninfo chans;
|
|
struct ieee80211req_chaninfo achans;
|
|
const struct ieee80211_channel *c;
|
|
int i, half;
|
|
|
|
if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) < 0)
|
|
errx(1, "unable to get channel information");
|
|
if (!allchans) {
|
|
uint8_t active[32];
|
|
|
|
if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANLIST, &active, sizeof(active)) < 0)
|
|
errx(1, "unable to get active channel list");
|
|
memset(&achans, 0, sizeof(achans));
|
|
for (i = 0; i < chans.ic_nchans; i++) {
|
|
c = &chans.ic_chans[i];
|
|
if (isset(active, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
|
|
achans.ic_chans[achans.ic_nchans++] = *c;
|
|
}
|
|
} else
|
|
achans = chans;
|
|
half = achans.ic_nchans / 2;
|
|
if (achans.ic_nchans % 2)
|
|
half++;
|
|
for (i = 0; i < achans.ic_nchans / 2; i++) {
|
|
print_chaninfo(&achans.ic_chans[i]);
|
|
print_chaninfo(&achans.ic_chans[half + i]);
|
|
printf("\n");
|
|
}
|
|
if (achans.ic_nchans % 2) {
|
|
print_chaninfo(&achans.ic_chans[i]);
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
list_keys(const char *ifname)
|
|
{
|
|
char cmd[256];
|
|
puts("[list_keys not implemented (yet). Spawning iwlist...]");
|
|
strcpy(cmd, "iwlist ");
|
|
strcat(cmd, ifname);
|
|
strcat(cmd, " key");
|
|
system(cmd);
|
|
}
|
|
|
|
#define IEEE80211_C_BITS \
|
|
"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\7FF\10TURBOP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
|
|
"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
|
|
"\31WPA2\32BURST\33WME"
|
|
|
|
/*
|
|
* Print a value a la the %b format of the kernel's printf
|
|
*/
|
|
static void
|
|
printb(const char *s, unsigned v, const char *bits)
|
|
{
|
|
int i, any = 0;
|
|
char c;
|
|
|
|
if (bits && *bits == 8)
|
|
printf("%s=%o", s, v);
|
|
else
|
|
printf("%s=%x", s, v);
|
|
bits++;
|
|
if (bits) {
|
|
putchar('<');
|
|
while ((i = *bits++) != '\0') {
|
|
if (v & (1 << (i-1))) {
|
|
if (any)
|
|
putchar(',');
|
|
any = 1;
|
|
for (; (c = *bits) > 32; bits++)
|
|
putchar(c);
|
|
} else
|
|
for (; *bits > 32; bits++);
|
|
}
|
|
putchar('>');
|
|
}
|
|
}
|
|
|
|
static void
|
|
list_capabilities(const char *ifname)
|
|
{
|
|
u_int32_t caps;
|
|
|
|
if (get80211param(ifname, IEEE80211_PARAM_DRIVER_CAPS, &caps,
|
|
sizeof(caps)) < 0)
|
|
errx(1, "unable to get driver capabilities");
|
|
printb(ifname, caps, IEEE80211_C_BITS);
|
|
putchar('\n');
|
|
}
|
|
|
|
static void
|
|
list_wme(const char *ifname)
|
|
{
|
|
#define GETPARAM() \
|
|
(get80211priv(ifname, IEEE80211_IOCTL_GETWMMPARAMS, param, sizeof(param)) != -1)
|
|
static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
|
|
int param[3];
|
|
int ac;
|
|
|
|
param[2] = 0; /* channel params */
|
|
for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
|
|
again:
|
|
if (param[2] != 0)
|
|
printf("\t%s", " ");
|
|
else
|
|
printf("\t%s", acnames[ac]);
|
|
|
|
param[1] = ac;
|
|
|
|
/* show WME BSS parameters */
|
|
param[0] = IEEE80211_WMMPARAMS_CWMIN;
|
|
if (GETPARAM())
|
|
printf(" cwmin %2u", param[0]);
|
|
param[0] = IEEE80211_WMMPARAMS_CWMAX;
|
|
if (GETPARAM())
|
|
printf(" cwmax %2u", param[0]);
|
|
param[0] = IEEE80211_WMMPARAMS_AIFS;
|
|
if (GETPARAM())
|
|
printf(" aifs %2u", param[0]);
|
|
param[0] = IEEE80211_WMMPARAMS_TXOPLIMIT;
|
|
if (GETPARAM())
|
|
printf(" txopLimit %3u", param[0]);
|
|
param[0] = IEEE80211_WMMPARAMS_ACM;
|
|
if (GETPARAM()) {
|
|
if (param[0])
|
|
printf(" acm");
|
|
else if (verbose)
|
|
printf(" -acm");
|
|
}
|
|
/* !BSS only */
|
|
if (param[2] == 0) {
|
|
param[0] = IEEE80211_WMMPARAMS_NOACKPOLICY;
|
|
if (GETPARAM()) {
|
|
if (param[0])
|
|
printf(" -ack");
|
|
else if (verbose)
|
|
printf(" ack");
|
|
}
|
|
}
|
|
printf("\n");
|
|
if (param[2] == 0) {
|
|
param[2] = 1; /* bss params */
|
|
goto again;
|
|
} else
|
|
param[2] = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ieee80211_status(const char *ifname)
|
|
{
|
|
/* XXX fill in */
|
|
char cmd[256];
|
|
puts("[status not implemented (yet). Spawning iwconfig...]");
|
|
strcpy(cmd, "iwconfig ");
|
|
strcat(cmd, ifname);
|
|
system(cmd);
|
|
}
|
|
|
|
static int
|
|
getsocket(void)
|
|
{
|
|
static int s = -1;
|
|
|
|
if (s < 0) {
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (s < 0)
|
|
err(1, "socket(SOCK_DGRAM)");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static int
|
|
get80211param(const char *ifname, int param, void *data, size_t len)
|
|
{
|
|
struct iwreq iwr;
|
|
|
|
memset(&iwr, 0, sizeof(iwr));
|
|
strncpy(iwr.ifr_name, ifname, IFNAMSIZ);
|
|
iwr.u.mode = param;
|
|
|
|
if (ioctl(getsocket(), IEEE80211_IOCTL_GETPARAM, &iwr) < 0) {
|
|
perror("ioctl[IEEE80211_IOCTL_GETPARAM]");
|
|
return -1;
|
|
}
|
|
if (len < IFNAMSIZ) {
|
|
/*
|
|
* Argument data fits inline; put it there.
|
|
*/
|
|
memcpy(data, iwr.u.name, len);
|
|
}
|
|
return iwr.u.data.length;
|
|
}
|
|
|
|
#define IOCTL_ERR(x) [x - SIOCIWFIRSTPRIV] = "ioctl[" #x "]"
|
|
static int
|
|
do80211priv(struct iwreq *iwr, const char *ifname, int op, void *data, size_t len)
|
|
{
|
|
memset(iwr, 0, sizeof(struct iwreq));
|
|
strncpy(iwr->ifr_name, ifname, IFNAMSIZ);
|
|
if (len < IFNAMSIZ) {
|
|
/*
|
|
* Argument data fits inline; put it there.
|
|
*/
|
|
memcpy(iwr->u.name, data, len);
|
|
} else {
|
|
/*
|
|
* Argument data too big for inline transfer; setup a
|
|
* parameter block instead; the kernel will transfer
|
|
* the data for the driver.
|
|
*/
|
|
iwr->u.data.pointer = data;
|
|
iwr->u.data.length = len;
|
|
}
|
|
|
|
if (ioctl(getsocket(), op, iwr) < 0) {
|
|
static const char *opnames[] = {
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETPARAM),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETPARAM),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETMODE),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETMODE),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETWMMPARAMS),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETWMMPARAMS),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETCHANLIST),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETCHANLIST),
|
|
IOCTL_ERR(IEEE80211_IOCTL_CHANSWITCH),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETCHANINFO),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETOPTIE),
|
|
IOCTL_ERR(IEEE80211_IOCTL_GETOPTIE),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETMLME),
|
|
IOCTL_ERR(IEEE80211_IOCTL_RADAR),
|
|
IOCTL_ERR(IEEE80211_IOCTL_SETKEY),
|
|
IOCTL_ERR(IEEE80211_IOCTL_DELKEY),
|
|
IOCTL_ERR(IEEE80211_IOCTL_HALMAP),
|
|
IOCTL_ERR(IEEE80211_IOCTL_ADDMAC),
|
|
IOCTL_ERR(IEEE80211_IOCTL_DELMAC),
|
|
IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC),
|
|
IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC),
|
|
IOCTL_ERR(IEEE80211_IOCTL_READREG),
|
|
IOCTL_ERR(IEEE80211_IOCTL_WRITEREG),
|
|
};
|
|
op -= SIOCIWFIRSTPRIV;
|
|
if (0 <= op && op < ARRAY_SIZE(opnames))
|
|
perror(opnames[op]);
|
|
else
|
|
perror("ioctl[unknown???]");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get80211priv(const char *ifname, int op, void *data, size_t len)
|
|
{
|
|
struct iwreq iwr;
|
|
|
|
if (do80211priv(&iwr, ifname, op, data, len) < 0)
|
|
return -1;
|
|
if (len < IFNAMSIZ)
|
|
memcpy(data, iwr.u.name, len);
|
|
return iwr.u.data.length;
|
|
}
|
|
|
|
/*
|
|
* Appends src to string dst of size siz (unlike strncat, siz is the
|
|
* full size of dst, not space left). At most siz-1 characters
|
|
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
|
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
|
* If retval >= siz, truncation occurred.
|
|
*/
|
|
size_t
|
|
strlcat(char *dst, const char *src, size_t siz)
|
|
{
|
|
char *d = dst;
|
|
const char *s = src;
|
|
size_t n = siz;
|
|
size_t dlen;
|
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
|
while (n-- != 0 && *d != '\0')
|
|
d++;
|
|
dlen = d - dst;
|
|
n = siz - dlen;
|
|
|
|
if (n == 0)
|
|
return(dlen + strlen(s));
|
|
while (*s != '\0') {
|
|
if (n != 1) {
|
|
*d++ = *s;
|
|
n--;
|
|
}
|
|
s++;
|
|
}
|
|
*d = '\0';
|
|
|
|
return(dlen + (s - src)); /* count does not include NUL */
|
|
}
|
|
|