iw modified for FTM

This is a modified version from
https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/commit/?id=
f32884808324ea38732e278ea2df910f96348a38.

Referred from https://bugzilla.kernel.org/show_bug.cgi?id=197187.
This commit is contained in:
HappyZ 2018-01-23 15:11:31 -06:00
commit 77eaa55d96
45 changed files with 18900 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
iw
*~
*.o
.config
version.c
iw.8.gz
*-stamp

22
Android.mk Normal file
View File

@ -0,0 +1,22 @@
LOCAL_PATH := $(call my-dir)
IW_SOURCE_DIR := $(LOCAL_PATH)
include $(CLEAR_VARS)
IW_ANDROID_BUILD=y
NO_PKG_CONFIG=y
include $(LOCAL_PATH)/Makefile
LOCAL_SRC_FILES := $(patsubst %.o,%.c,$(OBJS))
LOCAL_CFLAGS += -DCONFIG_LIBNL20
LOCAL_LDFLAGS := -Wl,--no-gc-sections
#LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libnl
LOCAL_MODULE := iw
$(IW_SOURCE_DIR)/version.c:
$(IW_SOURCE_DIR)/version.sh $(IW_SOURCE_DIR)/version.c
include $(BUILD_EXECUTABLE)

49
CONTRIBUTING Normal file
View File

@ -0,0 +1,49 @@
This project embraces the Developer Certificate of Origin (DCO) for
contributions. This means you must agree to the following prior to submitting
patches, if you agree with this developer certificate you acknowledge this by
adding a Signed-off-by tag to your patch commit log. Every submitted patch
must have this.
The source for the DCO:
http://developercertificate.org/
-----------------------------------------------------------------------
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

16
COPYING Normal file
View File

@ -0,0 +1,16 @@
Copyright (c) 2007, 2008 Johannes Berg
Copyright (c) 2007 Andy Lutomirski
Copyright (c) 2007 Mike Kershaw
Copyright (c) 2008-2009 Luis R. Rodriguez
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

128
Makefile Normal file
View File

@ -0,0 +1,128 @@
MAKEFLAGS += --no-print-directory
PREFIX ?= /usr
SBINDIR ?= $(PREFIX)/sbin
MANDIR ?= $(PREFIX)/share/man
PKG_CONFIG ?= pkg-config
MKDIR ?= mkdir -p
INSTALL ?= install
CC ?= "gcc"
CFLAGS ?= -O2 -g
CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration -Wsign-compare
OBJS = iw.o genl.o event.o info.o phy.o \
interface.o ibss.o station.o survey.o util.o ocb.o \
mesh.o mpath.o mpp.o scan.o reg.o version.o \
reason.o status.o connect.o link.o offch.o ps.o cqm.o \
bitrate.o wowlan.o coalesce.o roc.o p2p.o vendor.o mgmt.o \
ap.o measurements.o
OBJS += sections.o
OBJS-$(HWSIM) += hwsim.o
OBJS += $(OBJS-y) $(OBJS-Y)
ALL = iw
ifeq ($(NO_PKG_CONFIG),)
NL3xFOUND := $(shell $(PKG_CONFIG) --atleast-version=3.2 libnl-3.0 && echo Y)
ifneq ($(NL3xFOUND),Y)
NL31FOUND := $(shell $(PKG_CONFIG) --exact-version=3.1 libnl-3.1 && echo Y)
ifneq ($(NL31FOUND),Y)
NL3FOUND := $(shell $(PKG_CONFIG) --atleast-version=3 libnl-3.0 && echo Y)
ifneq ($(NL3FOUND),Y)
NL2FOUND := $(shell $(PKG_CONFIG) --atleast-version=2 libnl-2.0 && echo Y)
ifneq ($(NL2FOUND),Y)
NL1FOUND := $(shell $(PKG_CONFIG) --atleast-version=1 libnl-1 && echo Y)
endif
endif
endif
endif
ifeq ($(NL1FOUND),Y)
NLLIBNAME = libnl-1
endif
ifeq ($(NL2FOUND),Y)
CFLAGS += -DCONFIG_LIBNL20
LIBS += -lnl-genl
NLLIBNAME = libnl-2.0
endif
ifeq ($(NL3xFOUND),Y)
# libnl 3.2 might be found as 3.2 and 3.0
NL3FOUND = N
CFLAGS += -DCONFIG_LIBNL30
LIBS += -lnl-genl-3
NLLIBNAME = libnl-3.0
endif
ifeq ($(NL3FOUND),Y)
CFLAGS += -DCONFIG_LIBNL30
LIBS += -lnl-genl
NLLIBNAME = libnl-3.0
endif
# nl-3.1 has a broken libnl-gnl-3.1.pc file
# as show by pkg-config --debug --libs --cflags --exact-version=3.1 libnl-genl-3.1;echo $?
ifeq ($(NL31FOUND),Y)
CFLAGS += -DCONFIG_LIBNL30
LIBS += -lnl-genl
NLLIBNAME = libnl-3.1
endif
ifeq ($(NLLIBNAME),)
$(error Cannot find development files for any supported version of libnl)
endif
LIBS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME))
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME))
endif # NO_PKG_CONFIG
ifeq ($(V),1)
Q=
NQ=true
else
Q=@
NQ=echo
endif
all: $(ALL)
VERSION_OBJS := $(filter-out version.o, $(OBJS))
version.c: version.sh $(patsubst %.o,%.c,$(VERSION_OBJS)) nl80211.h iw.h Makefile \
$(wildcard .git/index .git/refs/tags)
@$(NQ) ' GEN ' $@
$(Q)./version.sh $@
%.o: %.c iw.h nl80211.h
@$(NQ) ' CC ' $@
$(Q)$(CC) $(CFLAGS) -c -o $@ $<
ifeq ($(IW_ANDROID_BUILD),)
iw: $(OBJS)
@$(NQ) ' CC ' iw
$(Q)$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o iw
endif
check:
$(Q)$(MAKE) all CC="REAL_CC=$(CC) CHECK=\"sparse -Wall\" cgcc"
%.gz: %
@$(NQ) ' GZIP' $<
$(Q)gzip < $< > $@
install: iw iw.8.gz
@$(NQ) ' INST iw'
$(Q)$(MKDIR) $(DESTDIR)$(SBINDIR)
$(Q)$(INSTALL) -m 755 iw $(DESTDIR)$(SBINDIR)
@$(NQ) ' INST iw.8'
$(Q)$(MKDIR) $(DESTDIR)$(MANDIR)/man8/
$(Q)$(INSTALL) -m 644 iw.8.gz $(DESTDIR)$(MANDIR)/man8/
clean:
$(Q)rm -f iw *.o *~ *.gz version.c *-stamp

15
README Normal file
View File

@ -0,0 +1,15 @@
This is 'iw', a tool to use nl80211.
To build iw, just enter 'make'. If that fails, set the
PKG_CONFIG_PATH environment variable to allow the Makefile
to find libnl.
'iw' is currently maintained at http://git.sipsolutions.net/iw.git/,
some more documentation is available at
http://wireless.kernel.org/en/users/Documentation/iw.
Please send all patches to Johannes Berg <johannes@sipsolutions.net>
and CC linux-wireless@vger.kernel.org for community review.

139
ap.c Normal file
View File

@ -0,0 +1,139 @@
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(ap);
static int handle_start_ap(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int res, parsed;
char *end;
int val, len;
char buf[2304];
if (argc < 6)
return 1;
/* SSID */
NLA_PUT(msg, NL80211_ATTR_SSID, strlen(argv[0]), argv[0]);
argv++;
argc--;
/* chandef */
res = parse_freqchan(&chandef, false, argc, argv, &parsed);
if (res)
return res;
argc -= parsed;
argv += parsed;
res = put_chandef(msg, &chandef);
if (res)
return res;
/* beacon interval */
val = strtoul(argv[0], &end, 10);
if (*end != '\0')
return -EINVAL;
NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, val);
argv++;
argc--;
/* dtim */
val = strtoul(argv[0], &end, 10);
if (*end != '\0')
return -EINVAL;
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, val);
argv++;
argc--;
if (strcmp(argv[0], "hidden-ssid") == 0) {
argc--;
argv++;
NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
NL80211_HIDDEN_SSID_ZERO_LEN);
} else if (strcmp(argv[0], "zeroed-ssid") == 0) {
argc--;
argv++;
NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
NL80211_HIDDEN_SSID_ZERO_CONTENTS);
}
/* beacon head must be provided */
if (strcmp(argv[0], "head") != 0)
return 1;
argv++;
argc--;
len = strlen(argv[0]);
if (!len || (len % 2))
return -EINVAL;
if (!hex2bin(&argv[0][0], buf))
return -EINVAL;
NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, (len / 2), &buf);
argv++;
argc--;
if (!argc)
return 0;
/* tail is optional */
if (strcmp(argv[0], "tail") == 0) {
argv++;
argc--;
if (!argc)
return -EINVAL;
len = strlen(argv[0]);
if (!len || (len % 2))
return -EINVAL;
if (!hex2bin(&argv[0][0], buf))
return -EINVAL;
NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, (len / 2), &buf);
argv++;
argc--;
}
if (!argc)
return 0;
if (strcmp(*argv, "key") != 0 && strcmp(*argv, "keys") != 0)
return 1;
argv++;
argc--;
return parse_keys(msg, argv, argc);
nla_put_failure:
return -ENOSPC;
}
COMMAND(ap, start, "",
NL80211_CMD_NEW_BEACON, 0, CIB_NETDEV, handle_start_ap,
"<SSID> <control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]"
" <beacon interval in TU> <DTIM period> [hidden-ssid|zeroed-ssid] head"
" <beacon head in hexadecimal> [tail <beacon tail in hexadecimal>]"
" [key0:abcde d:1:6162636465]\n");
static int handle_stop_ap(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return 0;
}
COMMAND(ap, stop, "",
NL80211_CMD_DEL_BEACON, 0, CIB_NETDEV, handle_stop_ap,
"Stop AP functionality\n");

267
bitrate.c Normal file
View File

@ -0,0 +1,267 @@
#include <errno.h>
#include "nl80211.h"
#include "iw.h"
static int parse_vht_chunk(const char *arg, __u8 *nss, __u16 *mcs)
{
unsigned int count, i;
unsigned int inss, mcs_start, mcs_end, tab[10];
*nss = 0; *mcs = 0;
if (strchr(arg, '-')) {
/* Format: NSS:MCS_START-MCS_END */
count = sscanf(arg, "%u:%u-%u", &inss, &mcs_start, &mcs_end);
if (count != 3)
return 0;
if (inss < 1 || inss > NL80211_VHT_NSS_MAX)
return 0;
if (mcs_start > mcs_end)
return 0;
if (mcs_start > 9 || mcs_end > 9)
return 0;
*nss = inss;
for (i = mcs_start; i <= mcs_end; i++)
*mcs |= 1 << i;
} else {
/* Format: NSS:MCSx,MCSy,... */
count = sscanf(arg, "%u:%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", &inss,
&tab[0], &tab[1], &tab[2], &tab[3], &tab[4], &tab[5],
&tab[6], &tab[7], &tab[8], &tab[9]);
if (count < 2)
return 0;
if (inss < 1 || inss > NL80211_VHT_NSS_MAX)
return 0;
*nss = inss;
for (i = 0; i < count - 1; i++) {
if (tab[i] > 9)
return 0;
*mcs |= 1 << tab[i];
}
}
return 1;
}
static int setup_vht(struct nl80211_txrate_vht *txrate_vht,
int argc, char **argv)
{
__u8 nss;
__u16 mcs;
int i;
memset(txrate_vht, 0, sizeof(*txrate_vht));
for (i = 0; i < argc; i++) {
if(!parse_vht_chunk(argv[i], &nss, &mcs))
return 0;
nss--;
txrate_vht->mcs[nss] |= mcs;
}
return 1;
}
#define VHT_ARGC_MAX 100
static int handle_bitrates(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
struct nlattr *nl_rates, *nl_band;
int i;
bool have_legacy_24 = false, have_legacy_5 = false;
uint8_t legacy_24[32], legacy_5[32];
int n_legacy_24 = 0, n_legacy_5 = 0;
uint8_t *legacy = NULL;
int *n_legacy = NULL;
bool have_ht_mcs_24 = false, have_ht_mcs_5 = false;
bool have_vht_mcs_24 = false, have_vht_mcs_5 = false;
uint8_t ht_mcs_24[77], ht_mcs_5[77];
int n_ht_mcs_24 = 0, n_ht_mcs_5 = 0;
struct nl80211_txrate_vht txrate_vht_24 = {};
struct nl80211_txrate_vht txrate_vht_5 = {};
uint8_t *mcs = NULL;
int *n_mcs = NULL;
char *vht_argv_5[VHT_ARGC_MAX] = {}; char *vht_argv_24[VHT_ARGC_MAX] = {};
char **vht_argv = NULL;
int vht_argc_5 = 0; int vht_argc_24 = 0;
int *vht_argc = NULL;
int sgi_24 = 0, sgi_5 = 0, lgi_24 = 0, lgi_5 = 0;
enum {
S_NONE,
S_LEGACY,
S_HT,
S_VHT,
S_GI,
} parser_state = S_NONE;
for (i = 0; i < argc; i++) {
char *end;
double tmpd;
long tmpl;
if (strcmp(argv[i], "legacy-2.4") == 0) {
if (have_legacy_24)
return 1;
parser_state = S_LEGACY;
legacy = legacy_24;
n_legacy = &n_legacy_24;
have_legacy_24 = true;
} else if (strcmp(argv[i], "legacy-5") == 0) {
if (have_legacy_5)
return 1;
parser_state = S_LEGACY;
legacy = legacy_5;
n_legacy = &n_legacy_5;
have_legacy_5 = true;
}
else if (strcmp(argv[i], "ht-mcs-2.4") == 0) {
if (have_ht_mcs_24)
return 1;
parser_state = S_HT;
mcs = ht_mcs_24;
n_mcs = &n_ht_mcs_24;
have_ht_mcs_24 = true;
} else if (strcmp(argv[i], "ht-mcs-5") == 0) {
if (have_ht_mcs_5)
return 1;
parser_state = S_HT;
mcs = ht_mcs_5;
n_mcs = &n_ht_mcs_5;
have_ht_mcs_5 = true;
} else if (strcmp(argv[i], "vht-mcs-2.4") == 0) {
if (have_vht_mcs_24)
return 1;
parser_state = S_VHT;
vht_argv = vht_argv_24;
vht_argc = &vht_argc_24;
have_vht_mcs_24 = true;
} else if (strcmp(argv[i], "vht-mcs-5") == 0) {
if (have_vht_mcs_5)
return 1;
parser_state = S_VHT;
vht_argv = vht_argv_5;
vht_argc = &vht_argc_5;
have_vht_mcs_5 = true;
} else if (strcmp(argv[i], "sgi-2.4") == 0) {
sgi_24 = 1;
parser_state = S_GI;
} else if (strcmp(argv[i], "sgi-5") == 0) {
sgi_5 = 1;
parser_state = S_GI;
} else if (strcmp(argv[i], "lgi-2.4") == 0) {
lgi_24 = 1;
parser_state = S_GI;
} else if (strcmp(argv[i], "lgi-5") == 0) {
lgi_5 = 1;
parser_state = S_GI;
} else switch (parser_state) {
case S_LEGACY:
tmpd = strtod(argv[i], &end);
if (*end != '\0')
return 1;
if (tmpd < 1 || tmpd > 255 * 2)
return 1;
legacy[(*n_legacy)++] = tmpd * 2;
break;
case S_HT:
tmpl = strtol(argv[i], &end, 0);
if (*end != '\0')
return 1;
if (tmpl < 0 || tmpl > 255)
return 1;
mcs[(*n_mcs)++] = tmpl;
break;
case S_VHT:
if (*vht_argc >= VHT_ARGC_MAX)
return 1;
vht_argv[(*vht_argc)++] = argv[i];
break;
case S_GI:
break;
default:
return 1;
}
}
if (have_vht_mcs_24)
if(!setup_vht(&txrate_vht_24, vht_argc_24, vht_argv_24))
return -EINVAL;
if (have_vht_mcs_5)
if(!setup_vht(&txrate_vht_5, vht_argc_5, vht_argv_5))
return -EINVAL;
if (sgi_5 && lgi_5)
return 1;
if (sgi_24 && lgi_24)
return 1;
nl_rates = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
if (!nl_rates)
goto nla_put_failure;
if (have_legacy_24 || have_ht_mcs_24 || have_vht_mcs_24 || sgi_24 || lgi_24) {
nl_band = nla_nest_start(msg, NL80211_BAND_2GHZ);
if (!nl_band)
goto nla_put_failure;
if (have_legacy_24)
nla_put(msg, NL80211_TXRATE_LEGACY, n_legacy_24, legacy_24);
if (have_ht_mcs_24)
nla_put(msg, NL80211_TXRATE_HT, n_ht_mcs_24, ht_mcs_24);
if (have_vht_mcs_24)
nla_put(msg, NL80211_TXRATE_VHT, sizeof(txrate_vht_24), &txrate_vht_24);
if (sgi_24)
nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_SGI);
if (lgi_24)
nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_LGI);
nla_nest_end(msg, nl_band);
}
if (have_legacy_5 || have_ht_mcs_5 || have_vht_mcs_5 || sgi_5 || lgi_5) {
nl_band = nla_nest_start(msg, NL80211_BAND_5GHZ);
if (!nl_band)
goto nla_put_failure;
if (have_legacy_5)
nla_put(msg, NL80211_TXRATE_LEGACY, n_legacy_5, legacy_5);
if (have_ht_mcs_5)
nla_put(msg, NL80211_TXRATE_HT, n_ht_mcs_5, ht_mcs_5);
if (have_vht_mcs_5)
nla_put(msg, NL80211_TXRATE_VHT, sizeof(txrate_vht_5), &txrate_vht_5);
if (sgi_5)
nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_SGI);
if (lgi_5)
nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_LGI);
nla_nest_end(msg, nl_band);
}
nla_nest_end(msg, nl_rates);
return 0;
nla_put_failure:
return -ENOBUFS;
}
#define DESCR_LEGACY "[legacy-<2.4|5> <legacy rate in Mbps>*]"
#define DESCR DESCR_LEGACY " [ht-mcs-<2.4|5> <MCS index>*] [vht-mcs-<2.4|5> <NSS:MCSx,MCSy... | NSS:MCSx-MCSy>*] [sgi-2.4|lgi-2.4] [sgi-5|lgi-5]"
COMMAND(set, bitrates, "[legacy-<2.4|5> <legacy rate in Mbps>*] [ht-mcs-<2.4|5> <MCS index>*] [vht-mcs-<2.4|5> <NSS:MCSx,MCSy... | NSS:MCSx-MCSy>*] [sgi-2.4|lgi-2.4] [sgi-5|lgi-5]",
NL80211_CMD_SET_TX_BITRATE_MASK, 0, CIB_NETDEV, handle_bitrates,
"Sets up the specified rate masks.\n"
"Not passing any arguments would clear the existing mask (if any).");

283
coalesce.c Normal file
View File

@ -0,0 +1,283 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(coalesce);
static int handle_coalesce_enable(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *nl_rules, *nl_rule = NULL, *nl_pats, *nl_pat;
unsigned char *pat, *mask;
size_t patlen;
int patnum = 0, pkt_offset, err = 1;
char *eptr, *value1, *value2, *sptr = NULL, *end, buf[16768];
enum nl80211_coalesce_condition condition;
FILE *f = fopen(argv[0], "r");
enum {
PS_DELAY,
PS_CONDITION,
PS_PATTERNS
} parse_state = PS_DELAY;
int rule_num = 0;
if (!f)
return 1;
nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
if (!nl_rules) {
fclose(f);
return -ENOBUFS;
}
while (!feof(f)) {
char *eol;
if (!fgets(buf, sizeof(buf), f))
break;
eol = strchr(buf + 5, '\r');
if (eol)
*eol = 0;
eol = strchr(buf + 5, '\n');
if (eol)
*eol = 0;
switch (parse_state) {
case PS_DELAY:
if (strncmp(buf, "delay=", 6) == 0) {
char *delay = buf + 6;
rule_num++;
nl_rule = nla_nest_start(msg, rule_num);
if (!nl_rule)
goto close;
NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
strtoul(delay, &end, 10));
if (*end != '\0')
goto close;
parse_state = PS_CONDITION;
} else {
goto close;
}
break;
case PS_CONDITION:
if (strncmp(buf, "condition=", 10) == 0) {
char *cond = buf + 10;
condition = strtoul(cond, &end, 10);
if (*end != '\0')
goto close;
NLA_PUT_U32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
condition);
parse_state = PS_PATTERNS;
} else {
goto close;
}
break;
case PS_PATTERNS:
if (strncmp(buf, "patterns=", 9) == 0) {
char *cur_pat = buf + 9;
char *next_pat = strchr(buf + 9, ',');
if (next_pat) {
*next_pat = 0;
next_pat++;
}
nl_pats = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
while (1) {
value1 = strtok_r(cur_pat, "+", &sptr);
value2 = strtok_r(NULL, "+", &sptr);
if (!value2) {
pkt_offset = 0;
if (!value1)
goto close;
value2 = value1;
} else {
pkt_offset = strtoul(value1, &eptr, 10);
if (eptr != value1 + strlen(value1))
goto close;
}
if (parse_hex_mask(value2, &pat, &patlen, &mask))
goto close;
nl_pat = nla_nest_start(msg, ++patnum);
NLA_PUT(msg, NL80211_PKTPAT_MASK,
DIV_ROUND_UP(patlen, 8), mask);
NLA_PUT(msg, NL80211_PKTPAT_PATTERN, patlen, pat);
NLA_PUT_U32(msg, NL80211_PKTPAT_OFFSET,
pkt_offset);
nla_nest_end(msg, nl_pat);
free(mask);
free(pat);
if (!next_pat)
break;
cur_pat = next_pat;
next_pat = strchr(cur_pat, ',');
if (next_pat) {
*next_pat = 0;
next_pat++;
}
}
nla_nest_end(msg, nl_pats);
nla_nest_end(msg, nl_rule);
parse_state = PS_DELAY;
} else {
goto close;
}
break;
default:
if (buf[0] == '#')
continue;
goto close;
}
}
if (parse_state == PS_DELAY)
err = 0;
else
err = 1;
goto close;
nla_put_failure:
err = -ENOBUFS;
close:
fclose(f);
nla_nest_end(msg, nl_rules);
return err;
}
COMMAND(coalesce, enable, "<config-file>",
NL80211_CMD_SET_COALESCE, 0, CIB_PHY, handle_coalesce_enable,
"Enable coalesce with given configuration.\n"
"The configuration file contains coalesce rules:\n"
" delay=<delay>\n"
" condition=<condition>\n"
" patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n"
" delay=<delay>\n"
" condition=<condition>\n"
" patterns=<[offset1+]<pattern1>,<[offset2+]<pattern2>,...>\n"
" ...\n"
"delay: maximum coalescing delay in msec.\n"
"condition: 1/0 i.e. 'not match'/'match' the patterns\n"
"patterns: each pattern is given as a bytestring with '-' in\n"
"places where any byte may be present, e.g. 00:11:22:-:44 will\n"
"match 00:11:22:33:44 and 00:11:22:33:ff:44 etc. Offset and\n"
"pattern should be separated by '+', e.g. 18+43:34:00:12 will\n"
"match '43:34:00:12' after 18 bytes of offset in Rx packet.\n");
static int
handle_coalesce_disable(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
/* just a set w/o coalesce attribute */
return 0;
}
COMMAND(coalesce, disable, "", NL80211_CMD_SET_COALESCE, 0, CIB_PHY,
handle_coalesce_disable, "Disable coalesce.");
static int print_coalesce_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *pattern, *rule;
int rem_pattern, rem_rule;
enum nl80211_coalesce_condition condition;
int delay;
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!attrs[NL80211_ATTR_COALESCE_RULE]) {
printf("Coalesce is disabled.\n");
return NL_SKIP;
}
printf("Coalesce is enabled:\n");
nla_for_each_nested(rule, attrs[NL80211_ATTR_COALESCE_RULE], rem_rule) {
struct nlattr *ruleattr[NUM_NL80211_ATTR_COALESCE_RULE];
nla_parse(ruleattr, NL80211_ATTR_COALESCE_RULE_MAX,
nla_data(rule), nla_len(rule), NULL);
delay = nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_DELAY]);
condition =
nla_get_u32(ruleattr[NL80211_ATTR_COALESCE_RULE_CONDITION]);
printf("Rule - max coalescing delay: %dmsec condition:", delay);
if (condition)
printf("not match\n");
else
printf("match\n");
if (ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) {
nla_for_each_nested(pattern,
ruleattr[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
rem_pattern) {
struct nlattr *patattr[NUM_NL80211_PKTPAT];
int i, patlen, masklen, pkt_offset;
uint8_t *mask, *pat;
nla_parse(patattr, MAX_NL80211_PKTPAT,
nla_data(pattern), nla_len(pattern),
NULL);
if (!patattr[NL80211_PKTPAT_MASK] ||
!patattr[NL80211_PKTPAT_PATTERN] ||
!patattr[NL80211_PKTPAT_OFFSET]) {
printf(" * (invalid pattern specification)\n");
continue;
}
masklen = nla_len(patattr[NL80211_PKTPAT_MASK]);
patlen = nla_len(patattr[NL80211_PKTPAT_PATTERN]);
pkt_offset = nla_get_u32(patattr[NL80211_PKTPAT_OFFSET]);
if (DIV_ROUND_UP(patlen, 8) != masklen) {
printf(" * (invalid pattern specification)\n");
continue;
}
printf(" * packet offset: %d", pkt_offset);
printf(" pattern: ");
pat = nla_data(patattr[NL80211_PKTPAT_PATTERN]);
mask = nla_data(patattr[NL80211_PKTPAT_MASK]);
for (i = 0; i < patlen; i++) {
if (mask[i / 8] & (1 << (i % 8)))
printf("%.2x", pat[i]);
else
printf("--");
if (i != patlen - 1)
printf(":");
}
printf("\n");
}
}
}
return NL_SKIP;
}
static int handle_coalesce_show(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
register_handler(print_coalesce_handler, NULL);
return 0;
}
COMMAND(coalesce, show, "", NL80211_CMD_GET_COALESCE, 0, CIB_PHY, handle_coalesce_show,
"Show coalesce status.");

238
connect.c Normal file
View File

