hostpad: Make hostapd to be able to remove the primary interface

Submitted by Edward Cheung on July 20, 2017, 3:04 p.m.

Details

Message ID 1500563044-4092-1-git-send-email-kccheung98@gmail.com
State New
Headers show

Commit Message

Edward Cheung July 20, 2017, 3:04 p.m.
The primary or the first interface created cannot be removed by wpa_cli,
only the secondary interfaces (bss) were allowed to be removed. After
applying the patch, even the primary interface can be removed and the
next interface will become the primary interface.

Signed-off-by: Edward Cheung <kccheung98@gmail.com>
---
 hostapd/main.c               |  12 ++++
 src/ap/hostapd.c             |  45 ++++++++++-----
 src/drivers/driver.h         |   1 +
 src/drivers/driver_nl80211.c | 132 ++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 174 insertions(+), 16 deletions(-)

Patch hide | download patch | download mbox

diff --git a/hostapd/main.c b/hostapd/main.c
index ce94d05..39efde9 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -443,6 +443,18 @@  static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
 		}
 	}
 
+	/* allow running in background mode if no configuration file provided */
+	if (ifaces->count == 0) {
+		if(!(ifaces->global_iface_name))
+			return -1;
+
+		if (daemonize && os_daemonize(pid_file)) {
+			perror("daemon");
+			return -1;
+		}
+		daemonize = 0;
+	}
+
 	eloop_run();
 
 	return 0;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 270e818..29ec7c1 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -933,6 +933,8 @@  static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 	}
 	hapd->started = 1;
 
+	/* make all interface can be removable, including the first one */
+	hapd->interface_added = 1;
 	if (!first || first == -1) {
 		u8 *addr = hapd->own_addr;
 
@@ -957,7 +959,6 @@  static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
 		}
 
-		hapd->interface_added = 1;
 		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
 				   conf->iface, addr, hapd,
 				   &hapd->drv_priv, force_ifname, if_addr,
