mirror of
https://github.com/proski/madwifi
synced 2024-11-25 15:59:39 +03:00
76cade5dac
git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3710 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 */
|
|
}
|
|
|