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:
commit
77eaa55d96
|
|
@ -0,0 +1,7 @@
|
|||
iw
|
||||
*~
|
||||
*.o
|
||||
.config
|
||||
version.c
|
||||
iw.8.gz
|
||||
*-stamp
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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");
|
||||
|
|
@ -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).");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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");
|
||||
|
|
@ -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");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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, "");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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, "");
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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, "");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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>";
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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, "");
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#include "iw.h"
|
||||
|
||||
SECTION(get);
|
||||
SECTION(set);
|
||||
|
|
@ -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");
|
||||
|
|
@ -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>";
|
||||
}
|
||||
|
|
@ -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");
|
||||
|
||||
|
|
@ -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, "");
|
||||
|
|
@ -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"
|
||||
|
|
@ -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.");
|
||||
Loading…
Reference in New Issue