@@ -2777,28 +2778,44 @@  static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
 
 int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
 {
+	const struct wpa_driver_ops *driver;
 	struct hostapd_iface *hapd_iface;
 	size_t i, j, k = 0;
+	int ret;
 
 	for (i = 0; i < interfaces->count; i++) {
 		hapd_iface = interfaces->iface[i];
 		if (hapd_iface == NULL)
 			return -1;
 		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
-			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
-			hapd_iface->driver_ap_teardown =
-				!!(hapd_iface->drv_flags &
-				   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
-
-			hostapd_interface_deinit_free(hapd_iface);
-			k = i;
-			while (k < (interfaces->count - 1)) {
-				interfaces->iface[k] =
-					interfaces->iface[k + 1];
-				k++;
+			if (hapd_iface->conf->num_bss == 1) {
+				wpa_printf(MSG_INFO, "Remove interface '%s'",
+					   buf);
+				hapd_iface->driver_ap_teardown =
+					!!(hapd_iface->drv_flags &
+					   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+				hostapd_interface_deinit_free(hapd_iface);
+				k = i;
+				while (k < (interfaces->count - 1)) {
+					interfaces->iface[k] =
+						interfaces->iface[k + 1];
+					k++;
+				}
+				interfaces->count--;
+				return 0;
+			} else {
+				wpa_printf(MSG_INFO, "Switch interface to %s",
+					   hapd_iface->bss[1]->conf->iface);
+
+				driver = hapd_iface->bss[0]->driver;
+				ret = driver->hapd_switch(hapd_iface->bss[1], 1);
+				if (ret < 0) {
+					wpa_printf(MSG_ERROR, "Interface switch"
+						   " error");
+					return ret;
+				}
 			}
-			interfaces->count--;
-			return 0;
 		}
 
 		for (j = 0; j < hapd_iface->conf->num_bss; j++) {
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index c35d1f7..d7e94fa 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2580,6 +2580,7 @@  struct wpa_driver_ops {
 	 */
 	void (*hapd_deinit)(void *priv);
 
+	int (*hapd_switch)(struct hostapd_data *hd, int idx);
 	/**
 	 * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
 	 * @priv: Private driver interface data
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 40889ce..67a017b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -39,6 +39,8 @@ 
 #include "rfkill.h"
 #include "driver_nl80211.h"
 
+#include "ap/hostapd.h"
+
 
 #ifndef CONFIG_LIBNL20
 /*
@@ -166,6 +168,18 @@  static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
 static void nl80211_global_deinit(void *priv);
 static void nl80211_check_global(struct nl80211_global *global);
 
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+				     const char *ifname,
+				     enum nl80211_iftype iftype,
+				     const u8 *addr, int wds,
+				     int (*handler)(struct nl_msg *, void *),
+				     void *arg);
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+				     const char *ifname, const u8 *addr,
+				     void *bss_ctx, void **drv_priv,
+				     char *force_ifname, u8 *if_addr,
+				     const char *bridge, int use_existing,
+				     int setup_ap);
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
 static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
 					    struct hostapd_freq_params *freq);
@@ -208,6 +222,26 @@  static int nl80211_put_mesh_config(struct nl_msg *msg,
 static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 			     int reason);
 
+static int phy_lookup(char *name)
+{
+	char buf[200];
+	int fd, pos;
+
+	wpa_printf(MSG_INFO, "%s: name: %s", __func__, name?name:"NULL");
+	snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
+
+	fd = open(buf, O_RDONLY);
+	if (fd < 0)
+		return -1;
+	pos = read(fd, buf, sizeof(buf) - 1);
+	if (pos < 0) {
+		close(fd);
+		return -1;
+	}
+	buf[pos] = '\0';
+	close(fd);
+	return atoi(buf);
+}
 
 /* Converts nl80211_chan_width to a common format */
 enum chan_width convert2width(int width)
@@ -461,9 +495,18 @@  void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
 
 static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
 {
+	struct hostapd_data *hapd;
+	int phy_idx;
+
 	if (bss->wdev_id_set)
 		return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-	return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+	else if (bss->ifindex > 0)
+		return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+	else {
+		hapd = bss->ctx;
+		phy_idx = phy_lookup(hapd->iface->phy);
+		return nla_put_u32(msg, NL80211_ATTR_WIPHY, phy_idx);
+	}
 }
 
 
@@ -1849,6 +1892,7 @@  static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
+	int ret;
 
 	if (global_priv == NULL)
 		return NULL;
@@ -1889,6 +1933,20 @@  static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
 	if (nl80211_init_bss(bss))
 		goto failed;
 
+	if (!if_nametoindex(ifname)) {
+		wpa_printf(MSG_DEBUG, "Pre-build new interface: %s", ifname);
+		ret = nl80211_create_iface_once(drv, ifname,
+						  NL80211_IFTYPE_AP, NULL, 0,
+						  NULL, NULL);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Cannot pre-build first interface!");
+			goto failed;
+		}
+	}
+
+	/* interface is removable at any time */
+	bss->added_if = 1;
+
 	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
 		goto failed;
 
@@ -6551,6 +6609,76 @@  static void i802_deinit(void *priv)
 	wpa_driver_nl80211_deinit(bss);
 }
 
+static int i802_switch(struct hostapd_data *hapd, int idx)
+{
+	struct i802_bss *i802bss, *i802targetbss, *prevbss;
+	struct i802_bss *nextbss1, *nextbss2;
+	struct wpa_driver_nl80211_data *drv;
+	struct hostapd_bss_config *bssconf;
+	struct hostapd_iface *hapd_iface;
+	struct hostapd_data *bssdata;
+
+	hapd_iface = hapd->iface;
+	i802bss = (struct i802_bss *)hapd_iface->bss[0]->drv_priv;
+	drv = i802bss->drv;
+
+	if (!os_strcmp(drv->first_bss->ifname, hapd->conf->iface)) {
+	    wpa_printf(MSG_ERROR, "Same interface, no need to switch!");
+	    return -1;
+	}
+
+	bssconf = hapd_iface->conf->bss[0];
+	bssdata = hapd_iface->bss[0];
+
+	hapd_iface->conf->bss[0] = hapd->conf;
+	hapd_iface->bss[0] = hapd;
+
+	prevbss = NULL;
+	for (i802targetbss = drv->first_bss; i802targetbss != NULL;) {
+		if (!os_strcmp(i802targetbss->ifname, hapd->conf->iface)) {
+			break;
+		} else {
+			prevbss = i802targetbss;
+			i802targetbss = i802targetbss->next;
+		}
+	}
+
+	if (!i802targetbss) {
+		wpa_printf(MSG_ERROR, "target is null!");
+		return -1;
+	}
+
+	nextbss1 = i802bss->next;
+	nextbss2 = i802targetbss->next;
+
+	drv->first_bss = i802targetbss;
+
+	if (nextbss1 == i802targetbss) {
+		drv->first_bss->next = i802bss;
+	} else {
+		drv->first_bss->next = nextbss1;
+		prevbss->next = i802bss;
+	}
+
+	i802bss->next = nextbss2;
+
+	/* All interface can be removed by cli */
+	i802bss->added_if = 1;
+	i802targetbss->added_if = 1;
+
+	memcpy(drv->perm_addr, hapd->own_addr, ETH_ALEN);
+	drv->ifindex = if_nametoindex(drv->first_bss->ifname);
+	drv->ctx = drv->first_bss->ctx;
+
+	bssdata->interface_added = 1;
+	hapd->interface_added = 1;
+
+	hapd_iface->bss[idx] = bssdata;
+	hapd_iface->bss[idx]->conf = bssconf;
+	hapd_iface->conf->bss[idx]  = bssconf;
+
+	return 0;
+}
 
 static enum nl80211_iftype wpa_driver_nl80211_if_type(
 	enum wpa_driver_if_type type)
@@ -6680,7 +6808,6 @@  static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
 		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
 					     0, NULL, NULL, use_existing);
 		if (use_existing && ifidx == -ENFILE) {
-			added = 0;
 			ifidx = if_nametoindex(ifname);
 		} else if (ifidx < 0) {
 			return -1;
@@ -10257,6 +10384,7 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
 	.hapd_init = i802_init,
 	.hapd_deinit = i802_deinit,
+	.hapd_switch = i802_switch,
 	.set_wds_sta = i802_set_wds_sta,
 	.get_seqnum = i802_get_seqnum,
 	.flush = i802_flush,