xref: /netbsd-src/external/bsd/wpa/dist/hostapd/main.c (revision 45d3cc13f775755ee348416d64536fb30df46e06)
18dbcf02cSchristos /*
28dbcf02cSchristos  * hostapd / main()
3*45d3cc13Schristos  * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
78dbcf02cSchristos  */
88dbcf02cSchristos 
98dbcf02cSchristos #include "utils/includes.h"
108dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
118dbcf02cSchristos #include <syslog.h>
123c260e60Schristos #include <grp.h>
138dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
148dbcf02cSchristos 
158dbcf02cSchristos #include "utils/common.h"
168dbcf02cSchristos #include "utils/eloop.h"
173c260e60Schristos #include "utils/uuid.h"
18*45d3cc13Schristos #include "crypto/crypto.h"
19111b9fd8Schristos #include "crypto/random.h"
208dbcf02cSchristos #include "crypto/tls.h"
218dbcf02cSchristos #include "common/version.h"
22460bb4fcSchristos #include "common/dpp.h"
238dbcf02cSchristos #include "drivers/driver.h"
248dbcf02cSchristos #include "eap_server/eap.h"
258dbcf02cSchristos #include "eap_server/tncs.h"
268dbcf02cSchristos #include "ap/hostapd.h"
278dbcf02cSchristos #include "ap/ap_config.h"
28e604d861Schristos #include "ap/ap_drv_ops.h"
29be6b3c4dSchristos #include "ap/dpp_hostapd.h"
30ecc36642Schristos #include "fst/fst.h"
318dbcf02cSchristos #include "config_file.h"
328dbcf02cSchristos #include "eap_register.h"
338dbcf02cSchristos #include "ctrl_iface.h"
348dbcf02cSchristos 
358dbcf02cSchristos 
36111b9fd8Schristos struct hapd_global {
37111b9fd8Schristos 	void **drv_priv;
38111b9fd8Schristos 	size_t drv_count;
39111b9fd8Schristos };
40111b9fd8Schristos 
41111b9fd8Schristos static struct hapd_global global;
42111b9fd8Schristos 
438dbcf02cSchristos 
448dbcf02cSchristos #ifndef CONFIG_NO_HOSTAPD_LOGGER
458dbcf02cSchristos static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
468dbcf02cSchristos 			      int level, const char *txt, size_t len)
478dbcf02cSchristos {
488dbcf02cSchristos 	struct hostapd_data *hapd = ctx;
498dbcf02cSchristos 	char *format, *module_str;
508dbcf02cSchristos 	int maxlen;
518dbcf02cSchristos 	int conf_syslog_level, conf_stdout_level;
528dbcf02cSchristos 	unsigned int conf_syslog, conf_stdout;
538dbcf02cSchristos 
548dbcf02cSchristos 	maxlen = len + 100;
558dbcf02cSchristos 	format = os_malloc(maxlen);
568dbcf02cSchristos 	if (!format)
578dbcf02cSchristos 		return;
588dbcf02cSchristos 
598dbcf02cSchristos 	if (hapd && hapd->conf) {
608dbcf02cSchristos 		conf_syslog_level = hapd->conf->logger_syslog_level;
618dbcf02cSchristos 		conf_stdout_level = hapd->conf->logger_stdout_level;
628dbcf02cSchristos 		conf_syslog = hapd->conf->logger_syslog;
638dbcf02cSchristos 		conf_stdout = hapd->conf->logger_stdout;
648dbcf02cSchristos 	} else {
658dbcf02cSchristos 		conf_syslog_level = conf_stdout_level = 0;
668dbcf02cSchristos 		conf_syslog = conf_stdout = (unsigned int) -1;
678dbcf02cSchristos 	}
688dbcf02cSchristos 
698dbcf02cSchristos 	switch (module) {
708dbcf02cSchristos 	case HOSTAPD_MODULE_IEEE80211:
718dbcf02cSchristos 		module_str = "IEEE 802.11";
728dbcf02cSchristos 		break;
738dbcf02cSchristos 	case HOSTAPD_MODULE_IEEE8021X:
748dbcf02cSchristos 		module_str = "IEEE 802.1X";
758dbcf02cSchristos 		break;
768dbcf02cSchristos 	case HOSTAPD_MODULE_RADIUS:
778dbcf02cSchristos 		module_str = "RADIUS";
788dbcf02cSchristos 		break;
798dbcf02cSchristos 	case HOSTAPD_MODULE_WPA:
808dbcf02cSchristos 		module_str = "WPA";
818dbcf02cSchristos 		break;
828dbcf02cSchristos 	case HOSTAPD_MODULE_DRIVER:
838dbcf02cSchristos 		module_str = "DRIVER";
848dbcf02cSchristos 		break;
858dbcf02cSchristos 	case HOSTAPD_MODULE_MLME:
868dbcf02cSchristos 		module_str = "MLME";
878dbcf02cSchristos 		break;
888dbcf02cSchristos 	default:
898dbcf02cSchristos 		module_str = NULL;
908dbcf02cSchristos 		break;
918dbcf02cSchristos 	}
928dbcf02cSchristos 
938dbcf02cSchristos 	if (hapd && hapd->conf && addr)
948dbcf02cSchristos 		os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
958dbcf02cSchristos 			    hapd->conf->iface, MAC2STR(addr),
963c260e60Schristos 			    module_str ? " " : "", module_str ? module_str : "",
973c260e60Schristos 			    txt);
988dbcf02cSchristos 	else if (hapd && hapd->conf)
998dbcf02cSchristos 		os_snprintf(format, maxlen, "%s:%s%s %s",
1008dbcf02cSchristos 			    hapd->conf->iface, module_str ? " " : "",
1013c260e60Schristos 			    module_str ? module_str : "", txt);
1028dbcf02cSchristos 	else if (addr)
1038dbcf02cSchristos 		os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
1048dbcf02cSchristos 			    MAC2STR(addr), module_str ? " " : "",
1053c260e60Schristos 			    module_str ? module_str : "", txt);
1068dbcf02cSchristos 	else
1078dbcf02cSchristos 		os_snprintf(format, maxlen, "%s%s%s",
1083c260e60Schristos 			    module_str ? module_str : "",
1093c260e60Schristos 			    module_str ? ": " : "", txt);
1108dbcf02cSchristos 
111be6b3c4dSchristos #ifdef CONFIG_DEBUG_SYSLOG
112be6b3c4dSchristos 	if (wpa_debug_syslog)
113be6b3c4dSchristos 		conf_stdout = 0;
114be6b3c4dSchristos #endif /* CONFIG_DEBUG_SYSLOG */
1158dbcf02cSchristos 	if ((conf_stdout & module) && level >= conf_stdout_level) {
1168dbcf02cSchristos 		wpa_debug_print_timestamp();
1173c260e60Schristos 		wpa_printf(MSG_INFO, "%s", format);
1188dbcf02cSchristos 	}
1198dbcf02cSchristos 
1208dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
1218dbcf02cSchristos 	if ((conf_syslog & module) && level >= conf_syslog_level) {
1228dbcf02cSchristos 		int priority;
1238dbcf02cSchristos 		switch (level) {
1248dbcf02cSchristos 		case HOSTAPD_LEVEL_DEBUG_VERBOSE:
1258dbcf02cSchristos 		case HOSTAPD_LEVEL_DEBUG:
1268dbcf02cSchristos 			priority = LOG_DEBUG;
1278dbcf02cSchristos 			break;
1288dbcf02cSchristos 		case HOSTAPD_LEVEL_INFO:
1298dbcf02cSchristos 			priority = LOG_INFO;
1308dbcf02cSchristos 			break;
1318dbcf02cSchristos 		case HOSTAPD_LEVEL_NOTICE:
1328dbcf02cSchristos 			priority = LOG_NOTICE;
1338dbcf02cSchristos 			break;
1348dbcf02cSchristos 		case HOSTAPD_LEVEL_WARNING:
1358dbcf02cSchristos 			priority = LOG_WARNING;
1368dbcf02cSchristos 			break;
1378dbcf02cSchristos 		default:
1388dbcf02cSchristos 			priority = LOG_INFO;
1398dbcf02cSchristos 			break;
1408dbcf02cSchristos 		}
1418dbcf02cSchristos 		syslog(priority, "%s", format);
1428dbcf02cSchristos 	}
1438dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
1448dbcf02cSchristos 
1458dbcf02cSchristos 	os_free(format);
1468dbcf02cSchristos }
1478dbcf02cSchristos #endif /* CONFIG_NO_HOSTAPD_LOGGER */
1488dbcf02cSchristos 
1498dbcf02cSchristos 
1508dbcf02cSchristos /**
1513c260e60Schristos  * hostapd_driver_init - Preparate driver interface
1528dbcf02cSchristos  */
1538dbcf02cSchristos static int hostapd_driver_init(struct hostapd_iface *iface)
1548dbcf02cSchristos {
1558dbcf02cSchristos 	struct wpa_init_params params;
1568dbcf02cSchristos 	size_t i;
1578dbcf02cSchristos 	struct hostapd_data *hapd = iface->bss[0];
1588dbcf02cSchristos 	struct hostapd_bss_config *conf = hapd->conf;
1598dbcf02cSchristos 	u8 *b = conf->bssid;
160111b9fd8Schristos 	struct wpa_driver_capa capa;
161*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
162*45d3cc13Schristos 	struct hostapd_data *h_hapd = NULL;
163*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
1648dbcf02cSchristos 
1658dbcf02cSchristos 	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
1668dbcf02cSchristos 		wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
1678dbcf02cSchristos 		return -1;
1688dbcf02cSchristos 	}
1698dbcf02cSchristos 
170*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
171*45d3cc13Schristos 	if (conf->mld_ap)
172*45d3cc13Schristos 		h_hapd = hostapd_mld_get_first_bss(hapd);
173*45d3cc13Schristos 
174*45d3cc13Schristos 	if (h_hapd) {
175*45d3cc13Schristos 		hapd->drv_priv = h_hapd->drv_priv;
176*45d3cc13Schristos 		hapd->interface_added = h_hapd->interface_added;
177*45d3cc13Schristos 
178*45d3cc13Schristos 		/*
179*45d3cc13Schristos 		 * All interfaces participating in the AP MLD would have
180*45d3cc13Schristos 		 * the same MLD address, which is the interface hardware
181*45d3cc13Schristos 		 * address, while the interface address would be
182*45d3cc13Schristos 		 * derived from the original interface address if BSSID
183*45d3cc13Schristos 		 * is not configured, and otherwise it would be the
184*45d3cc13Schristos 		 * configured BSSID.
185*45d3cc13Schristos 		 */
186*45d3cc13Schristos 		if (is_zero_ether_addr(b)) {
187*45d3cc13Schristos 			os_memcpy(hapd->own_addr, h_hapd->mld->mld_addr,
188*45d3cc13Schristos 				  ETH_ALEN);
189*45d3cc13Schristos 			random_mac_addr_keep_oui(hapd->own_addr);
190*45d3cc13Schristos 		} else {
191*45d3cc13Schristos 			os_memcpy(hapd->own_addr, b, ETH_ALEN);
192*45d3cc13Schristos 		}
193*45d3cc13Schristos 
194*45d3cc13Schristos 		hostapd_mld_add_link(hapd);
195*45d3cc13Schristos 		wpa_printf(MSG_DEBUG,
196*45d3cc13Schristos 			   "Setup of non first link (%d) BSS of MLD %s",
197*45d3cc13Schristos 			   hapd->mld_link_id, hapd->conf->iface);
198*45d3cc13Schristos 
199*45d3cc13Schristos 		goto setup_mld;
200*45d3cc13Schristos 	}
201*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
202*45d3cc13Schristos 
2038dbcf02cSchristos 	/* Initialize the driver interface */
204*45d3cc13Schristos 	if (is_zero_ether_addr(b))
2058dbcf02cSchristos 		b = NULL;
2068dbcf02cSchristos 
2078dbcf02cSchristos 	os_memset(&params, 0, sizeof(params));
208111b9fd8Schristos 	for (i = 0; wpa_drivers[i]; i++) {
209111b9fd8Schristos 		if (wpa_drivers[i] != hapd->driver)
210111b9fd8Schristos 			continue;
211111b9fd8Schristos 
212111b9fd8Schristos 		if (global.drv_priv[i] == NULL &&
213111b9fd8Schristos 		    wpa_drivers[i]->global_init) {
2140d1d60ceSroy 			global.drv_priv[i] =
2150d1d60ceSroy 				wpa_drivers[i]->global_init(iface->interfaces);
216111b9fd8Schristos 			if (global.drv_priv[i] == NULL) {
217111b9fd8Schristos 				wpa_printf(MSG_ERROR, "Failed to initialize "
218111b9fd8Schristos 					   "driver '%s'",
219111b9fd8Schristos 					   wpa_drivers[i]->name);
220111b9fd8Schristos 				return -1;
221111b9fd8Schristos 			}
222111b9fd8Schristos 		}
223111b9fd8Schristos 
224111b9fd8Schristos 		params.global_priv = global.drv_priv[i];
225111b9fd8Schristos 		break;
226111b9fd8Schristos 	}
2278dbcf02cSchristos 	params.bssid = b;
228*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
229*45d3cc13Schristos 	/*
230*45d3cc13Schristos 	 * Use the configured MLD MAC address as the interface hardware address
231*45d3cc13Schristos 	 * if this AP is a part of an AP MLD.
232*45d3cc13Schristos 	 */
233*45d3cc13Schristos 	if (hapd->conf->mld_ap) {
234*45d3cc13Schristos 		if (!is_zero_ether_addr(hapd->conf->mld_addr))
235*45d3cc13Schristos 			params.bssid = hapd->conf->mld_addr;
236*45d3cc13Schristos 		else
237*45d3cc13Schristos 			params.bssid = NULL;
238*45d3cc13Schristos 	}
239*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
240*45d3cc13Schristos 
2418dbcf02cSchristos 	params.ifname = hapd->conf->iface;
242bb610346Schristos 	params.driver_params = hapd->iconf->driver_params;
2438dbcf02cSchristos 	params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
2448dbcf02cSchristos 
2458dbcf02cSchristos 	params.num_bridge = hapd->iface->num_bss;
246e604d861Schristos 	params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
2478dbcf02cSchristos 	if (params.bridge == NULL)
2488dbcf02cSchristos 		return -1;
2498dbcf02cSchristos 	for (i = 0; i < hapd->iface->num_bss; i++) {
2508dbcf02cSchristos 		struct hostapd_data *bss = hapd->iface->bss[i];
2518dbcf02cSchristos 		if (bss->conf->bridge[0])
2528dbcf02cSchristos 			params.bridge[i] = bss->conf->bridge;
2538dbcf02cSchristos 	}
2548dbcf02cSchristos 
2558dbcf02cSchristos 	params.own_addr = hapd->own_addr;
2568dbcf02cSchristos 
2578dbcf02cSchristos 	hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
2588dbcf02cSchristos 	os_free(params.bridge);
2598dbcf02cSchristos 	if (hapd->drv_priv == NULL) {
2608dbcf02cSchristos 		wpa_printf(MSG_ERROR, "%s driver initialization failed.",
2618dbcf02cSchristos 			   hapd->driver->name);
2628dbcf02cSchristos 		hapd->driver = NULL;
2638dbcf02cSchristos 		return -1;
2648dbcf02cSchristos 	}
2658dbcf02cSchristos 
266*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
267*45d3cc13Schristos 	/*
268*45d3cc13Schristos 	 * This is the first interface added to the AP MLD, so have the
269*45d3cc13Schristos 	 * interface hardware address be the MLD address, while the link address
270*45d3cc13Schristos 	 * would be derived from the original interface address if BSSID is not
271*45d3cc13Schristos 	 * configured, and otherwise it would be the configured BSSID.
272*45d3cc13Schristos 	 */
273*45d3cc13Schristos 	if (hapd->conf->mld_ap) {
274*45d3cc13Schristos 		os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
275*45d3cc13Schristos 
276*45d3cc13Schristos 		if (!b)
277*45d3cc13Schristos 			random_mac_addr_keep_oui(hapd->own_addr);
278*45d3cc13Schristos 		else
279*45d3cc13Schristos 			os_memcpy(hapd->own_addr, b, ETH_ALEN);
280*45d3cc13Schristos 
281*45d3cc13Schristos 		hostapd_mld_add_link(hapd);
282*45d3cc13Schristos 		wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s",
283*45d3cc13Schristos 			   hapd->mld_link_id, hapd->conf->iface);
284*45d3cc13Schristos 	}
285*45d3cc13Schristos 
286*45d3cc13Schristos setup_mld:
287*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
288*45d3cc13Schristos 
289111b9fd8Schristos 	if (hapd->driver->get_capa &&
290e604d861Schristos 	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
291bb610346Schristos 		struct wowlan_triggers *triggs;
292bb610346Schristos 
293111b9fd8Schristos 		iface->drv_flags = capa.flags;
294*45d3cc13Schristos 		iface->drv_flags2 = capa.flags2;
295*45d3cc13Schristos 		iface->drv_rrm_flags = capa.rrm_flags;
296e604d861Schristos 		iface->probe_resp_offloads = capa.probe_resp_offloads;
297ecc36642Schristos 		/*
298ecc36642Schristos 		 * Use default extended capa values from per-radio information
299ecc36642Schristos 		 */
3003c260e60Schristos 		iface->extended_capa = capa.extended_capa;
3013c260e60Schristos 		iface->extended_capa_mask = capa.extended_capa_mask;
3023c260e60Schristos 		iface->extended_capa_len = capa.extended_capa_len;
3033c260e60Schristos 		iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
304bb610346Schristos 
305ecc36642Schristos 		/*
306ecc36642Schristos 		 * Override extended capa with per-interface type (AP), if
307ecc36642Schristos 		 * available from the driver.
308ecc36642Schristos 		 */
309ecc36642Schristos 		hostapd_get_ext_capa(iface);
310ecc36642Schristos 
311*45d3cc13Schristos 		hostapd_get_mld_capa(iface);
312*45d3cc13Schristos 
313bb610346Schristos 		triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
314bb610346Schristos 		if (triggs && hapd->driver->set_wowlan) {
315bb610346Schristos 			if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
316bb610346Schristos 				wpa_printf(MSG_ERROR, "set_wowlan failed");
317bb610346Schristos 		}
318bb610346Schristos 		os_free(triggs);
319*45d3cc13Schristos 
320*45d3cc13Schristos 		iface->mbssid_max_interfaces = capa.mbssid_max_interfaces;
321*45d3cc13Schristos 		iface->ema_max_periodicity = capa.ema_max_periodicity;
3228dbcf02cSchristos 	}
3238dbcf02cSchristos 
324*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
325*45d3cc13Schristos 	if (hapd->conf->mld_ap) {
326*45d3cc13Schristos 		if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) {
327*45d3cc13Schristos 			wpa_printf(MSG_INFO,
328*45d3cc13Schristos 				   "MLD: Not supported by the driver");
329*45d3cc13Schristos 			return -1;
330*45d3cc13Schristos 		}
331*45d3cc13Schristos 
332*45d3cc13Schristos 		/* Initialize the BSS parameter change to 1 */
333*45d3cc13Schristos 		hapd->eht_mld_bss_param_change = 1;
334*45d3cc13Schristos 
335*45d3cc13Schristos 		wpa_printf(MSG_DEBUG,
336*45d3cc13Schristos 			   "MLD: Set link_id=%u, mld_addr=" MACSTR
337*45d3cc13Schristos 			   ", own_addr=" MACSTR,
338*45d3cc13Schristos 			   hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
339*45d3cc13Schristos 			   MAC2STR(hapd->own_addr));
340*45d3cc13Schristos 
341*45d3cc13Schristos 		hostapd_drv_link_add(hapd, hapd->mld_link_id,
342*45d3cc13Schristos 				     hapd->own_addr);
343*45d3cc13Schristos 	}
344*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
345*45d3cc13Schristos 
346e604d861Schristos 	return 0;
3478dbcf02cSchristos }
3488dbcf02cSchristos 
3498dbcf02cSchristos 
3503c260e60Schristos /**
3513c260e60Schristos  * hostapd_interface_init - Read configuration file and init BSS data
3523c260e60Schristos  *
3533c260e60Schristos  * This function is used to parse configuration file for a full interface (one
3543c260e60Schristos  * or more BSSes sharing the same radio) and allocate memory for the BSS
355be6b3c4dSchristos  * interfaces. No actual driver operations are started.
3563c260e60Schristos  */
3578dbcf02cSchristos static struct hostapd_iface *
358ecc36642Schristos hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
3598dbcf02cSchristos 		       const char *config_fname, int debug)
3608dbcf02cSchristos {
3618dbcf02cSchristos 	struct hostapd_iface *iface;
3628dbcf02cSchristos 	int k;
3638dbcf02cSchristos 
364*45d3cc13Schristos 	wpa_printf(MSG_DEBUG, "Configuration file: %s", config_fname);
3653c260e60Schristos 	iface = hostapd_init(interfaces, config_fname);
3668dbcf02cSchristos 	if (!iface)
3678dbcf02cSchristos 		return NULL;
368ecc36642Schristos 
369ecc36642Schristos 	if (if_name) {
370ecc36642Schristos 		os_strlcpy(iface->conf->bss[0]->iface, if_name,
371ecc36642Schristos 			   sizeof(iface->conf->bss[0]->iface));
372ecc36642Schristos 	}
373ecc36642Schristos 
3748dbcf02cSchristos 	iface->interfaces = interfaces;
3758dbcf02cSchristos 
3768dbcf02cSchristos 	for (k = 0; k < debug; k++) {
3778dbcf02cSchristos 		if (iface->bss[0]->conf->logger_stdout_level > 0)
3788dbcf02cSchristos 			iface->bss[0]->conf->logger_stdout_level--;
3798dbcf02cSchristos 	}
3808dbcf02cSchristos 
3813c260e60Schristos 	if (iface->conf->bss[0]->iface[0] == '\0' &&
3823c260e60Schristos 	    !hostapd_drv_none(iface->bss[0])) {
383ecc36642Schristos 		wpa_printf(MSG_ERROR,
384ecc36642Schristos 			   "Interface name not specified in %s, nor by '-i' parameter",
3853c260e60Schristos 			   config_fname);
3868dbcf02cSchristos 		hostapd_interface_deinit_free(iface);
3878dbcf02cSchristos 		return NULL;
3888dbcf02cSchristos 	}
3898dbcf02cSchristos 
3908dbcf02cSchristos 	return iface;
3918dbcf02cSchristos }
3928dbcf02cSchristos 
3938dbcf02cSchristos 
3948dbcf02cSchristos /**
3958dbcf02cSchristos  * handle_term - SIGINT and SIGTERM handler to terminate hostapd process
3968dbcf02cSchristos  */
3978dbcf02cSchristos static void handle_term(int sig, void *signal_ctx)
3988dbcf02cSchristos {
3998dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
4008dbcf02cSchristos 	eloop_terminate();
4018dbcf02cSchristos }
4028dbcf02cSchristos 
4038dbcf02cSchristos 
4048dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
4058dbcf02cSchristos 
4068dbcf02cSchristos static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
4078dbcf02cSchristos {
4088dbcf02cSchristos 	if (hostapd_reload_config(iface) < 0) {
4098dbcf02cSchristos 		wpa_printf(MSG_WARNING, "Failed to read new configuration "
4108dbcf02cSchristos 			   "file - continuing with old.");
4118dbcf02cSchristos 	}
4128dbcf02cSchristos 	return 0;
4138dbcf02cSchristos }
4148dbcf02cSchristos 
4158dbcf02cSchristos 
4168dbcf02cSchristos /**
4178dbcf02cSchristos  * handle_reload - SIGHUP handler to reload configuration
4188dbcf02cSchristos  */
4198dbcf02cSchristos static void handle_reload(int sig, void *signal_ctx)
4208dbcf02cSchristos {
4218dbcf02cSchristos 	struct hapd_interfaces *interfaces = signal_ctx;
4228dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
4238dbcf02cSchristos 		   sig);
4248dbcf02cSchristos 	hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
4258dbcf02cSchristos }
4268dbcf02cSchristos 
4278dbcf02cSchristos 
4288dbcf02cSchristos static void handle_dump_state(int sig, void *signal_ctx)
4298dbcf02cSchristos {
4303c260e60Schristos 	/* Not used anymore - ignore signal */
4318dbcf02cSchristos }
4328dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
4338dbcf02cSchristos 
4348dbcf02cSchristos 
435111b9fd8Schristos static int hostapd_global_init(struct hapd_interfaces *interfaces,
436111b9fd8Schristos 			       const char *entropy_file)
4378dbcf02cSchristos {
438111b9fd8Schristos 	int i;
439111b9fd8Schristos 
440111b9fd8Schristos 	os_memset(&global, 0, sizeof(global));
441111b9fd8Schristos 
4428dbcf02cSchristos 	hostapd_logger_register_cb(hostapd_logger_cb);
4438dbcf02cSchristos 
4448dbcf02cSchristos 	if (eap_server_register_methods()) {
4458dbcf02cSchristos 		wpa_printf(MSG_ERROR, "Failed to register EAP methods");
4468dbcf02cSchristos 		return -1;
4478dbcf02cSchristos 	}
4488dbcf02cSchristos 
4498dbcf02cSchristos 	if (eloop_init()) {
4508dbcf02cSchristos 		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
4518dbcf02cSchristos 		return -1;
4528dbcf02cSchristos 	}
453ecc36642Schristos 	interfaces->eloop_initialized = 1;
4548dbcf02cSchristos 
455111b9fd8Schristos 	random_init(entropy_file);
456111b9fd8Schristos 
4578dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
4588dbcf02cSchristos 	eloop_register_signal(SIGHUP, handle_reload, interfaces);
4598dbcf02cSchristos 	eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
4608dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
4618dbcf02cSchristos 	eloop_register_signal_terminate(handle_term, interfaces);
4628dbcf02cSchristos 
4638dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
4648dbcf02cSchristos 	openlog("hostapd", 0, LOG_DAEMON);
4658dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
4668dbcf02cSchristos 
467111b9fd8Schristos 	for (i = 0; wpa_drivers[i]; i++)
468111b9fd8Schristos 		global.drv_count++;
469111b9fd8Schristos 	if (global.drv_count == 0) {
470111b9fd8Schristos 		wpa_printf(MSG_ERROR, "No drivers enabled");
471111b9fd8Schristos 		return -1;
472111b9fd8Schristos 	}
473e604d861Schristos 	global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
474111b9fd8Schristos 	if (global.drv_priv == NULL)
475111b9fd8Schristos 		return -1;
476111b9fd8Schristos 
4778dbcf02cSchristos 	return 0;
4788dbcf02cSchristos }
4798dbcf02cSchristos 
4808dbcf02cSchristos 
481ecc36642Schristos static void hostapd_global_deinit(const char *pid_file, int eloop_initialized)
4828dbcf02cSchristos {
483111b9fd8Schristos 	int i;
484111b9fd8Schristos 
485111b9fd8Schristos 	for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
486111b9fd8Schristos 		if (!global.drv_priv[i])
487111b9fd8Schristos 			continue;
488111b9fd8Schristos 		wpa_drivers[i]->global_deinit(global.drv_priv[i]);
489111b9fd8Schristos 	}
490111b9fd8Schristos 	os_free(global.drv_priv);
491111b9fd8Schristos 	global.drv_priv = NULL;
492111b9fd8Schristos 
4938dbcf02cSchristos #ifdef EAP_SERVER_TNC
4948dbcf02cSchristos 	tncs_global_deinit();
4958dbcf02cSchristos #endif /* EAP_SERVER_TNC */
4968dbcf02cSchristos 
497111b9fd8Schristos 	random_deinit();
498111b9fd8Schristos 
499ecc36642Schristos 	if (eloop_initialized)
5008dbcf02cSchristos 		eloop_destroy();
5018dbcf02cSchristos 
5028dbcf02cSchristos #ifndef CONFIG_NATIVE_WINDOWS
5038dbcf02cSchristos 	closelog();
5048dbcf02cSchristos #endif /* CONFIG_NATIVE_WINDOWS */
5058dbcf02cSchristos 
5068dbcf02cSchristos 	eap_server_unregister_methods();
5078dbcf02cSchristos 
5088dbcf02cSchristos 	os_daemonize_terminate(pid_file);
5098dbcf02cSchristos }
5108dbcf02cSchristos 
5118dbcf02cSchristos 
5128dbcf02cSchristos static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
5138dbcf02cSchristos 			      const char *pid_file)
5148dbcf02cSchristos {
5158dbcf02cSchristos #ifdef EAP_SERVER_TNC
5168dbcf02cSchristos 	int tnc = 0;
5178dbcf02cSchristos 	size_t i, k;
5188dbcf02cSchristos 
5198dbcf02cSchristos 	for (i = 0; !tnc && i < ifaces->count; i++) {
5208dbcf02cSchristos 		for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
5218dbcf02cSchristos 			if (ifaces->iface[i]->bss[0]->conf->tnc) {
5228dbcf02cSchristos 				tnc++;
5238dbcf02cSchristos 				break;
5248dbcf02cSchristos 			}
5258dbcf02cSchristos 		}
5268dbcf02cSchristos 	}
5278dbcf02cSchristos 
5288dbcf02cSchristos 	if (tnc && tncs_global_init() < 0) {
5298dbcf02cSchristos 		wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
5308dbcf02cSchristos 		return -1;
5318dbcf02cSchristos 	}
5328dbcf02cSchristos #endif /* EAP_SERVER_TNC */
5338dbcf02cSchristos 
534d2b81c07Sroy 	if (daemonize) {
535d2b81c07Sroy 		if (os_daemonize(pid_file)) {
536bb610346Schristos 			wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
5378dbcf02cSchristos 			return -1;
5388dbcf02cSchristos 		}
539d2b81c07Sroy 		if (eloop_sock_requeue()) {
540d2b81c07Sroy 			wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
541d2b81c07Sroy 				   strerror(errno));
542d2b81c07Sroy 			return -1;
543d2b81c07Sroy 		}
544d2b81c07Sroy 	}
5458dbcf02cSchristos 
5468dbcf02cSchristos 	eloop_run();
5478dbcf02cSchristos 
5488dbcf02cSchristos 	return 0;
5498dbcf02cSchristos }
5508dbcf02cSchristos 
5518dbcf02cSchristos 
5528dbcf02cSchristos static void show_version(void)
5538dbcf02cSchristos {
5548dbcf02cSchristos 	fprintf(stderr,
555*45d3cc13Schristos 		"hostapd v%s\n"
5568dbcf02cSchristos 		"User space daemon for IEEE 802.11 AP management,\n"
5578dbcf02cSchristos 		"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
558*45d3cc13Schristos 		"Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> "
559*45d3cc13Schristos 		"and contributors\n",
560*45d3cc13Schristos 		VERSION_STR);
5618dbcf02cSchristos }
5628dbcf02cSchristos 
5638dbcf02cSchristos 
5648dbcf02cSchristos static void usage(void)
5658dbcf02cSchristos {
5668dbcf02cSchristos 	show_version();
5678dbcf02cSchristos 	fprintf(stderr,
5688dbcf02cSchristos 		"\n"
569*45d3cc13Schristos 		"usage: hostapd [-hdBKtvq] [-P <PID file>] [-e <entropy file>] "
570e604d861Schristos 		"\\\n"
5713c260e60Schristos 		"         [-g <global ctrl_iface>] [-G <group>]\\\n"
572ecc36642Schristos 		"         [-i <comma-separated list of interface names>]\\\n"
5733c260e60Schristos 		"         <configuration file(s)>\n"
5748dbcf02cSchristos 		"\n"
5758dbcf02cSchristos 		"options:\n"
5768dbcf02cSchristos 		"   -h   show this usage\n"
5778dbcf02cSchristos 		"   -d   show more debug messages (-dd for even more)\n"
5788dbcf02cSchristos 		"   -B   run daemon in the background\n"
579111b9fd8Schristos 		"   -e   entropy file\n"
580e604d861Schristos 		"   -g   global control interface path\n"
5813c260e60Schristos 		"   -G   group for control interfaces\n"
5828dbcf02cSchristos 		"   -P   PID file\n"
5838dbcf02cSchristos 		"   -K   include key data in debug messages\n"
584111b9fd8Schristos #ifdef CONFIG_DEBUG_FILE
585111b9fd8Schristos 		"   -f   log output to debug file instead of stdout\n"
586111b9fd8Schristos #endif /* CONFIG_DEBUG_FILE */
5873c260e60Schristos #ifdef CONFIG_DEBUG_LINUX_TRACING
588be6b3c4dSchristos 		"   -T   record to Linux tracing in addition to logging\n"
5893c260e60Schristos 		"        (records all messages regardless of debug verbosity)\n"
5903c260e60Schristos #endif /* CONFIG_DEBUG_LINUX_TRACING */
591ecc36642Schristos 		"   -i   list of interface names to use\n"
592be6b3c4dSchristos #ifdef CONFIG_DEBUG_SYSLOG
593be6b3c4dSchristos 		"   -s   log output to syslog instead of stdout\n"
594be6b3c4dSchristos #endif /* CONFIG_DEBUG_SYSLOG */
595ecc36642Schristos 		"   -S   start all the interfaces synchronously\n"
5968dbcf02cSchristos 		"   -t   include timestamps in some debug messages\n"
597*45d3cc13Schristos 		"   -v   show hostapd version\n"
598*45d3cc13Schristos 		"   -q   show less debug messages (-qq for even less)\n");
5998dbcf02cSchristos 
6008dbcf02cSchristos 	exit(1);
6018dbcf02cSchristos }
6028dbcf02cSchristos 
6038dbcf02cSchristos 
604111b9fd8Schristos static const char * hostapd_msg_ifname_cb(void *ctx)
605111b9fd8Schristos {
606111b9fd8Schristos 	struct hostapd_data *hapd = ctx;
607ecc36642Schristos 	if (hapd && hapd->conf)
608ecc36642Schristos 		return hapd->conf->iface;
609111b9fd8Schristos 	return NULL;
610111b9fd8Schristos }
611111b9fd8Schristos 
612111b9fd8Schristos 
613e604d861Schristos static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
614e604d861Schristos 					 const char *path)
615e604d861Schristos {
616ecc36642Schristos #ifndef CONFIG_CTRL_IFACE_UDP
617e604d861Schristos 	char *pos;
618ecc36642Schristos #endif /* !CONFIG_CTRL_IFACE_UDP */
619ecc36642Schristos 
620e604d861Schristos 	os_free(interfaces->global_iface_path);
621e604d861Schristos 	interfaces->global_iface_path = os_strdup(path);
622e604d861Schristos 	if (interfaces->global_iface_path == NULL)
623e604d861Schristos 		return -1;
624ecc36642Schristos 
625ecc36642Schristos #ifndef CONFIG_CTRL_IFACE_UDP
626e604d861Schristos 	pos = os_strrchr(interfaces->global_iface_path, '/');
627e604d861Schristos 	if (pos == NULL) {
6283c260e60Schristos 		wpa_printf(MSG_ERROR, "No '/' in the global control interface "
6293c260e60Schristos 			   "file");
630e604d861Schristos 		os_free(interfaces->global_iface_path);
631e604d861Schristos 		interfaces->global_iface_path = NULL;
632e604d861Schristos 		return -1;
633e604d861Schristos 	}
634e604d861Schristos 
635e604d861Schristos 	*pos = '\0';
636e604d861Schristos 	interfaces->global_iface_name = pos + 1;
637ecc36642Schristos #endif /* !CONFIG_CTRL_IFACE_UDP */
638e604d861Schristos 
639e604d861Schristos 	return 0;
640e604d861Schristos }
641e604d861Schristos 
642e604d861Schristos 
6433c260e60Schristos static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
6443c260e60Schristos 					const char *group)
6453c260e60Schristos {
6463c260e60Schristos #ifndef CONFIG_NATIVE_WINDOWS
6473c260e60Schristos 	struct group *grp;
6483c260e60Schristos 	grp = getgrnam(group);
6493c260e60Schristos 	if (grp == NULL) {
6503c260e60Schristos 		wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
6513c260e60Schristos 		return -1;
6523c260e60Schristos 	}
6533c260e60Schristos 	interfaces->ctrl_iface_group = grp->gr_gid;
6543c260e60Schristos #endif /* CONFIG_NATIVE_WINDOWS */
6553c260e60Schristos 	return 0;
6563c260e60Schristos }
6573c260e60Schristos 
6583c260e60Schristos 
659ecc36642Schristos static int hostapd_get_interface_names(char ***if_names,
660ecc36642Schristos 				       size_t *if_names_size,
661be6b3c4dSchristos 				       char *arg)
662ecc36642Schristos {
663ecc36642Schristos 	char *if_name, *tmp, **nnames;
664ecc36642Schristos 	size_t i;
665ecc36642Schristos 
666be6b3c4dSchristos 	if (!arg)
667ecc36642Schristos 		return -1;
668be6b3c4dSchristos 	if_name = strtok_r(arg, ",", &tmp);
669ecc36642Schristos 
670ecc36642Schristos 	while (if_name) {
671ecc36642Schristos 		nnames = os_realloc_array(*if_names, 1 + *if_names_size,
672ecc36642Schristos 					  sizeof(char *));
673ecc36642Schristos 		if (!nnames)
674ecc36642Schristos 			goto fail;
675ecc36642Schristos 		*if_names = nnames;
676ecc36642Schristos 
677ecc36642Schristos 		(*if_names)[*if_names_size] = os_strdup(if_name);
678ecc36642Schristos 		if (!(*if_names)[*if_names_size])
679ecc36642Schristos 			goto fail;
680ecc36642Schristos 		(*if_names_size)++;
681ecc36642Schristos 		if_name = strtok_r(NULL, ",", &tmp);
682ecc36642Schristos 	}
683ecc36642Schristos 
684ecc36642Schristos 	return 0;
685ecc36642Schristos 
686ecc36642Schristos fail:
687ecc36642Schristos 	for (i = 0; i < *if_names_size; i++)
688ecc36642Schristos 		os_free((*if_names)[i]);
689ecc36642Schristos 	os_free(*if_names);
690ecc36642Schristos 	*if_names = NULL;
691ecc36642Schristos 	*if_names_size = 0;
692ecc36642Schristos 	return -1;
693ecc36642Schristos }
694ecc36642Schristos 
695ecc36642Schristos 
6963c260e60Schristos #ifdef CONFIG_WPS
6973c260e60Schristos static int gen_uuid(const char *txt_addr)
6983c260e60Schristos {
6993c260e60Schristos 	u8 addr[ETH_ALEN];
7003c260e60Schristos 	u8 uuid[UUID_LEN];
7013c260e60Schristos 	char buf[100];
7023c260e60Schristos 
7033c260e60Schristos 	if (hwaddr_aton(txt_addr, addr) < 0)
7043c260e60Schristos 		return -1;
7053c260e60Schristos 
7063c260e60Schristos 	uuid_gen_mac_addr(addr, uuid);
7073c260e60Schristos 	if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
7083c260e60Schristos 		return -1;
7093c260e60Schristos 
7103c260e60Schristos 	printf("%s\n", buf);
7113c260e60Schristos 
7123c260e60Schristos 	return 0;
7133c260e60Schristos }
7143c260e60Schristos #endif /* CONFIG_WPS */
7153c260e60Schristos 
7163c260e60Schristos 
717ecc36642Schristos #ifndef HOSTAPD_CLEANUP_INTERVAL
718ecc36642Schristos #define HOSTAPD_CLEANUP_INTERVAL 10
719ecc36642Schristos #endif /* HOSTAPD_CLEANUP_INTERVAL */
720ecc36642Schristos 
721ecc36642Schristos static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
722ecc36642Schristos {
723ecc36642Schristos 	hostapd_periodic_iface(iface);
724ecc36642Schristos 	return 0;
725ecc36642Schristos }
726ecc36642Schristos 
727ecc36642Schristos 
728ecc36642Schristos /* Periodic cleanup tasks */
729ecc36642Schristos static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
730ecc36642Schristos {
731ecc36642Schristos 	struct hapd_interfaces *interfaces = eloop_ctx;
732ecc36642Schristos 
733ecc36642Schristos 	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
734ecc36642Schristos 			       hostapd_periodic, interfaces, NULL);
735ecc36642Schristos 	hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
736ecc36642Schristos }
737ecc36642Schristos 
738ecc36642Schristos 
739*45d3cc13Schristos static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
740*45d3cc13Schristos {
741*45d3cc13Schristos #ifdef CONFIG_IEEE80211BE
742*45d3cc13Schristos 	size_t i;
743*45d3cc13Schristos 
744*45d3cc13Schristos 	if (!interfaces || !interfaces->mld)
745*45d3cc13Schristos 		return;
746*45d3cc13Schristos 
747*45d3cc13Schristos 	for (i = 0; i < interfaces->mld_count; i++) {
748*45d3cc13Schristos 		if (!interfaces->mld[i])
749*45d3cc13Schristos 			continue;
750*45d3cc13Schristos 
751*45d3cc13Schristos 		os_free(interfaces->mld[i]);
752*45d3cc13Schristos 		interfaces->mld[i] = NULL;
753*45d3cc13Schristos 	}
754*45d3cc13Schristos 
755*45d3cc13Schristos 	os_free(interfaces->mld);
756*45d3cc13Schristos 	interfaces->mld = NULL;
757*45d3cc13Schristos 	interfaces->mld_count = 0;
758*45d3cc13Schristos #endif /* CONFIG_IEEE80211BE */
759*45d3cc13Schristos }
760*45d3cc13Schristos 
761*45d3cc13Schristos 
7628dbcf02cSchristos int main(int argc, char *argv[])
7638dbcf02cSchristos {
7648dbcf02cSchristos 	struct hapd_interfaces interfaces;
7658dbcf02cSchristos 	int ret = 1;
7663c260e60Schristos 	size_t i, j;
7678dbcf02cSchristos 	int c, debug = 0, daemonize = 0;
7688dbcf02cSchristos 	char *pid_file = NULL;
769111b9fd8Schristos 	const char *log_file = NULL;
770111b9fd8Schristos 	const char *entropy_file = NULL;
7713c260e60Schristos 	char **bss_config = NULL, **tmp_bss;
7723c260e60Schristos 	size_t num_bss_configs = 0;
7733c260e60Schristos #ifdef CONFIG_DEBUG_LINUX_TRACING
7743c260e60Schristos 	int enable_trace_dbg = 0;
7753c260e60Schristos #endif /* CONFIG_DEBUG_LINUX_TRACING */
776ecc36642Schristos 	int start_ifaces_in_sync = 0;
777ecc36642Schristos 	char **if_names = NULL;
778ecc36642Schristos 	size_t if_names_size = 0;
779460bb4fcSchristos #ifdef CONFIG_DPP
780460bb4fcSchristos 	struct dpp_global_config dpp_conf;
781460bb4fcSchristos #endif /* CONFIG_DPP */
7828dbcf02cSchristos 
7838dbcf02cSchristos 	if (os_program_init())
7848dbcf02cSchristos 		return -1;
7858dbcf02cSchristos 
786e604d861Schristos 	os_memset(&interfaces, 0, sizeof(interfaces));
787e604d861Schristos 	interfaces.reload_config = hostapd_reload_config;
788e604d861Schristos 	interfaces.config_read_cb = hostapd_config_read;
789e604d861Schristos 	interfaces.for_each_interface = hostapd_for_each_interface;
790e604d861Schristos 	interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
791e604d861Schristos 	interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
792e604d861Schristos 	interfaces.driver_init = hostapd_driver_init;
793e604d861Schristos 	interfaces.global_iface_path = NULL;
794e604d861Schristos 	interfaces.global_iface_name = NULL;
795e604d861Schristos 	interfaces.global_ctrl_sock = -1;
796ecc36642Schristos 	dl_list_init(&interfaces.global_ctrl_dst);
797be6b3c4dSchristos #ifdef CONFIG_ETH_P_OUI
798be6b3c4dSchristos 	dl_list_init(&interfaces.eth_p_oui);
799be6b3c4dSchristos #endif /* CONFIG_ETH_P_OUI */
800be6b3c4dSchristos #ifdef CONFIG_DPP
801460bb4fcSchristos 	os_memset(&dpp_conf, 0, sizeof(dpp_conf));
802*45d3cc13Schristos 	dpp_conf.cb_ctx = &interfaces;
803*45d3cc13Schristos #ifdef CONFIG_DPP2
804*45d3cc13Schristos 	dpp_conf.remove_bi = hostapd_dpp_remove_bi;
805*45d3cc13Schristos #endif /* CONFIG_DPP2 */
806460bb4fcSchristos 	interfaces.dpp = dpp_global_init(&dpp_conf);
807460bb4fcSchristos 	if (!interfaces.dpp)
808460bb4fcSchristos 		return -1;
809be6b3c4dSchristos #endif /* CONFIG_DPP */
810e604d861Schristos 
8118dbcf02cSchristos 	for (;;) {
812*45d3cc13Schristos 		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
8138dbcf02cSchristos 		if (c < 0)
8148dbcf02cSchristos 			break;
8158dbcf02cSchristos 		switch (c) {
8168dbcf02cSchristos 		case 'h':
8178dbcf02cSchristos 			usage();
8188dbcf02cSchristos 			break;
8198dbcf02cSchristos 		case 'd':
8208dbcf02cSchristos 			debug++;
8218dbcf02cSchristos 			if (wpa_debug_level > 0)
8228dbcf02cSchristos 				wpa_debug_level--;
8238dbcf02cSchristos 			break;
8248dbcf02cSchristos 		case 'B':
8258dbcf02cSchristos 			daemonize++;
8268dbcf02cSchristos 			break;
827111b9fd8Schristos 		case 'e':
828111b9fd8Schristos 			entropy_file = optarg;
829111b9fd8Schristos 			break;
830111b9fd8Schristos 		case 'f':
831111b9fd8Schristos 			log_file = optarg;
832111b9fd8Schristos 			break;
8338dbcf02cSchristos 		case 'K':
8348dbcf02cSchristos 			wpa_debug_show_keys++;
8358dbcf02cSchristos 			break;
8368dbcf02cSchristos 		case 'P':
8378dbcf02cSchristos 			os_free(pid_file);
8388dbcf02cSchristos 			pid_file = os_rel2abs_path(optarg);
8398dbcf02cSchristos 			break;
8408dbcf02cSchristos 		case 't':
8418dbcf02cSchristos 			wpa_debug_timestamp++;
8428dbcf02cSchristos 			break;
8433c260e60Schristos #ifdef CONFIG_DEBUG_LINUX_TRACING
8443c260e60Schristos 		case 'T':
8453c260e60Schristos 			enable_trace_dbg = 1;
8463c260e60Schristos 			break;
8473c260e60Schristos #endif /* CONFIG_DEBUG_LINUX_TRACING */
8488dbcf02cSchristos 		case 'v':
8498dbcf02cSchristos 			show_version();
8508dbcf02cSchristos 			exit(1);
851e604d861Schristos 		case 'g':
8523c260e60Schristos 			if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
8533c260e60Schristos 				return -1;
854e604d861Schristos 			break;
8553c260e60Schristos 		case 'G':
8563c260e60Schristos 			if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
8573c260e60Schristos 				return -1;
8583c260e60Schristos 			break;
8593c260e60Schristos 		case 'b':
8603c260e60Schristos 			tmp_bss = os_realloc_array(bss_config,
8613c260e60Schristos 						   num_bss_configs + 1,
8623c260e60Schristos 						   sizeof(char *));
8633c260e60Schristos 			if (tmp_bss == NULL)
8643c260e60Schristos 				goto out;
8653c260e60Schristos 			bss_config = tmp_bss;
8663c260e60Schristos 			bss_config[num_bss_configs++] = optarg;
8673c260e60Schristos 			break;
868be6b3c4dSchristos #ifdef CONFIG_DEBUG_SYSLOG
869be6b3c4dSchristos 		case 's':
870be6b3c4dSchristos 			wpa_debug_syslog = 1;
871be6b3c4dSchristos 			break;
872be6b3c4dSchristos #endif /* CONFIG_DEBUG_SYSLOG */
873ecc36642Schristos 		case 'S':
874ecc36642Schristos 			start_ifaces_in_sync = 1;
875ecc36642Schristos 			break;
8763c260e60Schristos #ifdef CONFIG_WPS
8773c260e60Schristos 		case 'u':
8783c260e60Schristos 			return gen_uuid(optarg);
8793c260e60Schristos #endif /* CONFIG_WPS */
880ecc36642Schristos 		case 'i':
881ecc36642Schristos 			if (hostapd_get_interface_names(&if_names,
882ecc36642Schristos 							&if_names_size, optarg))
883ecc36642Schristos 				goto out;
884ecc36642Schristos 			break;
885*45d3cc13Schristos 		case 'q':
886*45d3cc13Schristos 			wpa_debug_level++;
887*45d3cc13Schristos 			break;
8888dbcf02cSchristos 		default:
8898dbcf02cSchristos 			usage();
8908dbcf02cSchristos 			break;
8918dbcf02cSchristos 		}
8928dbcf02cSchristos 	}
8938dbcf02cSchristos 
8943c260e60Schristos 	if (optind == argc && interfaces.global_iface_path == NULL &&
8953c260e60Schristos 	    num_bss_configs == 0)
8968dbcf02cSchristos 		usage();
8978dbcf02cSchristos 
898111b9fd8Schristos 	wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
899111b9fd8Schristos 
900111b9fd8Schristos 	if (log_file)
901111b9fd8Schristos 		wpa_debug_open_file(log_file);
902*45d3cc13Schristos 	if (!log_file && !wpa_debug_syslog)
903bb610346Schristos 		wpa_debug_setup_stdout();
904be6b3c4dSchristos #ifdef CONFIG_DEBUG_SYSLOG
905be6b3c4dSchristos 	if (wpa_debug_syslog)
906be6b3c4dSchristos 		wpa_debug_open_syslog();
907be6b3c4dSchristos #endif /* CONFIG_DEBUG_SYSLOG */
9083c260e60Schristos #ifdef CONFIG_DEBUG_LINUX_TRACING
9093c260e60Schristos 	if (enable_trace_dbg) {
9103c260e60Schristos 		int tret = wpa_debug_open_linux_tracing();
9113c260e60Schristos 		if (tret) {
9123c260e60Schristos 			wpa_printf(MSG_ERROR, "Failed to enable trace logging");
9133c260e60Schristos 			return -1;
9143c260e60Schristos 		}
9153c260e60Schristos 	}
9163c260e60Schristos #endif /* CONFIG_DEBUG_LINUX_TRACING */
917111b9fd8Schristos 
9188dbcf02cSchristos 	interfaces.count = argc - optind;
9193c260e60Schristos 	if (interfaces.count || num_bss_configs) {
9203c260e60Schristos 		interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
9218dbcf02cSchristos 					     sizeof(struct hostapd_iface *));
9228dbcf02cSchristos 		if (interfaces.iface == NULL) {
923111b9fd8Schristos 			wpa_printf(MSG_ERROR, "malloc failed");
9248dbcf02cSchristos 			return -1;
9258dbcf02cSchristos 		}
926e604d861Schristos 	}
9278dbcf02cSchristos 
9283c260e60Schristos 	if (hostapd_global_init(&interfaces, entropy_file)) {
929ecc36642Schristos 		wpa_printf(MSG_ERROR, "Failed to initialize global context");
9308dbcf02cSchristos 		return -1;
9313c260e60Schristos 	}
9328dbcf02cSchristos 
933ecc36642Schristos 	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
934ecc36642Schristos 			       hostapd_periodic, &interfaces, NULL);
935ecc36642Schristos 
936ecc36642Schristos 	if (fst_global_init()) {
937ecc36642Schristos 		wpa_printf(MSG_ERROR,
938ecc36642Schristos 			   "Failed to initialize global FST context");
939ecc36642Schristos 		goto out;
940ecc36642Schristos 	}
941ecc36642Schristos 
942ecc36642Schristos #if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
943ecc36642Schristos 	if (!fst_global_add_ctrl(fst_ctrl_cli))
944ecc36642Schristos 		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
945ecc36642Schristos #endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
946ecc36642Schristos 
9473c260e60Schristos 	/* Allocate and parse configuration for full interface files */
9488dbcf02cSchristos 	for (i = 0; i < interfaces.count; i++) {
949ecc36642Schristos 		char *if_name = NULL;
950ecc36642Schristos 
951ecc36642Schristos 		if (i < if_names_size)
952ecc36642Schristos 			if_name = if_names[i];
953ecc36642Schristos 
9548dbcf02cSchristos 		interfaces.iface[i] = hostapd_interface_init(&interfaces,
955ecc36642Schristos 							     if_name,
9568dbcf02cSchristos 							     argv[optind + i],
9578dbcf02cSchristos 							     debug);
9583c260e60Schristos 		if (!interfaces.iface[i]) {
9593c260e60Schristos 			wpa_printf(MSG_ERROR, "Failed to initialize interface");
9603c260e60Schristos 			goto out;
9613c260e60Schristos 		}
962ecc36642Schristos 		if (start_ifaces_in_sync)
963ecc36642Schristos 			interfaces.iface[i]->need_to_start_in_sync = 1;
9643c260e60Schristos 	}
9653c260e60Schristos 
9663c260e60Schristos 	/* Allocate and parse configuration for per-BSS files */
9673c260e60Schristos 	for (i = 0; i < num_bss_configs; i++) {
9683c260e60Schristos 		struct hostapd_iface *iface;
9693c260e60Schristos 		char *fname;
9703c260e60Schristos 
9713c260e60Schristos 		wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
9723c260e60Schristos 		fname = os_strchr(bss_config[i], ':');
9733c260e60Schristos 		if (fname == NULL) {
9743c260e60Schristos 			wpa_printf(MSG_ERROR,
9753c260e60Schristos 				   "Invalid BSS config identifier '%s'",
9763c260e60Schristos 				   bss_config[i]);
9773c260e60Schristos 			goto out;
9783c260e60Schristos 		}
9793c260e60Schristos 		*fname++ = '\0';
9803c260e60Schristos 		iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
9813c260e60Schristos 						   fname, debug);
9823c260e60Schristos 		if (iface == NULL)
9833c260e60Schristos 			goto out;
9843c260e60Schristos 		for (j = 0; j < interfaces.count; j++) {
9853c260e60Schristos 			if (interfaces.iface[j] == iface)
9863c260e60Schristos 				break;
9873c260e60Schristos 		}
9883c260e60Schristos 		if (j == interfaces.count) {
9893c260e60Schristos 			struct hostapd_iface **tmp;
9903c260e60Schristos 			tmp = os_realloc_array(interfaces.iface,
9913c260e60Schristos 					       interfaces.count + 1,
9923c260e60Schristos 					       sizeof(struct hostapd_iface *));
9933c260e60Schristos 			if (tmp == NULL) {
9943c260e60Schristos 				hostapd_interface_deinit_free(iface);
9953c260e60Schristos 				goto out;
9963c260e60Schristos 			}
9973c260e60Schristos 			interfaces.iface = tmp;
9983c260e60Schristos 			interfaces.iface[interfaces.count++] = iface;
9993c260e60Schristos 		}
10003c260e60Schristos 	}
10013c260e60Schristos 
10023c260e60Schristos 	/*
10033c260e60Schristos 	 * Enable configured interfaces. Depending on channel configuration,
10043c260e60Schristos 	 * this may complete full initialization before returning or use a
10053c260e60Schristos 	 * callback mechanism to complete setup in case of operations like HT
10063c260e60Schristos 	 * co-ex scans, ACS, or DFS are needed to determine channel parameters.
10073c260e60Schristos 	 * In such case, the interface will be enabled from eloop context within
10083c260e60Schristos 	 * hostapd_global_run().
10093c260e60Schristos 	 */
10103c260e60Schristos 	interfaces.terminate_on_error = interfaces.count;
10113c260e60Schristos 	for (i = 0; i < interfaces.count; i++) {
10123c260e60Schristos 		if (hostapd_driver_init(interfaces.iface[i]) ||
10133c260e60Schristos 		    hostapd_setup_interface(interfaces.iface[i]))
10148dbcf02cSchristos 			goto out;
10158dbcf02cSchristos 	}
10168dbcf02cSchristos 
1017e604d861Schristos 	hostapd_global_ctrl_iface_init(&interfaces);
1018e604d861Schristos 
10193c260e60Schristos 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
10203c260e60Schristos 		wpa_printf(MSG_ERROR, "Failed to start eloop");
10218dbcf02cSchristos 		goto out;
10223c260e60Schristos 	}
10238dbcf02cSchristos 
10248dbcf02cSchristos 	ret = 0;
10258dbcf02cSchristos 
10268dbcf02cSchristos  out:
1027e604d861Schristos 	hostapd_global_ctrl_iface_deinit(&interfaces);
10288dbcf02cSchristos 	/* Deinitialize all interfaces */
10293c260e60Schristos 	for (i = 0; i < interfaces.count; i++) {
10303c260e60Schristos 		if (!interfaces.iface[i])
10313c260e60Schristos 			continue;
10323c260e60Schristos 		interfaces.iface[i]->driver_ap_teardown =
10333c260e60Schristos 			!!(interfaces.iface[i]->drv_flags &
10343c260e60Schristos 			   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
10358dbcf02cSchristos 		hostapd_interface_deinit_free(interfaces.iface[i]);
1036*45d3cc13Schristos 		interfaces.iface[i] = NULL;
10373c260e60Schristos 	}
10388dbcf02cSchristos 	os_free(interfaces.iface);
1039*45d3cc13Schristos 	interfaces.iface = NULL;
1040*45d3cc13Schristos 	interfaces.count = 0;
1041*45d3cc13Schristos 
1042*45d3cc13Schristos 	hostapd_global_cleanup_mld(&interfaces);
10438dbcf02cSchristos 
1044be6b3c4dSchristos #ifdef CONFIG_DPP
1045460bb4fcSchristos 	dpp_global_deinit(interfaces.dpp);
1046be6b3c4dSchristos #endif /* CONFIG_DPP */
1047be6b3c4dSchristos 
1048ecc36642Schristos 	if (interfaces.eloop_initialized)
1049ecc36642Schristos 		eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
1050ecc36642Schristos 	hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
10518dbcf02cSchristos 	os_free(pid_file);
10528dbcf02cSchristos 
1053be6b3c4dSchristos 	wpa_debug_close_syslog();
1054111b9fd8Schristos 	if (log_file)
1055111b9fd8Schristos 		wpa_debug_close_file();
10563c260e60Schristos 	wpa_debug_close_linux_tracing();
10573c260e60Schristos 
10583c260e60Schristos 	os_free(bss_config);
1059111b9fd8Schristos 
1060ecc36642Schristos 	for (i = 0; i < if_names_size; i++)
1061ecc36642Schristos 		os_free(if_names[i]);
1062ecc36642Schristos 	os_free(if_names);
1063ecc36642Schristos 
1064ecc36642Schristos 	fst_global_deinit();
1065ecc36642Schristos 
1066*45d3cc13Schristos 	crypto_unload();
10678dbcf02cSchristos 	os_program_deinit();
10688dbcf02cSchristos 
10698dbcf02cSchristos 	return ret;
10708dbcf02cSchristos }
1071