@ -0,0 +1,238 @@
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
static int iw_conn(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char *end;
unsigned char bssid[6];
int freq;
int ret;
if (argc < 1)
return 1;
/* SSID */
NLA_PUT(msg, NL80211_ATTR_SSID, strlen(argv[0]), argv[0]);
argv++;
argc--;
/* freq */
if (argc) {
freq = strtoul(argv[0], &end, 10);
if (*end == '\0') {
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
argv++;
argc--;
}
}
/* bssid */
if (argc) {
if (mac_addr_a2n(bssid, argv[0]) == 0) {
NLA_PUT(msg, NL80211_ATTR_MAC, 6, bssid);
argv++;
argc--;
}
}
if (!argc)
return 0;
if (strcmp(*argv, "key") != 0 && strcmp(*argv, "keys") != 0)
return 1;
argv++;
argc--;
ret = parse_keys(msg, argv, argc);
if (ret)
return ret;
argc -= 4;
argv += 4;
if (!argc)
return 0;
if (!strcmp(*argv, "mfp:req"))
NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
else if (!strcmp(*argv, "mfp:opt"))
NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL);
else if (!strcmp(*argv, "mfp:no"))
NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_NO);
else
return -EINVAL;
return 0;
nla_put_failure:
return -ENOSPC;
}
static int disconnect(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return 0;
}
TOPLEVEL(disconnect, NULL,
NL80211_CMD_DISCONNECT, 0, CIB_NETDEV, disconnect,
"Disconnect from the current network.");
static int iw_connect(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char **conn_argv, *dev = argv[0];
static const __u32 cmds[] = {
NL80211_CMD_CONNECT,
};
struct print_event_args printargs = { };
int conn_argc, err;
bool wait = false;
int i;
/* strip "wlan0 connect" */
argc -= 2;
argv += 2;
/* check -w */
if (argc && strcmp(argv[0], "-w") == 0) {
wait = true;
argc--;
argv++;
}
err = __prepare_listen_events(state);
if (err)
return err;
conn_argc = 3 + argc;
conn_argv = calloc(conn_argc, sizeof(*conn_argv));
if (!conn_argv)
return -ENOMEM;
conn_argv[0] = dev;
conn_argv[1] = "connect";
conn_argv[2] = "establish";
for (i = 0; i < argc; i++)
conn_argv[i + 3] = argv[i];
err = handle_cmd(state, id, conn_argc, conn_argv);
free(conn_argv);
if (err)
return err;
if (!wait)
return 0;
/*
* WARNING: DO NOT COPY THIS CODE INTO YOUR APPLICATION
*
* This code has a bug:
*
* It is possible for a connect result message from another
* connect attempt to be processed here first, because we
* start listening to the multicast group before starting
* our own connect request, which may succeed but we get a
* fail message from a previous attempt that raced with us,
* or similar.
*
* The only proper way to fix this would be to listen to events
* before sending the command, and for the kernel to send the
* connect request or a cookie along with the event, so that you
* can match up whether the connect _you_ requested was finished
* or aborted.
*
* Alas, the kernel doesn't do that (yet).
*/
__do_listen_events(state, ARRAY_SIZE(cmds), cmds, &printargs);
return 0;
}
TOPLEVEL(connect, "[-w] <SSID> [<freq in MHz>] [<bssid>] [key 0:abcde d:1:6162636465] [mfp:req/opt/no]",
0, 0, CIB_NETDEV, iw_connect,
"Join the network with the given SSID (and frequency, BSSID).\n"
"With -w, wait for the connect to finish or fail.");
HIDDEN(connect, establish, "", NL80211_CMD_CONNECT, 0, CIB_NETDEV, iw_conn);
static int iw_auth(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char *end;
unsigned char bssid[6];
int freq;
bool need_key = false;
if (argc < 4)
return 1;
/* SSID */
NLA_PUT(msg, NL80211_ATTR_SSID, strlen(argv[0]), argv[0]);
argv++;
argc--;
/* bssid */
if (mac_addr_a2n(bssid, argv[0]) == 0) {
NLA_PUT(msg, NL80211_ATTR_MAC, 6, bssid);
argv++;
argc--;
} else {
return 1;
}
/* FIXME */
if (strcmp(argv[0], "open") == 0) {
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
NL80211_AUTHTYPE_OPEN_SYSTEM);
} else if (strcmp(argv[0], "shared") == 0) {
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
NL80211_AUTHTYPE_SHARED_KEY);
need_key = true;
} else {
return 1;
}
argv++;
argc--;
freq = strtoul(argv[0], &end, 10);
if (*end == '\0') {
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
argv++;
argc--;
} else {
return 1;
}
if (!argc && need_key)
return 1;
if (argc && !need_key)
return 1;
if (!argc)
return 0;
if (strcmp(*argv, "key") != 0 && strcmp(*argv, "keys") != 0)
return 1;
argv++;
argc--;
return parse_keys(msg, argv, argc);
nla_put_failure:
return -ENOSPC;
}
TOPLEVEL(auth, "<SSID> <bssid> <type:open|shared> <freq in MHz> [key 0:abcde d:1:6162636465]",
NL80211_CMD_AUTHENTICATE, 0, CIB_NETDEV, iw_auth,
"Authenticate with the given network.\n");

57
cqm.c Normal file
View File

@ -0,0 +1,57 @@
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
static int iw_cqm_rssi(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nl_msg *cqm = NULL;
int thold = 0;
int hyst = 0;
int ret = -ENOSPC;
/* get the required args */
if (argc < 1 || argc > 2)
return 1;
if (strcmp(argv[0], "off")) {
thold = atoi(argv[0]);
if (thold == 0)
return -EINVAL;
if (argc == 2)
hyst = atoi(argv[1]);
}
/* connection quality monitor attributes */
cqm = nlmsg_alloc();
if (!cqm)
return -ENOMEM;
NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, thold);
NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hyst);
nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
ret = 0;
nla_put_failure:
nlmsg_free(cqm);
return ret;
}
TOPLEVEL(cqm, "",
0, 0, CIB_NETDEV, NULL,
"Configure the WLAN connection quality monitor.\n");
COMMAND(cqm, rssi, "<threshold|off> [<hysteresis>]",
NL80211_CMD_SET_CQM, 0, CIB_NETDEV, iw_cqm_rssi,
"Set connection quality monitor RSSI threshold.\n");

899
event.c Normal file
View File

@ -0,0 +1,899 @@
#include <stdint.h>
#include <stdbool.h>
#include <net/if.h>
#include <errno.h>
#include "iw.h"
static int no_seq_check(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
struct ieee80211_beacon_channel {
__u16 center_freq;
bool no_ir;
bool no_ibss;
};
static int parse_beacon_hint_chan(struct nlattr *tb,
struct ieee80211_beacon_channel *chan)
{
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
static struct nla_policy beacon_freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
[__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
};
if (nla_parse_nested(tb_freq,
NL80211_FREQUENCY_ATTR_MAX,
tb,
beacon_freq_policy))
return -EINVAL;
chan->center_freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
chan->no_ir = true;
if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS])
chan->no_ibss = true;
return 0;
}
static void print_frame(struct print_event_args *args, struct nlattr *attr)
{
uint8_t *frame;
size_t len;
unsigned int i;
char macbuf[6*3];
uint16_t tmp;
if (!attr) {
printf(" [no frame]");
return;
}
frame = nla_data(attr);
len = nla_len(attr);
if (len < 26) {
printf(" [invalid frame: ");
goto print_frame;
}
mac_addr_n2a(macbuf, frame + 10);
printf(" %s -> ", macbuf);
mac_addr_n2a(macbuf, frame + 4);
printf("%s", macbuf);
switch (frame[0] & 0xfc) {
case 0x10: /* assoc resp */
case 0x30: /* reassoc resp */
/* status */
tmp = (frame[27] << 8) + frame[26];
printf(" status: %d: %s", tmp, get_status_str(tmp));
break;
case 0x00: /* assoc req */
case 0x20: /* reassoc req */
break;
case 0xb0: /* auth */
/* status */
tmp = (frame[29] << 8) + frame[28];
printf(" status: %d: %s", tmp, get_status_str(tmp));
break;
case 0xa0: /* disassoc */
case 0xc0: /* deauth */
/* reason */
tmp = (frame[25] << 8) + frame[24];
printf(" reason %d: %s", tmp, get_reason_str(tmp));
break;
}
if (!args->frame)
return;
printf(" [frame:");
print_frame:
for (i = 0; i < len; i++)
printf(" %.02x", frame[i]);
printf("]");
}
static void parse_cqm_event(struct nlattr **attrs)
{
static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
};
struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
struct nlattr *cqm_attr = attrs[NL80211_ATTR_CQM];
printf("CQM event: ");
if (!cqm_attr ||
nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, cqm_attr, cqm_policy)) {
printf("missing data!\n");
return;
}
if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) {
enum nl80211_cqm_rssi_threshold_event rssi_event;
bool found_one = false;
rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
switch (rssi_event) {
case NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH:
printf("RSSI went above threshold\n");
found_one = true;
break;
case NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW:
printf("RSSI went below threshold\n");
found_one = true;
break;
case NL80211_CQM_RSSI_BEACON_LOSS_EVENT:
printf("Beacon loss detected\n");
found_one = true;
break;
}
if (!found_one)
printf("Unknown event type: %i\n", rssi_event);
} else if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
if (attrs[NL80211_ATTR_MAC]) {
uint32_t frames;
char buf[3*6];
frames = nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
mac_addr_n2a(buf, nla_data(attrs[NL80211_ATTR_MAC]));
printf("peer %s didn't ACK %d packets\n", buf, frames);
} else {
printf("PKT-LOSS-EVENT did not have MAC attribute!\n");
}
} else if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
printf("beacon loss\n");
} else {
printf("unknown event\n");
}
}
static const char * key_type_str(enum nl80211_key_type key_type)
{
static char buf[30];
switch (key_type) {
case NL80211_KEYTYPE_GROUP:
return "Group";
case NL80211_KEYTYPE_PAIRWISE:
return "Pairwise";
case NL80211_KEYTYPE_PEERKEY:
return "PeerKey";
default:
snprintf(buf, sizeof(buf), "unknown(%d)", key_type);
return buf;
}
}
static void parse_mic_failure(struct nlattr **attrs)
{
printf("Michael MIC failure event:");
if (attrs[NL80211_ATTR_MAC]) {
char addr[3 * ETH_ALEN];
mac_addr_n2a(addr, nla_data(attrs[NL80211_ATTR_MAC]));
printf(" source MAC address %s", addr);
}
if (attrs[NL80211_ATTR_KEY_SEQ] &&
nla_len(attrs[NL80211_ATTR_KEY_SEQ]) == 6) {
unsigned char *seq = nla_data(attrs[NL80211_ATTR_KEY_SEQ]);
printf(" seq=%02x%02x%02x%02x%02x%02x",
seq[0], seq[1], seq[2], seq[3], seq[4], seq[5]);
}
if (attrs[NL80211_ATTR_KEY_TYPE]) {
enum nl80211_key_type key_type =
nla_get_u32(attrs[NL80211_ATTR_KEY_TYPE]);
printf(" Key Type %s", key_type_str(key_type));
}
if (attrs[NL80211_ATTR_KEY_IDX]) {
__u8 key_id = nla_get_u8(attrs[NL80211_ATTR_KEY_IDX]);
printf(" Key Id %d", key_id);
}
printf("\n");
}
static void parse_wowlan_wake_event(struct nlattr **attrs)
{
struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG],
*tb_match[NUM_NL80211_ATTR];
printf("WoWLAN wakeup\n");
if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
printf("\twakeup not due to WoWLAN\n");
return;
}
nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), NULL);
if (tb[NL80211_WOWLAN_TRIG_DISCONNECT])
printf("\t* was disconnected\n");
if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT])
printf("\t* magic packet received\n");
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN])
printf("\t* pattern index: %u\n",
nla_get_u32(tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]));
if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
printf("\t* GTK rekey failure\n");
if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
printf("\t* EAP identity request\n");
if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
printf("\t* 4-way handshake\n");
if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
printf("\t* RF-kill released\n");
if (tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS]) {
struct nlattr *match, *freq;
int rem_nst, rem_nst2;
printf("\t* network detected\n");
nla_for_each_nested(match,
tb[NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS],
rem_nst) {
nla_parse(tb_match, NUM_NL80211_ATTR, nla_data(match),
nla_len(match),
NULL);
printf("\t\tSSID: \"");
print_ssid_escaped(nla_len(tb_match[NL80211_ATTR_SSID]),
nla_data(tb_match[NL80211_ATTR_SSID]));
printf("\"");
if (tb_match[NL80211_ATTR_SCAN_FREQUENCIES]) {
printf(" freq(s):");
nla_for_each_nested(freq,
tb_match[NL80211_ATTR_SCAN_FREQUENCIES],
rem_nst2)
printf(" %d", nla_get_u32(freq));
}
printf("\n");
}
}
if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]) {
uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211]);
int i;
printf("\t* packet (might be truncated): ");
for (i = 0; i < l; i++) {
if (i > 0)
printf(":");
printf("%.2x", d[i]);
}
printf("\n");
}
if (tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]) {
uint8_t *d = nla_data(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
int l = nla_len(tb[NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023]);
int i;
printf("\t* packet (might be truncated): ");
for (i = 0; i < l; i++) {
if (i > 0)
printf(":");
printf("%.2x", d[i]);
}
printf("\n");
}
if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH])
printf("\t* TCP connection wakeup received\n");
if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST])
printf("\t* TCP connection lost\n");
if (tb[NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS])
printf("\t* TCP connection ran out of tokens\n");
}
static int parse_ftm_target(struct nlattr *attr, unsigned char *bssid)
{
struct nlattr *tb[NL80211_FTM_TARGET_ATTR_MAX + 1];
if (nla_parse_nested(tb, NL80211_FTM_TARGET_ATTR_MAX, attr, NULL))
return -1;
if (!tb[NL80211_FTM_TARGET_ATTR_BSSID])
return -1;
memcpy(bssid, nla_data(tb[NL80211_FTM_TARGET_ATTR_BSSID]), ETH_ALEN);
return 0;
}
/* Speed of light in cm/picosec */
#define SOL_CM_PSEC 0.02998
static void parse_ftm_results(struct nlattr **attrs, int status,
struct print_event_args *pargs)
{
struct nlattr *tb[NL80211_FTM_RESP_ENTRY_ATTR_MAX + 1];
unsigned char bssid[ETH_ALEN];
char macbuf[6 * 3];
long long int rtt, dist;
int err;
printf("FTM result! Status: %d\n", status);
if (!pargs)
return;
pargs->continue_listening = 0;
if (status != NL80211_MSRMENT_STATUS_SUCCESS &&
status != NL80211_MSRMENT_STATUS_TIMEOUT) {
printf("Failed to measure, status: %d\n", status);
return;
}
if (!attrs[NL80211_ATTR_MSRMENT_FTM_RESPONSE]) {
printf("Error parsing FTM response\n");
return;
}
err = nla_parse_nested(tb, NL80211_FTM_RESP_ENTRY_ATTR_MAX,
attrs[NL80211_ATTR_MSRMENT_FTM_RESPONSE], NULL);
if (err ||
!tb[NL80211_FTM_RESP_ENTRY_ATTR_STATUS] ||
!tb[NL80211_FTM_RESP_ENTRY_ATTR_TARGET] ||
!tb[NL80211_FTM_RESP_ENTRY_ATTR_RTT] ||
parse_ftm_target(tb[NL80211_FTM_RESP_ENTRY_ATTR_TARGET] ,bssid)) {
printf("Error parsing FTM target\n");
return;
}
mac_addr_n2a(macbuf, bssid);
rtt = (long long int)nla_get_u64(tb[NL80211_FTM_RESP_ENTRY_ATTR_RTT]);
dist = tb[NL80211_FTM_RESP_ENTRY_ATTR_DISTANCE] ?
(long long int)nla_get_u64(tb[NL80211_FTM_RESP_ENTRY_ATTR_DISTANCE]) :
rtt * SOL_CM_PSEC;
printf("Target: %s, status: %d, rtt: %lld psec, distance: %lld cm",
macbuf, nla_get_u8(tb[NL80211_FTM_RESP_ENTRY_ATTR_STATUS]), rtt,
dist);
if (tb[NL80211_FTM_RESP_ENTRY_ATTR_LCI])
iw_hexdump("LCI",
nla_data(tb[NL80211_FTM_RESP_ENTRY_ATTR_LCI]),
nla_len(tb[NL80211_FTM_RESP_ENTRY_ATTR_LCI]));
if (tb[NL80211_FTM_RESP_ENTRY_ATTR_CIVIC])
iw_hexdump("Civic",
nla_data(tb[NL80211_FTM_RESP_ENTRY_ATTR_CIVIC]),
nla_len(tb[NL80211_FTM_RESP_ENTRY_ATTR_CIVIC]));
printf("\n");
pargs->continue_listening = attrs[NL80211_ATTR_LAST_MSG] ? 0 : 1;
}
static void parse_measurement_response(struct nlattr **tb,
struct print_event_args *pargs)
{
int status;
if (!tb[NL80211_ATTR_MSRMENT_TYPE] ||
!tb[NL80211_ATTR_MSRMENT_STATUS]) {
printf("Measurements: failed to parse event!\n");
return;
}
status = nla_get_u8(tb[NL80211_ATTR_MSRMENT_STATUS]);
switch(nla_get_u32(tb[NL80211_ATTR_MSRMENT_TYPE])) {
case NL80211_MSRMENT_TYPE_FTM:
parse_ftm_results(tb, status, pargs);
break;
default:
printf("Measurements: type %d is not supported!\n",
nla_get_u32(tb[NL80211_ATTR_MSRMENT_TYPE]));
}
}
static int print_event(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1], *nst;
struct print_event_args *args = arg;
char ifname[100];
char macbuf[6*3];
__u8 reg_type;
struct ieee80211_beacon_channel chan_before_beacon, chan_after_beacon;
__u32 wiphy_idx = 0;
int rem_nst;
__u16 status;
if (args->time || args->reltime) {
unsigned long long usecs, previous;
previous = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec;
gettimeofday(&args->ts, NULL);
usecs = 1000000ULL * args->ts.tv_sec + args->ts.tv_usec;
if (args->reltime) {
if (!args->have_ts) {
usecs = 0;
args->have_ts = true;
} else
usecs -= previous;
}
printf("%llu.%06llu: ", usecs/1000000, usecs % 1000000);
}
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) {
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY]));
} else if (tb[NL80211_ATTR_WDEV] && tb[NL80211_ATTR_WIPHY]) {
printf("wdev 0x%llx (phy #%d): ",
(unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV]),
nla_get_u32(tb[NL80211_ATTR_WIPHY]));
} else if (tb[NL80211_ATTR_IFINDEX]) {
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname);
printf("%s: ", ifname);
} else if (tb[NL80211_ATTR_WDEV]) {
printf("wdev 0x%llx: ", (unsigned long long)nla_get_u64(tb[NL80211_ATTR_WDEV]));
} else if (tb[NL80211_ATTR_WIPHY]) {
printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
}
switch (gnlh->cmd) {
case NL80211_CMD_NEW_WIPHY:
printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]));
break;
case NL80211_CMD_TRIGGER_SCAN:
printf("scan started\n");
break;
case NL80211_CMD_NEW_SCAN_RESULTS:
printf("scan finished:");
case NL80211_CMD_SCAN_ABORTED:
if (gnlh->cmd == NL80211_CMD_SCAN_ABORTED)
printf("scan aborted:");
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem_nst)
printf(" %d", nla_get_u32(nst));
printf(",");
}
if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nst, tb[NL80211_ATTR_SCAN_SSIDS], rem_nst) {
printf(" \"");
print_ssid_escaped(nla_len(nst), nla_data(nst));
printf("\"");
}
}
printf("\n");
break;
case NL80211_CMD_START_SCHED_SCAN:
printf("scheduled scan started\n");
break;
case NL80211_CMD_SCHED_SCAN_STOPPED:
printf("sched scan stopped\n");
break;
case NL80211_CMD_SCHED_SCAN_RESULTS:
printf("got scheduled scan results\n");
break;
case NL80211_CMD_REG_CHANGE:
printf("regulatory domain change: ");
reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
switch (reg_type) {
case NL80211_REGDOM_TYPE_COUNTRY:
printf("set to %s by %s request",
nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
if (tb[NL80211_ATTR_WIPHY])
printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
break;
case NL80211_REGDOM_TYPE_WORLD:
printf("set to world roaming by %s request",
reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])));
break;
case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
printf("custom world roaming rules in place on phy%d by %s request",
nla_get_u32(tb[NL80211_ATTR_WIPHY]),
reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
break;
case NL80211_REGDOM_TYPE_INTERSECTION:
printf("intersection used due to a request made by %s",
reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR])));
if (tb[NL80211_ATTR_WIPHY])
printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY]));
break;
default:
printf("unknown source (upgrade this utility)");
break;
}
printf("\n");
break;
case NL80211_CMD_REG_BEACON_HINT:
wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
memset(&chan_before_beacon, 0, sizeof(chan_before_beacon));
memset(&chan_after_beacon, 0, sizeof(chan_after_beacon));
if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_BEFORE],
&chan_before_beacon))
break;
if (parse_beacon_hint_chan(tb[NL80211_ATTR_FREQ_AFTER],
&chan_after_beacon))
break;
if (chan_before_beacon.center_freq != chan_after_beacon.center_freq)
break;
/* A beacon hint is sent _only_ if something _did_ change */
printf("beacon hint:\n");
printf("phy%d %d MHz [%d]:\n",
wiphy_idx,
chan_before_beacon.center_freq,
ieee80211_frequency_to_channel(chan_before_beacon.center_freq));
if (chan_before_beacon.no_ir && !chan_after_beacon.no_ir) {
if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss)
printf("\to Initiating radiation enabled\n");
else
printf("\to active scan enabled\n");
} else if (chan_before_beacon.no_ibss && !chan_after_beacon.no_ibss) {
printf("\to ibss enabled\n");
}
break;
case NL80211_CMD_NEW_STATION:
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf("new station %s\n", macbuf);
break;
case NL80211_CMD_DEL_STATION:
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf("del station %s\n", macbuf);
break;
case NL80211_CMD_JOIN_IBSS:
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf("IBSS %s joined\n", macbuf);
break;
case NL80211_CMD_AUTHENTICATE:
printf("auth");
if (tb[NL80211_ATTR_FRAME])
print_frame(args, tb[NL80211_ATTR_FRAME]);
else if (tb[NL80211_ATTR_TIMED_OUT])
printf(": timed out");
else
printf(": unknown event");
printf("\n");
break;
case NL80211_CMD_ASSOCIATE:
printf("assoc");
if (tb[NL80211_ATTR_FRAME])
print_frame(args, tb[NL80211_ATTR_FRAME]);
else if (tb[NL80211_ATTR_TIMED_OUT])
printf(": timed out");
else
printf(": unknown event");
printf("\n");
break;
case NL80211_CMD_DEAUTHENTICATE:
printf("deauth");
print_frame(args, tb[NL80211_ATTR_FRAME]);
printf("\n");
break;
case NL80211_CMD_DISASSOCIATE:
printf("disassoc");
print_frame(args, tb[NL80211_ATTR_FRAME]);
printf("\n");
break;
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
printf("unprotected deauth");
print_frame(args, tb[NL80211_ATTR_FRAME]);
printf("\n");
break;
case NL80211_CMD_UNPROT_DISASSOCIATE:
printf("unprotected disassoc");
print_frame(args, tb[NL80211_ATTR_FRAME]);
printf("\n");
break;
case NL80211_CMD_CONNECT:
status = 0;
if (tb[NL80211_ATTR_TIMED_OUT])
printf("timed out");
else if (!tb[NL80211_ATTR_STATUS_CODE])
printf("unknown connect status");
else if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) == 0)
printf("connected");
else {
status = nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]);
printf("failed to connect");
}
if (tb[NL80211_ATTR_MAC]) {
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf(" to %s", macbuf);
}
if (status)
printf(", status: %d: %s", status, get_status_str(status));
printf("\n");
break;
case NL80211_CMD_ROAM:
printf("roamed");
if (tb[NL80211_ATTR_MAC]) {
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf(" to %s", macbuf);
}
printf("\n");
break;
case NL80211_CMD_DISCONNECT:
printf("disconnected");
if (tb[NL80211_ATTR_DISCONNECTED_BY_AP])
printf(" (by AP)");
else
printf(" (local request)");
if (tb[NL80211_ATTR_REASON_CODE])
printf(" reason: %d: %s", nla_get_u16(tb[NL80211_ATTR_REASON_CODE]),
get_reason_str(nla_get_u16(tb[NL80211_ATTR_REASON_CODE])));
printf("\n");
break;
case NL80211_CMD_REMAIN_ON_CHANNEL:
printf("remain on freq %d (%dms, cookie %llx)\n",
nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
nla_get_u32(tb[NL80211_ATTR_DURATION]),
(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
break;
case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
printf("done with remain on freq %d (cookie %llx)\n",
nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]),
(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]));
break;
case NL80211_CMD_NOTIFY_CQM:
parse_cqm_event(tb);
break;
case NL80211_CMD_MICHAEL_MIC_FAILURE:
parse_mic_failure(tb);
break;
case NL80211_CMD_FRAME_TX_STATUS:
printf("mgmt TX status (cookie %llx): %s\n",
(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]),
tb[NL80211_ATTR_ACK] ? "acked" : "no ack");
break;
case NL80211_CMD_PMKSA_CANDIDATE:
printf("PMKSA candidate found\n");
break;
case NL80211_CMD_SET_WOWLAN:
parse_wowlan_wake_event(tb);
break;
case NL80211_CMD_PROBE_CLIENT:
if (tb[NL80211_ATTR_MAC])
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
else
strcpy(macbuf, "??");
printf("probe client %s (cookie %llx): %s\n",
macbuf,
(unsigned long long)nla_get_u64(tb[NL80211_ATTR_COOKIE]),
tb[NL80211_ATTR_ACK] ? "acked" : "no ack");
break;
case NL80211_CMD_VENDOR:
printf("vendor event %.6x:%d\n",
nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]),
nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]));
if (args->frame && tb[NL80211_ATTR_VENDOR_DATA])
iw_hexdump("vendor event",
nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
break;
case NL80211_CMD_RADAR_DETECT: {
enum nl80211_radar_event event_type;
uint32_t freq;
if (!tb[NL80211_ATTR_RADAR_EVENT] ||
!tb[NL80211_ATTR_WIPHY_FREQ]) {
printf("BAD radar event\n");
break;
}
freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
switch (event_type) {
case NL80211_RADAR_DETECTED:
printf("%d MHz: radar detected\n", freq);
break;
case NL80211_RADAR_CAC_FINISHED:
printf("%d MHz: CAC finished\n", freq);
break;
case NL80211_RADAR_CAC_ABORTED:
printf("%d MHz: CAC was aborted\n", freq);
break;
case NL80211_RADAR_NOP_FINISHED:
printf("%d MHz: NOP finished\n", freq);
break;
default:
printf("%d MHz: unknown radar event\n", freq);
}
}
break;
case NL80211_CMD_DEL_WIPHY:
printf("delete wiphy\n");
break;
case NL80211_CMD_MSRMENT_RESPONSE:
parse_measurement_response(tb, args);
break;
default:
printf("unknown event %d (%s)\n",
gnlh->cmd, command_name(gnlh->cmd));
break;
}
fflush(stdout);
return NL_SKIP;
}
struct wait_event {
int n_cmds;
const __u32 *cmds;
__u32 cmd;
struct print_event_args *pargs;
};
static int wait_event(struct nl_msg *msg, void *arg)
{
struct wait_event *wait = arg;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
int i;
for (i = 0; i < wait->n_cmds; i++) {
if (gnlh->cmd == wait->cmds[i]) {
wait->cmd = gnlh->cmd;
if (wait->pargs)
print_event(msg, wait->pargs);
}
}
return NL_SKIP;
}
int __prepare_listen_events(struct nl80211_state *state)
{
int mcid, ret;
/* Configuration multicast group */
mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config");
if (mcid < 0)
return mcid;
ret = nl_socket_add_membership(state->nl_sock, mcid);
if (ret)
return ret;
/* Scan multicast group */
mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan");
if (mcid >= 0) {
ret = nl_socket_add_membership(state->nl_sock, mcid);
if (ret)
return ret;
}
/* Regulatory multicast group */
mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory");
if (mcid >= 0) {
ret = nl_socket_add_membership(state->nl_sock, mcid);
if (ret)
return ret;
}
/* MLME multicast group */
mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme");
if (mcid >= 0) {
ret = nl_socket_add_membership(state->nl_sock, mcid);
if (ret)
return ret;
}
mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "vendor");
if (mcid >= 0) {
ret = nl_socket_add_membership(state->nl_sock, mcid);
if (ret)
return ret;
}
return 0;
}
__u32 __do_listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits,
struct print_event_args *args)
{
struct nl_cb *cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
struct wait_event wait_ev;
if (!cb) {
fprintf(stderr, "failed to allocate netlink callbacks\n");
return -ENOMEM;
}
/* no sequence checking for multicast messages */
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, NULL);
if (n_waits && waits) {
wait_ev.cmds = waits;
wait_ev.n_cmds = n_waits;
wait_ev.pargs = args;
register_handler(wait_event, &wait_ev);
} else
register_handler(print_event, args);
wait_ev.cmd = 0;
while (!wait_ev.cmd || (args && args->continue_listening))
nl_recvmsgs(state->nl_sock, cb);
nl_cb_put(cb);
return wait_ev.cmd;
}
__u32 listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits)
{
int ret;
ret = __prepare_listen_events(state);
if (ret)
return ret;
return __do_listen_events(state, n_waits, waits, NULL);
}
static int print_events(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
struct print_event_args args;
int ret;
memset(&args, 0, sizeof(args));
argc--;
argv++;
while (argc > 0) {
if (strcmp(argv[0], "-f") == 0)
args.frame = true;
else if (strcmp(argv[0], "-t") == 0)
args.time = true;
else if (strcmp(argv[0], "-r") == 0)
args.reltime = true;
else
return 1;
argc--;
argv++;
}
if (args.time && args.reltime)
return 1;
if (argc)
return 1;
ret = __prepare_listen_events(state);
if (ret)
return ret;
return __do_listen_events(state, 0, NULL, &args);
}
TOPLEVEL(event, "[-t|-r] [-f]", 0, 0, CIB_NONE, print_events,
"Monitor events from the kernel.\n"
"-t - print timestamp\n"
"-r - print relative timstamp\n"
"-f - print full frame for auth/assoc etc.");

117
genl.c Normal file
View File

