Bug: 231272394 Test: connect/disconnect to WPA2, WPA3 networks Test: SoftAp & p2p connection Test: Regression test(b/231636895) BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source 27e828d72 ACS: Send EHT enabled info to driver 82066bd36 nl80211: Don't force VHT channel definition with EHT 43fe1ce35 EHT: Add [EHT] flag into AP mode STA command 696ad5c2d EHT: Indicate wifi_generation=7 in wpa_supplicant STATUS output 4994c41f2 EHT: Indicate ieee80211be configuration in hostapd STATUS output 50d883710 EHT: Fix invalid length checking for EHT Capability element 6c7b2be42 SAE: Send real status code to the driver when AP rejects external auth 2c78f11a9 Fix compilation due to forward declaration of macaddr_acl c8e822801 OpenSSL: Fix build with old library versions that do not support TLS 1.3 c24e18e5c LibreSSL: Fix compilation issue with TLS 1.3 session ticket limit eb5e63985 LibreSSL: Fix compilation issue with RSA-OAEP 5d56cf1c7 BoringSSL: Fix compilation error due to TLS 1.3 session tickets a561d12d2 EAP peer status notification for server not supporting RFC 5746 566ce69a8 EAP peer: Workaround for servers that do not support safe TLS renegotiation ccb3206b6 Fix tls_connection_set_success_data() in TLS library wrappers decac7cd1 OpenSSL: Do not send out a TLS 1.3 session ticket if caching disabled 05406f7ae EAP-PEAP server: Fix TLS 1.3 move to Phase 2 without a new session ticket 10746875e OpenSSL: Allow no OCSP response when resuming a session with TLS 1.3 2be1bcaf7 EAP-TLS peer: Fix protected success indication check for resumed session 1c66276d9 EAP-TLS server: Send final TLS message for resumed session with TLS 1.3 81e249888 OpenSSL: Limit the number of TLS 1.3 session tickets to one d26247c3d wpa_supplicant/README-WPS: Beautifications a8d058c93 OpenSSL: SSLKEYLOGFILE capability to allow Wireshark TLS decoding 23f389068 wolfSSL: Fix OCSP stapling a2971f8d8 wolfSSL: Allow TLS version 1.3 to be disabled a40e48fbe wolfSSL: Fix TLS 1.3 session handling 0c3f68f2a wolfSSL: Check for the too-short-password error in pbkdf2_sha1() ca2622481 Check the return of pbkdf2_sha1() for errors 013cd694d wolfSSL: Fixes for FIPS builds 9d5f8168f wolfSSL: Register a FIPS callback 8f36e6c0f wolfSSL: Implement crypto_ec_key wrappers 1f7e10177 wolfSSL: Add missing free calls for wolfSSL structs ec1cd91e7 wolfSSL: Support both DER and PEM blobs 42871a5d2 EAP-SIM/AKA peer: IMSI privacy 21098e39f EAP-SIM/AKA server: IMSI privacy 36b11bbcf OpenSSL: RSA-OAEP-SHA-256 encryption/decryption c3d389b72 EHT: Channel switch command support dae7940a4 EHT: Additions to hostapd_set_freq_params() e646b11fe EHT: Indicate EHT support in Neighbor Report element f915d52de EHT: Provide EHT capabilities in STA addition path a6d1b4c46 EHT: Process (Re)Association Request frame capabilities 340c0e212 EHT: Parse elements received in Management frames d54e3d049 EHT: Add operation element in AP mode Management frames 9b7202d66 EHT: Add capabilities element in AP mode Management frames a7ea72188 EHT: Add configuration options for beamforming capabilities 8db3881c7 EHT: Add operating channel width configuration 8dcc2139f EHT: AP mode configuration options to enable/disable the support 9f7da264b nl80211: Pass station's EHT capabilities to the driver in sta_add() 0c8a9aa5d nl80211: Parse EHT capabilities from the driver c08b735fd EHT: Define EHT elements 1a716f86a defconfig: Document IEEE 802.11ax as a published amendment 86310c220 Set hostapd hw_mode automatically based on 6 GHz op_class 664fd83d5 nl80211: Increase the buffer length for debug printing channels 563162a5f QCA vendor attribute to allow eMLSR HW mode 1e34bc49c OpenSSL: Track SSL_SESSION ex data separately 734fa392f MBO: Check association disallowed in Beacon frames, if newer 284e3ad19 Determine whether Beacon frame information is newer in scan results 28c9f29a3 scan: Print SSID in scan results dump 5a0471579 Install wpa_passphrase when not disabled f1686d776 hostapd: Allow enabling background radar 08d7738bb wolfSSL: Speed up crypto_ec_point_compute_y_sqr() f50d5c9a8 wolfSSL: Fix crypto_ec_point_compute_y_sqr() error case processing 7302aa761 wolfSSL: Fix the memory leak of crypto_ec_point_compute_y_sqr() e7dd0fff1 wolfSSL: Use wc_HmacInit() to avoid potential use of uninitialized values f7be558d6 OpenSSL: Fix build with BoringSSL 6d33ef362 OpenSSL: Remove compatibility options for older versions than 1.0.2 78c2a4cd0 OpenSSL: Drop compatibility options for LibreSSL older than 2.7 b06250767 OpenSSL: Implement crypto_ecdh routines without EC_KEY for OpenSSL 3.0 fc96f6802 OpenSSL: Use new name for the EC_POINT set/get coordinate functions 0aae045af ctrl: Print the source address of the received commands f94214968 wpa_ctrl: Wait for a total of 10 seconds, not 10 seconds per iteration 0d9be8855 wolfSSL: Fix certificate commonName checking 94e0f39d9 wolfSSL: Use wolfSSL_export_keying_material() when available c31fc7a64 wolfSSL: Fix crypto_dh_init() and dh5_init() d7b8c6eef wolfSSL: Fix crypto_ecdh_* with ECC_TIMING_RESISTANT ae1fb6455 EAP-EKE server: Fix a memory leak on an error path 166acab4e wolfSSL: TLS session caching 12dee16d7 wolfSSL: Add a debug logging callback a5d190650 wolfSSL: Implement tls_get_tls_unique() a419fef36 wolfSSL: Implement tls_connection_get_cipher_suite() 364876b7d wolfSSL: Implement tls_connection_get_peer_subject() d9c716400 wolfSSL: Implement tls_connection_get_own_cert_used() d677b9dc6 wolfSSL: Conditional build for aes_wrap/aes_unwrap() b0f016b87 eapol_test: Update with src/ap/ieee802_1x.c changes 747c5f228 Include MS_FUNCS=y for EAP-pwd peer build c7f71fb86 Include HMAC-SHA384/512 KDF for SAE if SHA384/512 is included 3a759dcc8 ACS: Honor acs_exclude_dfs with hostapd's ACS implementation 3240cedd6 eapol_test: Print out names for additional known EAP types f5c711c85 OpenSSL: Unload providers only at process exit 33c4dd26c BSS coloring: Handle the collision and CCA events coming from the kernel 27b4cc712 nl80211: Handle driver events for BSS coloring 399d6e64d nl80211: Add the switch_color() handler for BSS color changes 86bd90eb3 BSS coloring: Disable BSS color during CCA f7d0b740e BSS coloring: BSS Color Change Announcement element generation 654d2395d BSS coloring: Handling of collision events and triggering CCA 52e2516f1 wpa_supplicant: Add the CONFIG_HE_OVERRIDES option to the defconfig 6a2a60f1d OpenSSL: Do not use the deprecated RSAPrivateKey function ebb3055e1 OpenSSL: Generate DH parameters automatically if not set with dh_file bcd299b32 OpenSSL: Convert DH/DSA parameter loading to new API 28c1c91d0 Remove unused dh_blob parameter 4a774cf31 Remove useless DH file configuration from TLS library wrappers 65652c67f Remove DH file configuration from TLS client functionality b94371af8 RADIUS attributes for EAPOL-Key message details 24763e3cd RADIUS: Attributes with Extended Types (RFC 6929) feed2f9e7 BoringSSL: Use accessor functions for X509 key usage flags 80be88a08 BoringSSL: Replace stack-allocated X509_STORE_CTX with heap one b95ed17f6 OpenSSL: Fix build with BoringSSL and LibreSSL 3.3.x and older ae0f6ee97 OpenSSL: CMAC using the OpenSSL library for non-FIPS cases as well 0c61f6234 OpenSSL: Implement CMAC using the EVP_MAC API 4fcd29660 OpenSSL: Extend CMAC to support 192-bit AES 117617843 OpenSSL: Remove now unused compatibility wrapper for RSA_bits() a2dbb2558 Android: Compile hs20-osu-client to /vendor/bin in test builds b0769ce61 DPP: Allow a list of supported curves to be used in bootstrapping URI ef85328a6 QCA vendor command support to reset configuration for eLNA bypass 7008c50fa OpenSSL: Implement DH using the EVP API e31500ade OpenSSL: Implement HMAC using the EVP_MAC API 097ca6bf0 OpenSSL: Unload providers on deinit 092efd45a OpenSSL: Implement AES keywrap using the EVP API 7e4984d9c OpenSSL: Use a correct EVP_CIPHER_CTX freeing function on an error path 8e0ac5366 RRM: Include passive channels in active beacon report scan 0adc67612 wpa_supplicant: Use unique IDs for networks and credentials dacb6d278 Update IEEE P802.11ax draft references to published amendment 8128ea76a Add Transmit Power Envelope element in 6 GHz bc3dc72a3 Extend 6 GHz Operation Info field in HE Operation element 0eb686637 hostapd: Add config option to specify 6 GHz regulatory AP type ee06165e9 hostapd: Extend Country element to support 6 GHz band f5ad97245 PASN: Fix build without CONFIG_TESTING_OPTIONS=y 3467a701c wpa_supplicant: Do not associate on 6 GHz with forbidden configurations 43c6eb5e4 SAE-PK: Add the option to the defconfigs 0482251a6 EAP-TLS: Allow TLSv1.3 support to be enabled with build config 7114e5606 EAP-TLS: Testing functionality to skip protected success indication 95fd54b86 Disconnect STA on continuous EAP reauth without 4-way handshake completion 9e11e746f EAP-TLS: Do not allow TLSv1.3 success without protected result indication 6135a8a6a Stop authentication attemps if AP does not disconnect us 88ab59d71 EAP-TLS: Replace the Commitment Message term with RFC 9190 language 63f311b10 EAP-TLS: Update specification references to RFC 5216 and 9190 5ab385321 Revert "Android: Compile hs20-osu-client to /vendor/bin in test builds" b746cb28b Add support for not transmitting EAPOL-Key group msg 2/2 d27f7bd94 FILS: Fix config check to allow unsolicited broadcast Probe Response 65a3a273c OWE: Reuse own DH private key in AP if STA tries OWE association again 6ff8bda99 hostapd: Add the missing CONFIG_SAE option to the defconfig 1f5b6085c Fix SIGSEGV of eapol_test 576662d27 ieee802_11_auth: Coding style cleanup - NULL comparison 945acf3ef ieee802_11_auth: Coding style cleanup - no string constant splitting 1c3438fec RADIUS ACL/PSK check during 4-way handshake 5b5c954c0 Fix AP config check to recognize all PSK AKMs c5d9f9064 QCA vendor attribute to indicate NDP interface managemtn using nl80211 a9c90475b FT: Update current_bss to target AP before check for SME-in-driver 0c88d1487 Debug print on CONFIG_NO_TKIP=y prevent RSNE with TKIP as group cipher d5a9331f9 P2P: Copy only valid opclasses while filtering out 6 GHz channels 99c91beaa Sync with wireless-next.git include/uapi/linux/nl80211.h d9121335a wpa_cli: Add ACL and BTM control commands 00622fcfe Extend ACL to install allow/deny list to the driver dynamically 077bce96f Set drv_max_acl_mac_addrs in wpa_supplicant AP mode 9828aba16 Support ACL operations in wpa_supplicant AP mode fd0d738ff Add return value to ACL functions f5ac42811 Move ACL control interface commands into shared files 930695662 Add BSS-TM-QUERY event to indicate reception of BSS TM Query febcdf324 Support BTM operations in wpa_supplicant AP mode 0f8c6e995 Move BTM control interface commands into shared file e059d8ece Update the Extended Capability element to struct sta_info eb2e6b56b Enable BSS Transition Management in wpa_supplicant AP mode 30ecf0181 DPP: Update Controller parameters when it was already started b93d1083e DPP: Fix msg_ctx for PKEX over TCP as Controller/Responder 3085e1a67 hs20-osu-client: dNSName values from OSU server certificate for PPS MO ce86f2446 DFS: Remove unnecessary variable 760a5ae26 DFS: Switch to background radar channel if available b63d953fe DFS: Enable CSA for background radar detection 25663241c DFS: Introduce hostapd_dfs_request_channel_switch() 316a9dc63 DFS: Configure background radar/CAC detection bad12effe nl80211: Radar background flag setting effd6111b DFS: Rely on channel_type in dfs_downgrade_bandwidth() f9ba3d5c8 OpenSSL 3.0: Set SSL groups using SSL_set1_groups() 09c62aaf1 OpenSSL: Determine RSA key size without low-level routines b700a56e1 OpenSSL 3.0: Determine the prime length for an EC key group using EVP_PKEY 3c61f4db4 OpenSSL: Replace EC_GROUP_get_curve_GFp() calls with EC_GROUP_get_curve() e2cb0ca1a OpenSSL 3.0: Implement crypto_ec_key_group() with new API f6a53f64a OpenSSL: Replace EVP_PKEY_cmp() with EVP_PKEY_eq() when available 5b093570d D-Bus: Add 'wep_disabled' capability 56a14cc72 DFS: Don't let cac_time_left_seconds overflow ae512c30a DPP: Fix uninitialised variable on error path 3a157fe92 dbus: Set CurrentAuthMode to INACTIVE only if network is not selected 0ce8d55a2 hs20-osu-client: Allow EST server to use different host name 5eaf596e1 HTTP: Make URL available to the cert_cb abed7978f HS 2.0 server: Event log entry on missing configuration for the realm 1192d5721 Android: Compile hs20-osu-client to /vendor/bin in test builds 1fee1c40c Enhance QCA vendor interface to indicate TWT required capability of AP a192305a4 Add QCA vendor attributes for AFC support in external ACS de5939ef5 DPP: Allow Configurator net_access_key_curve to be changed 9638452a6 DPP: Update Configurator to require same netAccessKey curve to be used 2b406eece DPP: Update Auth-I derivation operations de64dfe98 DPP: Curve change for netAccessKey fd2eb7a41 DPP: Fix a memory leak on error path e9551efe0 DPP: Missing/invalid Protocol Version in Reconfig Auth Req eeb72e7c9 DPP: Extend DPP_PKEX_ADD ver=<1/2> to cover Responder role 6c3c431bb Add QCA vendor attribute to enable Spectral FFT recapture fcbdaae8a SAE: Add support for RADIUS passphrase as the SAE password 3d86fcee0 cleanup: Remove unreachable code 9683195ee qca-vendor: Fix typos 4c9ef9322 brcm_vendor: Fix typos d65285ab8 src/drivers: Fix typos 203a027b2 nl80211: Report background radar/CAC detection capability 0a73649b6 DFS: Add capability to select radar-only channels f39765369 DFS: Introduce dfs_set_valid_channel() utility routine d001b301b Fix removal of wpa_passphrase on 'make clean' cb41c214b build: Re-enable options for libwpa_client.so and wpa_passphrase dec626109 HE: Fix invalid length checking for HE Capability element 53be64f7d HE: Fix calculation of the PPE Threshold field length 738fef2f0 Clear PSK explicitly from memory in couple more cases on deinit 567b9764f Clear PMK explicitly even without FT support in AP build 0bd29c176 Remove duplicated pointer check 007fd6111 Clear temporary results from stack in PBKDF2-SHA1 1364f322b Remove GTK/IGTK/BIGTK from memory explicitly in AP mode af1f0694e Clear last set keys (for testing purposes) from memory explicitly 6c850a1c0 nl80211: Clear bss->freq when stopping AP mode a44fa15cb Define a vendor specific NDP attribute for NAN service id 414ca953f DPP: Clear SCANNING state when starting network introduction 0b5f8e3d8 DPP: Clear netrole on starting chirping or reconfiguration 2fcc076d1 Clear wpa_s->last/current_ssid in more cases 7a7f803a9 DPP: Stop offchannel frame TX wait on DPP_STOP_LISTEN in a corner case 7e941e7a1 macsec_linux: Support cipher suite configuration 46c635910 MACsec: Support GCM-AES-256 cipher suite 42944de69 nl80211: Do not store no-wait TX frame cookies to be cancelled 340ec48cd DPP: Clear state on configuration failure in GAS server hander 7e6f59c70 nl80211: Clear the last saved TX frame cookie on wait expiration 9d5fd3328 Update QCA vendor attribute to indicate maximum PCL attributes 19169a53a atheros: Do not include p2p.h f43d31dda nl80211: Debug print association comeback event data a91072503 OCV: Don't start SA Query timer on CSA when SA Query is offloaded f5c8697c0 Sync with mac80211-next.git include/uapi/linux/nl80211.h 632a9995c Clear ignore_old_scan_res on FLUSH command Change-Id: I35fd1fb999d045ced8c153fe3d8284c9a71069b1
2198 lines
52 KiB
C
2198 lines
52 KiB
C
/*
|
|
* hostapd - command line interface for hostapd daemon
|
|
* Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include <dirent.h>
|
|
|
|
#include "common/wpa_ctrl.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "utils/common.h"
|
|
#include "utils/eloop.h"
|
|
#include "utils/edit.h"
|
|
#include "common/version.h"
|
|
#include "common/cli.h"
|
|
|
|
#ifndef CONFIG_NO_CTRL_IFACE
|
|
|
|
static const char *const hostapd_cli_version =
|
|
"hostapd_cli v" VERSION_STR "\n"
|
|
"Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi> and contributors";
|
|
|
|
static struct wpa_ctrl *ctrl_conn;
|
|
static int hostapd_cli_quit = 0;
|
|
static int hostapd_cli_attached = 0;
|
|
|
|
#ifndef CONFIG_CTRL_IFACE_DIR
|
|
#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
|
|
#endif /* CONFIG_CTRL_IFACE_DIR */
|
|
static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
|
|
static const char *client_socket_dir = NULL;
|
|
|
|
static char *ctrl_ifname = NULL;
|
|
static const char *pid_file = NULL;
|
|
static const char *action_file = NULL;
|
|
static int ping_interval = 5;
|
|
static int interactive = 0;
|
|
static int event_handler_registered = 0;
|
|
|
|
static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
|
|
|
|
static void print_help(FILE *stream, const char *cmd);
|
|
static char ** list_cmd_list(void);
|
|
static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
|
|
static void update_stations(struct wpa_ctrl *ctrl);
|
|
static void cli_event(const char *str);
|
|
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr, "%s\n", hostapd_cli_version);
|
|
fprintf(stderr,
|
|
"\n"
|
|
"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
|
|
"[-a<path>] \\\n"
|
|
" [-P<pid file>] [-G<ping interval>] [command..]\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -h help (show this usage text)\n"
|
|
" -v shown version information\n"
|
|
" -p<path> path to find control sockets (default: "
|
|
"/var/run/hostapd)\n"
|
|
" -s<dir_path> dir path to open client sockets (default: "
|
|
CONFIG_CTRL_IFACE_DIR ")\n"
|
|
" -a<file> run in daemon mode executing the action file "
|
|
"based on events\n"
|
|
" from hostapd\n"
|
|
" -r try to reconnect when client socket is "
|
|
"disconnected.\n"
|
|
" This is useful only when used with -a.\n"
|
|
" -B run a daemon in the background\n"
|
|
" -i<ifname> Interface to listen on (default: first "
|
|
"interface found in the\n"
|
|
" socket path)\n\n");
|
|
print_help(stderr, NULL);
|
|
}
|
|
|
|
|
|
static void register_event_handler(struct wpa_ctrl *ctrl)
|
|
{
|
|
if (!ctrl_conn)
|
|
return;
|
|
if (interactive) {
|
|
event_handler_registered =
|
|
!eloop_register_read_sock(wpa_ctrl_get_fd(ctrl),
|
|
hostapd_cli_receive,
|
|
NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void unregister_event_handler(struct wpa_ctrl *ctrl)
|
|
{
|
|
if (!ctrl_conn)
|
|
return;
|
|
if (interactive && event_handler_registered) {
|
|
eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl));
|
|
event_handler_registered = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
|
|
{
|
|
#ifndef CONFIG_CTRL_IFACE_UDP
|
|
char *cfile;
|
|
int flen;
|
|
#endif /* !CONFIG_CTRL_IFACE_UDP */
|
|
|
|
if (ifname == NULL)
|
|
return NULL;
|
|
|
|
#ifdef CONFIG_CTRL_IFACE_UDP
|
|
ctrl_conn = wpa_ctrl_open(ifname);
|
|
return ctrl_conn;
|
|
#else /* CONFIG_CTRL_IFACE_UDP */
|
|
flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
|
|
cfile = malloc(flen);
|
|
if (cfile == NULL)
|
|
return NULL;
|
|
snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
|
|
|
|
if (client_socket_dir && client_socket_dir[0] &&
|
|
access(client_socket_dir, F_OK) < 0) {
|
|
perror(client_socket_dir);
|
|
free(cfile);
|
|
return NULL;
|
|
}
|
|
|
|
ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
|
|
free(cfile);
|
|
return ctrl_conn;
|
|
#endif /* CONFIG_CTRL_IFACE_UDP */
|
|
}
|
|
|
|
|
|
static void hostapd_cli_close_connection(void)
|
|
{
|
|
if (ctrl_conn == NULL)
|
|
return;
|
|
|
|
unregister_event_handler(ctrl_conn);
|
|
if (hostapd_cli_attached) {
|
|
wpa_ctrl_detach(ctrl_conn);
|
|
hostapd_cli_attached = 0;
|
|
}
|
|
wpa_ctrl_close(ctrl_conn);
|
|
ctrl_conn = NULL;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_reconnect(const char *ifname)
|
|
{
|
|
char *next_ctrl_ifname;
|
|
|
|
hostapd_cli_close_connection();
|
|
|
|
if (!ifname)
|
|
return -1;
|
|
|
|
next_ctrl_ifname = os_strdup(ifname);
|
|
os_free(ctrl_ifname);
|
|
ctrl_ifname = next_ctrl_ifname;
|
|
if (!ctrl_ifname)
|
|
return -1;
|
|
|
|
ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
|
|
if (!ctrl_conn)
|
|
return -1;
|
|
if (!interactive && !action_file)
|
|
return 0;
|
|
if (wpa_ctrl_attach(ctrl_conn) == 0) {
|
|
hostapd_cli_attached = 1;
|
|
register_event_handler(ctrl_conn);
|
|
update_stations(ctrl_conn);
|
|
} else {
|
|
printf("Warning: Failed to attach to hostapd.\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void hostapd_cli_msg_cb(char *msg, size_t len)
|
|
{
|
|
cli_event(msg);
|
|
printf("%s\n", msg);
|
|
}
|
|
|
|
|
|
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
|
|
{
|
|
char buf[4096];
|
|
size_t len;
|
|
int ret;
|
|
|
|
if (ctrl_conn == NULL) {
|
|
printf("Not connected to hostapd - command dropped.\n");
|
|
return -1;
|
|
}
|
|
len = sizeof(buf) - 1;
|
|
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
|
|
hostapd_cli_msg_cb);
|
|
if (ret == -2) {
|
|
printf("'%s' command timed out.\n", cmd);
|
|
return -2;
|
|
} else if (ret < 0) {
|
|
printf("'%s' command failed.\n", cmd);
|
|
return -1;
|
|
}
|
|
if (print) {
|
|
buf[len] = '\0';
|
|
printf("%s", buf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
|
|
{
|
|
return _wpa_ctrl_command(ctrl, cmd, 1);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd,
|
|
int min_args, int argc, char *argv[])
|
|
{
|
|
char buf[4096];
|
|
|
|
if (argc < min_args) {
|
|
printf("Invalid %s command - at least %d argument%s required.\n",
|
|
cmd, min_args, min_args > 1 ? "s are" : " is");
|
|
return -1;
|
|
}
|
|
if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "PING");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "RELOG");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
|
|
return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
|
|
return wpa_ctrl_command(ctrl, "STATUS");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
if (argc > 0) {
|
|
char buf[100];
|
|
os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
return wpa_ctrl_command(ctrl, "MIB");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_exec(const char *program, const char *arg1,
|
|
const char *arg2)
|
|
{
|
|
char *arg;
|
|
size_t len;
|
|
int res;
|
|
|
|
len = os_strlen(arg1) + os_strlen(arg2) + 2;
|
|
arg = os_malloc(len);
|
|
if (arg == NULL)
|
|
return -1;
|
|
os_snprintf(arg, len, "%s %s", arg1, arg2);
|
|
res = os_exec(program, arg, 1);
|
|
os_free(arg);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static void hostapd_cli_action_process(char *msg, size_t len)
|
|
{
|
|
const char *pos;
|
|
|
|
pos = msg;
|
|
if (*pos == '<') {
|
|
pos = os_strchr(pos, '>');
|
|
if (pos)
|
|
pos++;
|
|
else
|
|
pos = msg;
|
|
}
|
|
|
|
hostapd_cli_exec(action_file, ctrl_ifname, pos);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc < 1) {
|
|
printf("Invalid 'sta' command - at least one argument, STA "
|
|
"address, is required.\n");
|
|
return -1;
|
|
}
|
|
if (argc > 1)
|
|
snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
|
|
else
|
|
snprintf(buf, sizeof(buf), "STA %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static char ** hostapd_complete_stations(const char *str, int pos)
|
|
{
|
|
int arg = get_cmd_arg_num(str, pos);
|
|
char **res = NULL;
|
|
|
|
switch (arg) {
|
|
case 1:
|
|
res = cli_txt_list_array(&stations);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc != 1) {
|
|
printf("Invalid 'new_sta' command - exactly one argument, STA "
|
|
"address, is required.\n");
|
|
return -1;
|
|
}
|
|
snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc < 1) {
|
|
printf("Invalid 'deauthenticate' command - exactly one "
|
|
"argument, STA address, is required.\n");
|
|
return -1;
|
|
}
|
|
if (argc > 1)
|
|
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
|
|
argv[0], argv[1]);
|
|
else
|
|
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc < 1) {
|
|
printf("Invalid 'disassociate' command - exactly one "
|
|
"argument, STA address, is required.\n");
|
|
return -1;
|
|
}
|
|
if (argc > 1)
|
|
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
|
|
argv[0], argv[1]);
|
|
else
|
|
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_TAXONOMY
|
|
static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n");
|
|
return -1;
|
|
}
|
|
os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
#endif /* CONFIG_TAXONOMY */
|
|
|
|
|
|
static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc != 1) {
|
|
printf("Invalid 'sa_query' command - exactly one argument, "
|
|
"STA address, is required.\n");
|
|
return -1;
|
|
}
|
|
snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_WPS
|
|
static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[256];
|
|
if (argc < 2) {
|
|
printf("Invalid 'wps_pin' command - at least two arguments, "
|
|
"UUID and PIN, are required.\n");
|
|
return -1;
|
|
}
|
|
if (argc > 3)
|
|
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
|
|
argv[0], argv[1], argv[2], argv[3]);
|
|
else if (argc > 2)
|
|
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
|
|
argv[0], argv[1], argv[2]);
|
|
else
|
|
snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
|
|
if (argc != 1 && argc != 2) {
|
|
printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
|
|
"- PIN to be verified\n");
|
|
return -1;
|
|
}
|
|
|
|
if (argc == 2)
|
|
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
|
|
argv[0], argv[1]);
|
|
else
|
|
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
|
|
argv[0]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long WPS_CHECK_PIN command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "WPS_PBC");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "WPS_CANCEL");
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_WPS_NFC
|
|
static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
int ret;
|
|
char *buf;
|
|
size_t buflen;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'wps_nfc_tag_read' command - one argument "
|
|
"is required.\n");
|
|
return -1;
|
|
}
|
|
|
|
buflen = 18 + os_strlen(argv[0]);
|
|
buf = os_malloc(buflen);
|
|
if (buf == NULL)
|
|
return -1;
|
|
os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
|
|
|
|
ret = wpa_ctrl_command(ctrl, buf);
|
|
os_free(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char cmd[64];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'wps_nfc_config_token' command - one argument "
|
|
"is required.\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
|
|
argv[0]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char cmd[64];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'wps_nfc_token' command - one argument is "
|
|
"required.\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long WPS_NFC_TOKEN command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char cmd[64];
|
|
int res;
|
|
|
|
if (argc != 2) {
|
|
printf("Invalid 'nfc_get_handover_sel' command - two arguments "
|
|
"are required.\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
|
|
argv[0], argv[1]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long NFC_GET_HANDOVER_SEL command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
#endif /* CONFIG_WPS_NFC */
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[64];
|
|
if (argc < 1) {
|
|
printf("Invalid 'wps_ap_pin' command - at least one argument "
|
|
"is required.\n");
|
|
return -1;
|
|
}
|
|
if (argc > 2)
|
|
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
|
|
argv[0], argv[1], argv[2]);
|
|
else if (argc > 1)
|
|
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
|
|
argv[0], argv[1]);
|
|
else
|
|
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[256];
|
|
char ssid_hex[2 * SSID_MAX_LEN + 1];
|
|
char key_hex[2 * 64 + 1];
|
|
int i;
|
|
|
|
if (argc < 1) {
|
|
printf("Invalid 'wps_config' command - at least two arguments "
|
|
"are required.\n");
|
|
return -1;
|
|
}
|
|
|
|
ssid_hex[0] = '\0';
|
|
for (i = 0; i < SSID_MAX_LEN; i++) {
|
|
if (argv[0][i] == '\0')
|
|
break;
|
|
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
|
|
}
|
|
|
|
key_hex[0] = '\0';
|
|
if (argc > 3) {
|
|
for (i = 0; i < 64; i++) {
|
|
if (argv[3][i] == '\0')
|
|
break;
|
|
os_snprintf(&key_hex[i * 2], 3, "%02x",
|
|
argv[3][i]);
|
|
}
|
|
}
|
|
|
|
if (argc > 3)
|
|
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
|
|
ssid_hex, argv[1], argv[2], key_hex);
|
|
else if (argc > 2)
|
|
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
|
|
ssid_hex, argv[1], argv[2]);
|
|
else
|
|
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
|
|
ssid_hex, argv[1]);
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
#endif /* CONFIG_WPS */
|
|
|
|
|
|
static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[300];
|
|
int res;
|
|
|
|
if (argc < 2) {
|
|
printf("Invalid 'disassoc_imminent' command - two arguments "
|
|
"(STA addr and Disassociation Timer) are needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
|
|
argv[0], argv[1]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[300];
|
|
int res;
|
|
|
|
if (argc < 3) {
|
|
printf("Invalid 'ess_disassoc' command - three arguments (STA "
|
|
"addr, disassoc timer, and URL) are needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
|
|
argv[0], argv[1], argv[2]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[2000], *tmp;
|
|
int res, i, total;
|
|
|
|
if (argc < 1) {
|
|
printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
|
|
total = res;
|
|
for (i = 1; i < argc; i++) {
|
|
tmp = &buf[total];
|
|
res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
|
|
if (os_snprintf_error(sizeof(buf) - total, res))
|
|
return -1;
|
|
total += res;
|
|
}
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "GET_CONFIG");
|
|
}
|
|
|
|
|
|
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
|
|
char *addr, size_t addr_len, int print)
|
|
{
|
|
char buf[4096], *pos;
|
|
size_t len;
|
|
int ret;
|
|
|
|
if (ctrl_conn == NULL) {
|
|
printf("Not connected to hostapd - command dropped.\n");
|
|
return -1;
|
|
}
|
|
len = sizeof(buf) - 1;
|
|
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
|
|
hostapd_cli_msg_cb);
|
|
if (ret == -2) {
|
|
printf("'%s' command timed out.\n", cmd);
|
|
return -2;
|
|
} else if (ret < 0) {
|
|
printf("'%s' command failed.\n", cmd);
|
|
return -1;
|
|
}
|
|
|
|
buf[len] = '\0';
|
|
if (memcmp(buf, "FAIL", 4) == 0)
|
|
return -1;
|
|
if (print)
|
|
printf("%s", buf);
|
|
|
|
pos = buf;
|
|
while (*pos != '\0' && *pos != '\n')
|
|
pos++;
|
|
*pos = '\0';
|
|
os_strlcpy(addr, buf, addr_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char addr[32], cmd[64];
|
|
|
|
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
|
|
return 0;
|
|
do {
|
|
snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
|
|
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char addr[32], cmd[64];
|
|
|
|
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
|
|
return 0;
|
|
do {
|
|
if (os_strcmp(addr, "") != 0)
|
|
printf("%s\n", addr);
|
|
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
|
|
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
print_help(stdout, argc > 0 ? argv[0] : NULL);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char ** hostapd_cli_complete_help(const char *str, int pos)
|
|
{
|
|
int arg = get_cmd_arg_num(str, pos);
|
|
char **res = NULL;
|
|
|
|
switch (arg) {
|
|
case 1:
|
|
res = list_cmd_list();
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char buf[200];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'set_qos_map_set' command - "
|
|
"one argument (comma delimited QoS map set) "
|
|
"is needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char buf[50];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid 'send_qos_map_conf' command - "
|
|
"one argument (STA addr) is needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[300];
|
|
int res;
|
|
|
|
if (argc < 2) {
|
|
printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
|
|
"addr and URL) are needed\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
|
|
argv[0], argv[1]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char buf[300];
|
|
int res;
|
|
|
|
if (argc < 3) {
|
|
printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (argc > 3)
|
|
res = os_snprintf(buf, sizeof(buf),
|
|
"HS20_DEAUTH_REQ %s %s %s %s",
|
|
argv[0], argv[1], argv[2], argv[3]);
|
|
else
|
|
res = os_snprintf(buf, sizeof(buf),
|
|
"HS20_DEAUTH_REQ %s %s %s",
|
|
argv[0], argv[1], argv[2]);
|
|
if (os_snprintf_error(sizeof(buf), res))
|
|
return -1;
|
|
return wpa_ctrl_command(ctrl, buf);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
hostapd_cli_quit = 1;
|
|
if (interactive)
|
|
eloop_terminate();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char cmd[256];
|
|
if (argc != 1) {
|
|
printf("Invalid LEVEL command: needs one argument (debug "
|
|
"level)\n");
|
|
return 0;
|
|
}
|
|
snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static void update_stations(struct wpa_ctrl *ctrl)
|
|
{
|
|
char addr[32], cmd[64];
|
|
|
|
if (!ctrl || !interactive)
|
|
return;
|
|
|
|
cli_txt_list_flush(&stations);
|
|
|
|
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
|
|
return;
|
|
do {
|
|
if (os_strcmp(addr, "") != 0)
|
|
cli_txt_list_add(&stations, addr);
|
|
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
|
|
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
|
|
struct dl_list *interfaces)
|
|
{
|
|
struct dirent *dent;
|
|
DIR *dir;
|
|
|
|
if (!ctrl || !interfaces)
|
|
return;
|
|
dir = opendir(ctrl_iface_dir);
|
|
if (dir == NULL)
|
|
return;
|
|
|
|
while ((dent = readdir(dir))) {
|
|
if (strcmp(dent->d_name, ".") == 0 ||
|
|
strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
cli_txt_list_add(interfaces, dent->d_name);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
|
|
{
|
|
struct dirent *dent;
|
|
DIR *dir;
|
|
|
|
dir = opendir(ctrl_iface_dir);
|
|
if (dir == NULL) {
|
|
printf("Control interface directory '%s' could not be "
|
|
"opened.\n", ctrl_iface_dir);
|
|
return;
|
|
}
|
|
|
|
printf("Available interfaces:\n");
|
|
while ((dent = readdir(dir))) {
|
|
if (strcmp(dent->d_name, ".") == 0 ||
|
|
strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
printf("%s\n", dent->d_name);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
if (argc < 1) {
|
|
hostapd_cli_list_interfaces(ctrl);
|
|
return 0;
|
|
}
|
|
if (hostapd_cli_reconnect(argv[0]) != 0) {
|
|
printf("Could not connect to interface '%s' - re-trying\n",
|
|
ctrl_ifname);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char ** hostapd_complete_interface(const char *str, int pos)
|
|
{
|
|
int arg = get_cmd_arg_num(str, pos);
|
|
char **res = NULL;
|
|
DEFINE_DL_LIST(interfaces);
|
|
|
|
switch (arg) {
|
|
case 1:
|
|
hostapd_cli_get_interfaces(ctrl_conn, &interfaces);
|
|
res = cli_txt_list_array(&interfaces);
|
|
cli_txt_list_flush(&interfaces);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char cmd[2048];
|
|
int res;
|
|
|
|
if (argc != 2) {
|
|
printf("Invalid SET command: needs two arguments (variable "
|
|
"name and value)\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long SET command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static char ** hostapd_complete_set(const char *str, int pos)
|
|
{
|
|
int arg = get_cmd_arg_num(str, pos);
|
|
const char *fields[] = {
|
|
#ifdef CONFIG_WPS_TESTING
|
|
"wps_version_number", "wps_testing_stub_cred",
|
|
"wps_corrupt_pkhash",
|
|
#endif /* CONFIG_WPS_TESTING */
|
|
#ifdef CONFIG_INTERWORKING
|
|
"gas_frag_limit",
|
|
#endif /* CONFIG_INTERWORKING */
|
|
#ifdef CONFIG_TESTING_OPTIONS
|
|
"ext_mgmt_frame_handling", "ext_eapol_frame_io",
|
|
#endif /* CONFIG_TESTING_OPTIONS */
|
|
#ifdef CONFIG_MBO
|
|
"mbo_assoc_disallow",
|
|
#endif /* CONFIG_MBO */
|
|
"deny_mac_file", "accept_mac_file",
|
|
};
|
|
int i, num_fields = ARRAY_SIZE(fields);
|
|
|
|
if (arg == 1) {
|
|
char **res;
|
|
|
|
res = os_calloc(num_fields + 1, sizeof(char *));
|
|
if (!res)
|
|
return NULL;
|
|
for (i = 0; i < num_fields; i++) {
|
|
res[i] = os_strdup(fields[i]);
|
|
if (!res[i])
|
|
return res;
|
|
}
|
|
return res;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid GET command: needs one argument (variable "
|
|
"name)\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long GET command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static char ** hostapd_complete_get(const char *str, int pos)
|
|
{
|
|
int arg = get_cmd_arg_num(str, pos);
|
|
const char *fields[] = {
|
|
"version", "tls_library",
|
|
};
|
|
int i, num_fields = ARRAY_SIZE(fields);
|
|
|
|
if (arg == 1) {
|
|
char **res;
|
|
|
|
res = os_calloc(num_fields + 1, sizeof(char *));
|
|
if (!res)
|
|
return NULL;
|
|
for (i = 0; i < num_fields; i++) {
|
|
res[i] = os_strdup(fields[i]);
|
|
if (!res[i])
|
|
return res;
|
|
}
|
|
return res;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_FST
|
|
static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
int i;
|
|
int total;
|
|
|
|
if (argc <= 0) {
|
|
printf("FST command: parameters are required.\n");
|
|
return -1;
|
|
}
|
|
|
|
total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
|
|
argv[i]);
|
|
if (os_snprintf_error(sizeof(cmd) - total, res)) {
|
|
printf("Too long fst command.\n");
|
|
return -1;
|
|
}
|
|
total += res;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
#endif /* CONFIG_FST */
|
|
|
|
|
|
static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
int i;
|
|
char *tmp;
|
|
int total;
|
|
|
|
if (argc < 2) {
|
|
printf("Invalid chan_switch command: needs at least two "
|
|
"arguments (count and freq)\n"
|
|
"usage: <cs_count> <freq> [sec_channel_offset=] "
|
|
"[center_freq1=] [center_freq2=] [bandwidth=] "
|
|
"[blocktx] [ht|vht|he|eht]\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
|
|
argv[0], argv[1]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long CHAN_SWITCH command.\n");
|
|
return -1;
|
|
}
|
|
|
|
total = res;
|
|
for (i = 2; i < argc; i++) {
|
|
tmp = cmd + total;
|
|
res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
|
|
if (os_snprintf_error(sizeof(cmd) - total, res)) {
|
|
printf("Too long CHAN_SWITCH command.\n");
|
|
return -1;
|
|
}
|
|
total += res;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "ENABLE");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "RELOAD");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "DISABLE");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
|
|
if (argc < 2 || argc > 4) {
|
|
printf("Invalid vendor command\n"
|
|
"usage: <vendor id> <command id> [<hex formatted command argument>] [nested=<0|1>]\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
|
|
argv[1], argc >= 3 ? argv[2] : "",
|
|
argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long VENDOR command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "ERP_FLUSH");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
|
|
argc >= 1 ? " " : "",
|
|
argc >= 1 ? argv[0] : "",
|
|
argc == 2 ? " " : "",
|
|
argc == 2 ? argv[1] : "");
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long option\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
if (argc == 0)
|
|
return -1;
|
|
return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "PMKSA");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char cmd[2048];
|
|
int res;
|
|
|
|
if (argc < 3 || argc > 6) {
|
|
printf("Invalid set_neighbor command: needs 3-6 arguments\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
|
|
argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
|
|
argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long SET_NEIGHBOR command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_show_neighbor(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "SHOW_NEIGHBOR");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "REMOVE_NEIGHBOR", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
char cmd[256];
|
|
int res;
|
|
|
|
if (argc != 1) {
|
|
printf("Invalid req_lci command - requires destination address\n");
|
|
return -1;
|
|
}
|
|
|
|
res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]);
|
|
if (os_snprintf_error(sizeof(cmd), res)) {
|
|
printf("Too long REQ_LCI command.\n");
|
|
return -1;
|
|
}
|
|
return wpa_ctrl_command(ctrl, cmd);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
if (argc < 4) {
|
|
printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n");
|
|
return -1;
|
|
}
|
|
|
|
return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_DPP
|
|
|
|
static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl,
|
|
int argc, char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_DPP2
|
|
|
|
static int hostapd_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_controller_stop(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "DPP_CONTROLLER_STOP");
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
|
|
}
|
|
|
|
#endif /* CONFIG_DPP2 */
|
|
#endif /* CONFIG_DPP */
|
|
|
|
|
|
static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv);
|
|
}
|
|
|
|
|
|
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
|
|
char *argv[])
|
|
{
|
|
return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK");
|
|
}
|
|
|
|
|
|
#ifdef ANDROID
|
|
static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
return hostapd_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
|
|
}
|
|
#endif /* ANDROID */
|
|
|
|
|
|
struct hostapd_cli_cmd {
|
|
const char *cmd;
|
|
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
|
|
char ** (*completion)(const char *str, int pos);
|
|
const char *usage;
|
|
};
|
|
|
|
static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
|
|
{ "ping", hostapd_cli_cmd_ping, NULL,
|
|
"= pings hostapd" },
|
|
{ "mib", hostapd_cli_cmd_mib, NULL,
|
|
"= get MIB variables (dot1x, dot11, radius)" },
|
|
{ "relog", hostapd_cli_cmd_relog, NULL,
|
|
"= reload/truncate debug log output file" },
|
|
{ "status", hostapd_cli_cmd_status, NULL,
|
|
"= show interface status info" },
|
|
{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
|
|
"<addr> = get MIB variables for one station" },
|
|
{ "all_sta", hostapd_cli_cmd_all_sta, NULL,
|
|
"= get MIB variables for all stations" },
|
|
{ "list_sta", hostapd_cli_cmd_list_sta, NULL,
|
|
"= list all stations" },
|
|
{ "new_sta", hostapd_cli_cmd_new_sta, NULL,
|
|
"<addr> = add a new station" },
|
|
{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
|
|
hostapd_complete_stations,
|
|
"<addr> = deauthenticate a station" },
|
|
{ "disassociate", hostapd_cli_cmd_disassociate,
|
|
hostapd_complete_stations,
|
|
"<addr> = disassociate a station" },
|
|
#ifdef CONFIG_TAXONOMY
|
|
{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
|
|
"<addr> = get taxonomy signature for a station" },
|
|
#endif /* CONFIG_TAXONOMY */
|
|
{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
|
|
"<addr> = send SA Query to a station" },
|
|
#ifdef CONFIG_WPS
|
|
{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
|
|
"<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
|
|
{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
|
|
"<PIN> = verify PIN checksum" },
|
|
{ "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL,
|
|
"= indicate button pushed to initiate PBC" },
|
|
{ "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL,
|
|
"= cancel the pending WPS operation" },
|
|
#ifdef CONFIG_WPS_NFC
|
|
{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL,
|
|
"<hexdump> = report read NFC tag with WPS data" },
|
|
{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL,
|
|
"<WPS/NDEF> = build NFC configuration token" },
|
|
{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL,
|
|
"<WPS/NDEF/enable/disable> = manager NFC password token" },
|
|
{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL,
|
|
NULL },
|
|
#endif /* CONFIG_WPS_NFC */
|
|
{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL,
|
|
"<cmd> [params..] = enable/disable AP PIN" },
|
|
{ "wps_config", hostapd_cli_cmd_wps_config, NULL,
|
|
"<SSID> <auth> <encr> <key> = configure AP" },
|
|
{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
|
|
"= show current WPS status" },
|
|
#endif /* CONFIG_WPS */
|
|
{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
|
|
"= send Disassociation Imminent notification" },
|
|
{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
|
|
"= send ESS Dissassociation Imminent notification" },
|
|
{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL,
|
|
"= send BSS Transition Management Request" },
|
|
{ "get_config", hostapd_cli_cmd_get_config, NULL,
|
|
"= show current configuration" },
|
|
{ "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
|
|
"= show this usage help" },
|
|
{ "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
|
|
"[ifname] = show interfaces/select interface" },
|
|
#ifdef CONFIG_FST
|
|
{ "fst", hostapd_cli_cmd_fst, NULL,
|
|
"<params...> = send FST-MANAGER control interface command" },
|
|
#endif /* CONFIG_FST */
|
|
{ "raw", hostapd_cli_cmd_raw, NULL,
|
|
"<params..> = send unprocessed command" },
|
|
{ "level", hostapd_cli_cmd_level, NULL,
|
|
"<debug level> = change debug level" },
|
|
{ "license", hostapd_cli_cmd_license, NULL,
|
|
"= show full hostapd_cli license" },
|
|
{ "quit", hostapd_cli_cmd_quit, NULL,
|
|
"= exit hostapd_cli" },
|
|
{ "set", hostapd_cli_cmd_set, hostapd_complete_set,
|
|
"<name> <value> = set runtime variables" },
|
|
{ "get", hostapd_cli_cmd_get, hostapd_complete_get,
|
|
"<name> = get runtime info" },
|
|
{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
|
|
"<arg,arg,...> = set QoS Map set element" },
|
|
{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
|
|
hostapd_complete_stations,
|
|
"<addr> = send QoS Map Configure frame" },
|
|
{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
|
|
"<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
|
|
" [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
|
|
" = initiate channel switch announcement" },
|
|
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
|
|
"<addr> <url>\n"
|
|
" = send WNM-Notification Subscription Remediation Request" },
|
|
{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
|
|
"<addr> <code (0/1)> <Re-auth-Delay(sec)> [url]\n"
|
|
" = send WNM-Notification imminent deauthentication indication" },
|
|
{ "vendor", hostapd_cli_cmd_vendor, NULL,
|
|
"<vendor id> <sub command id> [<hex formatted data>]\n"
|
|
" = send vendor driver command" },
|
|
{ "enable", hostapd_cli_cmd_enable, NULL,
|
|
"= enable hostapd on current interface" },
|
|
{ "reload", hostapd_cli_cmd_reload, NULL,
|
|
"= reload configuration for current interface" },
|
|
{ "disable", hostapd_cli_cmd_disable, NULL,
|
|
"= disable hostapd on current interface" },
|
|
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
|
|
"= update Beacon frame contents\n"},
|
|
{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
|
|
"= drop all ERP keys"},
|
|
{ "log_level", hostapd_cli_cmd_log_level, NULL,
|
|
"[level] = show/change log verbosity level" },
|
|
{ "pmksa", hostapd_cli_cmd_pmksa, NULL,
|
|
" = show PMKSA cache entries" },
|
|
{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL,
|
|
" = flush PMKSA cache" },
|
|
{ "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
|
|
"<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n"
|
|
" = add AP to neighbor database" },
|
|
{ "show_neighbor", hostapd_cli_cmd_show_neighbor, NULL,
|
|
" = show neighbor database entries" },
|
|
{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
|
|
"<addr> [ssid=<hex>] = remove AP from neighbor database" },
|
|
{ "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
|
|
"<addr> = send LCI request to a station"},
|
|
{ "req_range", hostapd_cli_cmd_req_range, NULL,
|
|
" = send FTM range request"},
|
|
{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
|
|
" = show supported driver flags"},
|
|
#ifdef CONFIG_DPP
|
|
{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
|
|
"report a scanned DPP URI from a QR Code" },
|
|
{ "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
|
|
"type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
|
|
{ "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
|
|
"*|<id> = remove DPP bootstrap information" },
|
|
{ "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
|
|
"<id> = get DPP bootstrap URI" },
|
|
{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
|
|
"<id> = show DPP bootstrap information" },
|
|
{ "dpp_bootstrap_set", hostapd_cli_cmd_dpp_bootstrap_set, NULL,
|
|
"<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
|
|
{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
|
|
"peer=<id> [own=<id>] = initiate DPP bootstrapping" },
|
|
{ "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
|
|
"<freq in MHz> = start DPP listen" },
|
|
{ "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL,
|
|
"= stop DPP listen" },
|
|
{ "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
|
|
"[curve=..] [key=..] = add DPP configurator" },
|
|
{ "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
|
|
NULL,
|
|
"*|<id> = remove DPP configurator" },
|
|
{ "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key,
|
|
NULL,
|
|
"<id> = Get DPP configurator's private key" },
|
|
{ "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL,
|
|
"conf=<role> configurator=<id> = generate self DPP configuration" },
|
|
{ "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
|
|
"add PKEX code" },
|
|
{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
|
|
"*|<id> = remove DPP pkex information" },
|
|
#ifdef CONFIG_DPP2
|
|
{ "dpp_controller_start", hostapd_cli_cmd_dpp_controller_start, NULL,
|
|
"[tcp_port=<port>] [role=..] = start DPP controller" },
|
|
{ "dpp_controller_stop", hostapd_cli_cmd_dpp_controller_stop, NULL,
|
|
"= stop DPP controller" },
|
|
{ "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL,
|
|
"own=<BI ID> iter=<count> = start DPP chirp" },
|
|
{ "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
|
|
"= stop DPP chirp" },
|
|
#endif /* CONFIG_DPP2 */
|
|
#endif /* CONFIG_DPP */
|
|
{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
|
|
"=Add/Delete/Show/Clear accept MAC ACL" },
|
|
{ "deny_acl", hostapd_cli_cmd_deny_macacl, NULL,
|
|
"=Add/Delete/Show/Clear deny MAC ACL" },
|
|
{ "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations,
|
|
"<addr> = poll a STA to check connectivity with a QoS null frame" },
|
|
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
|
|
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
|
|
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
|
|
"= reload wpa_psk_file only" },
|
|
#ifdef ANDROID
|
|
{ "driver", hostapd_cli_cmd_driver, NULL,
|
|
"<driver sub command> [<hex formatted data>] = send driver command data" },
|
|
#endif /* ANDROID */
|
|
{ NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
/*
|
|
* Prints command usage, lines are padded with the specified string.
|
|
*/
|
|
static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd,
|
|
const char *pad)
|
|
{
|
|
char c;
|
|
size_t n;
|
|
|
|
if (cmd->usage == NULL)
|
|
return;
|
|
fprintf(stream, "%s%s ", pad, cmd->cmd);
|
|
for (n = 0; (c = cmd->usage[n]); n++) {
|
|
fprintf(stream, "%c", c);
|
|
if (c == '\n')
|
|
fprintf(stream, "%s", pad);
|
|
}
|
|
fprintf(stream, "\n");
|
|
}
|
|
|
|
|
|
static void print_help(FILE *stream, const char *cmd)
|
|
{
|
|
int n;
|
|
|
|
fprintf(stream, "commands:\n");
|
|
for (n = 0; hostapd_cli_commands[n].cmd; n++) {
|
|
if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd))
|
|
print_cmd_help(stream, &hostapd_cli_commands[n], " ");
|
|
}
|
|
}
|
|
|
|
|
|
static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
|
|
{
|
|
const struct hostapd_cli_cmd *cmd, *match = NULL;
|
|
int count;
|
|
|
|
count = 0;
|
|
cmd = hostapd_cli_commands;
|
|
while (cmd->cmd) {
|
|
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
|
|
match = cmd;
|
|
if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
|
|
/* we have an exact match */
|
|
count = 1;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
cmd++;
|
|
}
|
|
|
|
if (count > 1) {
|
|
printf("Ambiguous command '%s'; possible commands:", argv[0]);
|
|
cmd = hostapd_cli_commands;
|
|
while (cmd->cmd) {
|
|
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
|
|
0) {
|
|
printf(" %s", cmd->cmd);
|
|
}
|
|
cmd++;
|
|
}
|
|
printf("\n");
|
|
} else if (count == 0) {
|
|
printf("Unknown command '%s'\n", argv[0]);
|
|
} else {
|
|
match->handler(ctrl, argc - 1, &argv[1]);
|
|
}
|
|
}
|
|
|
|
|
|
static void cli_event(const char *str)
|
|
{
|
|
const char *start, *s;
|
|
|
|
start = os_strchr(str, '>');
|
|
if (start == NULL)
|
|
return;
|
|
|
|
start++;
|
|
|
|
if (str_starts(start, AP_STA_CONNECTED)) {
|
|
s = os_strchr(start, ' ');
|
|
if (s == NULL)
|
|
return;
|
|
cli_txt_list_add(&stations, s + 1);
|
|
return;
|
|
}
|
|
|
|
if (str_starts(start, AP_STA_DISCONNECTED)) {
|
|
s = os_strchr(start, ' ');
|
|
if (s == NULL)
|
|
return;
|
|
cli_txt_list_del_addr(&stations, s + 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
|
|
int action_monitor)
|
|
{
|
|
int first = 1;
|
|
if (ctrl_conn == NULL)
|
|
return;
|
|
while (wpa_ctrl_pending(ctrl)) {
|
|
char buf[4096];
|
|
size_t len = sizeof(buf) - 1;
|
|
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
|
|
buf[len] = '\0';
|
|
if (action_monitor)
|
|
hostapd_cli_action_process(buf, len);
|
|
else {
|
|
cli_event(buf);
|
|
if (in_read && first)
|
|
printf("\n");
|
|
first = 0;
|
|
printf("%s\n", buf);
|
|
}
|
|
} else {
|
|
printf("Could not read pending message.\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
|
{
|
|
hostapd_cli_recv_pending(ctrl_conn, 0, 0);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
|
|
printf("Connection to hostapd lost - trying to reconnect\n");
|
|
hostapd_cli_close_connection();
|
|
}
|
|
if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
|
|
printf("Connection to hostapd re-established\n");
|
|
if (ctrl_conn)
|
|
hostapd_cli_recv_pending(ctrl_conn, 1, 0);
|
|
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
|
|
{
|
|
eloop_terminate();
|
|
}
|
|
|
|
|
|
static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
|
|
{
|
|
char *argv[max_args];
|
|
int argc;
|
|
argc = tokenize_cmd(cmd, argv);
|
|
if (argc)
|
|
wpa_request(ctrl_conn, argc, argv);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_edit_eof_cb(void *ctx)
|
|
{
|
|
eloop_terminate();
|
|
}
|
|
|
|
|
|
static char ** list_cmd_list(void)
|
|
{
|
|
char **res;
|
|
int i, count;
|
|
|
|
count = ARRAY_SIZE(hostapd_cli_commands);
|
|
res = os_calloc(count + 1, sizeof(char *));
|
|
if (res == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; hostapd_cli_commands[i].cmd; i++) {
|
|
res[i] = os_strdup(hostapd_cli_commands[i].cmd);
|
|
if (res[i] == NULL)
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str,
|
|
int pos)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; hostapd_cli_commands[i].cmd; i++) {
|
|
if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0)
|
|
continue;
|
|
if (hostapd_cli_commands[i].completion)
|
|
return hostapd_cli_commands[i].completion(str, pos);
|
|
if (!hostapd_cli_commands[i].usage)
|
|
return NULL;
|
|
edit_clear_line();
|
|
printf("\r%s\n", hostapd_cli_commands[i].usage);
|
|
edit_redraw();
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str,
|
|
int pos)
|
|
{
|
|
char **res;
|
|
const char *end;
|
|
char *cmd;
|
|
|
|
end = os_strchr(str, ' ');
|
|
if (end == NULL || str + pos < end)
|
|
return list_cmd_list();
|
|
|
|
cmd = os_malloc(pos + 1);
|
|
if (cmd == NULL)
|
|
return NULL;
|
|
os_memcpy(cmd, str, pos);
|
|
cmd[end - str] = '\0';
|
|
res = hostapd_cli_cmd_completion(cmd, str, pos);
|
|
os_free(cmd);
|
|
return res;
|
|
}
|
|
|
|
|
|
static void hostapd_cli_interactive(void)
|
|
{
|
|
char *hfile = NULL;
|
|
char *home;
|
|
|
|
printf("\nInteractive mode\n\n");
|
|
|
|
#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
|
|
home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
|
|
#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
|
|
home = getenv("HOME");
|
|
#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
|
|
if (home) {
|
|
const char *fname = ".hostapd_cli_history";
|
|
int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
|
|
hfile = os_malloc(hfile_len);
|
|
if (hfile)
|
|
os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
|
|
}
|
|
|
|
eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
|
|
edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
|
|
hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
|
|
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
|
|
|
|
eloop_run();
|
|
|
|
cli_txt_list_flush(&stations);
|
|
edit_deinit(hfile, NULL);
|
|
os_free(hfile);
|
|
eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
|
|
}
|
|
|
|
|
|
static void hostapd_cli_cleanup(void)
|
|
{
|
|
hostapd_cli_close_connection();
|
|
if (pid_file)
|
|
os_daemonize_terminate(pid_file);
|
|
|
|
os_program_deinit();
|
|
}
|
|
|
|
|
|
static void hostapd_cli_action(struct wpa_ctrl *ctrl)
|
|
{
|
|
fd_set rfds;
|
|
int fd, res;
|
|
struct timeval tv;
|
|
char buf[256];
|
|
size_t len;
|
|
|
|
fd = wpa_ctrl_get_fd(ctrl);
|
|
|
|
while (!hostapd_cli_quit) {
|
|
FD_ZERO(&rfds);
|
|
FD_SET(fd, &rfds);
|
|
tv.tv_sec = ping_interval;
|
|
tv.tv_usec = 0;
|
|
res = select(fd + 1, &rfds, NULL, NULL, &tv);
|
|
if (res < 0 && errno != EINTR) {
|
|
perror("select");
|
|
break;
|
|
}
|
|
|
|
if (FD_ISSET(fd, &rfds))
|
|
hostapd_cli_recv_pending(ctrl, 0, 1);
|
|
else {
|
|
len = sizeof(buf) - 1;
|
|
if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
|
|
hostapd_cli_action_process) < 0 ||
|
|
len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
|
|
printf("hostapd did not reply to PING "
|
|
"command - exiting\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int warning_displayed = 0;
|
|
int c;
|
|
int daemonize = 0;
|
|
int reconnect = 0;
|
|
|
|
if (os_program_init())
|
|
return -1;
|
|
|
|
for (;;) {
|
|
c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
|
|
if (c < 0)
|
|
break;
|
|
switch (c) {
|
|
case 'a':
|
|
action_file = optarg;
|
|
break;
|
|
case 'B':
|
|
daemonize = 1;
|
|
break;
|
|
case 'G':
|
|
ping_interval = atoi(optarg);
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
return 0;
|
|
case 'v':
|
|
printf("%s\n", hostapd_cli_version);
|
|
return 0;
|
|
case 'i':
|
|
os_free(ctrl_ifname);
|
|
ctrl_ifname = os_strdup(optarg);
|
|
break;
|
|
case 'p':
|
|
ctrl_iface_dir = optarg;
|
|
break;
|
|
case 'P':
|
|
pid_file = optarg;
|
|
break;
|
|
case 'r':
|
|
reconnect = 1;
|
|
break;
|
|
case 's':
|
|
client_socket_dir = optarg;
|
|
break;
|
|
default:
|
|
usage();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
interactive = (argc == optind) && (action_file == NULL);
|
|
|
|
if (interactive) {
|
|
printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license);
|
|
}
|
|
|
|
if (eloop_init())
|
|
return -1;
|
|
|
|
for (;;) {
|
|
if (ctrl_ifname == NULL) {
|
|
struct dirent *dent;
|
|
DIR *dir = opendir(ctrl_iface_dir);
|
|
if (dir) {
|
|
while ((dent = readdir(dir))) {
|
|
if (os_strcmp(dent->d_name, ".") == 0
|
|
||
|
|
os_strcmp(dent->d_name, "..") == 0)
|
|
continue;
|
|
printf("Selected interface '%s'\n",
|
|
dent->d_name);
|
|
ctrl_ifname = os_strdup(dent->d_name);
|
|
break;
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
hostapd_cli_reconnect(ctrl_ifname);
|
|
if (ctrl_conn) {
|
|
if (warning_displayed)
|
|
printf("Connection established.\n");
|
|
break;
|
|
}
|
|
if (!interactive && !reconnect) {
|
|
perror("Failed to connect to hostapd - "
|
|
"wpa_ctrl_open");
|
|
return -1;
|
|
}
|
|
|
|
if (!warning_displayed) {
|
|
printf("Could not connect to hostapd - re-trying\n");
|
|
warning_displayed = 1;
|
|
}
|
|
os_sleep(1, 0);
|
|
continue;
|
|
}
|
|
|
|
if (action_file && !hostapd_cli_attached)
|
|
return -1;
|
|
if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
|
|
return -1;
|
|
if (reconnect && action_file && ctrl_ifname) {
|
|
while (!hostapd_cli_quit) {
|
|
if (ctrl_conn)
|
|
hostapd_cli_action(ctrl_conn);
|
|
os_sleep(1, 0);
|
|
hostapd_cli_reconnect(ctrl_ifname);
|
|
}
|
|
} else if (interactive)
|
|
hostapd_cli_interactive();
|
|
else if (action_file)
|
|
hostapd_cli_action(ctrl_conn);
|
|
else
|
|
wpa_request(ctrl_conn, argc - optind, &argv[optind]);
|
|
|
|
unregister_event_handler(ctrl_conn);
|
|
os_free(ctrl_ifname);
|
|
eloop_destroy();
|
|
hostapd_cli_cleanup();
|
|
return 0;
|
|
}
|
|
|
|
#else /* CONFIG_NO_CTRL_IFACE */
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
#endif /* CONFIG_NO_CTRL_IFACE */
|