f10ff62 Describe preferred mechanism for submitting contributions fcdb359 Use "STATUS-NO_EVENTS" instead of "STATUS" in get_wpa_status function 73ed03f wpa_supplicant: Add GTK RSC relaxation workaround ea6030c Restore previous wpa_state in scan-only result handler 1e74ae4 WNM: Clear BSS TM data if already associated with preferred candidate d129b02 EAP-pwd: Add support for Brainpool Elliptic Curves a34eace dbus: Remove unused dict helper functions cdcb2d0 wpa_cli: Add support for vendor_elem_* commands 17b7032 EAP peer: Clear ignore flag in INITIALIZE state 1f1e619 Add test programs for checking libwpa_client linking 736b7cb wpa_supplicant/Makefile: Fix libwpa_client build 2e38079 TLS: Fix memory leak with multiple TLS server instances 7b0f550 eap_sim_db: Implement eap_sim_db_expire_pending() 45c3e72 Add frequency to operating class determination for 5 GHz 100..140 e50c50d dbus: Expose interface globals via D-Bus properties 1aa0fb7 dbus: Pass property description to getters/setters c93b7e1 RSN: Check result of EAPOL-Key frame send request 95be79f Allow -1 as value to disable frag_threshold bc50bb0 Extend the range of values for the RTS threshold 053693d hostapd: Add feature to start all interfaces at the same time in sync 9578413 Reserve QCA vendor specific nl80211 commands 110..114 5d4c508 Assign QCA commands and attributes for Tx power scaling and OTA testing 5d1d69a P2P: Filter control chars in group client device name similarly to peer f67d1a0 TDLS: Do not send error case of TPK M3 if TX fails 1248e58 wpa_supplicant: Reopen debug log file upon receipt of SIGHUP signal d8fd633 Do not write ERROR level log entries if debug file is not used 67deaa5 l2_packet: Add build option to disable Linux packet socket workaround fa46426 RSN: Do not try to connect if PMF disabled and AP requires it 8acbe7f WNM: Verify WNM Sleep Mode element length dacd789 WNM: Mark set TFS buffer const ... f24b979 OpenSSL: Merge error returns 84d6a17 TLS: Remove unused tls_capabilities() 7867227 ms_funcs: Merge similar return cases 3596361 hw_features: Merge similar return case in check_40mhz_2g4() aac1efe Reject the initial 4-way handshake if initial GTK setup fails 2da5256 Add backtrace-based error path testing mechanism 55413ce P2P: Do not allow 40 MHz co-ex PRI/SEC switch to force MCC Next patches were skipped due to explicit cherry-pick: bddc51e RSN: Stop connection attempt on apparent PMK mismatch 3fdaaa8 Throttle control interface event message bursts a530fe7 Add wpa_supplicant EVENT_TEST control interface command ee1e3f5 hostapd: Global control interface notifications 2e95cfc Add debug prints for wpa_supplicant ctrl_iface socket send operations ce7d0eb Update AP WPA/RSN IE on all associations if driver can select BSS 844dfeb QCA vendor command support to set band to driver Change-Id: I909996c5afcd3b5d123ea1e23c0e1212021f7625 Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
813 lines
20 KiB
C
813 lines
20 KiB
C
/*
|
|
* hostapd / main()
|
|
* Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "utils/includes.h"
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
#include <syslog.h>
|
|
#include <grp.h>
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
#include "utils/common.h"
|
|
#include "utils/eloop.h"
|
|
#include "utils/uuid.h"
|
|
#include "crypto/random.h"
|
|
#include "crypto/tls.h"
|
|
#include "common/version.h"
|
|
#include "drivers/driver.h"
|
|
#include "eap_server/eap.h"
|
|
#include "eap_server/tncs.h"
|
|
#include "ap/hostapd.h"
|
|
#include "ap/ap_config.h"
|
|
#include "ap/ap_drv_ops.h"
|
|
#include "fst/fst.h"
|
|
#include "config_file.h"
|
|
#include "eap_register.h"
|
|
#include "ctrl_iface.h"
|
|
|
|
|
|
struct hapd_global {
|
|
void **drv_priv;
|
|
size_t drv_count;
|
|
};
|
|
|
|
static struct hapd_global global;
|
|
|
|
|
|
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
|
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
|
|
int level, const char *txt, size_t len)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
char *format, *module_str;
|
|
int maxlen;
|
|
int conf_syslog_level, conf_stdout_level;
|
|
unsigned int conf_syslog, conf_stdout;
|
|
|
|
maxlen = len + 100;
|
|
format = os_malloc(maxlen);
|
|
if (!format)
|
|
return;
|
|
|
|
if (hapd && hapd->conf) {
|
|
conf_syslog_level = hapd->conf->logger_syslog_level;
|
|
conf_stdout_level = hapd->conf->logger_stdout_level;
|
|
conf_syslog = hapd->conf->logger_syslog;
|
|
conf_stdout = hapd->conf->logger_stdout;
|
|
} else {
|
|
conf_syslog_level = conf_stdout_level = 0;
|
|
conf_syslog = conf_stdout = (unsigned int) -1;
|
|
}
|
|
|
|
switch (module) {
|
|
case HOSTAPD_MODULE_IEEE80211:
|
|
module_str = "IEEE 802.11";
|
|
break;
|
|
case HOSTAPD_MODULE_IEEE8021X:
|
|
module_str = "IEEE 802.1X";
|
|
break;
|
|
case HOSTAPD_MODULE_RADIUS:
|
|
module_str = "RADIUS";
|
|
break;
|
|
case HOSTAPD_MODULE_WPA:
|
|
module_str = "WPA";
|
|
break;
|
|
case HOSTAPD_MODULE_DRIVER:
|
|
module_str = "DRIVER";
|
|
break;
|
|
case HOSTAPD_MODULE_IAPP:
|
|
module_str = "IAPP";
|
|
break;
|
|
case HOSTAPD_MODULE_MLME:
|
|
module_str = "MLME";
|
|
break;
|
|
default:
|
|
module_str = NULL;
|
|
break;
|
|
}
|
|
|
|
if (hapd && hapd->conf && addr)
|
|
os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
|
|
hapd->conf->iface, MAC2STR(addr),
|
|
module_str ? " " : "", module_str ? module_str : "",
|
|
txt);
|
|
else if (hapd && hapd->conf)
|
|
os_snprintf(format, maxlen, "%s:%s%s %s",
|
|
hapd->conf->iface, module_str ? " " : "",
|
|
module_str ? module_str : "", txt);
|
|
else if (addr)
|
|
os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
|
|
MAC2STR(addr), module_str ? " " : "",
|
|
module_str ? module_str : "", txt);
|
|
else
|
|
os_snprintf(format, maxlen, "%s%s%s",
|
|
module_str ? module_str : "",
|
|
module_str ? ": " : "", txt);
|
|
|
|
if ((conf_stdout & module) && level >= conf_stdout_level) {
|
|
wpa_debug_print_timestamp();
|
|
wpa_printf(MSG_INFO, "%s", format);
|
|
}
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
if ((conf_syslog & module) && level >= conf_syslog_level) {
|
|
int priority;
|
|
switch (level) {
|
|
case HOSTAPD_LEVEL_DEBUG_VERBOSE:
|
|
case HOSTAPD_LEVEL_DEBUG:
|
|
priority = LOG_DEBUG;
|
|
break;
|
|
case HOSTAPD_LEVEL_INFO:
|
|
priority = LOG_INFO;
|
|
break;
|
|
case HOSTAPD_LEVEL_NOTICE:
|
|
priority = LOG_NOTICE;
|
|
break;
|
|
case HOSTAPD_LEVEL_WARNING:
|
|
priority = LOG_WARNING;
|
|
break;
|
|
default:
|
|
priority = LOG_INFO;
|
|
break;
|
|
}
|
|
syslog(priority, "%s", format);
|
|
}
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
os_free(format);
|
|
}
|
|
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
|
|
|
|
|
|
/**
|
|
* hostapd_driver_init - Preparate driver interface
|
|
*/
|
|
static int hostapd_driver_init(struct hostapd_iface *iface)
|
|
{
|
|
struct wpa_init_params params;
|
|
size_t i;
|
|
struct hostapd_data *hapd = iface->bss[0];
|
|
struct hostapd_bss_config *conf = hapd->conf;
|
|
u8 *b = conf->bssid;
|
|
struct wpa_driver_capa capa;
|
|
|
|
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
|
|
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize the driver interface */
|
|
if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
|
|
b = NULL;
|
|
|
|
os_memset(¶ms, 0, sizeof(params));
|
|
for (i = 0; wpa_drivers[i]; i++) {
|
|
if (wpa_drivers[i] != hapd->driver)
|
|
continue;
|
|
|
|
if (global.drv_priv[i] == NULL &&
|
|
wpa_drivers[i]->global_init) {
|
|
global.drv_priv[i] = wpa_drivers[i]->global_init();
|
|
if (global.drv_priv[i] == NULL) {
|
|
wpa_printf(MSG_ERROR, "Failed to initialize "
|
|
"driver '%s'",
|
|
wpa_drivers[i]->name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
params.global_priv = global.drv_priv[i];
|
|
break;
|
|
}
|
|
params.bssid = b;
|
|
params.ifname = hapd->conf->iface;
|
|
params.driver_params = hapd->iconf->driver_params;
|
|
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
|
|
|
|
params.num_bridge = hapd->iface->num_bss;
|
|
params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
|
|
if (params.bridge == NULL)
|
|
return -1;
|
|
for (i = 0; i < hapd->iface->num_bss; i++) {
|
|
struct hostapd_data *bss = hapd->iface->bss[i];
|
|
if (bss->conf->bridge[0])
|
|
params.bridge[i] = bss->conf->bridge;
|
|
}
|
|
|
|
params.own_addr = hapd->own_addr;
|
|
|
|
hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms);
|
|
os_free(params.bridge);
|
|
if (hapd->drv_priv == NULL) {
|
|
wpa_printf(MSG_ERROR, "%s driver initialization failed.",
|
|
hapd->driver->name);
|
|
hapd->driver = NULL;
|
|
return -1;
|
|
}
|
|
|
|
if (hapd->driver->get_capa &&
|
|
hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
|
|
struct wowlan_triggers *triggs;
|
|
|
|
iface->drv_flags = capa.flags;
|
|
iface->smps_modes = capa.smps_modes;
|
|
iface->probe_resp_offloads = capa.probe_resp_offloads;
|
|
iface->extended_capa = capa.extended_capa;
|
|
iface->extended_capa_mask = capa.extended_capa_mask;
|
|
iface->extended_capa_len = capa.extended_capa_len;
|
|
iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
|
|
|
|
triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
|
|
if (triggs && hapd->driver->set_wowlan) {
|
|
if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
|
|
wpa_printf(MSG_ERROR, "set_wowlan failed");
|
|
}
|
|
os_free(triggs);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* hostapd_interface_init - Read configuration file and init BSS data
|
|
*
|
|
* This function is used to parse configuration file for a full interface (one
|
|
* or more BSSes sharing the same radio) and allocate memory for the BSS
|
|
* interfaces. No actiual driver operations are started.
|
|
*/
|
|
static struct hostapd_iface *
|
|
hostapd_interface_init(struct hapd_interfaces *interfaces,
|
|
const char *config_fname, int debug)
|
|
{
|
|
struct hostapd_iface *iface;
|
|
int k;
|
|
|
|
wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
|
|
iface = hostapd_init(interfaces, config_fname);
|
|
if (!iface)
|
|
return NULL;
|
|
iface->interfaces = interfaces;
|
|
|
|
for (k = 0; k < debug; k++) {
|
|
if (iface->bss[0]->conf->logger_stdout_level > 0)
|
|
iface->bss[0]->conf->logger_stdout_level--;
|
|
}
|
|
|
|
if (iface->conf->bss[0]->iface[0] == '\0' &&
|
|
!hostapd_drv_none(iface->bss[0])) {
|
|
wpa_printf(MSG_ERROR, "Interface name not specified in %s",
|
|
config_fname);
|
|
hostapd_interface_deinit_free(iface);
|
|
return NULL;
|
|
}
|
|
|
|
return iface;
|
|
}
|
|
|
|
|
|
/**
|
|
* handle_term - SIGINT and SIGTERM handler to terminate hostapd process
|
|
*/
|
|
static void handle_term(int sig, void *signal_ctx)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
|
|
eloop_terminate();
|
|
}
|
|
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
|
|
static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
|
|
{
|
|
if (hostapd_reload_config(iface) < 0) {
|
|
wpa_printf(MSG_WARNING, "Failed to read new configuration "
|
|
"file - continuing with old.");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* handle_reload - SIGHUP handler to reload configuration
|
|
*/
|
|
static void handle_reload(int sig, void *signal_ctx)
|
|
{
|
|
struct hapd_interfaces *interfaces = signal_ctx;
|
|
wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
|
|
sig);
|
|
hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
|
|
}
|
|
|
|
|
|
static void handle_dump_state(int sig, void *signal_ctx)
|
|
{
|
|
/* Not used anymore - ignore signal */
|
|
}
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
|
|
static int hostapd_global_init(struct hapd_interfaces *interfaces,
|
|
const char *entropy_file)
|
|
{
|
|
int i;
|
|
|
|
os_memset(&global, 0, sizeof(global));
|
|
|
|
hostapd_logger_register_cb(hostapd_logger_cb);
|
|
|
|
if (eap_server_register_methods()) {
|
|
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
|
|
return -1;
|
|
}
|
|
|
|
if (eloop_init()) {
|
|
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
|
|
return -1;
|
|
}
|
|
|
|
random_init(entropy_file);
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
eloop_register_signal(SIGHUP, handle_reload, interfaces);
|
|
eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
eloop_register_signal_terminate(handle_term, interfaces);
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
openlog("hostapd", 0, LOG_DAEMON);
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
for (i = 0; wpa_drivers[i]; i++)
|
|
global.drv_count++;
|
|
if (global.drv_count == 0) {
|
|
wpa_printf(MSG_ERROR, "No drivers enabled");
|
|
return -1;
|
|
}
|
|
global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
|
|
if (global.drv_priv == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void hostapd_global_deinit(const char *pid_file)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
|
|
if (!global.drv_priv[i])
|
|
continue;
|
|
wpa_drivers[i]->global_deinit(global.drv_priv[i]);
|
|
}
|
|
os_free(global.drv_priv);
|
|
global.drv_priv = NULL;
|
|
|
|
#ifdef EAP_SERVER_TNC
|
|
tncs_global_deinit();
|
|
#endif /* EAP_SERVER_TNC */
|
|
|
|
random_deinit();
|
|
|
|
eloop_destroy();
|
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
closelog();
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
|
|
eap_server_unregister_methods();
|
|
|
|
os_daemonize_terminate(pid_file);
|
|
}
|
|
|
|
|
|
static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
|
|
const char *pid_file)
|
|
{
|
|
#ifdef EAP_SERVER_TNC
|
|
int tnc = 0;
|
|
size_t i, k;
|
|
|
|
for (i = 0; !tnc && i < ifaces->count; i++) {
|
|
for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
|
|
if (ifaces->iface[i]->bss[0]->conf->tnc) {
|
|
tnc++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tnc && tncs_global_init() < 0) {
|
|
wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
|
|
return -1;
|
|
}
|
|
#endif /* EAP_SERVER_TNC */
|
|
|
|
if (daemonize && os_daemonize(pid_file)) {
|
|
wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
eloop_run();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void show_version(void)
|
|
{
|
|
fprintf(stderr,
|
|
"hostapd v" VERSION_STR "\n"
|
|
"User space daemon for IEEE 802.11 AP management,\n"
|
|
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
|
|
"Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> "
|
|
"and contributors\n");
|
|
}
|
|
|
|
|
|
static void usage(void)
|
|
{
|
|
show_version();
|
|
fprintf(stderr,
|
|
"\n"
|
|
"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
|
|
"\\\n"
|
|
" [-g <global ctrl_iface>] [-G <group>] \\\n"
|
|
" <configuration file(s)>\n"
|
|
"\n"
|
|
"options:\n"
|
|
" -h show this usage\n"
|
|
" -d show more debug messages (-dd for even more)\n"
|
|
" -B run daemon in the background\n"
|
|
" -e entropy file\n"
|
|
" -g global control interface path\n"
|
|
" -G group for control interfaces\n"
|
|
" -P PID file\n"
|
|
" -K include key data in debug messages\n"
|
|
#ifdef CONFIG_DEBUG_FILE
|
|
" -f log output to debug file instead of stdout\n"
|
|
#endif /* CONFIG_DEBUG_FILE */
|
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
|
" -T = record to Linux tracing in addition to logging\n"
|
|
" (records all messages regardless of debug verbosity)\n"
|
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
|
" -S start all the interfaces synchronously\n"
|
|
" -t include timestamps in some debug messages\n"
|
|
" -v show hostapd version\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
static const char * hostapd_msg_ifname_cb(void *ctx)
|
|
{
|
|
struct hostapd_data *hapd = ctx;
|
|
if (hapd && hapd->iconf && hapd->iconf->bss &&
|
|
hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
|
|
return hapd->iconf->bss[0]->iface;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
|
|
const char *path)
|
|
{
|
|
char *pos;
|
|
os_free(interfaces->global_iface_path);
|
|
interfaces->global_iface_path = os_strdup(path);
|
|
if (interfaces->global_iface_path == NULL)
|
|
return -1;
|
|
pos = os_strrchr(interfaces->global_iface_path, '/');
|
|
if (pos == NULL) {
|
|
wpa_printf(MSG_ERROR, "No '/' in the global control interface "
|
|
"file");
|
|
os_free(interfaces->global_iface_path);
|
|
interfaces->global_iface_path = NULL;
|
|
return -1;
|
|
}
|
|
|
|
*pos = '\0';
|
|
interfaces->global_iface_name = pos + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
|
|
const char *group)
|
|
{
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
|
struct group *grp;
|
|
grp = getgrnam(group);
|
|
if (grp == NULL) {
|
|
wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
|
|
return -1;
|
|
}
|
|
interfaces->ctrl_iface_group = grp->gr_gid;
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_WPS
|
|
static int gen_uuid(const char *txt_addr)
|
|
{
|
|
u8 addr[ETH_ALEN];
|
|
u8 uuid[UUID_LEN];
|
|
char buf[100];
|
|
|
|
if (hwaddr_aton(txt_addr, addr) < 0)
|
|
return -1;
|
|
|
|
uuid_gen_mac_addr(addr, uuid);
|
|
if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
|
|
return -1;
|
|
|
|
printf("%s\n", buf);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_WPS */
|
|
|
|
|
|
#ifndef HOSTAPD_CLEANUP_INTERVAL
|
|
#define HOSTAPD_CLEANUP_INTERVAL 10
|
|
#endif /* HOSTAPD_CLEANUP_INTERVAL */
|
|
|
|
static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
|
|
{
|
|
hostapd_periodic_iface(iface);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Periodic cleanup tasks */
|
|
static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct hapd_interfaces *interfaces = eloop_ctx;
|
|
|
|
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
|
hostapd_periodic, interfaces, NULL);
|
|
hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct hapd_interfaces interfaces;
|
|
int ret = 1;
|
|
size_t i, j;
|
|
int c, debug = 0, daemonize = 0;
|
|
char *pid_file = NULL;
|
|
const char *log_file = NULL;
|
|
const char *entropy_file = NULL;
|
|
char **bss_config = NULL, **tmp_bss;
|
|
size_t num_bss_configs = 0;
|
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
|
int enable_trace_dbg = 0;
|
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
|
int start_ifaces_in_sync = 0;
|
|
|
|
if (os_program_init())
|
|
return -1;
|
|
|
|
os_memset(&interfaces, 0, sizeof(interfaces));
|
|
interfaces.reload_config = hostapd_reload_config;
|
|
interfaces.config_read_cb = hostapd_config_read;
|
|
interfaces.for_each_interface = hostapd_for_each_interface;
|
|
interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
|
|
interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
|
|
interfaces.driver_init = hostapd_driver_init;
|
|
interfaces.global_iface_path = NULL;
|
|
interfaces.global_iface_name = NULL;
|
|
interfaces.global_ctrl_sock = -1;
|
|
interfaces.global_ctrl_dst = NULL;
|
|
|
|
for (;;) {
|
|
c = getopt(argc, argv, "b:Bde:f:hKP:STtu:vg:G:");
|
|
if (c < 0)
|
|
break;
|
|
switch (c) {
|
|
case 'h':
|
|
usage();
|
|
break;
|
|
case 'd':
|
|
debug++;
|
|
if (wpa_debug_level > 0)
|
|
wpa_debug_level--;
|
|
break;
|
|
case 'B':
|
|
daemonize++;
|
|
break;
|
|
case 'e':
|
|
entropy_file = optarg;
|
|
break;
|
|
case 'f':
|
|
log_file = optarg;
|
|
break;
|
|
case 'K':
|
|
wpa_debug_show_keys++;
|
|
break;
|
|
case 'P':
|
|
os_free(pid_file);
|
|
pid_file = os_rel2abs_path(optarg);
|
|
break;
|
|
case 't':
|
|
wpa_debug_timestamp++;
|
|
break;
|
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
|
case 'T':
|
|
enable_trace_dbg = 1;
|
|
break;
|
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
|
case 'v':
|
|
show_version();
|
|
exit(1);
|
|
break;
|
|
case 'g':
|
|
if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
|
|
return -1;
|
|
break;
|
|
case 'G':
|
|
if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
|
|
return -1;
|
|
break;
|
|
case 'b':
|
|
tmp_bss = os_realloc_array(bss_config,
|
|
num_bss_configs + 1,
|
|
sizeof(char *));
|
|
if (tmp_bss == NULL)
|
|
goto out;
|
|
bss_config = tmp_bss;
|
|
bss_config[num_bss_configs++] = optarg;
|
|
break;
|
|
case 'S':
|
|
start_ifaces_in_sync = 1;
|
|
break;
|
|
#ifdef CONFIG_WPS
|
|
case 'u':
|
|
return gen_uuid(optarg);
|
|
#endif /* CONFIG_WPS */
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind == argc && interfaces.global_iface_path == NULL &&
|
|
num_bss_configs == 0)
|
|
usage();
|
|
|
|
wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
|
|
|
|
if (log_file)
|
|
wpa_debug_open_file(log_file);
|
|
else
|
|
wpa_debug_setup_stdout();
|
|
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
|
if (enable_trace_dbg) {
|
|
int tret = wpa_debug_open_linux_tracing();
|
|
if (tret) {
|
|
wpa_printf(MSG_ERROR, "Failed to enable trace logging");
|
|
return -1;
|
|
}
|
|
}
|
|
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
|
|
|
interfaces.count = argc - optind;
|
|
if (interfaces.count || num_bss_configs) {
|
|
interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
|
|
sizeof(struct hostapd_iface *));
|
|
if (interfaces.iface == NULL) {
|
|
wpa_printf(MSG_ERROR, "malloc failed");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (hostapd_global_init(&interfaces, entropy_file)) {
|
|
wpa_printf(MSG_ERROR, "Failed to initialize global context");
|
|
return -1;
|
|
}
|
|
|
|
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
|
hostapd_periodic, &interfaces, NULL);
|
|
|
|
if (fst_global_init()) {
|
|
wpa_printf(MSG_ERROR,
|
|
"Failed to initialize global FST context");
|
|
goto out;
|
|
}
|
|
|
|
#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
|
|
if (!fst_global_add_ctrl(fst_ctrl_cli))
|
|
wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
|
|
#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
|
|
|
|
/* Allocate and parse configuration for full interface files */
|
|
for (i = 0; i < interfaces.count; i++) {
|
|
interfaces.iface[i] = hostapd_interface_init(&interfaces,
|
|
argv[optind + i],
|
|
debug);
|
|
if (!interfaces.iface[i]) {
|
|
wpa_printf(MSG_ERROR, "Failed to initialize interface");
|
|
goto out;
|
|
}
|
|
if (start_ifaces_in_sync)
|
|
interfaces.iface[i]->need_to_start_in_sync = 1;
|
|
}
|
|
|
|
/* Allocate and parse configuration for per-BSS files */
|
|
for (i = 0; i < num_bss_configs; i++) {
|
|
struct hostapd_iface *iface;
|
|
char *fname;
|
|
|
|
wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
|
|
fname = os_strchr(bss_config[i], ':');
|
|
if (fname == NULL) {
|
|
wpa_printf(MSG_ERROR,
|
|
"Invalid BSS config identifier '%s'",
|
|
bss_config[i]);
|
|
goto out;
|
|
}
|
|
*fname++ = '\0';
|
|
iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
|
|
fname, debug);
|
|
if (iface == NULL)
|
|
goto out;
|
|
for (j = 0; j < interfaces.count; j++) {
|
|
if (interfaces.iface[j] == iface)
|
|
break;
|
|
}
|
|
if (j == interfaces.count) {
|
|
struct hostapd_iface **tmp;
|
|
tmp = os_realloc_array(interfaces.iface,
|
|
interfaces.count + 1,
|
|
sizeof(struct hostapd_iface *));
|
|
if (tmp == NULL) {
|
|
hostapd_interface_deinit_free(iface);
|
|
goto out;
|
|
}
|
|
interfaces.iface = tmp;
|
|
interfaces.iface[interfaces.count++] = iface;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Enable configured interfaces. Depending on channel configuration,
|
|
* this may complete full initialization before returning or use a
|
|
* callback mechanism to complete setup in case of operations like HT
|
|
* co-ex scans, ACS, or DFS are needed to determine channel parameters.
|
|
* In such case, the interface will be enabled from eloop context within
|
|
* hostapd_global_run().
|
|
*/
|
|
interfaces.terminate_on_error = interfaces.count;
|
|
for (i = 0; i < interfaces.count; i++) {
|
|
if (hostapd_driver_init(interfaces.iface[i]) ||
|
|
hostapd_setup_interface(interfaces.iface[i]))
|
|
goto out;
|
|
}
|
|
|
|
hostapd_global_ctrl_iface_init(&interfaces);
|
|
|
|
if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
|
|
wpa_printf(MSG_ERROR, "Failed to start eloop");
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
hostapd_global_ctrl_iface_deinit(&interfaces);
|
|
/* Deinitialize all interfaces */
|
|
for (i = 0; i < interfaces.count; i++) {
|
|
if (!interfaces.iface[i])
|
|
continue;
|
|
interfaces.iface[i]->driver_ap_teardown =
|
|
!!(interfaces.iface[i]->drv_flags &
|
|
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
|
|
hostapd_interface_deinit_free(interfaces.iface[i]);
|
|
}
|
|
os_free(interfaces.iface);
|
|
|
|
eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
|
|
hostapd_global_deinit(pid_file);
|
|
os_free(pid_file);
|
|
|
|
if (log_file)
|
|
wpa_debug_close_file();
|
|
wpa_debug_close_linux_tracing();
|
|
|
|
os_free(bss_config);
|
|
|
|
fst_global_deinit();
|
|
|
|
os_program_deinit();
|
|
|
|
return ret;
|
|
}
|