@ -0,0 +1,117 @@
/*
* This ought to be provided by libnl
*/
#include <asm/errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <linux/genetlink.h>
#include "iw.h"
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
int *ret = arg;
*ret = err->error;
return NL_STOP;
}
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_STOP;
}
struct handler_args {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct handler_args *grp = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int rem_mcgrp;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
nla_data(mcgrp), nla_len(mcgrp), NULL);
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
continue;
if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
continue;
grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
break;
}
return NL_SKIP;
}
int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group)
{
struct nl_msg *msg;
struct nl_cb *cb;
int ret, ctrlid;
struct handler_args grp = {
.group = group,
.id = -ENOENT,
};
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb) {
ret = -ENOMEM;
goto out_fail_cb;
}
ctrlid = genl_ctrl_resolve(sock, "nlctrl");
genlmsg_put(msg, 0, 0, ctrlid, 0,
0, CTRL_CMD_GETFAMILY, 0);
ret = -ENOBUFS;
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
ret = nl_send_auto_complete(sock, msg);
if (ret < 0)
goto out;
ret = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, family_handler, &grp);
while (ret > 0)
nl_recvmsgs(sock, cb);
if (ret == 0)
ret = grp.id;
nla_put_failure:
out:
nl_cb_put(cb);
out_fail_cb:
nlmsg_free(msg);
return ret;
}

148
hwsim.c Normal file
View File

@ -0,0 +1,148 @@
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
/* These enums need to be kept in sync with the kernel */
enum hwsim_testmode_attr {
__HWSIM_TM_ATTR_INVALID = 0,
HWSIM_TM_ATTR_CMD = 1,
HWSIM_TM_ATTR_PS = 2,
/* keep last */
__HWSIM_TM_ATTR_AFTER_LAST,
HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1
};
enum hwsim_testmode_cmd {
HWSIM_TM_CMD_SET_PS = 0,
HWSIM_TM_CMD_GET_PS = 1,
HWSIM_TM_CMD_STOP_QUEUES = 2,
HWSIM_TM_CMD_WAKE_QUEUES = 3,
};
SECTION(hwsim);
static int print_hwsim_ps_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!attrs[NL80211_ATTR_TESTDATA])
return NL_SKIP;
nla_parse(tb, HWSIM_TM_ATTR_MAX, nla_data(attrs[NL80211_ATTR_TESTDATA]),
nla_len(attrs[NL80211_ATTR_TESTDATA]), NULL);
printf("HWSIM PS: %d\n", nla_get_u32(tb[HWSIM_TM_ATTR_PS]));
return NL_SKIP;
}
static int handle_hwsim_getps(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *tmdata;
tmdata = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
if (!tmdata)
goto nla_put_failure;
NLA_PUT_U32(msg, HWSIM_TM_ATTR_CMD, HWSIM_TM_CMD_GET_PS);
nla_nest_end(msg, tmdata);
register_handler(print_hwsim_ps_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(hwsim, getps, "", NL80211_CMD_TESTMODE, 0, CIB_PHY, handle_hwsim_getps, "");
static int handle_hwsim_setps(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *tmdata;
__u32 ps;
char *end;
if (argc != 1)
return 1;
ps = strtoul(argv[0], &end, 0);
if (*end)
return 1;
tmdata = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
if (!tmdata)
goto nla_put_failure;
NLA_PUT_U32(msg, HWSIM_TM_ATTR_CMD, HWSIM_TM_CMD_SET_PS);
NLA_PUT_U32(msg, HWSIM_TM_ATTR_PS, ps);
nla_nest_end(msg, tmdata);
register_handler(print_hwsim_ps_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(hwsim, setps, "<value>", NL80211_CMD_TESTMODE, 0, CIB_PHY, handle_hwsim_setps, "");
static int handle_hwsim_stop_queues(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *tmdata;
if (argc != 0)
return 1;
tmdata = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
if (!tmdata)
goto nla_put_failure;
NLA_PUT_U32(msg, HWSIM_TM_ATTR_CMD, HWSIM_TM_CMD_STOP_QUEUES);
nla_nest_end(msg, tmdata);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(hwsim, stopqueues, "", NL80211_CMD_TESTMODE, 0, CIB_PHY, handle_hwsim_stop_queues, "");
static int handle_hwsim_wake_queues(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *tmdata;
if (argc != 0)
return 1;
tmdata = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
if (!tmdata)
goto nla_put_failure;
NLA_PUT_U32(msg, HWSIM_TM_ATTR_CMD, HWSIM_TM_CMD_WAKE_QUEUES);
nla_nest_end(msg, tmdata);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(hwsim, wakequeues, "", NL80211_CMD_TESTMODE, 0, CIB_PHY, handle_hwsim_wake_queues, "");

143
ibss.c Normal file
View File

@ -0,0 +1,143 @@
#include <errno.h>
#include <string.h>
#include <strings.h>
#include "nl80211.h"
#include "iw.h"
SECTION(ibss);
static int join_ibss(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
struct chandef chandef;
unsigned char abssid[6];
unsigned char rates[NL80211_MAX_SUPP_RATES];
int n_rates = 0;
char *value = NULL, *sptr = NULL;
float rate;
int bintval;
int parsed, err;
if (argc < 2)
return 1;
/* SSID */
NLA_PUT(msg, NL80211_ATTR_SSID, strlen(argv[0]), argv[0]);
argv++;
argc--;
err = parse_freqchan(&chandef, false, argc, argv, &parsed);
if (err)
return err;
argv += parsed;
argc -= parsed;
put_chandef(msg, &chandef);
if (err)
return err;
if (argc && strcmp(argv[0], "fixed-freq") == 0) {
NLA_PUT_FLAG(msg, NL80211_ATTR_FREQ_FIXED);
argv++;
argc--;
}
if (argc) {
if (mac_addr_a2n(abssid, argv[0]) == 0) {
NLA_PUT(msg, NL80211_ATTR_MAC, 6, abssid);
argv++;
argc--;
}
}
if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) {
argv++;
argc--;
bintval = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval);
argv++;
argc--;
}
/* basic rates */
if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) {
argv++;
argc--;
value = strtok_r(argv[0], ",", &sptr);
while (value && n_rates < NL80211_MAX_SUPP_RATES) {
rate = strtod(value, &end);
rates[n_rates] = rate * 2;
/* filter out suspicious values */
if (*end != '\0' || !rates[n_rates] ||
rate*2 != rates[n_rates])
return 1;
n_rates++;
value = strtok_r(NULL, ",", &sptr);
}
NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates);
argv++;
argc--;
}
/* multicast rate */
if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) {
argv++;
argc--;
rate = strtod(argv[0], &end);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
argv++;
argc--;
}
if (!argc)
return 0;
if (strcmp(*argv, "key") != 0 && strcmp(*argv, "keys") != 0)
return 1;
argv++;
argc--;
return parse_keys(msg, argv, argc);
nla_put_failure:
return -ENOSPC;
}
static int leave_ibss(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return 0;
}
COMMAND(ibss, leave, NULL,
NL80211_CMD_LEAVE_IBSS, 0, CIB_NETDEV, leave_ibss,
"Leave the current IBSS cell.");
COMMAND(ibss, join,
"<SSID> <freq in MHz> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [fixed-freq] [<fixed bssid>] [beacon-interval <TU>]"
" [basic-rates <rate in Mbps,rate2,...>] [mcast-rate <rate in Mbps>] "
"[key d:0:abcde]",
NL80211_CMD_JOIN_IBSS, 0, CIB_NETDEV, join_ibss,
"Join the IBSS cell with the given SSID, if it doesn't exist create\n"
"it on the given frequency. When fixed frequency is requested, don't\n"
"join/create a cell on a different frequency. When a fixed BSSID is\n"
"requested use that BSSID and do not adopt another cell's BSSID even\n"
"if it has higher TSF and the same SSID. If an IBSS is created, create\n"
"it with the specified basic-rates, multicast-rate and beacon-interval.");

61
ieee80211.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef __IEEE80211
#define __IEEE80211
/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
#define IEEE80211_HT_CAP_SGI_40 0x0040
#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
#define IEEE80211_HT_MCS_MASK_LEN 10
/**
* struct ieee80211_mcs_info - MCS information
* @rx_mask: RX mask
* @rx_highest: highest supported RX rate. If set represents
* the highest supported RX data rate in units of 1 Mbps.
* If this field is 0 this value should not be used to
* consider the highest RX data rate supported.
* @tx_params: TX parameters
*/
struct ieee80211_mcs_info {
__u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN];
__u16 rx_highest;
__u8 tx_params;
__u8 reserved[3];
} __attribute__ ((packed));
/**
* struct ieee80211_ht_cap - HT capabilities
*
* This structure is the "HT capabilities element" as
* described in 802.11n D5.0 7.3.2.57
*/
struct ieee80211_ht_cap {
__u16 cap_info;
__u8 ampdu_params_info;
/* 16 bytes MCS information */
struct ieee80211_mcs_info mcs;
__u16 extended_ht_cap_info;
__u32 tx_BF_cap_info;
__u8 antenna_selection_info;
} __attribute__ ((packed));
struct ieee80211_vht_mcs_info {
__u16 rx_vht_mcs;
__u16 rx_highest;
__u16 tx_vht_mcs;
__u16 tx_highest;
} __attribute__ ((packed));
struct ieee80211_vht_cap {
__u32 cap_info;
struct ieee80211_vht_mcs_info mcs;
} __attribute__ ((packed));
#endif /* __IEEE80211 */

705
info.c Normal file
View File

@ -0,0 +1,705 @@
#include <stdbool.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
static void print_flag(const char *name, int *open)
{
if (!*open)
printf(" (");
else
printf(", ");
printf("%s", name);
*open = 1;
}
static char *cipher_name(__u32 c)
{
static char buf[20];
switch (c) {
case 0x000fac01:
return "WEP40 (00-0f-ac:1)";
case 0x000fac05:
return "WEP104 (00-0f-ac:5)";
case 0x000fac02:
return "TKIP (00-0f-ac:2)";
case 0x000fac04:
return "CCMP-128 (00-0f-ac:4)";
case 0x000fac06:
return "CMAC (00-0f-ac:6)";
case 0x000fac08:
return "GCMP-128 (00-0f-ac:8)";
case 0x000fac09:
return "GCMP-256 (00-0f-ac:9)";
case 0x000fac0a:
return "CCMP-256 (00-0f-ac:10)";
case 0x000fac0b:
return "GMAC-128 (00-0f-ac:11)";
case 0x000fac0c:
return "GMAC-256 (00-0f-ac:12)";
case 0x000fac0d:
return "CMAC-256 (00-0f-ac:13)";
case 0x00147201:
return "WPI-SMS4 (00-14-72:1)";
default:
sprintf(buf, "%.2x-%.2x-%.2x:%d",
c >> 24, (c >> 16) & 0xff,
(c >> 8) & 0xff, c & 0xff);
return buf;
}
}
static int ext_feature_isset(const unsigned char *ext_features, int ext_features_len,
enum nl80211_ext_feature_index ftidx)
{
unsigned char ft_byte;
if ((int) ftidx / 8 >= ext_features_len)
return 0;
ft_byte = ext_features[ftidx / 8];
return (ft_byte & BIT(ftidx % 8)) != 0;
}
static int print_phy_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
[__NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
};
struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
};
struct nlattr *nl_band;
struct nlattr *nl_freq;
struct nlattr *nl_rate;
struct nlattr *nl_mode;
struct nlattr *nl_cmd;
struct nlattr *nl_if, *nl_ftype;
int rem_band, rem_freq, rem_rate, rem_mode, rem_cmd, rem_ftype, rem_if;
int open;
/*
* static variables only work here, other applications need to use the
* callback pointer and store them there so they can be multithreaded
* and/or have multiple netlink sockets, etc.
*/
static int64_t phy_id = -1;
static int last_band = -1;
static bool band_had_freq = false;
bool print_name = true;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb_msg[NL80211_ATTR_WIPHY]) {
if (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]) == phy_id)
print_name = false;
else
last_band = -1;
phy_id = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
}
if (print_name && tb_msg[NL80211_ATTR_WIPHY_NAME])
printf("Wiphy %s\n", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]));
/* needed for split dump */
if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
if (last_band != nl_band->nla_type) {
printf("\tBand %d:\n", nl_band->nla_type + 1);
band_had_freq = false;
}
last_band = nl_band->nla_type;
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
nla_len(nl_band), NULL);
if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
__u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
print_ht_capability(cap);
}
if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
__u8 exponent = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
print_ampdu_length(exponent);
}
if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
__u8 spacing = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
print_ampdu_spacing(spacing);
}
if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16)
print_ht_mcs(nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]));
if (tb_band[NL80211_BAND_ATTR_VHT_CAPA] &&
tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])
print_vht_info(nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]),
nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]));
if (tb_band[NL80211_BAND_ATTR_FREQS]) {
if (!band_had_freq) {
printf("\t\tFrequencies:\n");
band_had_freq = true;
}
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
uint32_t freq;
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
nla_len(nl_freq), freq_policy);
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
printf("\t\t\t* %d MHz [%d]", freq, ieee80211_frequency_to_channel(freq));
if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
printf(" (%.1f dBm)", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
open = 0;
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
print_flag("disabled", &open);
goto next;
}
/* If both flags are set assume an new kernel */
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) {
print_flag("no IR", &open);
} else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) {
print_flag("passive scan", &open);
} else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){
print_flag("no ibss", &open);
}
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
print_flag("radar detection", &open);
next:
if (open)
printf(")");
printf("\n");
}
}
if (tb_band[NL80211_BAND_ATTR_RATES]) {
printf("\t\tBitrates (non-HT):\n");
nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
nla_len(nl_rate), rate_policy);
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
continue;
printf("\t\t\t* %2.1f Mbps", 0.1 * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]));
open = 0;
if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE])
print_flag("short preamble supported", &open);
if (open)
printf(")");
printf("\n");
}
}
}
}
if (tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
printf("\tmax # scan SSIDs: %d\n",
nla_get_u8(tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]));
if (tb_msg[NL80211_ATTR_MAX_SCAN_IE_LEN])
printf("\tmax scan IEs length: %d bytes\n",
nla_get_u16(tb_msg[NL80211_ATTR_MAX_SCAN_IE_LEN]));
if (tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
printf("\tmax # sched scan SSIDs: %d\n",
nla_get_u8(tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]));
if (tb_msg[NL80211_ATTR_MAX_MATCH_SETS])
printf("\tmax # match sets: %d\n",
nla_get_u8(tb_msg[NL80211_ATTR_MAX_MATCH_SETS]));
if (tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS])
printf("\tmax # scan plans: %d\n",
nla_get_u32(tb_msg[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]));
if (tb_msg[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL])
printf("\tmax scan plan interval: %d\n",
nla_get_u32(tb_msg[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]));
if (tb_msg[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS])
printf("\tmax scan plan iterations: %d\n",
nla_get_u32(tb_msg[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]));
if (tb_msg[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
unsigned int frag;
frag = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
if (frag != (unsigned int)-1)
printf("\tFragmentation threshold: %d\n", frag);
}
if (tb_msg[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
unsigned int rts;
rts = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
if (rts != (unsigned int)-1)
printf("\tRTS threshold: %d\n", rts);
}
if (tb_msg[NL80211_ATTR_WIPHY_RETRY_SHORT] ||
tb_msg[NL80211_ATTR_WIPHY_RETRY_LONG]) {
unsigned char retry_short = 0, retry_long = 0;
if (tb_msg[NL80211_ATTR_WIPHY_RETRY_SHORT])
retry_short = nla_get_u8(tb_msg[NL80211_ATTR_WIPHY_RETRY_SHORT]);
if (tb_msg[NL80211_ATTR_WIPHY_RETRY_LONG])
retry_long = nla_get_u8(tb_msg[NL80211_ATTR_WIPHY_RETRY_LONG]);
if (retry_short == retry_long) {
printf("\tRetry short long limit: %d\n", retry_short);
} else {
printf("\tRetry short limit: %d\n", retry_short);
printf("\tRetry long limit: %d\n", retry_long);
}
}
if (tb_msg[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
unsigned char coverage;
coverage = nla_get_u8(tb_msg[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
/* See handle_distance() for an explanation where the '450' comes from */
printf("\tCoverage class: %d (up to %dm)\n", coverage, 450 * coverage);
}
if (tb_msg[NL80211_ATTR_CIPHER_SUITES]) {
int num = nla_len(tb_msg[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
int i;
__u32 *ciphers = nla_data(tb_msg[NL80211_ATTR_CIPHER_SUITES]);
if (num > 0) {
printf("\tSupported Ciphers:\n");
for (i = 0; i < num; i++)
printf("\t\t* %s\n",
cipher_name(ciphers[i]));
}
}
if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX] &&
tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX])
printf("\tAvailable Antennas: TX %#x RX %#x\n",
nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX]),
nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX]));
if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX])
printf("\tConfigured Antennas: TX %#x RX %#x\n",
nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_TX]),
nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_ANTENNA_RX]));
if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]) {
printf("\tSupported interface modes:\n");
nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
printf("\t\t * %s\n", iftype_name(nla_type(nl_mode)));
}
if (tb_msg[NL80211_ATTR_SOFTWARE_IFTYPES]) {
printf("\tsoftware interface modes (can always be added):\n");
nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SOFTWARE_IFTYPES], rem_mode)
printf("\t\t * %s\n", iftype_name(nla_type(nl_mode)));
}
if (tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS]) {
struct nlattr *nl_combi;
int rem_combi;
bool have_combinations = false;
nla_for_each_nested(nl_combi, tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS], rem_combi) {
static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
};
struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
};
struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
struct nlattr *nl_limit;
int err, rem_limit;
bool comma = false;
if (!have_combinations) {
printf("\tvalid interface combinations:\n");
have_combinations = true;
}
printf("\t\t * ");
err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
nl_combi, iface_combination_policy);
if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
!tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
!tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) {
printf(" <failed to parse>\n");
goto broken_combination;
}
nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], rem_limit) {
bool ift_comma = false;
err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
nl_limit, iface_limit_policy);
if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) {
printf("<failed to parse>\n");
goto broken_combination;
}
if (comma)
printf(", ");
comma = true;
printf("#{");
nla_for_each_nested(nl_mode, tb_limit[NL80211_IFACE_LIMIT_TYPES], rem_mode) {
printf("%s %s", ift_comma ? "," : "",
iftype_name(nla_type(nl_mode)));
ift_comma = true;
}
printf(" } <= %u", nla_get_u32(tb_limit[NL80211_IFACE_LIMIT_MAX]));
}
printf(",\n\t\t ");
printf("total <= %d, #channels <= %d%s",
nla_get_u32(tb_comb[NL80211_IFACE_COMB_MAXNUM]),
nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]),
tb_comb[NL80211_IFACE_COMB_STA_AP_BI_MATCH] ?
", STA/AP BI must match" : "");
if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) {
unsigned long widths = nla_get_u32(tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]);
if (widths) {
int width;
bool first = true;
printf(", radar detect widths: {");
for (width = 0; width < 32; width++)
if (widths & (1 << width)) {
printf("%s %s",
first ? "":",",
channel_width_name(width));
first = false;
}
printf(" }\n");
}
}
printf("\n");
broken_combination:
;
}
if (!have_combinations)
printf("\tinterface combinations are not supported\n");
}
if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) {
printf("\tSupported commands:\n");
nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], rem_cmd)
printf("\t\t * %s\n", command_name(nla_get_u32(nl_cmd)));
}
if (tb_msg[NL80211_ATTR_TX_FRAME_TYPES]) {
printf("\tSupported TX frame types:\n");
nla_for_each_nested(nl_if, tb_msg[NL80211_ATTR_TX_FRAME_TYPES], rem_if) {
bool printed = false;
nla_for_each_nested(nl_ftype, nl_if, rem_ftype) {
if (!printed)
printf("\t\t * %s:", iftype_name(nla_type(nl_if)));
printed = true;
printf(" 0x%.2x", nla_get_u16(nl_ftype));
}
if (printed)
printf("\n");
}
}
if (tb_msg[NL80211_ATTR_RX_FRAME_TYPES]) {
printf("\tSupported RX frame types:\n");
nla_for_each_nested(nl_if, tb_msg[NL80211_ATTR_RX_FRAME_TYPES], rem_if) {
bool printed = false;
nla_for_each_nested(nl_ftype, nl_if, rem_ftype) {
if (!printed)
printf("\t\t * %s:", iftype_name(nla_type(nl_if)));
printed = true;
printf(" 0x%.2x", nla_get_u16(nl_ftype));
}
if (printed)
printf("\n");
}
}
if (tb_msg[NL80211_ATTR_SUPPORT_IBSS_RSN])
printf("\tDevice supports RSN-IBSS.\n");
if (tb_msg[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]) {
struct nlattr *tb_wowlan[NUM_NL80211_WOWLAN_TRIG];
static struct nla_policy wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .minlen = 12 },
[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_U32 },
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
};
struct nl80211_pattern_support *pat;
int err;
err = nla_parse_nested(tb_wowlan, MAX_NL80211_WOWLAN_TRIG,
tb_msg[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED],
wowlan_policy);
printf("\tWoWLAN support:");
if (err) {
printf(" <failed to parse>\n");
} else {
printf("\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_ANY])
printf("\t\t * wake up on anything (device continues operating normally)\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_DISCONNECT])
printf("\t\t * wake up on disconnect\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_MAGIC_PKT])
printf("\t\t * wake up on magic packet\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
unsigned int len = nla_len(tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]);
pat = nla_data(tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]);
printf("\t\t * wake up on pattern match, up to %u patterns of %u-%u bytes,\n"
"\t\t maximum packet offset %u bytes\n",
pat->max_patterns, pat->min_pattern_len, pat->max_pattern_len,
len < sizeof(*pat) ? 0 : pat->max_pkt_offset);
}
if (tb_wowlan[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
printf("\t\t * can do GTK rekeying\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
printf("\t\t * wake up on GTK rekey failure\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
printf("\t\t * wake up on EAP identity request\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
printf("\t\t * wake up on 4-way handshake\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
printf("\t\t * wake up on rfkill release\n");
if (tb_wowlan[NL80211_WOWLAN_TRIG_NET_DETECT])
printf("\t\t * wake up on network detection, up to %d match sets\n",
nla_get_u32(tb_wowlan[NL80211_WOWLAN_TRIG_NET_DETECT]));
if (tb_wowlan[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
printf("\t\t * wake up on TCP connection\n");
}
}
if (tb_msg[NL80211_ATTR_ROAM_SUPPORT])
printf("\tDevice supports roaming.\n");
if (tb_msg[NL80211_ATTR_SUPPORT_AP_UAPSD])
printf("\tDevice supports AP-side u-APSD.\n");
if (tb_msg[NL80211_ATTR_HT_CAPABILITY_MASK]) {
struct ieee80211_ht_cap *cm;
unsigned int len = nla_len(tb_msg[NL80211_ATTR_HT_CAPABILITY_MASK]);
printf("\tHT Capability overrides:\n");
if (len >= sizeof(*cm)) {
cm = nla_data(tb_msg[NL80211_ATTR_HT_CAPABILITY_MASK]);
printf("\t\t * MCS: %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx"
" %02hhx %02hhx %02hhx %02hhx\n",
cm->mcs.rx_mask[0], cm->mcs.rx_mask[1],
cm->mcs.rx_mask[2], cm->mcs.rx_mask[3],
cm->mcs.rx_mask[4], cm->mcs.rx_mask[5],
cm->mcs.rx_mask[6], cm->mcs.rx_mask[7],
cm->mcs.rx_mask[8], cm->mcs.rx_mask[9]);
if (cm->cap_info & htole16(IEEE80211_HT_CAP_MAX_AMSDU))
printf("\t\t * maximum A-MSDU length\n");
if (cm->cap_info & htole16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))
printf("\t\t * supported channel width\n");
if (cm->cap_info & htole16(IEEE80211_HT_CAP_SGI_40))
printf("\t\t * short GI for 40 MHz\n");
if (cm->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR)
printf("\t\t * max A-MPDU length exponent\n");
if (cm->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY)
printf("\t\t * min MPDU start spacing\n");
} else {
printf("\tERROR: capabilities mask is too short, expected: %d, received: %d\n",
(int)(sizeof(*cm)),
(int)(nla_len(tb_msg[NL80211_ATTR_HT_CAPABILITY_MASK])));
}
}
if (tb_msg[NL80211_ATTR_FEATURE_FLAGS]) {
unsigned int features = nla_get_u32(tb_msg[NL80211_ATTR_FEATURE_FLAGS]);
if (features & NL80211_FEATURE_SK_TX_STATUS)
printf("\tDevice supports TX status socket option.\n");
if (features & NL80211_FEATURE_HT_IBSS)
printf("\tDevice supports HT-IBSS.\n");
if (features & NL80211_FEATURE_INACTIVITY_TIMER)
printf("\tDevice has client inactivity timer.\n");
if (features & NL80211_FEATURE_CELL_BASE_REG_HINTS)
printf("\tDevice accepts cell base station regulatory hints.\n");
if (features & NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
printf("\tP2P Device uses a channel (of the concurrent ones)\n");
if (features & NL80211_FEATURE_SAE)
printf("\tDevice supports SAE with AUTHENTICATE command\n");
if (features & NL80211_FEATURE_LOW_PRIORITY_SCAN)
printf("\tDevice supports low priority scan.\n");
if (features & NL80211_FEATURE_SCAN_FLUSH)
printf("\tDevice supports scan flush.\n");
if (features & NL80211_FEATURE_AP_SCAN)
printf("\tDevice supports AP scan.\n");
if (features & NL80211_FEATURE_VIF_TXPOWER)
printf("\tDevice supports per-vif TX power setting\n");
if (features & NL80211_FEATURE_NEED_OBSS_SCAN)
printf("\tUserspace should do OBSS scan and generate 20/40 coex reports\n");
if (features & NL80211_FEATURE_P2P_GO_CTWIN)
printf("\tP2P GO supports CT window setting\n");
if (features & NL80211_FEATURE_P2P_GO_OPPPS)
printf("\tP2P GO supports opportunistic powersave setting\n");
if (features & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
printf("\tDriver supports full state transitions for AP/GO clients\n");
if (features & NL80211_FEATURE_USERSPACE_MPM)
printf("\tDriver supports a userspace MPM\n");
if (features & NL80211_FEATURE_ACTIVE_MONITOR)
printf("\tDevice supports active monitor (which will ACK incoming frames)\n");
if (features & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
printf("\tDriver/device bandwidth changes during BSS lifetime (AP/GO mode)\n");
if (features & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
printf("\tDevice adds DS IE to probe requests\n");
if (features & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
printf("\tDevice adds WFA TPC Report IE to probe requests\n");
if (features & NL80211_FEATURE_QUIET)
printf("\tDevice supports quiet requests from AP\n");
if (features & NL80211_FEATURE_TX_POWER_INSERTION)
printf("\tDevice can update TPC Report IE\n");
if (features & NL80211_FEATURE_ACKTO_ESTIMATION)
printf("\tDevice supports ACK timeout estimation.\n");
if (features & NL80211_FEATURE_STATIC_SMPS)
printf("\tDevice supports static SMPS\n");
if (features & NL80211_FEATURE_DYNAMIC_SMPS)
printf("\tDevice supports dynamic SMPS\n");
if (features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
printf("\tDevice supports WMM-AC admission (TSPECs)\n");
if (features & NL80211_FEATURE_MAC_ON_CREATE)
printf("\tDevice supports configuring vdev MAC-addr on create.\n");
if (features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)
printf("\tDevice supports TDLS channel switching\n");
}
if (tb_msg[NL80211_ATTR_EXT_FEATURES]) {
struct nlattr *tb = tb_msg[NL80211_ATTR_EXT_FEATURES];
if (ext_feature_isset(nla_data(tb), nla_len(tb),
NL80211_EXT_FEATURE_VHT_IBSS))
printf("\tDevice supports VHT-IBSS.\n");
}
if (tb_msg[NL80211_ATTR_TDLS_SUPPORT])
printf("\tDevice supports T-DLS.\n");
if (tb_msg[NL80211_ATTR_COALESCE_RULE]) {
struct nl80211_coalesce_rule_support *rule;
struct nl80211_pattern_support *pat;
printf("\tCoalesce support:\n");
rule = nla_data(tb_msg[NL80211_ATTR_COALESCE_RULE]);
pat = &rule->pat;
printf("\t\t * Maximum %u coalesce rules supported\n"
"\t\t * Each rule contains upto %u patterns of %u-%u bytes,\n"
"\t\t maximum packet offset %u bytes\n"
"\t\t * Maximum supported coalescing delay %u msecs\n",
rule->max_rules, pat->max_patterns, pat->min_pattern_len,
pat->max_pattern_len, pat->max_pkt_offset, rule->max_delay);
}
return NL_SKIP;
}
static bool nl80211_has_split_wiphy = false;
static int handle_info(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *feat_args[] = { "features", "-q" };
int err;
err = handle_cmd(state, CIB_NONE, 2, feat_args);
if (!err && nl80211_has_split_wiphy) {
nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
}
register_handler(print_phy_handler, NULL);
return 0;
}
__COMMAND(NULL, info, "info", NULL, NL80211_CMD_GET_WIPHY, 0, 0, CIB_PHY, handle_info,
"Show capabilities for the specified wireless device.", NULL);
TOPLEVEL(list, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info,
"List all wireless devices and their capabilities.");
TOPLEVEL(phy, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info, NULL);
static int handle_commands(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
int i;
for (i = 1; i <= NL80211_CMD_MAX; i++)
printf("%d (0x%x): %s\n", i, i, command_name(i));
/* don't send netlink messages */
return 2;
}
TOPLEVEL(commands, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_NONE, handle_commands,
"list all known commands and their decimal & hex value");
static int print_feature_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
bool print = (unsigned long)arg;
#define maybe_printf(...) do { if (print) printf(__VA_ARGS__); } while (0)
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) {
uint32_t feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
maybe_printf("nl80211 features: 0x%x\n", feat);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) {
maybe_printf("\t* split wiphy dump\n");
nl80211_has_split_wiphy = true;
}
}
return NL_SKIP;
}
static int handle_features(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
unsigned long print = argc == 0 || strcmp(argv[0], "-q");
register_handler(print_feature_handler, (void *)print);
return 0;
}
TOPLEVEL(features, "", NL80211_CMD_GET_PROTOCOL_FEATURES, 0, CIB_NONE,
handle_features, "");

717
interface.c Normal file
View File

@ -0,0 +1,717 @@
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
#define VALID_FLAGS "none: no special flags\n"\
"fcsfail: show frames with FCS errors\n"\
"control: show control frames\n"\
"otherbss: show frames from other BSSes\n"\
"cook: use cooked mode\n"\
"active: use active mode (ACK incoming unicast packets)\n"\
"mumimo-groupid <GROUP_ID>: use MUMIMO according to a group id\n"\
"mumimo-follow-mac <MAC_ADDRESS>: use MUMIMO according to a MAC address"
SECTION(interface);
static char *mntr_flags[NL80211_MNTR_FLAG_MAX + 1] = {
"none",
"fcsfail",
"plcpfail",
"control",
"otherbss",
"cook",
"active",
};
static int parse_mumimo_options(int *_argc, char ***_argv, struct nl_msg *msg)
{
uint8_t mumimo_group[VHT_MUMIMO_GROUP_LEN];
unsigned char mac_addr[ETH_ALEN];
char **argv = *_argv;
int argc = *_argc;
int i;
unsigned int val;
if (strcmp(*argv, "mumimo-groupid") == 0) {
argc--;
argv++;
if (!argc || strlen(*argv) != VHT_MUMIMO_GROUP_LEN*2) {
fprintf(stderr, "Invalid groupID: %s\n", *argv);
return 1;
}
for (i = 0; i < VHT_MUMIMO_GROUP_LEN; i++) {
if (sscanf((*argv) + i*2, "%2x", &val) != 1) {
fprintf(stderr, "Failed reading groupID\n");
return 1;
}
mumimo_group[i] = val;
}
NLA_PUT(msg,
NL80211_ATTR_MU_MIMO_GROUP_DATA,
VHT_MUMIMO_GROUP_LEN,
mumimo_group);
argc--;
argv++;
} else if (strcmp(*argv, "mumimo-follow-mac") == 0) {
argc--;
argv++;
if (!argc || mac_addr_a2n(mac_addr, *argv)) {
fprintf(stderr, "Invalid MAC address\n");
return 1;
}
NLA_PUT(msg, NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
ETH_ALEN, mac_addr);
argc--;
argv++;
}
nla_put_failure:
*_argc = argc;
*_argv = argv;
return 0;
}
static int parse_mntr_flags(int *_argc, char ***_argv,
struct nl_msg *msg)
{
struct nl_msg *flags;
int err = -ENOBUFS;
enum nl80211_mntr_flags flag;
int argc = *_argc;
char **argv = *_argv;
flags = nlmsg_alloc();
if (!flags)
return -ENOMEM;
while (argc) {
int ok = 0;
/* parse MU-MIMO options */
err = parse_mumimo_options(&argc, &argv, msg);
if (err)
goto out;
else if (!argc)
break;
/* parse monitor flags */
for (flag = __NL80211_MNTR_FLAG_INVALID;
flag <= NL80211_MNTR_FLAG_MAX; flag++) {
if (strcmp(*argv, mntr_flags[flag]) == 0) {
ok = 1;
/*
* This shouldn't be adding "flag" if that is
* zero, but due to a problem in the kernel's
* nl80211 code (using NLA_NESTED policy) it
* will reject an empty nested attribute but
* not one that contains an invalid attribute
*/
NLA_PUT_FLAG(flags, flag);
break;
}
}
if (!ok) {
err = -EINVAL;
goto out;
}
argc--;
argv++;
}
nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags);
err = 0;
nla_put_failure:
out:
nlmsg_free(flags);
*_argc = argc;
*_argv = argv;
return err;
}
/* for help */
#define IFACE_TYPES "Valid interface types are: managed, ibss, monitor, mesh, wds."
/* return 0 if ok, internal error otherwise */
static int get_if_type(int *argc, char ***argv, enum nl80211_iftype *type,
bool need_type)
{
char *tpstr;
if (*argc < 1 + !!need_type)
return 1;
if (need_type && strcmp((*argv)[0], "type"))
return 1;
tpstr = (*argv)[!!need_type];
*argc -= 1 + !!need_type;
*argv += 1 + !!need_type;
if (strcmp(tpstr, "adhoc") == 0 ||
strcmp(tpstr, "ibss") == 0) {
*type = NL80211_IFTYPE_ADHOC;
return 0;
} else if (strcmp(tpstr, "ocb") == 0) {
*type = NL80211_IFTYPE_OCB;
return 0;
} else if (strcmp(tpstr, "monitor") == 0) {
*type = NL80211_IFTYPE_MONITOR;
return 0;
} else if (strcmp(tpstr, "master") == 0 ||
strcmp(tpstr, "ap") == 0) {
*type = NL80211_IFTYPE_UNSPECIFIED;
fprintf(stderr, "You need to run a management daemon, e.g. hostapd,\n");
fprintf(stderr, "see http://wireless.kernel.org/en/users/Documentation/hostapd\n");
fprintf(stderr, "for more information on how to do that.\n");
return 2;
} else if (strcmp(tpstr, "__ap") == 0) {
*type = NL80211_IFTYPE_AP;
return 0;
} else if (strcmp(tpstr, "__ap_vlan") == 0) {
*type = NL80211_IFTYPE_AP_VLAN;
return 0;
} else if (strcmp(tpstr, "wds") == 0) {
*type = NL80211_IFTYPE_WDS;
return 0;
} else if (strcmp(tpstr, "managed") == 0 ||
strcmp(tpstr, "mgd") == 0 ||
strcmp(tpstr, "station") == 0) {
*type = NL80211_IFTYPE_STATION;
return 0;
} else if (strcmp(tpstr, "mp") == 0 ||
strcmp(tpstr, "mesh") == 0) {
*type = NL80211_IFTYPE_MESH_POINT;
return 0;
} else if (strcmp(tpstr, "__p2pcl") == 0) {
*type = NL80211_IFTYPE_P2P_CLIENT;
return 0;
} else if (strcmp(tpstr, "__p2pdev") == 0) {
*type = NL80211_IFTYPE_P2P_DEVICE;
return 0;
} else if (strcmp(tpstr, "__p2pgo") == 0) {
*type = NL80211_IFTYPE_P2P_GO;
return 0;
} else if (strcmp(tpstr, "__nan") == 0) {
*type = NL80211_IFTYPE_NAN;
return 0;
}
fprintf(stderr, "invalid interface type %s\n", tpstr);
return 2;
}
static int parse_4addr_flag(const char *value, struct nl_msg *msg)
{
if (strcmp(value, "on") == 0)
NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 1);
else if (strcmp(value, "off") == 0)
NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, 0);
else
return 1;
return 0;
nla_put_failure:
return 1;
}
static int handle_interface_add(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *name;
char *mesh_id = NULL;
enum nl80211_iftype type;
int tpset;
unsigned char mac_addr[ETH_ALEN];
int found_mac = 0;
if (argc < 1)
return 1;
name = argv[0];
argc--;
argv++;
tpset = get_if_type(&argc, &argv, &type, true);
if (tpset)
return tpset;
try_another:
if (argc) {
if (strcmp(argv[0], "mesh_id") == 0) {
argc--;
argv++;
if (!argc)
return 1;
mesh_id = argv[0];
argc--;
argv++;
} else if (strcmp(argv[0], "addr") == 0) {
argc--;
argv++;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "Invalid MAC address\n");
return 2;
}
argc--;
argv++;
found_mac = 1;
goto try_another;
} else if (strcmp(argv[0], "4addr") == 0) {
argc--;
argv++;
if (parse_4addr_flag(argv[0], msg)) {
fprintf(stderr, "4addr error\n");
return 2;
}
argc--;
argv++;
} else if (strcmp(argv[0], "flags") == 0) {
argc--;
argv++;
if (parse_mntr_flags(&argc, &argv, msg)) {
fprintf(stderr, "flags error\n");
return 2;
}
} else {
return 1;
}
}
if (argc)
return 1;
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type);
if (mesh_id)
NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(mesh_id), mesh_id);
if (found_mac)
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [4addr on|off] [flags <flag>*] [addr <mac-addr>]",
NL80211_CMD_NEW_INTERFACE, 0, CIB_PHY, handle_interface_add,
"Add a new virtual interface with the given configuration.\n"
IFACE_TYPES "\n\n"
"The flags are only used for monitor interfaces, valid flags are:\n"
VALID_FLAGS "\n\n"
"The mesh_id is used only for mesh mode.");
COMMAND(interface, add, "<name> type <type> [mesh_id <meshid>] [4addr on|off] [flags <flag>*] [addr <mac-addr>]",
NL80211_CMD_NEW_INTERFACE, 0, CIB_NETDEV, handle_interface_add, NULL);
static int handle_interface_del(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return 0;
}
TOPLEVEL(del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del,
"Remove this virtual interface");
HIDDEN(interface, del, NULL, NL80211_CMD_DEL_INTERFACE, 0, CIB_NETDEV, handle_interface_del);
static char *channel_type_name(enum nl80211_channel_type channel_type)
{
switch (channel_type) {
case NL80211_CHAN_NO_HT:
return "NO HT";
case NL80211_CHAN_HT20:
return "HT20";
case NL80211_CHAN_HT40MINUS:
return "HT40-";
case NL80211_CHAN_HT40PLUS:
return "HT40+";
default:
return "unknown";
}
}
char *channel_width_name(enum nl80211_chan_width width)
{
switch (width) {
case NL80211_CHAN_WIDTH_20_NOHT:
return "20 MHz (no HT)";
case NL80211_CHAN_WIDTH_20:
return "20 MHz";
case NL80211_CHAN_WIDTH_40:
return "40 MHz";
case NL80211_CHAN_WIDTH_80:
return "80 MHz";
case NL80211_CHAN_WIDTH_80P80:
return "80+80 MHz";
case NL80211_CHAN_WIDTH_160:
return "160 MHz";
case NL80211_CHAN_WIDTH_5:
return "5 MHz";
case NL80211_CHAN_WIDTH_10:
return "10 MHz";
default:
return "unknown";
}
}
static int print_iface_handler(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
unsigned int *wiphy = arg;
const char *indent = "";
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (wiphy && tb_msg[NL80211_ATTR_WIPHY]) {
unsigned int thiswiphy = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
indent = "\t";
if (*wiphy != thiswiphy)
printf("phy#%d\n", thiswiphy);
*wiphy = thiswiphy;
}
if (tb_msg[NL80211_ATTR_IFNAME])
printf("%sInterface %s\n", indent, nla_get_string(tb_msg[NL80211_ATTR_IFNAME]));
else
printf("%sUnnamed/non-netdev interface\n", indent);
if (tb_msg[NL80211_ATTR_IFINDEX])
printf("%s\tifindex %d\n", indent, nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]));
if (tb_msg[NL80211_ATTR_WDEV])
printf("%s\twdev 0x%llx\n", indent,
(unsigned long long)nla_get_u64(tb_msg[NL80211_ATTR_WDEV]));
if (tb_msg[NL80211_ATTR_MAC]) {
char mac_addr[20];
mac_addr_n2a(mac_addr, nla_data(tb_msg[NL80211_ATTR_MAC]));
printf("%s\taddr %s\n", indent, mac_addr);
}
if (tb_msg[NL80211_ATTR_SSID]) {
printf("%s\tssid ", indent);
print_ssid_escaped(nla_len(tb_msg[NL80211_ATTR_SSID]),
nla_data(tb_msg[NL80211_ATTR_SSID]));
printf("\n");
}
if (tb_msg[NL80211_ATTR_IFTYPE])
printf("%s\ttype %s\n", indent, iftype_name(nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE])));
if (!wiphy && tb_msg[NL80211_ATTR_WIPHY])
printf("%s\twiphy %d\n", indent, nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]));
if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
uint32_t freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
printf("%s\tchannel %d (%d MHz)", indent,
ieee80211_frequency_to_channel(freq), freq);
if (tb_msg[NL80211_ATTR_CHANNEL_WIDTH]) {
printf(", width: %s",
channel_width_name(nla_get_u32(tb_msg[NL80211_ATTR_CHANNEL_WIDTH])));
if (tb_msg[NL80211_ATTR_CENTER_FREQ1])
printf(", center1: %d MHz",
nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ1]));
if (tb_msg[NL80211_ATTR_CENTER_FREQ2])
printf(", center2: %d MHz",
nla_get_u32(tb_msg[NL80211_ATTR_CENTER_FREQ2]));
} else if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
enum nl80211_channel_type channel_type;
channel_type = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
printf(" %s", channel_type_name(channel_type));
}
printf("\n");
}
if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) {
uint32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
printf("%s\ttxpower %d.%.2d dBm\n",
indent, txp / 100, txp % 100);
}
return NL_SKIP;
}
static int handle_interface_info(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
register_handler(print_iface_handler, NULL);
return 0;
}
TOPLEVEL(info, NULL, NL80211_CMD_GET_INTERFACE, 0, CIB_NETDEV, handle_interface_info,
"Show information for this interface.");
static int handle_interface_set(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (!argc)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
switch (parse_mntr_flags(&argc, &argv, msg)) {
case 0:
return 0;
case 1:
return 1;
case -ENOMEM:
fprintf(stderr, "failed to allocate flags\n");
return 2;
case -EINVAL:
fprintf(stderr, "unknown flag %s\n", *argv);
return 2;
default:
return 2;
}
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, monitor, "<flag>*",
NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_set,
"Set monitor flags. Valid flags are:\n"
VALID_FLAGS);
static int handle_interface_meshid(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *mesh_id = NULL;
if (argc != 1)
return 1;
mesh_id = argv[0];
NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(mesh_id), mesh_id);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, meshid, "<meshid>",
NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_meshid, NULL);
static unsigned int dev_dump_wiphy;
static int handle_dev_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
dev_dump_wiphy = -1;
register_handler(print_iface_handler, &dev_dump_wiphy);
return 0;
}
TOPLEVEL(dev, NULL, NL80211_CMD_GET_INTERFACE, NLM_F_DUMP, CIB_NONE, handle_dev_dump,
"List all network interfaces for wireless hardware.");
static int handle_interface_type(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
enum nl80211_iftype type;
int tpset;
tpset = get_if_type(&argc, &argv, &type, false);
if (tpset)
return tpset;
if (argc)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, type);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, type, "<type>",
NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_type,
"Set interface type/mode.\n"
IFACE_TYPES);
static int handle_interface_4addr(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (argc != 1)
return 1;
return parse_4addr_flag(argv[0], msg);
}
COMMAND(set, 4addr, "<on|off>",
NL80211_CMD_SET_INTERFACE, 0, CIB_NETDEV, handle_interface_4addr,
"Set interface 4addr (WDS) mode.");
static int handle_interface_noack_map(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
uint16_t noack_map;
char *end;
if (argc != 1)
return 1;
noack_map = strtoul(argv[0], &end, 16);
if (*end)
return 1;
NLA_PUT_U16(msg, NL80211_ATTR_NOACK_MAP, noack_map);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, noack_map, "<map>",
NL80211_CMD_SET_NOACK_MAP, 0, CIB_NETDEV, handle_interface_noack_map,
"Set the NoAck map for the TIDs. (0x0009 = BE, 0x0006 = BK, 0x0030 = VI, 0x00C0 = VO)");
static int handle_interface_wds_peer(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char mac_addr[ETH_ALEN];
if (argc < 1)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "Invalid MAC address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, peer, "<MAC address>",
NL80211_CMD_SET_WDS_PEER, 0, CIB_NETDEV, handle_interface_wds_peer,
"Set interface WDS peer.");
static int set_mcast_rate(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
float rate;
char *end;
if (argc != 1)
return 1;
rate = strtod(argv[0], &end);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, mcast_rate, "<rate in Mbps>",
NL80211_CMD_SET_MCAST_RATE, 0, CIB_NETDEV, set_mcast_rate,
"Set the multicast bitrate.");
static int handle_chanfreq(struct nl80211_state *state, struct nl_msg *msg,
bool chan, int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int res;
int parsed;
char *end;
res = parse_freqchan(&chandef, chan, argc, argv, &parsed);
if (res)
return res;
argc -= parsed;
argv += parsed;
while (argc) {
unsigned int beacons = 10;
if (strcmp(argv[0], "beacons") == 0) {
if (argc < 2)
return 1;
beacons = strtol(argv[1], &end, 10);
if (*end)
return 1;
argc -= 2;
argv += 2;
NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, beacons);
} else if (strcmp(argv[0], "block-tx") == 0) {
argc -= 1;
argv += 1;
NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX);
} else {
return 1;
}
}
return put_chandef(msg, &chandef);
nla_put_failure:
return -ENOBUFS;
}
static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return handle_chanfreq(state, msg, false, argc, argv, id);
}
static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
return handle_chanfreq(state, msg, true, argc, argv, id);
}
SECTION(switch);
COMMAND(switch, freq,
"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [beacons <count>] [block-tx]\n"
"<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]] [beacons <count>] [block-tx]",
NL80211_CMD_CHANNEL_SWITCH, 0, CIB_NETDEV, handle_freq,
"Switch the operating channel by sending a channel switch announcement (CSA).");
COMMAND(switch, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [beacons <count>] [block-tx]",
NL80211_CMD_CHANNEL_SWITCH, 0, CIB_NETDEV, handle_chan, NULL);

