├── .travis.yml ├── Makefile ├── hwsim_mgmt ├── hwsim_mgmt_event.h ├── hwsim_mgmt_cli.h ├── Makefile ├── hwsim_mgmt_func.h ├── hwsim_mgmt_event.c ├── hwsim_mgmt_func.c └── hwsim_mgmt_cli.c ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── cmake ├── FindLibEvent.cmake └── FindNetLink.cmake └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | before_install: 3 | - sudo apt-get update -qq 4 | - sudo apt-get install -qq libnl-3-dev libnl-genl-3-dev libevent-dev 5 | script: make -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/sh 2 | MAKE = make 3 | SUBDIRS ?= hwsim_mgmt 4 | BIN = hwsim_mgmt/hwsim_mgmt 5 | BINDIR = /usr/bin 6 | 7 | all: 8 | @for i in $(SUBDIRS); do \ 9 | echo "make all in $$i..."; \ 10 | (cd $$i; $(MAKE) all); done 11 | 12 | clean: 13 | @for i in $(SUBDIRS); do \ 14 | echo "Clearing in $$i..."; \ 15 | (cd $$i; $(MAKE) clean); done 16 | 17 | install: all 18 | install -m 0755 $(BIN) $(BINDIR) 19 | -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #ifndef MAC80211_HWSIM_MGMT_HWSIM_MGMT_EVENT_H 7 | #define MAC80211_HWSIM_MGMT_HWSIM_MGMT_EVENT_H 8 | 9 | #include "hwsim_mgmt_cli.h" 10 | 11 | #define UNUSED(x) (void)(x) 12 | 13 | int register_event(hwsim_cli_ctx *ctx); 14 | 15 | int wait_for_event(); 16 | 17 | #endif //MAC80211_HWSIM_MGMT_HWSIM_MGMT_EVENT_H 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Executable 2 | hwsim_mgmt/hwsim_mgmt 3 | 4 | ### Jetbrains 5 | .idea 6 | 7 | ### CMake template 8 | CMakeCache.txt 9 | CMakeFiles 10 | CMakeScripts 11 | cmake_install.cmake 12 | install_manifest.txt 13 | CTestTestfile.cmake 14 | cmake-build-debug 15 | ### C template 16 | # Prerequisites 17 | *.d 18 | 19 | # Object files 20 | *.o 21 | *.ko 22 | *.obj 23 | *.elf 24 | 25 | # Linker output 26 | *.ilk 27 | *.map 28 | *.exp 29 | 30 | # Precompiled Headers 31 | *.gch 32 | *.pch 33 | 34 | # Libraries 35 | *.lib 36 | *.a 37 | *.la 38 | *.lo 39 | 40 | # Shared objects (inc. Windows DLLs) 41 | *.dll 42 | *.so 43 | *.so.* 44 | *.dylib 45 | 46 | # Executables 47 | *.exe 48 | *.out 49 | *.app 50 | *.i*86 51 | *.x86_64 52 | *.hex 53 | 54 | # Debug files 55 | *.dSYM/ 56 | *.su 57 | *.idb 58 | *.pdb 59 | 60 | # Kernel Module Compile Results 61 | *.mod* 62 | *.cmd 63 | modules.order 64 | Module.symvers 65 | Mkfile.old 66 | dkms.conf 67 | 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(mac80211_hwsim_mgmt) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | # find libraries 7 | include(${CMAKE_SOURCE_DIR}/cmake/FindNetLink.cmake) 8 | include(${CMAKE_SOURCE_DIR}/cmake/FindLibEvent.cmake) 9 | find_package(Threads) 10 | 11 | # include libnl directories 12 | include_directories(${NL_INCLUDE_DIRS}) 13 | 14 | # set source files 15 | set(SOURCE_FILES 16 | hwsim_mgmt/hwsim_mgmt_cli.c 17 | hwsim_mgmt/hwsim_mgmt_cli.h 18 | hwsim_mgmt/hwsim_mgmt_func.c 19 | hwsim_mgmt/hwsim_mgmt_func.h 20 | hwsim_mgmt/hwsim_mgmt_event.c 21 | hwsim_mgmt/hwsim_mgmt_event.h) 22 | 23 | # add executables 24 | add_executable(mac80211_hwsim_mgmt ${SOURCE_FILES}) 25 | 26 | # link required libraries 27 | target_link_libraries(mac80211_hwsim_mgmt ${M_LIB}) 28 | target_link_libraries(mac80211_hwsim_mgmt ${NL_LIBRARIES}) 29 | target_link_libraries(mac80211_hwsim_mgmt ${LIBEVENT_LIB}) 30 | target_link_libraries(mac80211_hwsim_mgmt ${CMAKE_THREAD_LIBS_INIT}) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Patrick Große 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmake/FindLibEvent.cmake: -------------------------------------------------------------------------------- 1 | # - Find LibEvent (a cross event library) 2 | # This module defines 3 | # LIBEVENT_INCLUDE_DIR, where to find LibEvent headers 4 | # LIBEVENT_LIB, LibEvent libraries 5 | # LibEvent_FOUND, If false, do not try to use libevent 6 | 7 | set(LibEvent_EXTRA_PREFIXES /usr/local /opt/local "$ENV{HOME}") 8 | foreach (prefix ${LibEvent_EXTRA_PREFIXES}) 9 | list(APPEND LibEvent_INCLUDE_PATHS "${prefix}/include") 10 | list(APPEND LibEvent_LIB_PATHS "${prefix}/lib") 11 | endforeach () 12 | 13 | find_path(LIBEVENT_INCLUDE_DIR event.h PATHS ${LibEvent_INCLUDE_PATHS}) 14 | find_library(LIBEVENT_LIB NAMES event PATHS ${LibEvent_LIB_PATHS}) 15 | 16 | if (LIBEVENT_LIB AND LIBEVENT_INCLUDE_DIR) 17 | set(LibEvent_FOUND TRUE) 18 | set(LIBEVENT_LIB ${LIBEVENT_LIB}) 19 | else () 20 | set(LibEvent_FOUND FALSE) 21 | endif () 22 | 23 | if (LibEvent_FOUND) 24 | if (NOT LibEvent_FIND_QUIETLY) 25 | message(STATUS "Found libevent: ${LIBEVENT_LIB}") 26 | endif () 27 | else () 28 | if (LibEvent_FIND_REQUIRED) 29 | message(FATAL_ERROR "Could NOT find libevent.") 30 | endif () 31 | message(STATUS "libevent NOT found.") 32 | endif () 33 | 34 | mark_as_advanced( 35 | LIBEVENT_LIB 36 | LIBEVENT_INCLUDE_DIR 37 | ) -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_cli.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #ifndef MAC80211_HWSIM_MGMT_HWSIM_MGMT_H 7 | #define MAC80211_HWSIM_MGMT_HWSIM_MGMT_H 8 | 9 | #include 10 | #include 11 | #include "hwsim_mgmt_func.h" 12 | 13 | enum op_mode { 14 | HWSIM_OP_NONE, 15 | HWSIM_OP_CREATE, 16 | HWSIM_OP_DELETE_BY_ID, 17 | HWSIM_OP_DELETE_BY_NAME, 18 | HWSIM_OP_SET_RSSI 19 | }; 20 | 21 | typedef struct { 22 | enum op_mode mode; 23 | char *c_hwname; 24 | uint32_t c_channels; 25 | bool c_no_vif; 26 | bool c_use_chanctx; 27 | char *c_reg_alpha2; 28 | uint32_t c_reg_custom_reg; 29 | uint32_t del_radio_id; 30 | char *del_radio_name; 31 | uint32_t rssi_radio; 32 | } hwsim_args; 33 | 34 | typedef struct { 35 | struct argp hwsim_argp; 36 | hwsim_args args; 37 | netlink_ctx nl_ctx; 38 | } hwsim_cli_ctx; 39 | 40 | int handleCreate(const hwsim_args *args); 41 | 42 | int handleDeleteById(const hwsim_args *args); 43 | 44 | int handleDeleteByName(const hwsim_args *args); 45 | 46 | int handleSetRSSI(const hwsim_args *args, char *rssi); 47 | 48 | void notify_device_creation(int id); 49 | 50 | void notify_device_deletion(); 51 | 52 | void notify_device_setRSSI(); 53 | 54 | #endif //MAC80211_HWSIM_MGMT_HWSIM_MGMT_H 55 | -------------------------------------------------------------------------------- /hwsim_mgmt/Makefile: -------------------------------------------------------------------------------- 1 | # Look for libnl libraries 2 | PKG_CONFIG ?= pkg-config 3 | NL2FOUND := $(shell $(PKG_CONFIG) --atleast-version=2 libnl-2.0 && echo Y) 4 | NL3FOUND := $(shell $(PKG_CONFIG) --atleast-version=3 libnl-3.0 && echo Y) 5 | NL31FOUND := $(shell $(PKG_CONFIG) --exact-version=3.1 libnl-3.1 && echo Y) 6 | NL3xFOUND := $(shell $(PKG_CONFIG) --atleast-version=3.2 libnl-3.0 && echo Y) 7 | 8 | CFLAGS = -g -Wall -Wextra -O2 9 | LDFLAGS = -lpthread -levent 10 | 11 | ifeq ($(NL2FOUND),Y) 12 | CFLAGS += -DCONFIG_LIBNL20 13 | LDFLAGS += -lnl-genl 14 | NLLIBNAME = libnl-2.0 15 | endif 16 | 17 | ifeq ($(NL3xFOUND),Y) 18 | # libnl 3.2 might be found as 3.2 and 3.0 19 | NL3FOUND = N 20 | CFLAGS += -DCONFIG_LIBNL30 21 | LDFLAGS += -lnl-genl-3 22 | NLLIBNAME = libnl-3.0 23 | endif 24 | 25 | ifeq ($(NL3FOUND),Y) 26 | CFLAGS += -DCONFIG_LIBNL30 27 | LDFLAGS += -lnl-genl 28 | NLLIBNAME = libnl-3.0 29 | endif 30 | 31 | # nl-3.1 has a broken libnl-gnl-3.1.pc file 32 | # as show by pkg-config --debug --libs --cflags --exact-version=3.1 libnl-genl-3.1;echo $? 33 | ifeq ($(NL31FOUND),Y) 34 | CFLAGS += -DCONFIG_LIBNL30 35 | LDFLAGS += -lnl-genl 36 | NLLIBNAME = libnl-3.1 37 | endif 38 | 39 | ifeq ($(NLLIBNAME),) 40 | $(error Cannot find development files for any supported version of libnl) 41 | endif 42 | 43 | LDFLAGS += $(shell $(PKG_CONFIG) --libs $(NLLIBNAME)) 44 | CFLAGS += $(shell $(PKG_CONFIG) --cflags $(NLLIBNAME)) 45 | 46 | OBJECTS=hwsim_mgmt_cli.o hwsim_mgmt_func.o hwsim_mgmt_event.o 47 | 48 | all: hwsim_mgmt 49 | 50 | hwsim_mgmt: $(OBJECTS) 51 | $(CC) -o $@ $(OBJECTS) $(LDFLAGS) 52 | 53 | clean: 54 | rm -f $(OBJECTS) hwsim_mgmt 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mac80211_hwsim_mgmt 2 | [![Build Status](https://travis-ci.org/patgrosse/mac80211_hwsim_mgmt.svg?branch=master)](https://travis-ci.org/patgrosse/mac80211_hwsim_mgmt) 3 | 4 | Management tool for mac80211_hwsim kernel module 5 | 6 | ## Features 7 | * Create mac80211_hwsim stations after the kernel module has been loaded 8 | * Delete mac80211_hwsim stations 9 | * Set mac80211_hwsim properties per station 10 | * Uses netlink genl messages for kernel communication 11 | 12 | ## Installation 13 | Will install to `/usr/bin/hwsim_mgmt` and may require root 14 | ```bash 15 | git clone https://github.com/patgrosse/mac80211_hwsim_mgmt.git 16 | cd mac80211_hwsim_mgmt 17 | make install 18 | ``` 19 | 20 | ### About Set RSSI 21 | The feature Set RSSI requires minor changes in mac80211_hwsim: https://www.youtube.com/watch?v=gtaHCpaHBGc 22 | 23 | ### Requirements 24 | * A kernel containing the mac80211_hwsim module 25 | * libevent and at least libnl-2.0 26 | 27 | ## Usage 28 | ``` 29 | hwsim_mgmt [OPTION...] 30 | 31 | Modes: [-c [OPTION...]|-d|-x] 32 | -c, --create Create a new radio 33 | -d, --delid=ID Delete an existing radio by its id 34 | -x, --delname=NAME Delete an existing radio by its name 35 | -k, --setrssi=NUM Set RSSI to specific radio 36 | 37 | Create options: 38 | -a, --alphareg=STR reg_alpha2 hint 39 | -n, --name=NAME The requested name (may not be available) 40 | -o, --channels=NUM Number of concurrent channels 41 | -r, --customreg=REG reg_domain ID int 42 | -t, --chanctx Use chantx (flag) 43 | -v, --novif No auto vif (flag) 44 | 45 | General: 46 | -?, --help Give this help list 47 | --usage Give a short usage message 48 | -V, --version Print program version 49 | ``` 50 | -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_func.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #ifndef MAC80211_HWSIM_MGMT_HWSIM_MGMT_FUNC_H 7 | #define MAC80211_HWSIM_MGMT_HWSIM_MGMT_FUNC_H 8 | 9 | #include 10 | 11 | #define HWSIM_CMD_UNSPEC 0 12 | #define HWSIM_CMD_REGISTER 1 13 | #define HWSIM_CMD_FRAME 2 14 | #define HWSIM_CMD_TX_INFO_FRAME 3 15 | #define HWSIM_CMD_NEW_RADIO 4 16 | #define HWSIM_CMD_DEL_RADIO 5 17 | #define HWSIM_CMD_GET_RADIO 6 18 | #define __HWSIM_CMD_MAX 7 19 | 20 | #define HWSIM_ATTR_UNSPEC 0 21 | #define HWSIM_ATTR_ADDR_RECEIVER 1 22 | #define HWSIM_ATTR_ADDR_TRANSMITTER 2 23 | #define HWSIM_ATTR_FRAME 3 24 | #define HWSIM_ATTR_FLAGS 4 25 | #define HWSIM_ATTR_RX_RATE 5 26 | #define HWSIM_ATTR_SIGNAL 6 27 | #define HWSIM_ATTR_TX_INFO 7 28 | #define HWSIM_ATTR_COOKIE 8 29 | #define HWSIM_ATTR_CHANNELS 9 30 | #define HWSIM_ATTR_RADIO_ID 10 31 | #define HWSIM_ATTR_REG_HINT_ALPHA2 11 32 | #define HWSIM_ATTR_REG_CUSTOM_REG 12 33 | #define HWSIM_ATTR_REG_STRICT_REG 13 34 | #define HWSIM_ATTR_SUPPORT_P2P_DEVICE 14 35 | #define HWSIM_ATTR_USE_CHANCTX 15 36 | #define HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE 16 37 | #define HWSIM_ATTR_RADIO_NAME 17 38 | #define HWSIM_ATTR_NO_VIF 18 39 | #define HWSIM_ATTR_FREQ 19 40 | #define HWSIM_ATTR_PAD 20 41 | #define __HWSIM_ATTR_MAX 21 42 | 43 | typedef struct { 44 | struct nl_cb *cb; 45 | struct nl_sock *sock; 46 | struct nl_cache *cache; 47 | struct genl_family *family; 48 | } netlink_ctx; 49 | 50 | int init_netlink(netlink_ctx *ctx); 51 | 52 | int create_radio(const netlink_ctx *ctx, const uint32_t channels, const bool no_vif, const char *hwname, 53 | const bool use_chanctx, const char *reg_alpha2, 54 | const uint32_t reg_custom_reg); 55 | 56 | int delete_radio_by_id(const netlink_ctx *ctx, const uint32_t radio_id); 57 | 58 | int delete_radio_by_name(const netlink_ctx *ctx, const char *radio_name); 59 | 60 | int set_rssi(const netlink_ctx *ctx, const uint32_t radio_id, const uint32_t rssi); 61 | 62 | #endif //MAC80211_HWSIM_MGMT_HWSIM_MGMT_FUNC_H 63 | -------------------------------------------------------------------------------- /cmake/FindNetLink.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # $Id$ 3 | # 4 | # Find the native netlink includes and library 5 | # 6 | # If they exist, differentiate between versions 1, 2 and 3. 7 | # Version 1 does not have netlink/version.h 8 | # Version 3 does have the major version number as a suffix 9 | # to the libnl name (libnl-3) 10 | # 11 | # NL_INCLUDE_DIRS - where to find libnl.h, etc. 12 | # NL_LIBRARIES - List of libraries when using libnl3. 13 | # NL_FOUND - True if libnl found. 14 | 15 | IF (NL_LIBRARIES AND NL_INCLUDE_DIRS) 16 | # in cache already 17 | SET(NL_FOUND TRUE) 18 | ELSE (NL_LIBRARIES AND NL_INCLUDE_DIRS) 19 | SET(SEARCHPATHS 20 | /opt/local 21 | /sw 22 | /usr 23 | /usr/local 24 | ) 25 | FIND_PATH(NL_INCLUDE_DIR 26 | PATH_SUFFIXES 27 | include/libnl3 28 | NAMES 29 | netlink/version.h 30 | PATHS 31 | $(SEARCHPATHS) 32 | ) 33 | # NL version >= 2 34 | IF (NL_INCLUDE_DIR) 35 | FIND_LIBRARY(NL_LIBRARY 36 | NAMES 37 | nl-3 nl 38 | PATH_SUFFIXES 39 | lib64 lib 40 | PATHS 41 | $(SEARCHPATHS) 42 | ) 43 | FIND_LIBRARY(NLGENL_LIBRARY 44 | NAMES 45 | nl-genl-3 nl-genl 46 | PATH_SUFFIXES 47 | lib64 lib 48 | PATHS 49 | $(SEARCHPATHS) 50 | ) 51 | # 52 | # If we don't have all of those libraries, we can't use libnl. 53 | # 54 | IF (NOT NLGENL_LIBRARY) 55 | SET(NL_LIBRARY NOTFOUND) 56 | ENDIF (NOT NLGENL_LIBRARY) 57 | IF (NL_LIBRARY) 58 | STRING(REGEX REPLACE ".*nl-([^.,;]*).*" "\\1" NLSUFFIX ${NL_LIBRARY}) 59 | IF (NLSUFFIX) 60 | SET(HAVE_LIBNL3 1) 61 | ELSE (NLSUFFIX) 62 | SET(HAVE_LIBNL2 1) 63 | ENDIF (NLSUFFIX) 64 | SET(HAVE_LIBNL 1) 65 | ENDIF (NL_LIBRARY) 66 | ELSE (NL_INCLUDE_DIR) 67 | # NL version 1 ? 68 | FIND_PATH(NL_INCLUDE_DIR 69 | NAMES 70 | netlink/netlink.h 71 | PATHS 72 | $(SEARCHPATHS) 73 | ) 74 | FIND_LIBRARY(NL_LIBRARY 75 | NAMES 76 | nl 77 | PATH_SUFFIXES 78 | lib64 lib 79 | PATHS 80 | $(SEARCHPATHS) 81 | ) 82 | if (NL_INCLUDE_DIR) 83 | SET(HAVE_LIBNL1 1) 84 | ENDIF (NL_INCLUDE_DIR) 85 | ENDIF (NL_INCLUDE_DIR) 86 | ENDIF (NL_LIBRARIES AND NL_INCLUDE_DIRS) 87 | # MESSAGE(STATUS "LIB Found: ${NL_LIBRARY}, Suffix: ${NLSUFFIX}\n 1:${HAVE_LIBNL1}, 2:${HAVE_LIBNL2}, 3:${HAVE_LIBNL3}.") 88 | 89 | # handle the QUIETLY and REQUIRED arguments and set NL_FOUND to TRUE if 90 | # all listed variables are TRUE 91 | INCLUDE(FindPackageHandleStandardArgs) 92 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(NL DEFAULT_MSG NL_LIBRARY NL_INCLUDE_DIR) 93 | 94 | IF (NL_FOUND) 95 | SET(NL_LIBRARIES ${NLGENL_LIBRARY} ${NL_LIBRARY}) 96 | SET(NL_INCLUDE_DIRS ${NL_INCLUDE_DIR}) 97 | ELSE () 98 | SET(NL_LIBRARIES) 99 | SET(NL_INCLUDE_DIRS) 100 | ENDIF () 101 | 102 | MARK_AS_ADVANCED(NL_LIBRARIES NL_INCLUDE_DIRS) -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "hwsim_mgmt_event.h" 13 | 14 | static pthread_mutex_t nl_cb_mutex; 15 | 16 | static int nl_msg_cb(struct nl_msg *msg, void *rctx) { 17 | hwsim_cli_ctx *ctx = rctx; 18 | struct nlmsghdr *nlh = nlmsg_hdr(msg); 19 | struct genlmsghdr *gnlh = nlmsg_data(nlh); 20 | int retcode = gnlh->cmd; 21 | if (retcode == 0) { 22 | if (!pthread_mutex_trylock(&nl_cb_mutex)) { 23 | if (ctx->args.mode == HWSIM_OP_CREATE) { 24 | notify_device_creation(0); 25 | } else if (ctx->args.mode == HWSIM_OP_DELETE_BY_ID || ctx->args.mode == HWSIM_OP_DELETE_BY_NAME) { 26 | notify_device_deletion(); 27 | } else if (ctx->args.mode == HWSIM_OP_SET_RSSI) { 28 | notify_device_setRSSI(); 29 | } 30 | // should not be needed 31 | pthread_mutex_unlock(&nl_cb_mutex); 32 | } 33 | } 34 | return 0; 35 | } 36 | 37 | static int nl_err_cb(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *rctx) { 38 | UNUSED(nla); 39 | hwsim_cli_ctx *ctx = rctx; 40 | if (!pthread_mutex_trylock(&nl_cb_mutex)) { 41 | if (ctx->args.mode == HWSIM_OP_DELETE_BY_ID || ctx->args.mode == HWSIM_OP_DELETE_BY_NAME) { 42 | if (nlerr->error == -ENODEV) { 43 | fprintf(stderr, "Device not found\n"); 44 | } else { 45 | fprintf(stderr, "Unknown error on device deletion with errid %d\nstrerror: %s\n", nlerr->error, 46 | strerror(abs(nlerr->error))); 47 | } 48 | exit(EXIT_FAILURE); 49 | } else if (ctx->args.mode == HWSIM_OP_CREATE) { 50 | if (nlerr->error < 0) { 51 | fprintf(stderr, "Unknown error on device creation with errid %d\nstrerror: %s\n", nlerr->error, 52 | strerror(abs(nlerr->error))); 53 | exit(EXIT_FAILURE); 54 | } 55 | notify_device_creation(nlerr->error); 56 | } else if (ctx->args.mode == HWSIM_OP_SET_RSSI) { 57 | if (nlerr->error == -ENODEV) { 58 | fprintf(stderr, "Device not found\n"); 59 | } else { 60 | fprintf(stderr, "Unknown error while setting RSSI with errid %d\nstrerror: %s\n", nlerr->error, 61 | strerror(abs(nlerr->error))); 62 | } 63 | exit(EXIT_FAILURE); 64 | } 65 | // should not be needed 66 | pthread_mutex_unlock(&nl_cb_mutex); 67 | } 68 | return NL_SKIP; 69 | } 70 | 71 | static void nl_event_handler(int fd, short what, void *rctx) { 72 | UNUSED(fd); 73 | UNUSED(what); 74 | hwsim_cli_ctx *ctx = rctx; 75 | nl_recvmsgs_default(ctx->nl_ctx.sock); 76 | } 77 | 78 | static void *run_nl_event_dispatcher(void *rctx) { 79 | hwsim_cli_ctx *ctx = rctx; 80 | struct event_base *ev_base = event_base_new(); 81 | struct event *ev_cmd = event_new(ev_base, nl_socket_get_fd(ctx->nl_ctx.sock), EV_READ | EV_PERSIST, 82 | nl_event_handler, ctx); 83 | event_add(ev_cmd, NULL); 84 | event_base_dispatch(ev_base); 85 | event_base_free(ev_base); 86 | event_free(ev_cmd); 87 | return NULL; 88 | } 89 | 90 | int register_event(hwsim_cli_ctx *ctx) { 91 | if (nl_cb_set(ctx->nl_ctx.cb, NL_CB_MSG_IN, NL_CB_CUSTOM, nl_msg_cb, ctx)) { 92 | fprintf(stderr, "Error on callback registration\n"); 93 | return -1; 94 | } 95 | if (nl_cb_err(ctx->nl_ctx.cb, NL_CB_CUSTOM, nl_err_cb, ctx)) { 96 | fprintf(stderr, "Error on callback registration (2)\n"); 97 | return -1; 98 | } 99 | 100 | pthread_t libe_thread; 101 | return pthread_create(&libe_thread, NULL, run_nl_event_dispatcher, ctx); 102 | } 103 | 104 | int wait_for_event() { 105 | sleep(2); 106 | fprintf(stderr, "Did not receive netlink event after 2 sec\n"); 107 | return EXIT_FAILURE; 108 | } 109 | -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_func.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "hwsim_mgmt_func.h" 12 | 13 | int init_netlink(netlink_ctx *ctx) { 14 | int ret; 15 | 16 | ctx->cb = nl_cb_alloc(NL_CB_CUSTOM); 17 | if (!ctx->cb) { 18 | fprintf(stderr, "Error allocating netlink callbacks\n"); 19 | return EXIT_FAILURE; 20 | } 21 | 22 | ctx->sock = nl_socket_alloc_cb(ctx->cb); 23 | if (!ctx->sock) { 24 | fprintf(stderr, "Error allocating netlink socket\n"); 25 | return EXIT_FAILURE; 26 | } 27 | 28 | ret = genl_connect(ctx->sock); 29 | if (ret < 0) { 30 | fprintf(stderr, "Error connecting netlink socket ret=%d\n", ret); 31 | return EXIT_FAILURE; 32 | } 33 | 34 | ret = genl_ctrl_alloc_cache(ctx->sock, &ctx->cache); 35 | if (ret < 0) { 36 | fprintf(stderr, "Error allocationg netlink cache ret=%d\n", ret); 37 | return EXIT_FAILURE; 38 | } 39 | 40 | ctx->family = genl_ctrl_search_by_name(ctx->cache, "MAC80211_HWSIM"); 41 | 42 | if (!ctx->family) { 43 | fprintf(stderr, "Family MAC80211_HWSIM not registered\n"); 44 | return EXIT_FAILURE; 45 | } 46 | 47 | return EXIT_SUCCESS; 48 | } 49 | 50 | int create_radio(const netlink_ctx *ctx, const uint32_t channels, const bool no_vif, const char *hwname, 51 | const bool use_chanctx, const char *reg_alpha2, 52 | const uint32_t reg_custom_reg) { 53 | struct nl_msg *msg; 54 | msg = nlmsg_alloc(); 55 | 56 | if (!msg) { 57 | fprintf(stderr, "Error allocating new message!\n"); 58 | return EXIT_FAILURE; 59 | } 60 | int fam_id = genl_family_get_id(ctx->family); 61 | if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, 62 | fam_id, 0, 63 | NLM_F_REQUEST, HWSIM_CMD_NEW_RADIO, 64 | 1) == NULL) { 65 | fprintf(stderr, "Error in genlmsg_put!\n"); 66 | nlmsg_free(msg); 67 | return EXIT_FAILURE; 68 | } 69 | if (channels != 0) { 70 | nla_put_u32(msg, HWSIM_ATTR_CHANNELS, channels); 71 | } 72 | if (no_vif) { 73 | nla_put_flag(msg, HWSIM_ATTR_NO_VIF); 74 | } 75 | if (hwname) { 76 | nla_put_string(msg, HWSIM_ATTR_RADIO_NAME, hwname); 77 | } 78 | if (use_chanctx) { 79 | nla_put_flag(msg, HWSIM_ATTR_USE_CHANCTX); 80 | } 81 | if (reg_alpha2 != NULL) { 82 | nla_put_string(msg, HWSIM_ATTR_REG_HINT_ALPHA2, reg_alpha2); 83 | } 84 | if (reg_custom_reg != 0) { 85 | nla_put_u32(msg, HWSIM_ATTR_REG_CUSTOM_REG, reg_custom_reg); 86 | } 87 | if (nl_send_auto(ctx->sock, msg) < 0) { 88 | fprintf(stderr, "Error sending message!\n"); 89 | nlmsg_free(msg); 90 | return EXIT_FAILURE; 91 | } 92 | return EXIT_SUCCESS; 93 | } 94 | 95 | int delete_radio_by_id(const netlink_ctx *ctx, const uint32_t radio_id) { 96 | struct nl_msg *msg; 97 | msg = nlmsg_alloc(); 98 | if (!msg) { 99 | fprintf(stderr, "Error allocating new message!\n"); 100 | return EXIT_FAILURE; 101 | } 102 | if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, 103 | genl_family_get_id(ctx->family), 0, 104 | NLM_F_REQUEST, HWSIM_CMD_DEL_RADIO, 105 | 1) == NULL) { 106 | fprintf(stderr, "Error in genlmsg_put!\n"); 107 | nlmsg_free(msg); 108 | return EXIT_FAILURE; 109 | } 110 | nla_put_u32(msg, HWSIM_ATTR_RADIO_ID, radio_id); 111 | if (nl_send_auto(ctx->sock, msg) < 0) { 112 | fprintf(stderr, "Error sending message!\n"); 113 | nlmsg_free(msg); 114 | return EXIT_FAILURE; 115 | } 116 | return EXIT_SUCCESS; 117 | } 118 | 119 | int delete_radio_by_name(const netlink_ctx *ctx, const char *radio_name) { 120 | struct nl_msg *msg; 121 | msg = nlmsg_alloc(); 122 | if (!msg) { 123 | fprintf(stderr, "Error allocating new message!\n"); 124 | return EXIT_FAILURE; 125 | } 126 | if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, 127 | genl_family_get_id(ctx->family), 0, 128 | NLM_F_REQUEST, HWSIM_CMD_DEL_RADIO, 129 | 1) == NULL) { 130 | fprintf(stderr, "Error in genlmsg_put!\n"); 131 | nlmsg_free(msg); 132 | return EXIT_FAILURE; 133 | } 134 | nla_put_string(msg, HWSIM_ATTR_RADIO_NAME, radio_name); 135 | if (nl_send_auto(ctx->sock, msg) < 0) { 136 | fprintf(stderr, "Error sending message!\n"); 137 | nlmsg_free(msg); 138 | return EXIT_FAILURE; 139 | } 140 | return EXIT_SUCCESS; 141 | 142 | } 143 | 144 | int set_rssi(const netlink_ctx *ctx, const uint32_t radio_id, const uint32_t rssi) { 145 | struct nl_msg *msg; 146 | 147 | msg = nlmsg_alloc(); 148 | if (!msg) { 149 | fprintf(stderr, "Error allocating new message!\n"); 150 | return EXIT_FAILURE; 151 | } 152 | if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, 153 | genl_family_get_id(ctx->family), 0, 154 | NLM_F_REQUEST, HWSIM_CMD_GET_RADIO, 155 | 1) == NULL) { 156 | fprintf(stderr, "Error in genlmsg_put!\n"); 157 | nlmsg_free(msg); 158 | return EXIT_FAILURE; 159 | } 160 | nla_put_u32(msg, HWSIM_ATTR_SIGNAL, rssi * -1); 161 | nla_put_u32(msg, HWSIM_ATTR_RADIO_ID, radio_id); 162 | if (nl_send_auto(ctx->sock, msg) < 0) { 163 | fprintf(stderr, "Error sending message!\n"); 164 | nlmsg_free(msg); 165 | return EXIT_FAILURE; 166 | } 167 | return EXIT_SUCCESS; 168 | } 169 | -------------------------------------------------------------------------------- /hwsim_mgmt/hwsim_mgmt_cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mac80211_hwsim_mgmt - management tool for mac80211_hwsim kernel module 3 | * Copyright (c) 2016, Patrick Grosse 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "hwsim_mgmt_cli.h" 10 | #include "hwsim_mgmt_event.h" 11 | 12 | const char *argp_program_version = "mac80211_hwsim_mgmt v0.1"; 13 | const char *argp_program_bug_address = ""; 14 | static char *program_executable = "hwsim_mgmt"; 15 | static const char doc[] = "Management tool for mac80211_hwsim kernel module"; 16 | static struct argp_option options[] = { 17 | {0, 0, 0, 0, "Modes: [-c [OPTION...]|-d|-x]", 1}, 18 | {"create", 'c', 0, 0, "Create a new radio", 1}, 19 | {"delid", 'd', "ID", 0, "Delete an existing radio by its id", 1}, 20 | {"delname", 'x', "NAME", 0, "Delete an existing radio by its name", 1}, 21 | {"setrssi", 'k', "NUM", 0, "Set RSSI to specific radio", 1}, 22 | {0, 0, 0, 0, "Create options:", 2}, 23 | {"name", 'n', "NAME", 0, "The requested name (may not be available)", 2}, 24 | {"channels", 'o', "NUM", 0, "Number of concurrent channels", 2}, 25 | {"novif", 'v', 0, 0, "No auto vif (flag)", 2}, 26 | {"chanctx", 't', 0, 0, "Use chantx (flag)", 2}, 27 | {"alphareg", 'a', "STR", 0, "reg_alpha2 hint", 2}, 28 | {"customreg", 'r', "REG", 0, "reg_domain ID int", 2}, 29 | {0, 0, 0, 0, "General:", -1}, 30 | {0, 0, 0, 0, 0, 0} 31 | }; 32 | static const char *msg_duplicate_mode = "Exactly one parameter out of -c, -d, -x is required\n"; 33 | 34 | static hwsim_cli_ctx ctx; 35 | 36 | static void argp_err_and_usage(const char *err_msg, ...) { 37 | va_list(args); 38 | va_start(args, err_msg); 39 | vfprintf(stderr, err_msg, args); 40 | argp_help(&ctx.hwsim_argp, stdout, ARGP_HELP_STD_USAGE, program_executable); 41 | exit(EXIT_SUCCESS); 42 | } 43 | 44 | uint32_t cli_get_uint32(const char opt, const char *arg) { 45 | char *endptr = NULL; 46 | unsigned long ul = strtoul(arg, &endptr, 10); 47 | unsigned long parsed_len = endptr - arg; 48 | if (strlen(arg) != parsed_len || (ul == ULONG_MAX && errno == ERANGE) || ul > UINT32_MAX) { 49 | argp_err_and_usage("-%c requires a positive integer attribute (max 32 bit)\n", opt); 50 | } 51 | return (uint32_t) ul; 52 | } 53 | 54 | error_t hwsim_parse_argp(int key, char *arg, struct argp_state *state) { 55 | hwsim_args *arguments = state->input; 56 | switch (key) { 57 | case 'd': 58 | if (arguments->mode != HWSIM_OP_NONE) { 59 | argp_err_and_usage(msg_duplicate_mode); 60 | } 61 | arguments->mode = HWSIM_OP_DELETE_BY_ID; 62 | arguments->del_radio_id = cli_get_uint32('d', arg); 63 | break; 64 | case 'x': 65 | if (arguments->mode != HWSIM_OP_NONE) { 66 | argp_err_and_usage(msg_duplicate_mode); 67 | } 68 | arguments->del_radio_name = arg; 69 | arguments->mode = HWSIM_OP_DELETE_BY_NAME; 70 | break; 71 | case 'k': 72 | if (arguments->mode != HWSIM_OP_NONE) { 73 | argp_err_and_usage(msg_duplicate_mode); 74 | } 75 | arguments->rssi_radio = cli_get_uint32('k', arg); 76 | arguments->mode = HWSIM_OP_SET_RSSI; 77 | break; 78 | case 'c': 79 | if (arguments->mode != HWSIM_OP_NONE) { 80 | argp_err_and_usage(msg_duplicate_mode); 81 | } 82 | arguments->mode = HWSIM_OP_CREATE; 83 | break; 84 | case 'n': 85 | arguments->c_hwname = arg; 86 | break; 87 | case 'o': 88 | arguments->c_channels = cli_get_uint32('o', arg); 89 | break; 90 | case 'v': 91 | arguments->c_no_vif = true; 92 | break; 93 | case 't': 94 | arguments->c_use_chanctx = true; 95 | break; 96 | case 'a': 97 | arguments->c_reg_alpha2 = arg; 98 | break; 99 | case 'r': 100 | arguments->c_reg_custom_reg = cli_get_uint32('r', arg); 101 | break; 102 | case 'h': 103 | argp_help(&ctx.hwsim_argp, stdout, ARGP_HELP_STD_HELP, program_executable); 104 | exit(EXIT_SUCCESS); 105 | case ARGP_KEY_ARG: 106 | return 0; 107 | default: 108 | return ARGP_ERR_UNKNOWN; 109 | } 110 | return 0; 111 | } 112 | 113 | static int prepareCommand() { 114 | if (init_netlink(&ctx.nl_ctx)) { 115 | fprintf(stderr, "Error initializing netlink context!\n"); 116 | return -1; 117 | } 118 | if (register_event(&ctx)) { 119 | fprintf(stderr, "Error registering events!\n"); 120 | return -1; 121 | } 122 | return 0; 123 | } 124 | 125 | int handleCreate(const hwsim_args *args) { 126 | int ret; 127 | if ((ret = prepareCommand())) { 128 | return ret; 129 | }; 130 | if ((ret = create_radio(&ctx.nl_ctx, args->c_channels, args->c_no_vif, args->c_hwname, args->c_use_chanctx, 131 | args->c_reg_alpha2, 132 | args->c_reg_custom_reg))) { 133 | return ret; 134 | } 135 | return wait_for_event(); 136 | } 137 | 138 | int handleDeleteById(const hwsim_args *args) { 139 | int ret; 140 | if ((ret = prepareCommand())) { 141 | return ret; 142 | }; 143 | printf("Deleting radio with id '%d'...\n", args->del_radio_id); 144 | if ((ret = delete_radio_by_id(&ctx.nl_ctx, args->del_radio_id))) { 145 | return ret; 146 | } 147 | return wait_for_event(); 148 | } 149 | 150 | int handleDeleteByName(const hwsim_args *args) { 151 | int ret; 152 | if ((ret = prepareCommand())) { 153 | return ret; 154 | }; 155 | printf("Deleting radio with name '%s'...\n", args->del_radio_name); 156 | if ((ret = delete_radio_by_name(&ctx.nl_ctx, args->del_radio_name))) { 157 | return ret; 158 | } 159 | return wait_for_event(); 160 | } 161 | 162 | int handleSetRSSI(const hwsim_args *args, char *rssi) { 163 | int ret; 164 | if ((ret = prepareCommand())) { 165 | return ret; 166 | }; 167 | 168 | if ((ret = set_rssi(&ctx.nl_ctx, args->rssi_radio, cli_get_uint32('d', rssi)))) { 169 | return ret; 170 | } 171 | return wait_for_event(); 172 | } 173 | 174 | void notify_device_creation(int id) { 175 | printf("Created device with ID %d\n", id); 176 | exit(EXIT_SUCCESS); 177 | } 178 | 179 | void notify_device_deletion() { 180 | if (ctx.args.mode == HWSIM_OP_DELETE_BY_ID) { 181 | printf("Successfully deleted device with ID %d\n", ctx.args.del_radio_id); 182 | } else { 183 | printf("Successfully deleted device with name '%s'\n", ctx.args.del_radio_name); 184 | } 185 | exit(EXIT_SUCCESS); 186 | } 187 | 188 | void notify_device_setRSSI() { 189 | if (ctx.args.mode == HWSIM_OP_SET_RSSI) { 190 | printf("new RSSI defined to interface %d\n", ctx.args.rssi_radio); 191 | } 192 | exit(EXIT_SUCCESS); 193 | } 194 | 195 | int main(int argc, char **argv) { 196 | hwsim_args args = { 197 | .mode = HWSIM_OP_NONE, 198 | .c_hwname = NULL, 199 | .c_channels = 0, 200 | .c_no_vif = false, 201 | .c_use_chanctx = false, 202 | .c_reg_alpha2 = NULL, 203 | .c_reg_custom_reg = 0, 204 | .del_radio_id = 0, 205 | .del_radio_name = NULL, 206 | .rssi_radio = 0 207 | }; 208 | 209 | ctx.args = args; 210 | struct argp hwsim_argp = {options, hwsim_parse_argp, 0, doc, 0, 0, 0}; 211 | ctx.hwsim_argp = hwsim_argp; 212 | 213 | argp_parse(&hwsim_argp, argc, argv, 0, 0, &ctx.args); 214 | switch (ctx.args.mode) { 215 | case HWSIM_OP_CREATE: 216 | return handleCreate(&ctx.args); 217 | case HWSIM_OP_DELETE_BY_ID: 218 | return handleDeleteById(&ctx.args); 219 | case HWSIM_OP_DELETE_BY_NAME: 220 | return handleDeleteByName(&ctx.args); 221 | case HWSIM_OP_SET_RSSI: 222 | return handleSetRSSI(&ctx.args, argv[3]); 223 | case HWSIM_OP_NONE: 224 | argp_err_and_usage(msg_duplicate_mode); 225 | break; 226 | } 227 | return EXIT_FAILURE; 228 | } 229 | --------------------------------------------------------------------------------