71
iw.8 Normal file
View File

@ -0,0 +1,71 @@
.TH IW 8 "7 June 2012" "iw" "Linux"
.SH NAME
iw \- show / manipulate wireless devices and their configuration
.SH SYNOPSIS
.ad l
.in +8
.ti -8
.B iw
.RI [ " OPTIONS " ] " " { "
.BR help " [ "
.RI ""command " ]"
.BR "|"
.RI ""OBJECT " " COMMAND " }"
.sp
.ti -8
.IR OBJECT " := { "
.BR dev " | " phy " | " reg " }"
.sp
.ti -8
.IR OPTIONS " := { --version | --debug }"
.SH OPTIONS
.TP
.BR " --version"
print version information and exit.
.TP
.BR " --debug"
enable netlink message debugging.
.SH IW - COMMAND SYNTAX
.SS
.I OBJECT
.TP
.B dev <interface name>
- network interface.
.TP
.B phy <phy name>
- wireless hardware device (by name).
.TP
.B phy#<phy index>
- wireless hardware device (by index).
.TP
.B reg
- regulatory agent.
.SS
.I COMMAND
Specifies the action to perform on the object.
The set of possible actions depends on the object type.
.B iw help
will print all supported commands, while
.B iw help command
will print the help for all matching commands.
.SH SEE ALSO
.BR ip (8),
.BR crda (8),
.BR regdbdump (8),
.BR regulatory.bin (5)
.BR http://wireless.kernel.org/en/users/Documentation/iw

619
iw.c Normal file
View File

@ -0,0 +1,619 @@
/*
* nl80211 userspace tool
*
* Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net>
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <linux/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
/* libnl 1.x compatibility code */
#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
static inline struct nl_handle *nl_socket_alloc(void)
{
return nl_handle_alloc();
}
static inline void nl_socket_free(struct nl_sock *h)
{
nl_handle_destroy(h);
}
static inline int nl_socket_set_buffer_size(struct nl_sock *sk,
int rxbuf, int txbuf)
{
return nl_set_buffer_size(sk, rxbuf, txbuf);
}
#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */
int iw_debug = 0;
static int nl80211_init(struct nl80211_state *state)
{
int err;
state->nl_sock = nl_socket_alloc();
if (!state->nl_sock) {
fprintf(stderr, "Failed to allocate netlink socket.\n");
return -ENOMEM;
}
if (genl_connect(state->nl_sock)) {
fprintf(stderr, "Failed to connect to generic netlink.\n");
err = -ENOLINK;
goto out_handle_destroy;
}
nl_socket_set_buffer_size(state->nl_sock, 8192, 8192);
/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
err = 1;
setsockopt(nl_socket_get_fd(state->nl_sock), SOL_NETLINK,
NETLINK_EXT_ACK, &err, sizeof(err));
state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211");
if (state->nl80211_id < 0) {
fprintf(stderr, "nl80211 not found.\n");
err = -ENOENT;
goto out_handle_destroy;
}
return 0;
out_handle_destroy:
nl_socket_free(state->nl_sock);
return err;
}
static void nl80211_cleanup(struct nl80211_state *state)
{
nl_socket_free(state->nl_sock);
}
static int cmd_size;
extern struct cmd __start___cmd;
extern struct cmd __stop___cmd;
#define for_each_cmd(_cmd) \
for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \
_cmd = (const struct cmd *)((char *)_cmd + cmd_size))
static void __usage_cmd(const struct cmd *cmd, char *indent, bool full)
{
const char *start, *lend, *end;
printf("%s", indent);
switch (cmd->idby) {
case CIB_NONE:
break;
case CIB_PHY:
printf("phy <phyname> ");
break;
case CIB_NETDEV:
printf("dev <devname> ");
break;
case CIB_WDEV:
printf("wdev <idx> ");
break;
}
if (cmd->parent && cmd->parent->name)
printf("%s ", cmd->parent->name);
printf("%s", cmd->name);
if (cmd->args) {
/* print line by line */
start = cmd->args;
end = strchr(start, '\0');
printf(" ");
do {
lend = strchr(start, '\n');
if (!lend)
lend = end;
if (start != cmd->args) {
printf("\t");
switch (cmd->idby) {
case CIB_NONE:
break;
case CIB_PHY:
printf("phy <phyname> ");
break;
case CIB_NETDEV:
printf("dev <devname> ");
break;
case CIB_WDEV:
printf("wdev <idx> ");
break;
}
if (cmd->parent && cmd->parent->name)
printf("%s ", cmd->parent->name);
printf("%s ", cmd->name);
}
printf("%.*s\n", (int)(lend - start), start);
start = lend + 1;
} while (end != lend);
} else
printf("\n");
if (!full || !cmd->help)
return;
/* hack */
if (strlen(indent))
indent = "\t\t";
else
printf("\n");
/* print line by line */
start = cmd->help;
end = strchr(start, '\0');
do {
lend = strchr(start, '\n');
if (!lend)
lend = end;
printf("%s", indent);
printf("%.*s\n", (int)(lend - start), start);
start = lend + 1;
} while (end != lend);
printf("\n");
}
static void usage_options(void)
{
printf("Options:\n");
printf("\t--debug\t\tenable netlink debugging\n");
}
static const char *argv0;
static void usage(int argc, char **argv)
{
const struct cmd *section, *cmd;
bool full = argc >= 0;
const char *sect_filt = NULL;
const char *cmd_filt = NULL;
if (argc > 0)
sect_filt = argv[0];
if (argc > 1)
cmd_filt = argv[1];
printf("Usage:\t%s [options] command\n", argv0);
usage_options();
printf("\t--version\tshow version (%s)\n", iw_version);
printf("Commands:\n");
for_each_cmd(section) {
if (section->parent)
continue;
if (sect_filt && strcmp(section->name, sect_filt))
continue;
if (section->handler && !section->hidden)
__usage_cmd(section, "\t", full);
for_each_cmd(cmd) {
if (section != cmd->parent)
continue;
if (!cmd->handler || cmd->hidden)
continue;
if (cmd_filt && strcmp(cmd->name, cmd_filt))
continue;
__usage_cmd(cmd, "\t", full);
}
}
printf("\nCommands that use the netdev ('dev') can also be given the\n"
"'wdev' instead to identify the device.\n");
printf("\nYou can omit the 'phy' or 'dev' if "
"the identification is unique,\n"
"e.g. \"iw wlan0 info\" or \"iw phy0 info\". "
"(Don't when scripting.)\n\n"
"Do NOT screenscrape this tool, we don't "
"consider its output stable.\n\n");
}
static int print_help(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
exit(3);
}
TOPLEVEL(help, "[command]", 0, 0, CIB_NONE, print_help,
"Print usage for all or a specific command, e.g.\n"
"\"help wowlan\" or \"help wowlan enable\".");
static void usage_cmd(const struct cmd *cmd)
{
printf("Usage:\t%s [options] ", argv0);
__usage_cmd(cmd, "", true);
usage_options();
}
static void version(void)
{
printf("iw version %s\n", iw_version);
}
static int phy_lookup(char *name)
{
char buf[200];
int fd, pos;
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
fd = open(buf, O_RDONLY);
if (fd < 0)
return -1;
pos = read(fd, buf, sizeof(buf) - 1);
if (pos < 0) {
close(fd);
return -1;
}
buf[pos] = '\0';
close(fd);
return atoi(buf);
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
int len = nlh->nlmsg_len;
struct nlattr *attrs;
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
int *ret = arg;
int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
*ret = err->error;
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
return NL_STOP;
if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
ack_len += err->msg.nlmsg_len - sizeof(*nlh);
if (len <= ack_len)
return NL_STOP;
attrs = (void *)((unsigned char *)nlh + ack_len);
len -= ack_len;
nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
if (tb[NLMSGERR_ATTR_MSG]) {
len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]),
nla_len(tb[NLMSGERR_ATTR_MSG]));
fprintf(stderr, "kernel reports: %*s\n", len,
(char *)nla_data(tb[NLMSGERR_ATTR_MSG]));
}
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_STOP;
}
static int (*registered_handler)(struct nl_msg *, void *);
static void *registered_handler_data;
void register_handler(int (*handler)(struct nl_msg *, void *), void *data)
{
registered_handler = handler;
registered_handler_data = data;
}
int valid_handler(struct nl_msg *msg, void *arg)
{
if (registered_handler)
return registered_handler(msg, registered_handler_data);
return NL_OK;
}
static int __handle_cmd(struct nl80211_state *state, enum id_input idby,
int argc, char **argv, const struct cmd **cmdout)
{
const struct cmd *cmd, *match = NULL, *sectcmd;
struct nl_cb *cb;
struct nl_cb *s_cb;
struct nl_msg *msg;
signed long long devidx = 0;
int err, o_argc;
const char *command, *section;
char *tmp, **o_argv;
enum command_identify_by command_idby = CIB_NONE;
if (argc <= 1 && idby != II_NONE)
return 1;
o_argc = argc;
o_argv = argv;
switch (idby) {
case II_PHY_IDX:
command_idby = CIB_PHY;
devidx = strtoul(*argv + 4, &tmp, 0);
if (*tmp != '\0')
return 1;
argc--;
argv++;
break;
case II_PHY_NAME:
command_idby = CIB_PHY;
devidx = phy_lookup(*argv);
argc--;
argv++;
break;
case II_NETDEV:
command_idby = CIB_NETDEV;
devidx = if_nametoindex(*argv);
if (devidx == 0)
devidx = -1;
argc--;
argv++;
break;
case II_WDEV:
command_idby = CIB_WDEV;
devidx = strtoll(*argv, &tmp, 0);
if (*tmp != '\0')
return 1;
argc--;
argv++;
default:
break;
}
if (devidx < 0)
return -errno;
section = *argv;
argc--;
argv++;
for_each_cmd(sectcmd) {
if (sectcmd->parent)
continue;
/* ok ... bit of a hack for the dupe 'info' section */
if (match && sectcmd->idby != command_idby)
continue;
if (strcmp(sectcmd->name, section) == 0)
match = sectcmd;
}
sectcmd = match;
match = NULL;
if (!sectcmd)
return 1;
if (argc > 0) {
command = *argv;
for_each_cmd(cmd) {
if (!cmd->handler)
continue;
if (cmd->parent != sectcmd)
continue;
/*
* ignore mismatch id by, but allow WDEV
* in place of NETDEV
*/
if (cmd->idby != command_idby &&
!(cmd->idby == CIB_NETDEV &&
command_idby == CIB_WDEV))
continue;
if (strcmp(cmd->name, command))
continue;
if (argc > 1 && !cmd->args)
continue;
match = cmd;
break;
}
if (match) {
argc--;
argv++;
}
}
if (match)
cmd = match;
else {
/* Use the section itself, if possible. */
cmd = sectcmd;
if (argc && !cmd->args)
return 1;
if (cmd->idby != command_idby &&
!(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV))
return 1;
if (!cmd->handler)
return 1;
}
if (cmd->selector) {
cmd = cmd->selector(argc, argv);
if (!cmd)
return 1;
}
if (cmdout)
*cmdout = cmd;
if (!cmd->cmd) {
argc = o_argc;
argv = o_argv;
return cmd->handler(state, NULL, argc, argv, idby);
}
msg = nlmsg_alloc();
if (!msg) {
fprintf(stderr, "failed to allocate netlink message\n");
return 2;
}
cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
if (!cb || !s_cb) {
fprintf(stderr, "failed to allocate netlink callbacks\n");
err = 2;
goto out;
}
genlmsg_put(msg, 0, 0, state->nl80211_id, 0,
cmd->nl_msg_flags, cmd->cmd, 0);
switch (command_idby) {
case CIB_PHY:
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
break;
case CIB_NETDEV:
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
break;
case CIB_WDEV:
NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx);
break;
default:
break;
}
err = cmd->handler(state, msg, argc, argv, idby);
if (err)
goto out;
nl_socket_set_cb(state->nl_sock, s_cb);
err = nl_send_auto_complete(state->nl_sock, msg);
if (err < 0)
goto out;
err = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, NULL);
while (err > 0)
nl_recvmsgs(state->nl_sock, cb);
out:
nl_cb_put(cb);
nl_cb_put(s_cb);
nlmsg_free(msg);
return err;
nla_put_failure:
fprintf(stderr, "building message failed\n");
return 2;
}
int handle_cmd(struct nl80211_state *state, enum id_input idby,
int argc, char **argv)
{
return __handle_cmd(state, idby, argc, argv, NULL);
}
int main(int argc, char **argv)
{
struct nl80211_state nlstate;
int err;
const struct cmd *cmd = NULL;
/* calculate command size including padding */
cmd_size = labs((long)&__section_set - (long)&__section_get);
/* strip off self */
argc--;
argv0 = *argv++;
if (argc > 0 && strcmp(*argv, "--debug") == 0) {
iw_debug = 1;
argc--;
argv++;
}
if (argc > 0 && strcmp(*argv, "--version") == 0) {
version();
return 0;
}
/* need to treat "help" command specially so it works w/o nl80211 */
if (argc == 0 || strcmp(*argv, "help") == 0) {
usage(argc - 1, argv + 1);
return 0;
}
err = nl80211_init(&nlstate);
if (err)
return 1;
if (strcmp(*argv, "dev") == 0 && argc > 1) {
argc--;
argv++;
err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd);
} else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) {
if (strlen(*argv) == 3) {
argc--;
argv++;
err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd);
} else if (*(*argv + 3) == '#')
err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd);
else
goto detect;
} else if (strcmp(*argv, "wdev") == 0 && argc > 1) {
argc--;
argv++;
err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd);
} else {
int idx;
enum id_input idby = II_NONE;
detect:
if ((idx = if_nametoindex(argv[0])) != 0)
idby = II_NETDEV;
else if ((idx = phy_lookup(argv[0])) >= 0)
idby = II_PHY_NAME;
err = __handle_cmd(&nlstate, idby, argc, argv, &cmd);
}
if (err == HANDLER_RET_USAGE) {
if (cmd)
usage_cmd(cmd);
else
usage(0, NULL);
} else if (err == HANDLER_RET_DONE) {
err = 0;
} else if (err < 0)
fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err);
nl80211_cleanup(&nlstate);
return err;
}

238
iw.h Normal file
View File

@ -0,0 +1,238 @@
#ifndef __IW_H
#define __IW_H
#include <stdbool.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <endian.h>
#include "nl80211.h"
#include "ieee80211.h"
/* support for extack if compilation headers are too old */
#ifndef NETLINK_EXT_ACK
#define NETLINK_EXT_ACK 11
enum nlmsgerr_attrs {
NLMSGERR_ATTR_UNUSED,
NLMSGERR_ATTR_MSG,
NLMSGERR_ATTR_OFFS,
NLMSGERR_ATTR_COOKIE,
__NLMSGERR_ATTR_MAX,
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
};
#endif
#ifndef NLM_F_CAPPED
#define NLM_F_CAPPED 0x100
#endif
#ifndef NLM_F_ACK_TLVS
#define NLM_F_ACK_TLVS 0x200
#endif
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
#define ETH_ALEN 6
#define VHT_MUMIMO_GROUP_LEN 24
/* libnl 1.x compatibility code */
#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
# define nl_sock nl_handle
#endif
struct nl80211_state {
struct nl_sock *nl_sock;
int nl80211_id;
};
enum command_identify_by {
CIB_NONE,
CIB_PHY,
CIB_NETDEV,
CIB_WDEV,
};
enum id_input {
II_NONE,
II_NETDEV,
II_PHY_NAME,
II_PHY_IDX,
II_WDEV,
};
#define HANDLER_RET_USAGE 1
#define HANDLER_RET_DONE 3
struct cmd {
const char *name;
const char *args;
const char *help;
const enum nl80211_commands cmd;
int nl_msg_flags;
int hidden;
const enum command_identify_by idby;
/*
* The handler should return a negative error code,
* zero on success, 1 if the arguments were wrong.
* Return 2 iff you provide the error message yourself.
*/
int (*handler)(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id);
const struct cmd *(*selector)(int argc, char **argv);
const struct cmd *parent;
};
struct chanmode {
const char *name;
unsigned int width;
int freq1_diff;
int chantype; /* for older kernel */
};
struct chandef {
enum nl80211_chan_width width;
unsigned int control_freq;
unsigned int center_freq1;
unsigned int center_freq2;
};
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
#define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))
#define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel)\
static struct cmd \
__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden\
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (_name), \
.args = (_args), \
.cmd = (_nlcmd), \
.nl_msg_flags = (_flags), \
.hidden = (_hidden), \
.idby = (_idby), \
.handler = (_handler), \
.help = (_help), \
.parent = _section, \
.selector = (_sel), \
}
#define __ACMD(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel, _alias)\
__COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel);\
static const struct cmd *_alias = &__cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden
#define COMMAND(section, name, args, cmd, flags, idby, handler, help) \
__COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, NULL)
#define COMMAND_ALIAS(section, name, args, cmd, flags, idby, handler, help, selector, alias)\
__ACMD(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help, selector, alias)
#define HIDDEN(section, name, args, cmd, flags, idby, handler) \
__COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 1, idby, handler, NULL, NULL)
#define TOPLEVEL(_name, _args, _nlcmd, _flags, _idby, _handler, _help) \
struct cmd \
__section ## _ ## _name \
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (#_name), \
.args = (_args), \
.cmd = (_nlcmd), \
.nl_msg_flags = (_flags), \
.idby = (_idby), \
.handler = (_handler), \
.help = (_help), \
}
#define SECTION(_name) \
struct cmd __section ## _ ## _name \
__attribute__((used)) __attribute__((section("__cmd"))) = { \
.name = (#_name), \
.hidden = 1, \
}
#define DECLARE_SECTION(_name) \
extern struct cmd __section ## _ ## _name;
extern const char iw_version[];
extern int iw_debug;
int handle_cmd(struct nl80211_state *state, enum id_input idby,
int argc, char **argv);
struct print_event_args {
struct timeval ts; /* internal */
bool have_ts; /* must be set false */
bool frame, time, reltime;
bool continue_listening;
};
__u32 listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits);
int __prepare_listen_events(struct nl80211_state *state);
__u32 __do_listen_events(struct nl80211_state *state,
const int n_waits, const __u32 *waits,
struct print_event_args *args);
int valid_handler(struct nl_msg *msg, void *arg);
void register_handler(int (*handler)(struct nl_msg *, void *), void *data);
int mac_addr_a2n(unsigned char *mac_addr, char *arg);
void mac_addr_n2a(char *mac_addr, unsigned char *arg);
int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len,
unsigned char **mask);
unsigned char *parse_hex(char *hex, size_t *outlen);
int parse_keys(struct nl_msg *msg, char **argv, int argc);
int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv, int *parsed);
enum nl80211_chan_width str_to_bw(const char *str);
int put_chandef(struct nl_msg *msg, struct chandef *chandef);
void print_ht_mcs(const __u8 *mcs);
void print_ampdu_length(__u8 exponent);
void print_ampdu_spacing(__u8 spacing);
void print_ht_capability(__u16 cap);
void print_vht_info(__u32 capa, const __u8 *mcs);
char *channel_width_name(enum nl80211_chan_width width);
const char *iftype_name(enum nl80211_iftype iftype);
const char *command_name(enum nl80211_commands cmd);
int ieee80211_channel_to_frequency(int chan, enum nl80211_band band);
int ieee80211_frequency_to_channel(int freq);
void print_ssid_escaped(const uint8_t len, const uint8_t *data);
int nl_get_multicast_id(struct nl_sock *sock, const char *family, const char *group);
char *reg_initiator_to_string(__u8 initiator);
const char *get_reason_str(uint16_t reason);
const char *get_status_str(uint16_t status);
enum print_ie_type {
PRINT_SCAN,
PRINT_LINK,
};
#define BIT(x) (1ULL<<(x))
void print_ies(unsigned char *ie, int ielen, bool unknown,
enum print_ie_type ptype);
void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen);
void iw_hexdump(const char *prefix, const __u8 *data, size_t len);
int get_cf1(const struct chanmode *chanmode, unsigned long freq);
#define SCHED_SCAN_OPTIONS "[interval <in_msecs> | scan_plans [<interval_secs:iterations>*] <interval_secs>] " \
"[delay <in_secs>] [freqs <freq>+] [matches [ssid <ssid>]+]] [active [ssid <ssid>]+|passive] " \
"[randomise[=<addr>/<mask>]]"
int parse_sched_scan(struct nl_msg *msg, int *argc, char ***argv);
DECLARE_SECTION(switch);
DECLARE_SECTION(set);
DECLARE_SECTION(get);
char *hex2bin(const char *hex, char *buf);
void iwl_parse_event(__u32 vendor_id, struct nlattr **attrs);
#endif /* __IW_H */

272
link.c Normal file
View File

@ -0,0 +1,272 @@
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
struct link_result {
uint8_t bssid[8];
bool link_found;
bool anything_found;
};
static struct link_result lr = { .link_found = false };
static int link_bss_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *bss[NL80211_BSS_MAX + 1];
static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
[NL80211_BSS_TSF] = { .type = NLA_U64 },
[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
[NL80211_BSS_BSSID] = { },
[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
[NL80211_BSS_INFORMATION_ELEMENTS] = { },
[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
};
struct link_result *result = arg;
char mac_addr[20], dev[20];
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_BSS]) {
fprintf(stderr, "bss info missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(bss, NL80211_BSS_MAX,
tb[NL80211_ATTR_BSS],
bss_policy)) {
fprintf(stderr, "failed to parse nested attributes!\n");
return NL_SKIP;
}
if (!bss[NL80211_BSS_BSSID])
return NL_SKIP;
if (!bss[NL80211_BSS_STATUS])
return NL_SKIP;
mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
case NL80211_BSS_STATUS_ASSOCIATED:
printf("Connected to %s (on %s)\n", mac_addr, dev);
break;
case NL80211_BSS_STATUS_AUTHENTICATED:
printf("Authenticated with %s (on %s)\n", mac_addr, dev);
return NL_SKIP;
case NL80211_BSS_STATUS_IBSS_JOINED:
printf("Joined IBSS %s (on %s)\n", mac_addr, dev);
break;
default:
return NL_SKIP;
}
result->anything_found = true;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS])
print_ies(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]),
false, PRINT_LINK);
if (bss[NL80211_BSS_FREQUENCY])
printf("\tfreq: %d\n",
nla_get_u32(bss[NL80211_BSS_FREQUENCY]));
if (nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED)
return NL_SKIP;
/* only in the assoc case do we want more info from station get */
result->link_found = true;
memcpy(result->bssid, nla_data(bss[NL80211_BSS_BSSID]), 6);
return NL_SKIP;
}
static int handle_scan_for_link(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (argc > 0)
return 1;
register_handler(link_bss_handler, &lr);
return 0;
}
static int print_link_sta(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
struct nlattr *binfo[NL80211_STA_BSS_PARAM_MAX + 1];
static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
};
static struct nla_policy bss_policy[NL80211_STA_BSS_PARAM_MAX + 1] = {
[NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
[NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_STA_INFO]) {
fprintf(stderr, "sta stats missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
tb[NL80211_ATTR_STA_INFO],
stats_policy)) {
fprintf(stderr, "failed to parse nested attributes!\n");
return NL_SKIP;
}
if (sinfo[NL80211_STA_INFO_RX_BYTES] && sinfo[NL80211_STA_INFO_RX_PACKETS])
printf("\tRX: %u bytes (%u packets)\n",
nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]),
nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
if (sinfo[NL80211_STA_INFO_TX_BYTES] && sinfo[NL80211_STA_INFO_TX_PACKETS])
printf("\tTX: %u bytes (%u packets)\n",
nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]),
nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
if (sinfo[NL80211_STA_INFO_SIGNAL])
printf("\tsignal: %d dBm\n",
(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]));
if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
char buf[100];
parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
printf("\ttx bitrate: %s\n", buf);
}
if (sinfo[NL80211_STA_INFO_BSS_PARAM]) {
if (nla_parse_nested(binfo, NL80211_STA_BSS_PARAM_MAX,
sinfo[NL80211_STA_INFO_BSS_PARAM],
bss_policy)) {
fprintf(stderr, "failed to parse nested bss parameters!\n");
} else {
char *delim = "";
printf("\n\tbss flags:\t");
if (binfo[NL80211_STA_BSS_PARAM_CTS_PROT]) {
printf("CTS-protection");
delim = " ";
}
if (binfo[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE]) {
printf("%sshort-preamble", delim);
delim = " ";
}
if (binfo[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME])
printf("%sshort-slot-time", delim);
printf("\n\tdtim period:\t%d",
nla_get_u8(binfo[NL80211_STA_BSS_PARAM_DTIM_PERIOD]));
printf("\n\tbeacon int:\t%d",
nla_get_u16(binfo[NL80211_STA_BSS_PARAM_BEACON_INTERVAL]));
printf("\n");
}
}
return NL_SKIP;
}
static int handle_link_sta(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char mac_addr[ETH_ALEN];
if (argc < 1)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
register_handler(print_link_sta, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int handle_link(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char *link_argv[] = {
NULL,
"link",
"get_bss",
NULL,
};
char *station_argv[] = {
NULL,
"link",
"get_sta",
NULL,
NULL,
};
char bssid_buf[3*6];
int err;
link_argv[0] = argv[0];
err = handle_cmd(state, id, 3, link_argv);
if (err)
return err;
if (!lr.link_found) {
if (!lr.anything_found)
printf("Not connected.\n");
return 0;
}
mac_addr_n2a(bssid_buf, lr.bssid);
bssid_buf[17] = '\0';
station_argv[0] = argv[0];
station_argv[3] = bssid_buf;
return handle_cmd(state, id, 4, station_argv);
}
TOPLEVEL(link, NULL, 0, 0, CIB_NETDEV, handle_link,
"Print information about the current link, if any.");
HIDDEN(link, get_sta, "", NL80211_CMD_GET_STATION, 0,
CIB_NETDEV, handle_link_sta);
HIDDEN(link, get_bss, NULL, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
CIB_NETDEV, handle_scan_for_link);

329
measurements.c Normal file
View File

@ -0,0 +1,329 @@
#include <errno.h>
#include "nl80211.h"
#include "iw.h"
#include <unistd.h>
SECTION(measurement);
/**
* struct ftm_target - data for an FTM target (FTM responder)
*
* @freq: target's frequency
* @bw: target's bandwith. one of @enum nl80211_chan_width
* @center_freq1: target's center frequency, 1st segment
* @center_freq2: target's center frequency, 2nd segment(if relevant)
* @target: target's mac address.
* @samples_per_burst: number of FTM frames in a single burst.
* @one_sided: whether to perform a one-sided or two-sided measurement.
* @asap: Whether to perform the measurement in ASAP mode. Ignored if
* one-sided.
*/
struct ftm_target {
int freq;
char bw;
int center_freq1;
int center_freq2;
char target[ETH_ALEN];
char samples_per_burst;
char one_sided;
char asap;
char num_of_bursts_exp;
unsigned short burst_period;
char retries;
char burst_duration;
char query_lci;
char query_civic;
};
static int ftm_put_target(struct nl_msg *msg, struct ftm_target *tgt)
{
if (nla_put(msg, NL80211_FTM_TARGET_ATTR_BSSID, ETH_ALEN,
tgt->target) ||
nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_BW, tgt->bw) ||
nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_FREQ, tgt->freq) ||
(tgt->center_freq1 &&
nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1,
tgt->center_freq1)) ||
(tgt->center_freq2 &&
nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2,
tgt->center_freq2)) ||
(tgt->samples_per_burst &&
nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST,
tgt->samples_per_burst)) ||
(tgt->num_of_bursts_exp &&
nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS_EXP,
tgt->num_of_bursts_exp)) ||
(tgt->burst_period &&
nla_put_u16(msg, NL80211_FTM_TARGET_ATTR_BURST_PERIOD,
tgt->burst_period)) ||
(tgt->retries &&
nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_RETRIES,
tgt->retries)) ||
(tgt->burst_duration &&
nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_BURST_DURATION,
tgt->burst_duration)) ||
(tgt->query_lci &&
nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_QUERY_LCI)) ||
(tgt->query_civic &&
nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_QUERY_CIVIC)))
return -ENOBUFS;
if (tgt->one_sided) {
if (nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_ONE_SIDED))
return -ENOBUFS;
} else if (tgt->asap) {
if (nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_ASAP))
return -ENOBUFS;
}
return 0;
}
static int parse_ftm_target(char *str, struct ftm_target *target)
{
int res, consumed;
char bw[6] = {0}, *pos, *tmp, *save_ptr, *delims = " \t\n";
res = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx bw=%5s cf=%d%n",
&target->target[0], &target->target[1], &target->target[2],
&target->target[3], &target->target[4], &target->target[5],
bw, &target->freq, &consumed);
if (res != 8)
return -1;
target->bw = str_to_bw(bw);
str += consumed;
pos = strtok_r(str, delims, &save_ptr);
while (pos) {
if (strncmp(pos, "cf1=", 4) == 0) {
target->center_freq1 = strtol(pos + 4, &tmp, 10);
if (*tmp) {
printf("Invalid cf1 value!\n");
return -1;
}
} else if (strncmp(pos, "cf2=", 4) == 0) {
target->center_freq2 = strtol(pos + 4, &tmp, 10);
if (*tmp) {
printf("Invalid cf2 value!\n");
return -1;
}
} else if (strncmp(pos, "bursts_exp=", 11) == 0) {
target->num_of_bursts_exp = strtol(pos + 11, &tmp, 10);
if (*tmp) {
printf("Invalid bursts_exp value!\n");
return -1;
}
} else if (strncmp(pos, "burst_period=", 13) == 0) {
target->burst_period= strtol(pos + 13, &tmp, 10);
if (*tmp) {
printf("Invalid burst_period value!\n");
return -1;
}
} else if (strncmp(pos, "retries=", 8) == 0) {
target->retries = strtol(pos + 8, &tmp, 10);
if (*tmp) {
printf("Invalid retries value!\n");
return -1;
}
} else if (strncmp(pos, "burst_duration=", 15) == 0) {
target->burst_duration = strtol(pos + 15, &tmp, 10);
if (*tmp) {
printf("Invalid burst_duration value!\n");
return -1;
}
} else if (strncmp(pos, "spb=", 4) == 0) {
target->samples_per_burst = strtol(pos + 4, &tmp, 10);
if (tmp - pos <= 4) {
printf("Invalid spb value!\n");
return -1;
}
} else if (strcmp(pos, "one-sided") == 0) {
target->one_sided = 1;
} else if (strcmp(pos, "asap") == 0) {
target->asap = 1;
} else if (strcmp(pos, "civic") == 0) {
target->query_civic = 1;
} else if (strcmp(pos, "lci") == 0) {
target->query_lci = 1;
} else {
printf("Unknown parameter %s\n", pos);
return -1;
}
pos = strtok_r(NULL, delims, &save_ptr);
}
return 0;
}
static int parse_ftm_mac_rand(char *str, __u8 *template, __u8 *mask)
{
int res;
char macbuf[6 * 3];
char macmask[6 * 3];
res = sscanf(str, "template=%s mask=%s", macbuf, macmask);
if (res != 2)
return 0;
if (mac_addr_a2n(template, macbuf))
return -1;
if (mac_addr_a2n(mask, macmask))
return -1;
/* Don't randomize the MC bit */
if (!(mask[0] & 0x01)) {
printf("The MC bit must not be randomized");
return -1;
}
return 1;
}
static int parse_ftm_config(const char *conf_file, struct ftm_target **ptargets,
__u8 *template, __u8 *mask)
{
FILE *input;
char line[256];
int i, line_num;
struct ftm_target *targets = NULL, *tmp;
int res, mac_set = 0;
input = fopen(conf_file, "r");
if (!input) {
printf("The given path does not exist!\n");
return -1;
}
for (i = 0, line_num = 1; fgets(line, sizeof(line), input); line_num++) {
struct ftm_target tgt = {0};
if (strncmp(line, "#", 1) == 0)
continue;
res = parse_ftm_mac_rand(line, template, mask);
if (res < 0 || (res > 0 && mac_set)) {
printf("Invalid FTM configuration at line %d!\n",
line_num);
free(targets);
return -1;
}
if (res > 0) {
mac_set = 1;
continue;
}
if (parse_ftm_target(line, &tgt)) {
printf("Invalid FTM configuration at line %d!\n",
line_num);
free(targets);
return -1;
}
i++;
tmp = realloc(targets, i * sizeof(struct ftm_target));
if (!tmp) {
printf("Failed to allocate targets\n");
free(targets);
return -1;
}
targets = tmp;
targets[i - 1] = tgt;
}
*ptargets = targets;
return i;
}
static int handle_ftm_req(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
int err;
static char *req_argv[4] = {
NULL,
"measurement",
"ftm_request_send",
NULL,
};
static const __u32 cmds[] = {
NL80211_CMD_MSRMENT_REQUEST,
NL80211_CMD_MSRMENT_RESPONSE,
};
struct print_event_args printargs = { };
if (argc != 4)
return 1;
req_argv[0] = argv[0];
req_argv[3] = argv[3];
err = handle_cmd(state, id, 4, req_argv);
if (err)
return err;
__do_listen_events(state, ARRAY_SIZE(cmds), cmds, &printargs);
return 0;
}
static int handle_ftm_req_send(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
struct nlattr *nl_ftm_req, *nl_targets, *nl_target;
__u8 macaddr[ETH_ALEN] = {0};
/* Dont randomize the MC bit */
__u8 macaddr_mask[ETH_ALEN] = {0x01, };
struct ftm_target *targets;
int i, num_targets = 0;
if (argc != 1)
return 1;
num_targets = parse_ftm_config(argv[0], &targets, macaddr, macaddr_mask);
if (num_targets <= 0)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_MSRMENT_TYPE, NL80211_MSRMENT_TYPE_FTM);
nl_ftm_req = nla_nest_start(msg, NL80211_ATTR_MSRMENT_FTM_REQUEST);
if (!nl_ftm_req)
goto nla_put_failure;
NLA_PUT(msg, NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE, ETH_ALEN, macaddr);
NLA_PUT(msg, NL80211_FTM_REQ_ATTR_MACADDR_MASK, ETH_ALEN, macaddr_mask);
nl_targets = nla_nest_start(msg, NL80211_FTM_REQ_ATTR_TARGETS);
if (!nl_targets)
goto nla_put_failure;
for (i = 0; i < num_targets; i++) {
nl_target = nla_nest_start(msg, i);
if (!nl_target || ftm_put_target(msg, &targets[i]))
goto nla_put_failure;
nla_nest_end(msg, nl_target);
}
nla_nest_end(msg, nl_targets);
nla_nest_end(msg, nl_ftm_req);
free(targets);
return 0;
nla_put_failure:
free(targets);
return -ENOBUFS;
}
COMMAND(measurement, ftm_request, "<config-file>", 0, 0,
CIB_NETDEV, handle_ftm_req,
"Send an FTM request to the targets supplied in the config file.\n"
"Each line in the file represents a target, with the following format:\n"
"<bssid> bw=<[20|40|80|80+80|160]> cf=<center_freq> [cf1=<center_freq1>] [cf2=<center_freq2>] [spb=<samples per burst>] [one-sided] [asap] [bursts_exp=<num of bursts exponent>] [burst_period=<burst period>] [retries=<num of retries>] [burst_duration=<burst duration>] [civic] [lci]\n"
"To set the MAC address randomization template and mask, add the line:\n"
"template=<mac address template> mask=<mac address mask>");
HIDDEN(measurement, ftm_request_send, "<config-file>", NL80211_CMD_MSRMENT_REQUEST, 0,
CIB_NETDEV, handle_ftm_req_send);

585
mesh.c Normal file
View File

@ -0,0 +1,585 @@
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(mesh);
typedef struct _any_t {
union {
uint32_t as_32;
int32_t as_s32;
uint16_t as_16;
uint8_t as_8;
} u;
} _any;
/* describes a mesh parameter */
struct mesh_param_descr {
const char *name;
enum nl80211_meshconf_params mesh_param_num;
int (*nla_put_fn)(struct nl_msg*, int, _any*);
uint32_t (*parse_fn)(const char*, _any*);
void (*nla_print_fn)(struct nlattr *);
};
/* utility functions for manipulating and printing u8/u16/u32 values and
* timesouts. */
static int _my_nla_put_u8(struct nl_msg *n, int mesh_param_num, _any *value)
{
return nla_put(n, mesh_param_num, sizeof(uint8_t), &value->u.as_8);
}
static int _my_nla_put_u16(struct nl_msg *n, int mesh_param_num, _any *value)
{
return nla_put(n, mesh_param_num, sizeof(uint16_t), &value->u.as_16);
}
static int _my_nla_put_u32(struct nl_msg *n, int mesh_param_num, _any *value)
{
return nla_put(n, mesh_param_num, sizeof(uint32_t), &value->u.as_32);
}
static uint32_t _parse_u8(const char *str, _any *ret)
{
char *endptr = NULL;
unsigned long int v = strtoul(str, &endptr, 10);
if (*endptr != '\0')
return 0xff;
if (v > 0xff)
return 0xff;
ret->u.as_8 = (uint8_t)v;
return 0;
}
static uint32_t _parse_u8_as_bool(const char *str, _any *ret)
{
char *endptr = NULL;
unsigned long int v = strtoul(str, &endptr, 10);
if (*endptr != '\0')
return 0x1;
if (v > 0x1)
return 0x1;
ret->u.as_8 = (uint8_t)v;
return 0;
}
static uint32_t _parse_u16(const char *str, _any *ret)
{
char *endptr = NULL;
long int v = strtol(str, &endptr, 10);
if (*endptr != '\0')
return 0xffff;
if ((v < 0) || (v > 0xffff))
return 0xffff;
ret->u.as_16 = (uint16_t)v;
return 0;
}
static uint32_t _parse_u32(const char *str, _any *ret)
{
char *endptr = NULL;
long long int v = strtoll(str, &endptr, 10);
if (*endptr != '\0')
return 0xffffffff;
if ((v < 0) || (v > 0xffffffff))
return 0xffffffff;
ret->u.as_32 = (uint32_t)v;
return 0;
}
static uint32_t _parse_s32(const char *str, _any *ret)
{
char *endptr = NULL;
long int v = strtol(str, &endptr, 10);
if (*endptr != '\0')
return 0xffffffff;
if (v > 0xff)
return 0xffffffff;
ret->u.as_s32 = (int32_t)v;
return 0;
}
static uint32_t _parse_u32_power_mode(const char *str, _any *ret)
{
unsigned long int v;
/* Parse attribute for the name of power mode */
if (!strcmp(str, "active"))
v = NL80211_MESH_POWER_ACTIVE;
else if (!strcmp(str, "light"))
v = NL80211_MESH_POWER_LIGHT_SLEEP;
else if (!strcmp(str, "deep"))
v = NL80211_MESH_POWER_DEEP_SLEEP;
else
return 0xff;
ret->u.as_32 = (uint32_t)v;
return 0;
}
static void _print_u8(struct nlattr *a)
{
printf("%d", nla_get_u8(a));
}
static void _print_u16(struct nlattr *a)
{
printf("%d", nla_get_u16(a));
}
static void _print_u16_timeout(struct nlattr *a)
{
printf("%d milliseconds", nla_get_u16(a));
}
static void _print_u16_in_TUs(struct nlattr *a)
{
printf("%d TUs", nla_get_u16(a));
}
static void _print_u32(struct nlattr *a)
{
printf("%d", nla_get_u32(a));
}
static void _print_u32_timeout(struct nlattr *a)
{
printf("%u milliseconds", nla_get_u32(a));
}
static void _print_u32_in_seconds(struct nlattr *a)
{
printf("%d seconds", nla_get_u32(a));
}
static void _print_u32_in_TUs(struct nlattr *a)
{
printf("%d TUs", nla_get_u32(a));
}
static void _print_u32_power_mode(struct nlattr *a)
{
unsigned long v = nla_get_u32(a);
switch (v) {
case NL80211_MESH_POWER_ACTIVE:
printf("active");
break;
case NL80211_MESH_POWER_LIGHT_SLEEP:
printf("light");
break;
case NL80211_MESH_POWER_DEEP_SLEEP:
printf("deep");
break;
default:
printf("undefined");
break;
}
}
static void _print_s32_in_dBm(struct nlattr *a)
{
printf("%d dBm", (int32_t) nla_get_u32(a));
}
/* The current mesh parameters */
const static struct mesh_param_descr _mesh_param_descrs[] =
{
{"mesh_retry_timeout",
NL80211_MESHCONF_RETRY_TIMEOUT,
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
{"mesh_confirm_timeout",
NL80211_MESHCONF_CONFIRM_TIMEOUT,
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
{"mesh_holding_timeout",
NL80211_MESHCONF_HOLDING_TIMEOUT,
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
{"mesh_max_peer_links",
NL80211_MESHCONF_MAX_PEER_LINKS,
_my_nla_put_u16, _parse_u16, _print_u16},
{"mesh_max_retries",
NL80211_MESHCONF_MAX_RETRIES,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_ttl",
NL80211_MESHCONF_TTL,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_element_ttl",
NL80211_MESHCONF_ELEMENT_TTL,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_auto_open_plinks",
NL80211_MESHCONF_AUTO_OPEN_PLINKS,
_my_nla_put_u8, _parse_u8_as_bool, _print_u8},
{"mesh_hwmp_max_preq_retries",
NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_path_refresh_time",
NL80211_MESHCONF_PATH_REFRESH_TIME,
_my_nla_put_u32, _parse_u32, _print_u32_timeout},
{"mesh_min_discovery_timeout",
NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
_my_nla_put_u16, _parse_u16, _print_u16_timeout},
{"mesh_hwmp_active_path_timeout",
NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
_my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
{"mesh_hwmp_preq_min_interval",
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_hwmp_net_diameter_traversal_time",
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_hwmp_rootmode", NL80211_MESHCONF_HWMP_ROOTMODE,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_hwmp_rann_interval", NL80211_MESHCONF_HWMP_RANN_INTERVAL,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_gate_announcements", NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
_my_nla_put_u8, _parse_u8, _print_u8},
{"mesh_fwding", NL80211_MESHCONF_FORWARDING,
_my_nla_put_u8, _parse_u8_as_bool, _print_u8},
{"mesh_sync_offset_max_neighor",
NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
_my_nla_put_u32, _parse_u32, _print_u32},
{"mesh_rssi_threshold", NL80211_MESHCONF_RSSI_THRESHOLD,
_my_nla_put_u32, _parse_s32, _print_s32_in_dBm},
{"mesh_hwmp_active_path_to_root_timeout",
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
_my_nla_put_u32, _parse_u32, _print_u32_in_TUs},
{"mesh_hwmp_root_interval", NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_hwmp_confirmation_interval",
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_power_mode", NL80211_MESHCONF_POWER_MODE,
_my_nla_put_u32, _parse_u32_power_mode, _print_u32_power_mode},
{"mesh_awake_window", NL80211_MESHCONF_AWAKE_WINDOW,
_my_nla_put_u16, _parse_u16, _print_u16_in_TUs},
{"mesh_plink_timeout", NL80211_MESHCONF_PLINK_TIMEOUT,
_my_nla_put_u32, _parse_u32, _print_u32_in_seconds},
};
static void print_all_mesh_param_descr(void)
{
unsigned int i;
printf("Possible mesh parameters are:\n");
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++)
printf(" - %s\n", _mesh_param_descrs[i].name);
}
static const struct mesh_param_descr *find_mesh_param(const char *name)
{
unsigned int i;
/* Find out what mesh parameter we want to change. */
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
if (strcmp(_mesh_param_descrs[i].name, name) == 0)
return _mesh_param_descrs + i;
}
return NULL;
}
/* Setter */
static int set_interface_meshparam(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
const struct mesh_param_descr *mdescr;
struct nlattr *container;
uint32_t ret;
int err;
container = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
if (!container)
return -ENOBUFS;
if (!argc) {
print_all_mesh_param_descr();
return 1;
}
while (argc) {
const char *name;
char *value;
_any any;
memset(&any, 0, sizeof(_any));
name = argv[0];
value = strchr(name, '=');
if (value) {
*value = '\0';
value++;
argc--;
argv++;
} else {
/* backward compat -- accept w/o '=' */
if (argc < 2) {
printf("Must specify a value for %s.\n", name);
return 2;
}
value = argv[1];
argc -= 2;
argv += 2;
}
mdescr = find_mesh_param(name);
if (!mdescr) {
printf("Could not find the parameter %s.\n", name);
print_all_mesh_param_descr();
return 2;
}
/* Parse the new value */
ret = mdescr->parse_fn(value, &any);
if (ret != 0) {
if (mdescr->mesh_param_num
== NL80211_MESHCONF_POWER_MODE)
printf("%s must be set to active, light or "
"deep.\n", mdescr->name);
else
printf("%s must be set to a number "
"between 0 and %u\n",
mdescr->name, ret);
return 2;
}
err = mdescr->nla_put_fn(msg, mdescr->mesh_param_num, &any);
if (err)
return err;
}
nla_nest_end(msg, container);
return err;
}
COMMAND(set, mesh_param, "<param>=<value> [<param>=<value>]*",
NL80211_CMD_SET_MESH_PARAMS, 0, CIB_NETDEV, set_interface_meshparam,
"Set mesh parameter (run command without any to see available ones).");
/* Getter */
static int print_mesh_param_handler(struct nl_msg *msg, void *arg)
{
const struct mesh_param_descr *mdescr = arg;
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct nlattr *parent_attr;
struct nlattr *mesh_params[NL80211_MESHCONF_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
/* locate NL80211_ATTR_MESH_PARAMS */
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
parent_attr = attrs[NL80211_ATTR_MESH_PARAMS];
if (!parent_attr)
return -EINVAL;
/* unpack the mesh parameters */
if (nla_parse_nested(mesh_params, NL80211_MESHCONF_ATTR_MAX,
parent_attr, NULL))
return -EINVAL;
if (!mdescr) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(_mesh_param_descrs); i++) {
mdescr = &_mesh_param_descrs[i];
printf("%s = ", mdescr->name);
mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
printf("\n");
}
return NL_SKIP;
}
/* print out the mesh parameter */
mdescr->nla_print_fn(mesh_params[mdescr->mesh_param_num]);
printf("\n");
return NL_SKIP;
}
static int get_interface_meshparam(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
const struct mesh_param_descr *mdescr = NULL;
if (argc == 0) {
print_all_mesh_param_descr();
return 1;
} else if (argc == 1) {
mdescr = find_mesh_param(argv[0]);
if (!mdescr) {
printf("Could not find the parameter %s.\n", argv[0]);
print_all_mesh_param_descr();
return 2;
}
} else {
return 1;
}
register_handler(print_mesh_param_handler, (void *)mdescr);
return 0;
}
COMMAND(get, mesh_param, "[<param>]",
NL80211_CMD_GET_MESH_PARAMS, 0, CIB_NETDEV, get_interface_meshparam,
"Retrieve mesh parameter (run command without any to see available ones).");
static int join_mesh(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *container;
float rate;
unsigned char rates[NL80211_MAX_SUPP_RATES];
int bintval, dtim_period, n_rates = 0;
char *end, *value = NULL, *sptr = NULL;
if (argc < 1)
return 1;
NLA_PUT(msg, NL80211_ATTR_MESH_ID, strlen(argv[0]), argv[0]);
argc--;
argv++;
/* freq */
if (argc > 1 && strcmp(argv[0], "freq") == 0) {
struct chandef chandef;
int err, parsed;
err = parse_freqchan(&chandef, false, argc - 1, argv + 1,
&parsed);
if (err)
return err;
argv += parsed + 1;
argc -= parsed + 1;
put_chandef(msg, &chandef);
if (err)
return err;
}
/* basic rates */
if (argc > 1 && strcmp(argv[0], "basic-rates") == 0) {
argv++;
argc--;
value = strtok_r(argv[0], ",", &sptr);
while (value && n_rates < NL80211_MAX_SUPP_RATES) {
rate = strtod(value, &end);
rates[n_rates] = rate * 2;
/* filter out suspicious values */
if (*end != '\0' || !rates[n_rates] ||
rate*2 != rates[n_rates])
return 1;
n_rates++;
value = strtok_r(NULL, ",", &sptr);
}
NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, n_rates, rates);
argv++;
argc--;
}
/* multicast rate */
if (argc > 1 && strcmp(argv[0], "mcast-rate") == 0) {
argv++;
argc--;
rate = strtod(argv[0], &end);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_MCAST_RATE, (int)(rate * 10));
argv++;
argc--;
}
if (argc > 1 && strcmp(argv[0], "beacon-interval") == 0) {
argc--;
argv++;
bintval = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, bintval);
argv++;
argc--;
}
if (argc > 1 && strcmp(argv[0], "dtim-period") == 0) {
argc--;
argv++;
dtim_period = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
argv++;
argc--;
}
container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
if (!container)
return -ENOBUFS;
if (argc > 1 && strcmp(argv[0], "vendor_sync") == 0) {
argv++;
argc--;
if (strcmp(argv[0], "on") == 0)
NLA_PUT_U8(msg,
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 1);
else
NLA_PUT_U8(msg,
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, 0);
argv++;
argc--;
}
/* parse and put other NL80211_ATTR_MESH_SETUP elements here */
nla_nest_end(msg, container);
if (!argc)
return 0;
return set_interface_meshparam(state, msg, argc, argv, id);
nla_put_failure:
return -ENOBUFS;
}
COMMAND(mesh, join, "<mesh ID> [[freq <freq in MHz> <NOHT|HT20|HT40+|HT40-|80MHz>]"
" [basic-rates <rate in Mbps,rate2,...>]], [mcast-rate <rate in Mbps>]"
" [beacon-interval <time in TUs>] [dtim-period <value>]"
" [vendor_sync on|off] [<param>=<value>]*",
NL80211_CMD_JOIN_MESH, 0, CIB_NETDEV, join_mesh,
"Join a mesh with the given mesh ID with frequency, basic-rates,\n"
"mcast-rate and mesh parameters. Basic-rates are applied only if\n"
"frequency is provided.");
static int leave_mesh(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
if (argc)
return 1;
return 0;
}
COMMAND(mesh, leave, NULL, NL80211_CMD_LEAVE_MESH, 0, CIB_NETDEV, leave_mesh,
"Leave a mesh.");

150
mgmt.c Normal file
View File

@ -0,0 +1,150 @@
#include <string.h>
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(mgmt);
static int seq_handler(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int dump_mgmt_frame(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) {
uint32_t freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
printf("freq %u MHz\n", freq);
}
if (tb_msg[NL80211_ATTR_RX_SIGNAL_DBM]) {
/* nl80211_send_mgmt sends signed dBm value as u32 */
int dbm = nla_get_u32(tb_msg[NL80211_ATTR_RX_SIGNAL_DBM]);
printf("rssi %d dBm\n", dbm);
}
if (tb_msg[NL80211_ATTR_FRAME]) {
int len = nla_len(tb_msg[NL80211_ATTR_FRAME]);
uint8_t *data = nla_data(tb_msg[NL80211_ATTR_FRAME]);
iw_hexdump("mgmt", data, len);
}
return 0;
}
static int register_mgmt_frame(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
unsigned int type;
unsigned char *match;
size_t match_len;
int ret;
ret = sscanf(argv[0], "%x", &type);
if (ret != 1) {
printf("invalid frame type: %s\n", argv[0]);
return 2;
}
match = parse_hex(argv[1], &match_len);
if (!match) {
printf("invalid frame pattern: %s\n", argv[1]);
return 2;
}
NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int handle_mgmt_reg(struct nl80211_state *state,
struct nl_msg *msg, int argc,
char **argv, enum id_input id)
{
return register_mgmt_frame(state, msg, argc, argv, id);
}
HIDDEN(mgmt, reg, "", NL80211_CMD_REGISTER_FRAME, 0, CIB_NETDEV, handle_mgmt_reg);
static int handle_mgmt_dump(struct nl80211_state *state,
struct nl_msg *msg, int argc,
char **argv, enum id_input id)
{
struct nl_cb *mgmt_cb;
char *ndev = argv[0];
int mgmt_argc = 5;
char **mgmt_argv;
unsigned int count = 0;
int err = 0;
int i;
mgmt_argv = calloc(mgmt_argc, sizeof(char*));
if (!mgmt_argv)
return -ENOMEM;
mgmt_argv[0] = ndev;
mgmt_argv[1] = "mgmt";
mgmt_argv[2] = "reg";
for (i = 3; i < argc; i += 3) {
if (strcmp(argv[i], "count") == 0) {
count = 1 + atoi(argv[i + 1]);
break;
}
if (strcmp(argv[i], "frame") != 0) {
err = 1;
goto out;
}
mgmt_argv[3] = argv[i + 1];
mgmt_argv[4] = argv[i + 2];
err = handle_cmd(state, II_NETDEV, mgmt_argc, mgmt_argv);
if (err)
goto out;
}
mgmt_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
if (!mgmt_cb) {
err = 1;
goto out;
}
/* need to turn off sequence number checking */
nl_cb_set(mgmt_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_handler, NULL);
nl_cb_set(mgmt_cb, NL_CB_VALID, NL_CB_CUSTOM, dump_mgmt_frame, NULL);
while (--count)
nl_recvmsgs(state->nl_sock, mgmt_cb);
nl_cb_put(mgmt_cb);
out:
free(mgmt_argv);
return err;
}
COMMAND(mgmt, dump, "frame <type as hex ab> <pattern as hex ab:cd:..> [frame <type> <pattern>]* [count <frames>]",
0, 0, CIB_NETDEV, handle_mgmt_dump,
"Register for receiving certain mgmt frames and print them.\n"
"Frames are selected by their type and pattern containing\n"
"the first several bytes of the frame that should match.\n\n"
"Example: iw dev wlan0 mgmt dump frame 40 00 frame 40 01:02 count 10\n");

188
mpath.c Normal file
View File

@ -0,0 +1,188 @@
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(mpath);
enum plink_state {
LISTEN,
OPN_SNT,
OPN_RCVD,
CNF_RCVD,
ESTAB,
HOLDING,
BLOCKED
};
static int print_mpath_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *pinfo[NL80211_MPATH_INFO_MAX + 1];
char dst[20], next_hop[20], dev[20];
static struct nla_policy mpath_policy[NL80211_MPATH_INFO_MAX + 1] = {
[NL80211_MPATH_INFO_FRAME_QLEN] = { .type = NLA_U32 },
[NL80211_MPATH_INFO_SN] = { .type = NLA_U32 },
[NL80211_MPATH_INFO_METRIC] = { .type = NLA_U32 },
[NL80211_MPATH_INFO_EXPTIME] = { .type = NLA_U32 },
[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT] = { .type = NLA_U32 },
[NL80211_MPATH_INFO_DISCOVERY_RETRIES] = { .type = NLA_U8 },
[NL80211_MPATH_INFO_FLAGS] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
/*
* TODO: validate the interface and mac address!
* Otherwise, there's a race condition as soon as
* the kernel starts sending mpath notifications.
*/
if (!tb[NL80211_ATTR_MPATH_INFO]) {
fprintf(stderr, "mpath info missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(pinfo, NL80211_MPATH_INFO_MAX,
tb[NL80211_ATTR_MPATH_INFO],
mpath_policy)) {
fprintf(stderr, "failed to parse nested attributes!\n");
return NL_SKIP;
}
mac_addr_n2a(dst, nla_data(tb[NL80211_ATTR_MAC]));
mac_addr_n2a(next_hop, nla_data(tb[NL80211_ATTR_MPATH_NEXT_HOP]));
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
printf("%s %s %s", dst, next_hop, dev);
if (pinfo[NL80211_MPATH_INFO_SN])
printf("\t%u",
nla_get_u32(pinfo[NL80211_MPATH_INFO_SN]));
if (pinfo[NL80211_MPATH_INFO_METRIC])
printf("\t%u",
nla_get_u32(pinfo[NL80211_MPATH_INFO_METRIC]));
if (pinfo[NL80211_MPATH_INFO_FRAME_QLEN])
printf("\t%u",
nla_get_u32(pinfo[NL80211_MPATH_INFO_FRAME_QLEN]));
if (pinfo[NL80211_MPATH_INFO_EXPTIME])
printf("\t%u",
nla_get_u32(pinfo[NL80211_MPATH_INFO_EXPTIME]));
if (pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT])
printf("\t%u",
nla_get_u32(pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT]));
if (pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES])
printf("\t%u",
nla_get_u8(pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES]));
if (pinfo[NL80211_MPATH_INFO_FLAGS])
printf("\t0x%x",
nla_get_u8(pinfo[NL80211_MPATH_INFO_FLAGS]));
printf("\n");
return NL_SKIP;
}
static int handle_mpath_get(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char dst[ETH_ALEN];
if (argc < 1)
return 1;
if (mac_addr_a2n(dst, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
register_handler(print_mpath_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(mpath, get, "<MAC address>",
NL80211_CMD_GET_MPATH, 0, CIB_NETDEV, handle_mpath_get,
"Get information on mesh path to the given node.");
COMMAND(mpath, del, "<MAC address>",
NL80211_CMD_DEL_MPATH, 0, CIB_NETDEV, handle_mpath_get,
"Remove the mesh path to the given node.");
static int handle_mpath_set(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char dst[ETH_ALEN];
unsigned char next_hop[ETH_ALEN];
if (argc < 3)
return 1;
if (mac_addr_a2n(dst, argv[0])) {
fprintf(stderr, "invalid destination mac address\n");
return 2;
}
argc--;
argv++;
if (strcmp("next_hop", argv[0]) != 0)
return 1;
argc--;
argv++;
if (mac_addr_a2n(next_hop, argv[0])) {
fprintf(stderr, "invalid next hop mac address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
register_handler(print_mpath_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(mpath, new, "<destination MAC address> next_hop <next hop MAC address>",
NL80211_CMD_NEW_MPATH, 0, CIB_NETDEV, handle_mpath_set,
"Create a new mesh path (instead of relying on automatic discovery).");
COMMAND(mpath, set, "<destination MAC address> next_hop <next hop MAC address>",
NL80211_CMD_SET_MPATH, 0, CIB_NETDEV, handle_mpath_set,
"Set an existing mesh path's next hop.");
static int handle_mpath_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
printf("DEST ADDR NEXT HOP IFACE\tSN\tMETRIC\tQLEN\t"
"EXPTIME\t\tDTIM\tDRET\tFLAGS\n");
register_handler(print_mpath_handler, NULL);
return 0;
}
COMMAND(mpath, dump, NULL,
NL80211_CMD_GET_MPATH, NLM_F_DUMP, CIB_NETDEV, handle_mpath_dump,
"List known mesh paths.");

81
mpp.c Normal file
View File

@ -0,0 +1,81 @@
#include <net/if.h>
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(mpp);
static int print_mpp_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
char dst[20], next_hop[20], dev[20];
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
/*
* TODO: validate the interface and mac address!
* Otherwise, there's a race condition as soon as
* the kernel starts sending mpath notifications.
*/
mac_addr_n2a(dst, nla_data(tb[NL80211_ATTR_MAC]));
mac_addr_n2a(next_hop, nla_data(tb[NL80211_ATTR_MPATH_NEXT_HOP]));
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
printf("%s %s %s\n", dst, next_hop, dev);
return NL_SKIP;
}
static int handle_mpp_get(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char dst[ETH_ALEN];
if (argc < 1)
return 1;
if (mac_addr_a2n(dst, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
register_handler(print_mpp_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(mpp, get, "<MAC address>",
NL80211_CMD_GET_MPP, 0, CIB_NETDEV, handle_mpp_get,
"Get information on mesh proxy path to the given node.");
static int handle_mpp_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
printf("DEST ADDR PROXY NODE IFACE\n");
register_handler(print_mpp_handler, NULL);
return 0;
}
COMMAND(mpp, dump, NULL,
NL80211_CMD_GET_MPP, NLM_F_DUMP, CIB_NETDEV, handle_mpp_dump,
"List known mesh proxy paths.");

6059
nl80211.h Normal file

File diff suppressed because it is too large Load Diff

44
ocb.c Normal file
View File

@ -0,0 +1,44 @@
#include <errno.h>
#include <string.h>
#include "nl80211.h"
#include "iw.h"
SECTION(ocb);
static int join_ocb(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int err, parsed;
if (argc < 2)
return 1;
err = parse_freqchan(&chandef, false, argc, argv, &parsed);
if (err)
return err;
put_chandef(msg, &chandef);
if (err)
return err;
return 0;
}
COMMAND(ocb, join, "<freq in MHz> <5MHz|10MHz>",
NL80211_CMD_JOIN_OCB, 0, CIB_NETDEV, join_ocb,
"Join the OCB mode network.");
static int leave_ocb(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
if (argc)
return 1;
return 0;
}
COMMAND(ocb, leave, NULL, NL80211_CMD_LEAVE_OCB, 0, CIB_NETDEV, leave_ocb,
"Leave the OCB mode network.");

47
offch.c Normal file
View File

@ -0,0 +1,47 @@
#include <errno.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
static int offchannel(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char *end;
if (argc < 2)
return 1;
/* freq */
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ,
strtoul(argv[0], &end, 10));
if (*end != '\0')
return 1;
argv++;
argc--;
/* duration */
NLA_PUT_U32(msg, NL80211_ATTR_DURATION,
strtoul(argv[0], &end, 10));
if (*end != '\0')
return 1;
argv++;
argc--;
if (argc)
return 1;
return 0;
nla_put_failure:
return -ENOSPC;
}
TOPLEVEL(offchannel, "<freq> <duration>", NL80211_CMD_REMAIN_ON_CHANNEL, 0,
CIB_NETDEV, offchannel,
"Leave operating channel and go to the given channel for a while.");

26
p2p.c Normal file
View File

@ -0,0 +1,26 @@
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(p2p);
static int handle_p2p_start(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
return 0;
}
COMMAND(p2p, start, "", NL80211_CMD_START_P2P_DEVICE, 0, CIB_WDEV, handle_p2p_start, "");
static int handle_p2p_stop(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
return 0;
}
COMMAND(p2p, stop, "", NL80211_CMD_STOP_P2P_DEVICE, 0, CIB_WDEV, handle_p2p_stop, "");

729
phy.c Normal file
View File

@ -0,0 +1,729 @@
#include <stdbool.h>
#include <errno.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
struct channels_ctx {
int last_band;
bool width_40;
bool width_80;
bool width_160;
};
static char *dfs_state_name(enum nl80211_dfs_state state)
{
switch (state) {
case NL80211_DFS_USABLE:
return "usable";
case NL80211_DFS_AVAILABLE:
return "available";
case NL80211_DFS_UNAVAILABLE:
return "unavailable";
default:
return "unknown";
}
}
static int print_channels_handler(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct channels_ctx *ctx = arg;
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
struct nlattr *nl_band;
struct nlattr *nl_freq;
int rem_band, rem_freq;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
if (ctx->last_band != nl_band->nla_type) {
printf("Band %d:\n", nl_band->nla_type + 1);
ctx->width_40 = false;
ctx->width_80 = false;
ctx->width_160 = false;
ctx->last_band = nl_band->nla_type;
}
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL);
if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
__u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
if (cap & BIT(1))
ctx->width_40 = true;
}
if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
__u32 capa;
ctx->width_80 = true;
capa = nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
switch ((capa >> 2) & 3) {
case 2:
/* width_80p80 = true; */
/* fall through */
case 1:
ctx->width_160 = true;
break;
}
}
if (tb_band[NL80211_BAND_ATTR_FREQS]) {
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
uint32_t freq;
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), NULL);
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
printf("\t* %d MHz [%d] ", freq, ieee80211_frequency_to_channel(freq));
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
printf("(disabled)\n");
continue;
}
printf("\n");
if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
printf("\t Maximum TX power: %.1f dBm\n", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
/* If both flags are set assume an new kernel */
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) {
printf("\t No IR\n");
} else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) {
printf("\t Passive scan\n");
} else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){
printf("\t No IBSS\n");
}
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
printf("\t Radar detection\n");
printf("\t Channel widths:");
if (!tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
printf(" 20MHz");
if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
printf(" HT40-");
if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
printf(" HT40+");
if (ctx->width_80 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
printf(" VHT80");
if (ctx->width_160 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
printf(" VHT160");
printf("\n");
if (!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED] && tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
unsigned long time;
printf("\t DFS state: %s", dfs_state_name(state));
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
printf(" (for %lu sec)", time / 1000);
}
printf("\n");
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME])
printf("\t DFS CAC time: %u ms\n",
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]));
}
}
}
}
}
return NL_SKIP;
}
static int handle_channels(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
static struct channels_ctx ctx = {
.last_band = -1,
};
nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
register_handler(print_channels_handler, &ctx);
return 0;
}
TOPLEVEL(channels, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_channels, "Show available channels.");
static int handle_name(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (argc != 1)
return 1;
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
"Rename this wireless device.");
static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int res;
res = parse_freqchan(&chandef, false, argc, argv, NULL);
if (res)
return res;
return put_chandef(msg, &chandef);
}
COMMAND(set, freq,
"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
"Set frequency/channel the hardware is using, including HT\n"
"configuration.");
COMMAND(set, freq,
"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"<control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int res;
res = parse_freqchan(&chandef, true, argc, argv, NULL);
if (res)
return res;
return put_chandef(msg, &chandef);
}
COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
COMMAND(set, channel, "<channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
struct cac_event {
int ret;
uint32_t freq;
};
static int print_cac_event(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
enum nl80211_radar_event event_type;
struct cac_event *cac_event = arg;
uint32_t freq;
if (gnlh->cmd != NL80211_CMD_RADAR_DETECT)
return NL_SKIP;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_RADAR_EVENT] || !tb[NL80211_ATTR_WIPHY_FREQ])
return NL_SKIP;
freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
if (freq != cac_event->freq)
return NL_SKIP;
switch (event_type) {
case NL80211_RADAR_DETECTED:
printf("%d MHz: radar detected\n", freq);
break;
case NL80211_RADAR_CAC_FINISHED:
printf("%d MHz: CAC finished\n", freq);
break;
case NL80211_RADAR_CAC_ABORTED:
printf("%d MHz: CAC was aborted\n", freq);
break;
case NL80211_RADAR_NOP_FINISHED:
printf("%d MHz: NOP finished\n", freq);
break;
default:
printf("%d MHz: unknown radar event\n", freq);
}
cac_event->ret = 0;
return NL_SKIP;
}
static int handle_cac_trigger(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
struct chandef chandef;
int res;
if (argc < 2)
return 1;
if (strcmp(argv[0], "channel") == 0) {
res = parse_freqchan(&chandef, true, argc - 1, argv + 1, NULL);
} else if (strcmp(argv[0], "freq") == 0) {
res = parse_freqchan(&chandef, false, argc - 1, argv + 1, NULL);
} else {
return 1;
}
if (res)
return res;
return put_chandef(msg, &chandef);
}
static int no_seq_check(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int handle_cac(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
int err;
struct nl_cb *radar_cb;
struct chandef chandef;
struct cac_event cac_event;
char **cac_trigger_argv = NULL;
radar_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
if (!radar_cb)
return 1;
if (argc < 3)
return 1;
if (strcmp(argv[2], "channel") == 0) {
err = parse_freqchan(&chandef, true, argc - 3, argv + 3, NULL);
} else if (strcmp(argv[2], "freq") == 0) {
err = parse_freqchan(&chandef, false, argc - 3, argv + 3, NULL);
} else {
return 1;
}
cac_trigger_argv = calloc(argc + 1, sizeof(char*));
if (!cac_trigger_argv)
return -ENOMEM;
cac_trigger_argv[0] = argv[0];
cac_trigger_argv[1] = "cac";
cac_trigger_argv[2] = "trigger";
memcpy(&cac_trigger_argv[3], &argv[2], (argc - 2) * sizeof(char*));
err = handle_cmd(state, id, argc + 1, cac_trigger_argv);
free(cac_trigger_argv);
if (err)
return err;
cac_event.ret = 1;
cac_event.freq = chandef.control_freq;
__prepare_listen_events(state);
nl_socket_set_cb(state->nl_sock, radar_cb);
/* need to turn off sequence number checking */
nl_cb_set(radar_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
nl_cb_set(radar_cb, NL_CB_VALID, NL_CB_CUSTOM, print_cac_event, &cac_event);
while (cac_event.ret > 0)
nl_recvmsgs(state->nl_sock, radar_cb);
return 0;
}
TOPLEVEL(cac, "channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"freq <freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"freq <control freq> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
0, 0, CIB_NETDEV, handle_cac, NULL);
COMMAND(cac, trigger,
"channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"freq <frequency> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz]\n"
"freq <frequency> [5|10|20|40|80|80+80|160] [<center1_freq> [<center2_freq>]]",
NL80211_CMD_RADAR_DETECT, 0, CIB_NETDEV, handle_cac_trigger,
"Start or trigger a channel availability check (CAC) looking to look for\n"
"radars on the given channel.");
static int handle_fragmentation(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned int frag;
if (argc != 1)
return 1;
if (strcmp("off", argv[0]) == 0)
frag = -1;
else {
char *end;
if (!*argv[0])
return 1;
frag = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, frag, "<fragmentation threshold|off>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
"Set fragmentation threshold.");
static int handle_rts(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned int rts;
if (argc != 1)
return 1;
if (strcmp("off", argv[0]) == 0)
rts = -1;
else {
char *end;
if (!*argv[0])
return 1;
rts = strtoul(argv[0], &end, 10);
if (*end != '\0')
return 1;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, rts, "<rts threshold|off>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
"Set rts threshold.");
static int handle_retry(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
unsigned int retry_short = 0, retry_long = 0;
bool have_retry_s = false, have_retry_l = false;
int i;
enum {
S_NONE,
S_SHORT,
S_LONG,
} parser_state = S_NONE;
if (!argc || (argc != 2 && argc != 4))
return 1;
for (i = 0; i < argc; i++) {
char *end;
unsigned int tmpul;
if (strcmp(argv[i], "short") == 0) {
if (have_retry_s)
return 1;
parser_state = S_SHORT;
have_retry_s = true;
} else if (strcmp(argv[i], "long") == 0) {
if (have_retry_l)
return 1;
parser_state = S_LONG;
have_retry_l = true;
} else {
tmpul = strtoul(argv[i], &end, 10);
if (*end != '\0')
return 1;
if (!tmpul || tmpul > 255)
return -EINVAL;
switch (parser_state) {
case S_SHORT:
retry_short = tmpul;
break;
case S_LONG:
retry_long = tmpul;
break;
default:
return 1;
}
}
}
if (!have_retry_s && !have_retry_l)
return 1;
if (have_retry_s)
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
if (have_retry_l)
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, retry, "[short <limit>] [long <limit>]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
"Set retry limit.");
#ifndef NETNS_RUN_DIR
#define NETNS_RUN_DIR "/var/run/netns"
#endif
static int netns_get_fd(const char *name)
{
char pathbuf[MAXPATHLEN];
const char *path, *ptr;
path = name;
ptr = strchr(name, '/');
if (!ptr) {
snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
NETNS_RUN_DIR, name );
path = pathbuf;
}
return open(path, O_RDONLY);
}
static int handle_netns(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
int fd;
if (argc < 1 || !*argv[0])
return 1;
if (argc == 1) {
NLA_PUT_U32(msg, NL80211_ATTR_PID,
strtoul(argv[0], &end, 10));
if (*end != '\0') {
printf("Invalid parameter: pid(%s)\n", argv[0]);
return 1;
}
return 0;
}
if (argc != 2 || strcmp(argv[0], "name"))
return 1;
if ((fd = netns_get_fd(argv[1])) >= 0) {
NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
return 0;
} else {
printf("Invalid parameter: nsname(%s)\n", argv[0]);
}
return 1;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, netns, "{ <pid> | name <nsname> }",
NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
"Put this wireless device into a different network namespace:\n"
" <pid> - change network namespace by process id\n"
" <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
" or by absolute path (man ip-netns)\n");
static int handle_coverage(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
unsigned int coverage;
if (argc != 1)
return 1;
if (!*argv[0])
return 1;
coverage = strtoul(argv[0], &end, 10);
if (coverage > 255)
return 1;
if (*end)
return 1;
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, coverage, "<coverage class>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
"Set coverage class (1 for every 3 usec of air propagation time).\n"
"Valid values: 0 - 255.");
static int handle_distance(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
if (argc != 1)
return 1;
if (!*argv[0])
return 1;
if (strcmp("auto", argv[0]) == 0) {
NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
} else {
char *end;
unsigned int distance, coverage;
distance = strtoul(argv[0], &end, 10);
if (*end)
return 1;
/*
* Divide double the distance by the speed of light
* in m/usec (300) to get round-trip time in microseconds
* and then divide the result by three to get coverage class
* as specified in IEEE 802.11-2007 table 7-27.
* Values are rounded upwards.
*/
coverage = (distance + 449) / 450;
if (coverage > 255)
return 1;
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
}
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, distance, "<auto|distance>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
"Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
"coverage class for given link distance in meters.\n"
"To disable dynack set valid value for coverage class.\n"
"Valid values: 0 - 114750");
static int handle_txpower(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
enum nl80211_tx_power_setting type;
int mbm;
/* get the required args */
if (argc != 1 && argc != 2)
return 1;
if (!strcmp(argv[0], "auto"))
type = NL80211_TX_POWER_AUTOMATIC;
else if (!strcmp(argv[0], "fixed"))
type = NL80211_TX_POWER_FIXED;
else if (!strcmp(argv[0], "limit"))
type = NL80211_TX_POWER_LIMITED;
else {
printf("Invalid parameter: %s\n", argv[0]);
return 2;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
if (type != NL80211_TX_POWER_AUTOMATIC) {
char *endptr;
if (argc != 2) {
printf("Missing TX power level argument.\n");
return 2;
}
mbm = strtol(argv[1], &endptr, 10);
if (*endptr)
return 2;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
} else if (argc != 1)
return 1;
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
"Specify transmit power level and setting type.");
COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
"Specify transmit power level and setting type.");
static int handle_antenna(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
uint32_t tx_ant = 0, rx_ant = 0;
if (argc == 1 && strcmp(argv[0], "all") == 0) {
tx_ant = 0xffffffff;
rx_ant = 0xffffffff;
} else if (argc == 1) {
tx_ant = rx_ant = strtoul(argv[0], &end, 0);
if (*end)
return 1;
}
else if (argc == 2) {
tx_ant = strtoul(argv[0], &end, 0);
if (*end)
return 1;
rx_ant = strtoul(argv[1], &end, 0);
if (*end)
return 1;
} else
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
"Set a bitmap of allowed antennas to use for TX and RX.\n"
"The driver may reject antenna configurations it cannot support.");

80
ps.c Normal file
View File

@ -0,0 +1,80 @@
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
static int set_power_save(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
enum nl80211_ps_state ps_state;
if (argc != 1)
return 1;
if (strcmp(argv[0], "on") == 0)
ps_state = NL80211_PS_ENABLED;
else if (strcmp(argv[0], "off") == 0)
ps_state = NL80211_PS_DISABLED;
else {
printf("Invalid parameter: %s\n", argv[0]);
return 2;
}
NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(set, power_save, "<on|off>",
NL80211_CMD_SET_POWER_SAVE, 0, CIB_NETDEV, set_power_save,
"Set power save state to on or off.");
static int print_power_save_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
const char *s;
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!attrs[NL80211_ATTR_PS_STATE])
return NL_SKIP;
switch (nla_get_u32(attrs[NL80211_ATTR_PS_STATE])) {
case NL80211_PS_ENABLED:
s = "on";
break;
case NL80211_PS_DISABLED:
default:
s = "off";
break;
}
printf("Power save: %s\n", s);
return NL_SKIP;
}
static int get_power_save(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
register_handler(print_power_save_handler, NULL);
return 0;
}
COMMAND(get, power_save, "<param>",
NL80211_CMD_GET_POWER_SAVE, 0, CIB_NETDEV, get_power_save,
"Retrieve power save state.");

50
reason.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdint.h>
#include "iw.h"
static const char *reason_table[] = {
[1] = "Unspecified",
[2] = "Previous authentication no longer valid",
[3] = "Deauthenticated because sending station is leaving (or has left) the IBSS or ESS",
[4] = "Disassociated due to inactivity",
[5] = "Disassociated because AP is unable to handle all currently associated STA",
[6] = "Class 2 frame received from non-authenticated station",
[7] = "Class 3 frame received from non-authenticated station",
[8] = "Disassociated because sending station is leaving (or has left) the BSS",
[9] = "Station requesting (re)association is not authenticated with responding station",
[10] = "Disassociated because the information in the Power Capability element is unacceptable",
[11] = "Disassociated because the information in the Supported Channels element is unacceptable",
[13] = "Invalid information element",
[14] = "MIC failure",
[15] = "4-way handshake timeout",
[16] = "Group key update timeout",
[17] = "Information element in 4-way handshake different from (Re-)associate request/Probe response/Beacon",
[18] = "Multicast cipher is not valid",
[19] = "Unicast cipher is not valid",
[20] = "AKMP is not valid",
[21] = "Unsupported RSNE version",
[22] = "Invalid RSNE capabilities",
[23] = "IEEE 802.1X authentication failed",
[24] = "Cipher Suite rejected per security policy",
[31] = "TS deleted because QoS AP lacks sufficient bandwidth for this QoS STA due to a change in BSS service characteristics or operational mode",
[32] = "Disassociated for unspecified QoS-related reason",
[33] = "Disassociated because QAP lacks sufficient bandwidth for this STA",
[34] = "Disassociated because of excessive frame losses and/or poor channel conditions",
[35] = "Disassociated because QSTA is transmitting outside the limits of its polled TXOPs",
[36] = "Requested from peer QSTA as the QSTA is leaving the QBSS (or resetting)",
[37] = "Requested from peer QSTA as it does not want to use Traffic Stream",
[38] = "Requested from peer QSTA as the QSTA received frames indicated Traffic Stream for which it has not set up",
[39] = "Requested from peer QSTA due to time out",
[40] = "Requested from peer QSTA as the QSTA is leaving the QBSS (or resetting)",
[41] = "Requested from peer QSTA as it does not want to receive frames directly from the QSTA",
[42] = "Requested from peer QSTA as the QSTA received DLP frames for which it has not set up",
[43] = "Requested from peer QSTA as it does not want to use Block Ack",
[44] = "Requested from peer QSTA as the QSTA received frames indicated Block Acknowledgement policy for which it has not set up",
[45] = "Peer QSTA does not support the requested cipher suite",
};
const char *get_reason_str(uint16_t reason)
{
if (reason < ARRAY_SIZE(reason_table) && reason_table[reason])
return reason_table[reason];
return "<unknown>";
}

261
reg.c Normal file
View File

@ -0,0 +1,261 @@
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(reg);
#define MHZ_TO_KHZ(freq) ((freq) * 1000)
#define KHZ_TO_MHZ(freq) ((freq) / 1000)
#define DBI_TO_MBI(gain) ((gain) * 100)
#define MBI_TO_DBI(gain) ((gain) / 100)
#define DBM_TO_MBM(gain) ((gain) * 100)
#define MBM_TO_DBM(gain) ((gain) / 100)
static bool isalpha_upper(char letter)
{
if (letter >= 65 && letter <= 90)
return true;
return false;
}
static bool is_alpha2(char *alpha2)
{
if (isalpha_upper(alpha2[0]) && isalpha_upper(alpha2[1]))
return true;
return false;
}
static bool is_world_regdom(char *alpha2)
{
/* ASCII 0 */
if (alpha2[0] == 48 && alpha2[1] == 48)
return true;
return false;
}
char *reg_initiator_to_string(__u8 initiator)
{
switch (initiator) {
case NL80211_REGDOM_SET_BY_CORE:
return "the wireless core upon initialization";
case NL80211_REGDOM_SET_BY_USER:
return "a user";
case NL80211_REGDOM_SET_BY_DRIVER:
return "a driver";
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
return "a country IE";
default:
return "BUG";
}
}
static const char *dfs_domain_name(enum nl80211_dfs_regions region)
{
switch (region) {
case NL80211_DFS_UNSET:
return "DFS-UNSET";
case NL80211_DFS_FCC:
return "DFS-FCC";
case NL80211_DFS_ETSI:
return "DFS-ETSI";
case NL80211_DFS_JP:
return "DFS-JP";
default:
return "DFS-invalid";
}
}
static int handle_reg_set(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char alpha2[3];
if (argc < 1)
return 1;
if (!is_alpha2(argv[0]) && !is_world_regdom(argv[0])) {
fprintf(stderr, "not a valid ISO/IEC 3166-1 alpha2\n");
fprintf(stderr, "Special non-alpha2 usable entries:\n");
fprintf(stderr, "\t00\tWorld Regulatory domain\n");
return 2;
}
alpha2[0] = argv[0][0];
alpha2[1] = argv[0][1];
alpha2[2] = '\0';
argc--;
argv++;
if (argc)
return 1;
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(reg, set, "<ISO/IEC 3166-1 alpha2>",
NL80211_CMD_REQ_SET_REG, 0, CIB_NONE, handle_reg_set,
"Notify the kernel about the current regulatory domain.");
static int print_reg_handler(struct nl_msg *msg, void *arg)
{
#define PARSE_FLAG(nl_flag, string_value) do { \
if ((flags & nl_flag)) { \
printf(", %s", string_value); \
} \
} while (0)
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
char *alpha2;
struct nlattr *nl_rule;
int rem_rule;
enum nl80211_dfs_regions dfs_domain;
static struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
[NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 },
};
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
printf("No alpha2\n");
return NL_SKIP;
}
if (!tb_msg[NL80211_ATTR_REG_RULES]) {
printf("No reg rules\n");
return NL_SKIP;
}
if (tb_msg[NL80211_ATTR_WIPHY])
printf("phy#%d%s\n", nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]),
tb_msg[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] ?
" (self-managed)" : "");
else
printf("global\n");
if (tb_msg[NL80211_ATTR_DFS_REGION])
dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
else
dfs_domain = NL80211_DFS_UNSET;
alpha2 = nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]);
printf("country %c%c: %s\n", alpha2[0], alpha2[1], dfs_domain_name(dfs_domain));
nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) {
struct nlattr *tb_rule[NL80211_REG_RULE_ATTR_MAX + 1];
__u32 flags, start_freq_khz, end_freq_khz, max_bw_khz, max_ant_gain_mbi, max_eirp_mbm;
nla_parse(tb_rule, NL80211_REG_RULE_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_rule_policy);
flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
start_freq_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]);
end_freq_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]);
max_bw_khz = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
max_ant_gain_mbi = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
max_eirp_mbm = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
printf("\t(%d - %d @ %d), (",
KHZ_TO_MHZ(start_freq_khz), KHZ_TO_MHZ(end_freq_khz), KHZ_TO_MHZ(max_bw_khz));
if (MBI_TO_DBI(max_ant_gain_mbi))
printf("%d", MBI_TO_DBI(max_ant_gain_mbi));
else
printf("N/A");
printf(", %d)", MBM_TO_DBM(max_eirp_mbm));
if ((flags & NL80211_RRF_DFS) && tb_rule[NL80211_ATTR_DFS_CAC_TIME])
printf(", (%u ms)", nla_get_u32(tb_rule[NL80211_ATTR_DFS_CAC_TIME]));
else
printf(", (N/A)");
if (!flags) {
printf("\n");
continue;
}
/* Sync this output format to match that of dbparse.py from wireless-regdb.git */
PARSE_FLAG(NL80211_RRF_NO_OFDM, "NO-OFDM");
PARSE_FLAG(NL80211_RRF_NO_CCK, "NO-CCK");
PARSE_FLAG(NL80211_RRF_NO_INDOOR, "NO-INDOOR");
PARSE_FLAG(NL80211_RRF_NO_OUTDOOR, "NO-OUTDOOR");
PARSE_FLAG(NL80211_RRF_DFS, "DFS");
PARSE_FLAG(NL80211_RRF_PTP_ONLY, "PTP-ONLY");
PARSE_FLAG(NL80211_RRF_AUTO_BW, "AUTO-BW");
PARSE_FLAG(NL80211_RRF_IR_CONCURRENT, "IR-CONCURRENT");
PARSE_FLAG(NL80211_RRF_NO_HT40MINUS, "NO-HT40MINUS");
PARSE_FLAG(NL80211_RRF_NO_HT40PLUS, "NO-HT40PLUS");
PARSE_FLAG(NL80211_RRF_NO_80MHZ, "NO-80MHZ");
PARSE_FLAG(NL80211_RRF_NO_160MHZ, "NO-160MHZ");
/* Kernels that support NO_IR always turn on both flags */
if ((flags & NL80211_RRF_NO_IR) && (flags & __NL80211_RRF_NO_IBSS)) {
printf(", NO-IR");
} else {
PARSE_FLAG(NL80211_RRF_PASSIVE_SCAN, "PASSIVE-SCAN");
PARSE_FLAG(__NL80211_RRF_NO_IBSS, "NO-IBSS");
}
printf("\n");
}
printf("\n");
return NL_SKIP;
#undef PARSE_FLAG
}
static int handle_reg_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
register_handler(print_reg_handler, NULL);
return 0;
}
static int handle_reg_get(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *dump_args[] = { "reg", "dump" };
int err;
err = handle_cmd(state, CIB_NONE, 2, dump_args);
/*
* dump might fail since it's not supported on older kernels,
* in that case the handler is still registered already
*/
if (err == -EOPNOTSUPP)
return 0;
return err ?: HANDLER_RET_DONE;
}
COMMAND(reg, get, NULL, NL80211_CMD_GET_REG, 0, CIB_NONE, handle_reg_get,
"Print out the kernel's current regulatory domain information.");
COMMAND(reg, get, NULL, NL80211_CMD_GET_REG, 0, CIB_PHY, handle_reg_get,
"Print out the devices' current regulatory domain information.");
HIDDEN(reg, dump, NULL, NL80211_CMD_GET_REG, NLM_F_DUMP, CIB_NONE,
handle_reg_dump);

40
roc.c Normal file
View File

@ -0,0 +1,40 @@
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(roc);
static int handle_roc_start(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
char *end;
int freq, time;
if (argc != 2)
return 1;
freq = strtol(argv[0], &end, 0);
if (!end || *end)
return 1;
time = strtol(argv[1], &end, 0);
if (!end || *end)
return 1;
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, time);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(roc, start, "<freq> <time in ms>", NL80211_CMD_REMAIN_ON_CHANNEL, 0, CIB_NETDEV, handle_roc_start, "");

2331
scan.c Normal file

File diff suppressed because it is too large Load Diff

4
sections.c Normal file
View File

@ -0,0 +1,4 @@
#include "iw.h"
SECTION(get);
SECTION(set);

746
station.c Normal file
View File

@ -0,0 +1,746 @@
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(station);
enum plink_state {
LISTEN,
OPN_SNT,
OPN_RCVD,
CNF_RCVD,
ESTAB,
HOLDING,
BLOCKED
};
static void print_power_mode(struct nlattr *a)
{
enum nl80211_mesh_power_mode pm = nla_get_u32(a);
switch (pm) {
case NL80211_MESH_POWER_ACTIVE:
printf("ACTIVE");
break;
case NL80211_MESH_POWER_LIGHT_SLEEP:
printf("LIGHT SLEEP");
break;
case NL80211_MESH_POWER_DEEP_SLEEP:
printf("DEEP SLEEP");
break;
default:
printf("UNKNOWN");
break;
}
}
void parse_tid_stats(struct nlattr *tid_stats_attr)
{
struct nlattr *stats_info[NL80211_TID_STATS_MAX + 1], *tidattr, *info;
static struct nla_policy stats_policy[NL80211_TID_STATS_MAX + 1] = {
[NL80211_TID_STATS_RX_MSDU] = { .type = NLA_U64 },
[NL80211_TID_STATS_TX_MSDU] = { .type = NLA_U64 },
[NL80211_TID_STATS_TX_MSDU_RETRIES] = { .type = NLA_U64 },
[NL80211_TID_STATS_TX_MSDU_FAILED] = { .type = NLA_U64 },
};
int rem, i = 0;
printf("\n\tMSDU:\n\t\tTID\trx\ttx\ttx retries\ttx failed");
nla_for_each_nested(tidattr, tid_stats_attr, rem) {
if (nla_parse_nested(stats_info, NL80211_TID_STATS_MAX,
tidattr, stats_policy)) {
printf("failed to parse nested stats attributes!");
return;
}
printf("\n\t\t%d", i++);
info = stats_info[NL80211_TID_STATS_RX_MSDU];
if (info)
printf("\t%llu", (unsigned long long)nla_get_u64(info));
info = stats_info[NL80211_TID_STATS_TX_MSDU];
if (info)
printf("\t%llu", (unsigned long long)nla_get_u64(info));
info = stats_info[NL80211_TID_STATS_TX_MSDU_RETRIES];
if (info)
printf("\t%llu", (unsigned long long)nla_get_u64(info));
info = stats_info[NL80211_TID_STATS_TX_MSDU_FAILED];
if (info)
printf("\t\t%llu", (unsigned long long)nla_get_u64(info));
}
}
void parse_bss_param(struct nlattr *bss_param_attr)
{
struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info;
static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = {
[NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
[NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
[NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
};
if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX,
bss_param_attr, bss_poilcy)) {
printf("failed to parse nested bss param attributes!");
}
info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD];
if (info)
printf("\n\tDTIM period:\t%u", nla_get_u8(info));
info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL];
if (info)
printf("\n\tbeacon interval:%u", nla_get_u16(info));
info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT];
if (info) {
printf("\n\tCTS protection:");
if (nla_get_u16(info))
printf("\tyes");
else
printf("\tno");
}
info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE];
if (info) {
printf("\n\tshort preamble:");
if (nla_get_u16(info))
printf("\tyes");
else
printf("\tno");
}
info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME];
if (info) {
printf("\n\tshort slot time:");
if (nla_get_u16(info))
printf("yes");
else
printf("no");
}
}
void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
{
int rate = 0;
char *pos = buf;
struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
};
if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
bitrate_attr, rate_policy)) {
snprintf(buf, buflen, "failed to parse nested rate attributes!");
return;
}
if (rinfo[NL80211_RATE_INFO_BITRATE32])
rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
else if (rinfo[NL80211_RATE_INFO_BITRATE])
rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
if (rate > 0)
pos += snprintf(pos, buflen - (pos - buf),
"%d.%d MBit/s", rate / 10, rate % 10);
else
pos += snprintf(pos, buflen - (pos - buf), "(unknown)");
if (rinfo[NL80211_RATE_INFO_MCS])
pos += snprintf(pos, buflen - (pos - buf),
" MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
if (rinfo[NL80211_RATE_INFO_VHT_MCS])
pos += snprintf(pos, buflen - (pos - buf),
" VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
if (rinfo[NL80211_RATE_INFO_SHORT_GI])
pos += snprintf(pos, buflen - (pos - buf), " short GI");
if (rinfo[NL80211_RATE_INFO_VHT_NSS])
pos += snprintf(pos, buflen - (pos - buf),
" VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
}
static char *get_chain_signal(struct nlattr *attr_list)
{
struct nlattr *attr;
static char buf[64];
char *cur = buf;
int i = 0, rem;
const char *prefix;
if (!attr_list)
return "";
nla_for_each_nested(attr, attr_list, rem) {
if (i++ > 0)
prefix = ", ";
else
prefix = "[";
cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
(int8_t) nla_get_u8(attr));
}
if (i)
snprintf(cur, sizeof(buf) - (cur - buf), "] ");
return buf;
}
static int print_sta_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
char mac_addr[20], state_name[10], dev[20];
struct nl80211_sta_flag_update *sta_flags;
static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64},
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
[NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
[NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
[NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
[NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
[NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
[NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32},
[NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64},
[NL80211_STA_INFO_STA_FLAGS] =
{ .minlen = sizeof(struct nl80211_sta_flag_update) },
[NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32},
[NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32},
[NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32},
[NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED },
[NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED },
[NL80211_STA_INFO_TID_STATS] = { .type = NLA_NESTED },
[NL80211_STA_INFO_BSS_PARAM] = { .type = NLA_NESTED },
[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
};
char *chain;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
/*
* TODO: validate the interface and mac address!
* Otherwise, there's a race condition as soon as
* the kernel starts sending station notifications.
*/
if (!tb[NL80211_ATTR_STA_INFO]) {
fprintf(stderr, "sta stats missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
tb[NL80211_ATTR_STA_INFO],
stats_policy)) {
fprintf(stderr, "failed to parse nested attributes!\n");
return NL_SKIP;
}
mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
printf("Station %s (on %s)", mac_addr, dev);
if (sinfo[NL80211_STA_INFO_INACTIVE_TIME])
printf("\n\tinactive time:\t%u ms",
nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]));
if (sinfo[NL80211_STA_INFO_RX_BYTES64])
printf("\n\trx bytes:\t%llu",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]));
else if (sinfo[NL80211_STA_INFO_RX_BYTES])
printf("\n\trx bytes:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]));
if (sinfo[NL80211_STA_INFO_RX_PACKETS])
printf("\n\trx packets:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]));
if (sinfo[NL80211_STA_INFO_TX_BYTES64])
printf("\n\ttx bytes:\t%llu",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]));
else if (sinfo[NL80211_STA_INFO_TX_BYTES])
printf("\n\ttx bytes:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]));
if (sinfo[NL80211_STA_INFO_TX_PACKETS])
printf("\n\ttx packets:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]));
if (sinfo[NL80211_STA_INFO_TX_RETRIES])
printf("\n\ttx retries:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]));
if (sinfo[NL80211_STA_INFO_TX_FAILED])
printf("\n\ttx failed:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]));
if (sinfo[NL80211_STA_INFO_BEACON_LOSS])
printf("\n\tbeacon loss:\t%u",
nla_get_u32(sinfo[NL80211_STA_INFO_BEACON_LOSS]));
if (sinfo[NL80211_STA_INFO_BEACON_RX])
printf("\n\tbeacon rx:\t%llu",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_BEACON_RX]));
if (sinfo[NL80211_STA_INFO_RX_DROP_MISC])
printf("\n\trx drop misc:\t%llu",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC]));
chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
if (sinfo[NL80211_STA_INFO_SIGNAL])
printf("\n\tsignal: \t%d %sdBm",
(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]),
chain);
chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
printf("\n\tsignal avg:\t%d %sdBm",
(int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]),
chain);
if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
printf("\n\tbeacon signal avg:\t%d dBm",
nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]));
if (sinfo[NL80211_STA_INFO_T_OFFSET])
printf("\n\tToffset:\t%llu us",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]));
if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
char buf[100];
parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
printf("\n\ttx bitrate:\t%s", buf);
}
if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
char buf[100];
parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
printf("\n\trx bitrate:\t%s", buf);
}
if (sinfo[NL80211_STA_INFO_RX_DURATION])
printf("\n\trx duration:\t%lld us",
(unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION]));
if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]) {
uint32_t thr;
thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
/* convert in Mbps but scale by 1000 to save kbps units */
thr = thr * 1000 / 1024;
printf("\n\texpected throughput:\t%u.%uMbps",
thr / 1000, thr % 1000);
}
if (sinfo[NL80211_STA_INFO_LLID])
printf("\n\tmesh llid:\t%d",
nla_get_u16(sinfo[NL80211_STA_INFO_LLID]));
if (sinfo[NL80211_STA_INFO_PLID])
printf("\n\tmesh plid:\t%d",
nla_get_u16(sinfo[NL80211_STA_INFO_PLID]));
if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
switch (nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE])) {
case LISTEN:
strcpy(state_name, "LISTEN");
break;
case OPN_SNT:
strcpy(state_name, "OPN_SNT");
break;
case OPN_RCVD:
strcpy(state_name, "OPN_RCVD");
break;
case CNF_RCVD:
strcpy(state_name, "CNF_RCVD");
break;
case ESTAB:
strcpy(state_name, "ESTAB");
break;
case HOLDING:
strcpy(state_name, "HOLDING");
break;
case BLOCKED:
strcpy(state_name, "BLOCKED");
break;
default:
strcpy(state_name, "UNKNOWN");
break;
}
printf("\n\tmesh plink:\t%s", state_name);
}
if (sinfo[NL80211_STA_INFO_LOCAL_PM]) {
printf("\n\tmesh local PS mode:\t");
print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]);
}
if (sinfo[NL80211_STA_INFO_PEER_PM]) {
printf("\n\tmesh peer PS mode:\t");
print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]);
}
if (sinfo[NL80211_STA_INFO_NONPEER_PM]) {
printf("\n\tmesh non-peer PS mode:\t");
print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]);
}
if (sinfo[NL80211_STA_INFO_STA_FLAGS]) {
sta_flags = (struct nl80211_sta_flag_update *)
nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
printf("\n\tauthorized:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED))
printf("yes");
else
printf("no");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
printf("\n\tauthenticated:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
printf("yes");
else
printf("no");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
printf("\n\tassociated:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_ASSOCIATED))
printf("yes");
else
printf("no");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
printf("\n\tpreamble:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
printf("short");
else
printf("long");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) {
printf("\n\tWMM/WME:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_WME))
printf("yes");
else
printf("no");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) {
printf("\n\tMFP:\t\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP))
printf("yes");
else
printf("no");
}
if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
printf("\n\tTDLS peer:\t");
if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER))
printf("yes");
else
printf("no");
}
}
if (sinfo[NL80211_STA_INFO_TID_STATS] && arg != NULL &&
!strcmp((char *)arg, "-v"))
parse_tid_stats(sinfo[NL80211_STA_INFO_TID_STATS]);
if (sinfo[NL80211_STA_INFO_BSS_PARAM])
parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM]);
if (sinfo[NL80211_STA_INFO_CONNECTED_TIME])
printf("\n\tconnected time:\t%u seconds",
nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]));
printf("\n");
return NL_SKIP;
}
static int handle_station_get(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char mac_addr[ETH_ALEN];
if (argc < 1)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
register_handler(print_sta_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(station, get, "<MAC address>",
NL80211_CMD_GET_STATION, 0, CIB_NETDEV, handle_station_get,
"Get information for a specific station.");
static int handle_station_del(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
char *end;
unsigned char mac_addr[ETH_ALEN];
int subtype;
int reason_code;
if (argc < 1)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
if (argc > 1 && strcmp(argv[0], "subtype") == 0) {
argv++;
argc--;
subtype = strtod(argv[0], &end);
if (*end != '\0')
return 1;
NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, subtype);
argv++;
argc--;
}
if (argc > 1 && strcmp(argv[0], "reason-code") == 0) {
argv++;
argc--;
reason_code = strtod(argv[0], &end);
if (*end != '\0')
return 1;
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
argv++;
argc--;
}
if (argc)
return 1;
register_handler(print_sta_handler, NULL);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND(station, del, "<MAC address> [subtype <subtype>] [reason-code <code>]",
NL80211_CMD_DEL_STATION, 0, CIB_NETDEV, handle_station_del,
"Remove the given station entry (use with caution!)\n"
"Example subtype values: 0xA (disassociation), 0xC (deauthentication)");
static const struct cmd *station_set_plink;
static const struct cmd *station_set_vlan;
static const struct cmd *station_set_mesh_power_mode;
static const struct cmd *select_station_cmd(int argc, char **argv)
{
if (argc < 2)
return NULL;
if (strcmp(argv[1], "plink_action") == 0)
return station_set_plink;
if (strcmp(argv[1], "vlan") == 0)
return station_set_vlan;
if (strcmp(argv[1], "mesh_power_mode") == 0)
return station_set_mesh_power_mode;
return NULL;
}
static int handle_station_set_plink(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char plink_action;
unsigned char mac_addr[ETH_ALEN];
if (argc < 3)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (strcmp("plink_action", argv[0]) != 0)
return 1;
argc--;
argv++;
if (strcmp("open", argv[0]) == 0)
plink_action = NL80211_PLINK_ACTION_OPEN;
else if (strcmp("block", argv[0]) == 0)
plink_action = NL80211_PLINK_ACTION_BLOCK;
else {
fprintf(stderr, "plink action not supported\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
NLA_PUT_U8(msg, NL80211_ATTR_STA_PLINK_ACTION, plink_action);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND_ALIAS(station, set, "<MAC address> plink_action <open|block>",
NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_plink,
"Set mesh peer link action for this station (peer).",
select_station_cmd, station_set_plink);
static int handle_station_set_vlan(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char mac_addr[ETH_ALEN];
unsigned long sta_vlan = 0;
char *err = NULL;
if (argc < 3)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (strcmp("vlan", argv[0]) != 0)
return 1;
argc--;
argv++;
sta_vlan = strtoul(argv[0], &err, 0);
if (err && *err) {
fprintf(stderr, "invalid vlan id\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, sta_vlan);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND_ALIAS(station, set, "<MAC address> vlan <ifindex>",
NL80211_CMD_SET_STATION, 0, CIB_NETDEV, handle_station_set_vlan,
"Set an AP VLAN for this station.",
select_station_cmd, station_set_vlan);
static int handle_station_set_mesh_power_mode(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
unsigned char mesh_power_mode;
unsigned char mac_addr[ETH_ALEN];
if (argc < 3)
return 1;
if (mac_addr_a2n(mac_addr, argv[0])) {
fprintf(stderr, "invalid mac address\n");
return 2;
}
argc--;
argv++;
if (strcmp("mesh_power_mode", argv[0]) != 0)
return 1;
argc--;
argv++;
if (strcmp("active", argv[0]) == 0)
mesh_power_mode = NL80211_MESH_POWER_ACTIVE;
else if (strcmp("light", argv[0]) == 0)
mesh_power_mode = NL80211_MESH_POWER_LIGHT_SLEEP;
else if (strcmp("deep", argv[0]) == 0)
mesh_power_mode = NL80211_MESH_POWER_DEEP_SLEEP;
else {
fprintf(stderr, "unknown mesh power mode\n");
return 2;
}
argc--;
argv++;
if (argc)
return 1;
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
NLA_PUT_U32(msg, NL80211_ATTR_LOCAL_MESH_POWER_MODE, mesh_power_mode);
return 0;
nla_put_failure:
return -ENOBUFS;
}
COMMAND_ALIAS(station, set, "<MAC address> mesh_power_mode "
"<active|light|deep>", NL80211_CMD_SET_STATION, 0, CIB_NETDEV,
handle_station_set_mesh_power_mode,
"Set link-specific mesh power mode for this station",
select_station_cmd, station_set_mesh_power_mode);
static int handle_station_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
register_handler(print_sta_handler, *argv);
return 0;
}
COMMAND(station, dump, "[-v]",
NL80211_CMD_GET_STATION, NLM_F_DUMP, CIB_NETDEV, handle_station_dump,
"List all stations known, e.g. the AP on managed interfaces");

59
status.c Normal file
View File

@ -0,0 +1,59 @@
#include <stdint.h>
#include "iw.h"
static const char *status_table[] = {
[0] = "Successful",
[1] = "Unspecified failure",
[10] = "Cannot support all requested capabilities in the capability information field",
[11] = "Reassociation denied due to inability to confirm that association exists",
[12] = "Association denied due to reason outside the scope of this standard",
[13] = "Responding station does not support the specified authentication algorithm",
[14] = "Received an authentication frame with authentication transaction sequence number out of expected sequence",
[15] = "Authentication rejected because of challenge failure",
[16] = "Authentication rejected due to timeout waiting for next frame in sequence",
[17] = "Association denied because AP is unable to handle additional associated STA",
[18] = "Association denied due to requesting station not supporting all of the data rates in the BSSBasicRateSet parameter",
[19] = "Association denied due to requesting station not supporting the short preamble option",
[20] = "Association denied due to requesting station not supporting the PBCC modulation option",
[21] = "Association denied due to requesting station not supporting the channel agility option",
[22] = "Association request rejected because Spectrum Management capability is required",
[23] = "Association request rejected because the information in the Power Capability element is unacceptable",
[24] = "Association request rejected because the information in the Supported Channels element is unacceptable",
[25] = "Association request rejected due to requesting station not supporting the short slot time option",
[26] = "Association request rejected due to requesting station not supporting the ER-PBCC modulation option",
[27] = "Association denied due to requesting STA not supporting HT features",
[28] = "R0KH Unreachable",
[29] = "Association denied because the requesting STA does not support the PCO transition required by the AP",
[30] = "Association request rejected temporarily; try again later",
[31] = "Robust Management frame policy violation",
[32] = "Unspecified, QoS related failure",
[33] = "Association denied due to QAP having insufficient bandwidth to handle another QSTA",
[34] = "Association denied due to poor channel conditions",
[35] = "Association (with QBSS) denied due to requesting station not supporting the QoS facility",
[37] = "The request has been declined",
[38] = "The request has not been successful as one or more parameters have invalid values",
[39] = "The TS has not been created because the request cannot be honored. However, a suggested Tspec is provided so that the initiating QSTA may attempt to send another TS with the suggested changes to the TSpec",
[40] = "Invalid Information Element",
[41] = "Group Cipher is not valid",
[42] = "Pairwise Cipher is not valid",
[43] = "AKMP is not valid",
[44] = "Unsupported RSN IE version",
[45] = "Invalid RSN IE Capabilities",
[46] = "Cipher suite is rejected per security policy",
[47] = "The TS has not been created. However, the HC may be capable of creating a TS, in response to a request, after the time indicated in the TS Delay element",
[48] = "Direct link is not allowed in the BSS by policy",
[49] = "Destination STA is not present within this QBSS",
[50] = "The destination STA is not a QSTA",
[51] = "Association denied because Listen Interval is too large",
[52] = "Invalid Fast BSS Transition Action Frame Count",
[53] = "Invalid PMKID",
[54] = "Invalid MDIE",
[55] = "Invalid FTIE",
};
const char *get_status_str(uint16_t status)
{
if (status < ARRAY_SIZE(status_table) && status_table[status])
return status_table[status];
return "<unknown>";
}

80
survey.c Normal file
View File

@ -0,0 +1,80 @@
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(survey);
static int print_survey_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
char dev[20];
static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
printf("Survey data from %s\n", dev);
if (!tb[NL80211_ATTR_SURVEY_INFO]) {
fprintf(stderr, "survey data missing!\n");
return NL_SKIP;
}
if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
tb[NL80211_ATTR_SURVEY_INFO],
survey_policy)) {
fprintf(stderr, "failed to parse nested attributes!\n");
return NL_SKIP;
}
if (sinfo[NL80211_SURVEY_INFO_FREQUENCY])
printf("\tfrequency:\t\t\t%u MHz%s\n",
nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]),
sinfo[NL80211_SURVEY_INFO_IN_USE] ? " [in use]" : "");
if (sinfo[NL80211_SURVEY_INFO_NOISE])
printf("\tnoise:\t\t\t\t%d dBm\n",
(int8_t)nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]));
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME])
printf("\tchannel active time:\t\t%llu ms\n",
(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]));
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY])
printf("\tchannel busy time:\t\t%llu ms\n",
(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]));
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY])
printf("\textension channel busy time:\t%llu ms\n",
(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY]));
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX])
printf("\tchannel receive time:\t\t%llu ms\n",
(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]));
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX])
printf("\tchannel transmit time:\t\t%llu ms\n",
(unsigned long long)nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]));
return NL_SKIP;
}
static int handle_survey_dump(struct nl80211_state *state,
struct nl_msg *msg,
int argc, char **argv,
enum id_input id)
{
register_handler(print_survey_handler, NULL);
return 0;
}
COMMAND(survey, dump, NULL,
NL80211_CMD_GET_SURVEY, NLM_F_DUMP, CIB_NETDEV, handle_survey_dump,
"List all gathered channel survey data");

1129
util.c Normal file

File diff suppressed because it is too large Load Diff

144
vendor.c Normal file
View File

@ -0,0 +1,144 @@
#include <errno.h>
#include <string.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include "nl80211.h"
#include "iw.h"
SECTION(vendor);
static int print_vendor_response(struct nl_msg *msg, void *arg)
{
struct nlattr *attr;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
bool print_ascii = (bool) arg;
uint8_t *data;
int len;
attr = nla_find(genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0),
NL80211_ATTR_VENDOR_DATA);
if (!attr) {
fprintf(stderr, "vendor data attribute missing!\n");
return NL_SKIP;
}
data = (uint8_t *) nla_data(attr);
len = nla_len(attr);
if (print_ascii)
iw_hexdump("vendor response", data, len);
else
fwrite(data, 1, len, stdout);
return NL_OK;
}
static int read_file(FILE *file, char *buf, size_t size)
{
size_t count = 0;
int data;
while ((data = fgetc(file)) != EOF) {
if (count >= size)
return -EINVAL;
buf[count] = data;
count++;
}
return count;
}
static int read_hex(unsigned int argc, char **argv, char *buf, size_t size)
{
unsigned int i, data;
int res;
if (argc > size)
return -EINVAL;
for (i = 0; i < argc; i++) {
res = sscanf(argv[i], "0x%x", &data);
if (res != 1 || data > 0xff)
return -EINVAL;
buf[i] = data;
}
return argc;
}
static int handle_vendor(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
unsigned int oui;
unsigned int subcmd;
char buf[2048] = {};
int res, count = 0;
FILE *file = NULL;
if (argc < 3)
return 1;
res = sscanf(argv[0], "0x%x", &oui);
if (res != 1) {
printf("Vendor command must start with 0x\n");
return 2;
}
res = sscanf(argv[1], "0x%x", &subcmd);
if (res != 1) {
printf("Sub command must start with 0x\n");
return 2;
}
if (!strcmp(argv[2], "-"))
file = stdin;
else
file = fopen(argv[2], "r");
NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, oui);
NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd);
if (file) {
count = read_file(file, buf, sizeof(buf));
fclose(file);
} else
count = read_hex(argc - 2, &argv[2], buf, sizeof(buf));
if (count < 0)
return -EINVAL;
if (count > 0)
NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, count, buf);
return 0;
nla_put_failure:
return -ENOBUFS;
}
static int handle_vendor_recv(struct nl80211_state *state,
struct nl_msg *msg, int argc,
char **argv, enum id_input id)
{
register_handler(print_vendor_response, (void *) true);
return handle_vendor(state, msg, argc, argv, id);
}
static int handle_vendor_recv_bin(struct nl80211_state *state,
struct nl_msg *msg, int argc,
char **argv, enum id_input id)
{
register_handler(print_vendor_response, (void *) false);
return handle_vendor(state, msg, argc, argv, id);
}
COMMAND(vendor, send, "<oui> <subcmd> <filename|-|hex data>", NL80211_CMD_VENDOR, 0, CIB_NETDEV, handle_vendor, "");
COMMAND(vendor, recv, "<oui> <subcmd> <filename|-|hex data>", NL80211_CMD_VENDOR, 0, CIB_NETDEV, handle_vendor_recv, "");
COMMAND(vendor, recvbin, "<oui> <subcmd> <filename|-|hex data>", NL80211_CMD_VENDOR, 0, CIB_NETDEV, handle_vendor_recv_bin, "");

41
version.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/sh
VERSION="4.14"
OUT="$1"
# get the absolute path for the OUT file
OUT_NAME=$(basename ${OUT})
OUT_DIR=$(cd $(dirname ${OUT}); pwd)
OUT="${OUT_DIR}/${OUT_NAME}"
# the version check should be under the source directory
# where this script is located, instead of the currect directory
# where this script is excuted.
SRC_DIR=$(dirname $0)
SRC_DIR=$(cd ${SRC_DIR}; pwd)
cd "${SRC_DIR}"
v=""
if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then
git update-index --refresh --unmerged > /dev/null
descr=$(git describe --match=v* 2>/dev/null)
if [ $? -eq 0 ]; then
# on git builds check that the version number above
# is correct...
if [ "${descr%%-*}" = "v$VERSION" ]; then
v="${descr#v}"
if git diff-index --name-only HEAD | read dummy ; then
v="$v"-dirty
fi
fi
fi
fi
# set to the default version when failed to get the version
# information with git
if [ -z "${v}" ]; then
v="$VERSION"
fi
echo '#include "iw.h"' > "$OUT"
echo "const char iw_version[] = \"$v\";" >> "$OUT"

486
wowlan.c Normal file
View File

@ -0,0 +1,486 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <arpa/inet.h>
#include "nl80211.h"
#include "iw.h"
SECTION(wowlan);
static int wowlan_parse_tcp_file(struct nl_msg *msg, const char *fn)
{
char buf[16768];
int err = 1;
FILE *f = fopen(fn, "r");
struct nlattr *tcp;
if (!f)
return 1;
tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
if (!tcp)
goto nla_put_failure;
while (!feof(f)) {
char *eol;
if (!fgets(buf, sizeof(buf), f))
break;
eol = strchr(buf + 5, '\r');
if (eol)
*eol = 0;
eol = strchr(buf + 5, '\n');
if (eol)
*eol = 0;
if (strncmp(buf, "source=", 7) == 0) {
struct in_addr in_addr;
char *addr = buf + 7;
char *port = strchr(buf + 7, ':');
if (port) {
*port = 0;
port++;
}
if (inet_aton(addr, &in_addr) == 0)
goto close;
NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_SRC_IPV4,
in_addr.s_addr);
if (port)
NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_SRC_PORT,
atoi(port));
} else if (strncmp(buf, "dest=", 5) == 0) {
struct in_addr in_addr;
char *addr = buf + 5;
char *port = strchr(buf + 5, ':');
char *mac;
unsigned char macbuf[6];
if (!port)
goto close;
*port = 0;
port++;
mac = strchr(port, '@');
if (!mac)
goto close;
*mac = 0;
mac++;
if (inet_aton(addr, &in_addr) == 0)
goto close;
NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DST_IPV4,
in_addr.s_addr);
NLA_PUT_U16(msg, NL80211_WOWLAN_TCP_DST_PORT,
atoi(port));
if (mac_addr_a2n(macbuf, mac))
goto close;
NLA_PUT(msg, NL80211_WOWLAN_TCP_DST_MAC,
6, macbuf);
} else if (strncmp(buf, "data=", 5) == 0) {
size_t len;
unsigned char *pkt = parse_hex(buf + 5, &len);
if (!pkt)
goto close;
NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, len, pkt);
free(pkt);
} else if (strncmp(buf, "data.interval=", 14) == 0) {
NLA_PUT_U32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
atoi(buf + 14));
} else if (strncmp(buf, "wake=", 5) == 0) {
unsigned char *pat, *mask;
size_t patlen;
if (parse_hex_mask(buf + 5, &pat, &patlen, &mask))
goto close;
NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
DIV_ROUND_UP(patlen, 8), mask);
NLA_PUT(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
patlen, pat);
free(mask);
free(pat);
} else if (strncmp(buf, "data.seq=", 9) == 0) {
struct nl80211_wowlan_tcp_data_seq seq = {};
char *len, *offs, *start;
len = buf + 9;
offs = strchr(len, ',');
if (!offs)
goto close;
*offs = 0;
offs++;
start = strchr(offs, ',');
if (start) {
*start = 0;
start++;
seq.start = atoi(start);
}
seq.len = atoi(len);
seq.offset = atoi(offs);
NLA_PUT(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
sizeof(seq), &seq);
} else if (strncmp(buf, "data.tok=", 9) == 0) {
struct nl80211_wowlan_tcp_data_token *tok;
size_t stream_len;
char *len, *offs, *toks;
unsigned char *stream;
len = buf + 9;
offs = strchr(len, ',');
if (!offs)
goto close;
*offs = 0;
offs++;
toks = strchr(offs, ',');
if (!toks)
goto close;
*toks = 0;
toks++;
stream = parse_hex(toks, &stream_len);
if (!stream)
goto close;
tok = malloc(sizeof(*tok) + stream_len);
if (!tok) {
free(stream);
err = -ENOMEM;
goto close;
}
tok->len = atoi(len);
tok->offset = atoi(offs);
memcpy(tok->token_stream, stream, stream_len);
if (nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
sizeof(*tok) + stream_len, tok) < 0) {
free(stream);
free(tok);
goto nla_put_failure;
}
free(stream);
free(tok);
} else {
if (buf[0] == '#')
continue;
goto close;
}
}
err = 0;
goto close;
nla_put_failure:
err = -ENOBUFS;
close:
fclose(f);
if (tcp)
nla_nest_end(msg, tcp);
return err;
}
static int wowlan_parse_net_detect(struct nl_msg *msg, int *argc, char ***argv)
{
struct nlattr *nd;
int err = 0;
nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT);
if (!nd)
return -ENOBUFS;
err = parse_sched_scan(msg, argc, argv);
nla_nest_end(msg, nd);
return err;
}
static int handle_wowlan_enable(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
struct nlattr *wowlan, *pattern;
struct nl_msg *patterns = NULL;
enum {
PS_REG,
PS_PAT,
} parse_state = PS_REG;
int err = -ENOBUFS;
unsigned char *pat, *mask;
size_t patlen;
int patnum = 0, pkt_offset;
char *eptr, *value1, *value2, *sptr = NULL;
wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
if (!wowlan)
return -ENOBUFS;
while (argc) {
switch (parse_state) {
case PS_REG:
if (strcmp(argv[0], "any") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
else if (strcmp(argv[0], "disconnect") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
else if (strcmp(argv[0], "magic-packet") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
else if (strcmp(argv[0], "gtk-rekey-failure") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
else if (strcmp(argv[0], "eap-identity-request") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
else if (strcmp(argv[0], "4way-handshake") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
else if (strcmp(argv[0], "rfkill-release") == 0)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
else if (strcmp(argv[0], "tcp") == 0) {
argv++;
argc--;
if (!argc) {
err = 1;
goto nla_put_failure;
}
err = wowlan_parse_tcp_file(msg, argv[0]);
if (err)
goto nla_put_failure;
} else if (strcmp(argv[0], "patterns") == 0) {
parse_state = PS_PAT;
patterns = nlmsg_alloc();
if (!patterns) {
err = -ENOMEM;
goto nla_put_failure;
}
} else if (strcmp(argv[0], "net-detect") == 0) {
argv++;
argc--;
if (!argc) {
err = 1;
goto nla_put_failure;
}
err = wowlan_parse_net_detect(msg, &argc, &argv);
if (err)
goto nla_put_failure;
continue;
} else {
err = 1;
goto nla_put_failure;
}
break;
case PS_PAT:
value1 = strtok_r(argv[0], "+", &sptr);
value2 = strtok_r(NULL, "+", &sptr);
if (!value2) {
pkt_offset = 0;
value2 = value1;
} else {
pkt_offset = strtoul(value1, &eptr, 10);
if (eptr != value1 + strlen(value1)) {
err = 1;
goto nla_put_failure;
}
}
if (parse_hex_mask(value2, &pat, &patlen, &mask)) {
err = 1;
goto nla_put_failure;
}
pattern = nla_nest_start(patterns, ++patnum);
NLA_PUT(patterns, NL80211_PKTPAT_MASK,
DIV_ROUND_UP(patlen, 8), mask);
NLA_PUT(patterns, NL80211_PKTPAT_PATTERN, patlen, pat);
NLA_PUT_U32(patterns, NL80211_PKTPAT_OFFSET,
pkt_offset);
nla_nest_end(patterns, pattern);
free(mask);
free(pat);
break;
}
argv++;
argc--;
}
if (patterns)
nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
patterns);
nla_nest_end(msg, wowlan);
err = 0;
nla_put_failure:
nlmsg_free(patterns);
return err;
}
COMMAND(wowlan, enable, "[any] [disconnect] [magic-packet] [gtk-rekey-failure] [eap-identity-request]"
" [4way-handshake] [rfkill-release] [net-detect " SCHED_SCAN_OPTIONS "]"
" [tcp <config-file>] [patterns [offset1+]<pattern1> ...]",
NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_enable,
"Enable WoWLAN with the given triggers.\n"
"Each pattern is given as a bytestring with '-' in places where any byte\n"
"may be present, e.g. 00:11:22:-:44 will match 00:11:22:33:44 and\n"
"00:11:22:33:ff:44 etc.\n"
"Offset and pattern should be separated by '+', e.g. 18+43:34:00:12 will match "
"'43:34:00:12' after 18 bytes of offset in Rx packet.\n\n"
"The TCP configuration file contains:\n"
" source=ip[:port]\n"
" dest=ip:port@mac\n"
" data=<hex data packet>\n"
" data.interval=seconds\n"
" [wake=<hex packet with masked out bytes indicated by '-'>]\n"
" [data.seq=len,offset[,start]]\n"
" [data.tok=len,offset,<token stream>]\n\n"
"Net-detect configuration example:\n"
" iw phy0 wowlan enable net-detect interval 5000 delay 30 freqs 2412 2422 matches ssid foo ssid bar");
static int handle_wowlan_disable(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
/* just a set w/o wowlan attribute */
return 0;
}
COMMAND(wowlan, disable, "", NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_disable,
"Disable WoWLAN.");
static int print_wowlan_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
struct nlattr *trig[NUM_NL80211_WOWLAN_TRIG];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *pattern;
int rem_pattern;
nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
printf("WoWLAN is disabled.\n");
return NL_SKIP;
}
/* XXX: use policy */
nla_parse(trig, MAX_NL80211_WOWLAN_TRIG,
nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
NULL);
printf("WoWLAN is enabled:\n");
if (trig[NL80211_WOWLAN_TRIG_ANY])
printf(" * wake up on special any trigger\n");
if (trig[NL80211_WOWLAN_TRIG_DISCONNECT])
printf(" * wake up on disconnect\n");
if (trig[NL80211_WOWLAN_TRIG_MAGIC_PKT])
printf(" * wake up on magic packet\n");
if (trig[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
printf(" * wake up on GTK rekeying failure\n");
if (trig[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
printf(" * wake up on EAP identity request\n");
if (trig[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
printf(" * wake up on 4-way handshake\n");
if (trig[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
printf(" * wake up on RF-kill release\n");
if (trig[NL80211_WOWLAN_TRIG_NET_DETECT]) {
struct nlattr *match, *freq,
*nd[NUM_NL80211_ATTR], *tb[NUM_NL80211_ATTR];
int rem_match;
printf(" * wake up on network detection\n");
nla_parse(nd, NUM_NL80211_ATTR,
nla_data(trig[NL80211_WOWLAN_TRIG_NET_DETECT]),
nla_len(trig[NL80211_WOWLAN_TRIG_NET_DETECT]), NULL);
if (nd[NL80211_ATTR_SCHED_SCAN_INTERVAL])
printf("\tscan interval: %u msecs\n",
nla_get_u32(nd[NL80211_ATTR_SCHED_SCAN_INTERVAL]));
if (nd[NL80211_ATTR_SCHED_SCAN_DELAY])
printf("\tinitial scan delay: %u secs\n",
nla_get_u32(nd[NL80211_ATTR_SCHED_SCAN_DELAY]));
if (nd[NL80211_ATTR_SCHED_SCAN_MATCH]) {
printf("\tmatches:\n");
nla_for_each_nested(match,
nd[NL80211_ATTR_SCHED_SCAN_MATCH],
rem_match) {
nla_parse(tb, NUM_NL80211_ATTR, nla_data(match),
nla_len(match),
NULL);
printf("\t\tSSID: ");
print_ssid_escaped(
nla_len(tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]),
nla_data(tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]));
printf("\n");
}
}
if (nd[NL80211_ATTR_SCAN_FREQUENCIES]) {
printf("\tfrequencies:");
nla_for_each_nested(freq,
nd[NL80211_ATTR_SCAN_FREQUENCIES],
rem_match) {
printf(" %d", nla_get_u32(freq));
}
printf("\n");
}
}
if (trig[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
nla_for_each_nested(pattern,
trig[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem_pattern) {
struct nlattr *patattr[NUM_NL80211_PKTPAT];
int i, patlen, masklen;
uint8_t *mask, *pat;
nla_parse(patattr, MAX_NL80211_PKTPAT,
nla_data(pattern), nla_len(pattern), NULL);
if (!patattr[NL80211_PKTPAT_MASK] ||
!patattr[NL80211_PKTPAT_PATTERN]) {
printf(" * (invalid pattern specification)\n");
continue;
}
masklen = nla_len(patattr[NL80211_PKTPAT_MASK]);
patlen = nla_len(patattr[NL80211_PKTPAT_PATTERN]);
if (DIV_ROUND_UP(patlen, 8) != masklen) {
printf(" * (invalid pattern specification)\n");
continue;
}
if (patattr[NL80211_PKTPAT_OFFSET]) {
int pkt_offset =
nla_get_u32(patattr[NL80211_PKTPAT_OFFSET]);
printf(" * wake up on packet offset: %d", pkt_offset);
}
printf(" pattern: ");
pat = nla_data(patattr[NL80211_PKTPAT_PATTERN]);
mask = nla_data(patattr[NL80211_PKTPAT_MASK]);
for (i = 0; i < patlen; i++) {
if (mask[i / 8] & (1 << (i % 8)))
printf("%.2x", pat[i]);
else
printf("--");
if (i != patlen - 1)
printf(":");
}
printf("\n");
}
}
if (trig[NL80211_WOWLAN_TRIG_TCP_CONNECTION])
printf(" * wake up on TCP connection\n");
return NL_SKIP;
}
static int handle_wowlan_show(struct nl80211_state *state,
struct nl_msg *msg, int argc, char **argv,
enum id_input id)
{
register_handler(print_wowlan_handler, NULL);
return 0;
}
COMMAND(wowlan, show, "", NL80211_CMD_GET_WOWLAN, 0, CIB_PHY, handle_wowlan_show,
"Show WoWLAN status.");