All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax
@ 2021-03-05 15:09 Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 02/10] ap: Add missing ap_config_free() in error path Andrew Zaborowski
                   ` (9 more replies)
  0 siblings, 10 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 15545 bytes --]

After the profile support was added some settings were passed to
ap_start() through the ap_config struct and others were loaded inside
ap_start() based on the profile path.  Instead make profiles transparent
to ap_start():

* don't pass the profile path to ap_start(),
* add storage for the profile settings directly to ap_config,
* load and validate the settings from the profile file directly
  in the caller (ap_dbus_start_profile).

Now any user of the ap_start() api can set those values and the
difference between .Start and .StartProfile is only that one loads the
extra settings from the profile whlie the other relies on the defaults.

Temporarily drop most of the DHCP setup code as it needs to be rewritten
to account for the new setting syntax and setup order, this happens in
the subsequent commits in steps.

While I'm rewriting the profile loading I also expand the [IPv4].Address
syntax to allow a new format: a list of <IP>/<prefix_len> -form strings
that define the address space for subnet address selection.
---
 src/ap.c | 425 ++++++++++++++++++++++---------------------------------
 src/ap.h |  10 +-
 2 files changed, 180 insertions(+), 255 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 2042cde6..677ba8fa 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -225,6 +225,171 @@ static const char *broadcast_from_ip(const char *ip)
 	return inet_ntoa(ia);
 }
 
+#define AP_DEFAULT_IPV4_PREFIX_LEN 28
+
+struct ap_config *ap_config_load_profile(const char *ssid)
+{
+	L_AUTO_FREE_VAR(char *, profile_path) =
+		storage_get_path("ap/%s.ap", ssid);
+	L_AUTO_FREE_OBJ(l_settings, profile) = l_settings_new();
+	L_AUTO_FREE_VAR(char *, passphrase) = NULL;
+	struct ap_config *config;
+	uint32_t static_addr4 = 0;
+	unsigned int lease_time;
+
+	if (!l_settings_load_from_file(profile, profile_path))
+		return NULL;
+
+	passphrase = l_settings_get_string(profile, "Security",
+							"Passphrase");
+	if (!passphrase) {
+		l_error("[Security].Passphrase not found in "
+			"AP profile");
+		return NULL;
+	}
+
+	/* Full checks done later in crypto_passphrase_is_valid() */
+	if (strlen(passphrase) > 63) {
+		l_error("[Security].Passphrase must not exceed "
+			"63 characters");
+		return NULL;
+	}
+
+	config = l_new(struct ap_config, 1);
+	config->ssid = l_strdup(ssid);
+	strcpy(config->passphrase, passphrase);
+
+	if (!l_settings_has_group(profile, "IPv4")) {
+		config->disable_netconfig4 = true;
+		return config;
+	}
+
+	if (l_settings_get_value(profile, "IPv4", "Address")) {
+		struct in_addr ia;
+
+		config->addr4_str_list = l_settings_get_string_list(profile,
+								"IPv4",
+								"Address", ',');
+		if (!config->addr4_str_list || !*config->addr4_str_list) {
+			l_error("Can't parse the profile [IPv4].Address "
+				"setting as a string list");
+			goto invalid_format;
+		}
+
+		/* Check for the static IP syntax: Address=<IP> */
+		if (l_strv_length((char **) config->addr4_str_list) == 1 &&
+				inet_pton(AF_INET, *config->addr4_str_list,
+						&ia) == 1)
+			static_addr4 = ntohl(ia.s_addr);
+	}
+
+	if (l_settings_get_value(profile, "IPv4", "Netmask")) {
+		L_AUTO_FREE_VAR(char *, netmask_str) =
+			l_settings_get_string(profile, "IPv4", "Netmask");
+		struct in_addr ia;
+
+		if (inet_pton(AF_INET, netmask_str, &ia) != 1) {
+			l_error("Can't parse the profile [IPv4].Netmask "
+				"setting");
+			goto invalid_format;
+		}
+
+		config->prefix_len4 = __builtin_popcount(ia.s_addr);
+
+		if (ntohl(ia.s_addr) !=
+				util_netmask_from_prefix(config->prefix_len4)) {
+			l_error("Invalid profile [IPv4].Netmask value");
+			goto invalid_format;
+		}
+	}
+
+	if (l_settings_get_value(profile, "IPv4", "DNSList")) {
+		config->dns_addr4_str_list = l_settings_get_string_list(
+								profile, "IPv4",
+								"DNSList", ',');
+		if (!config->dns_addr4_str_list ||
+				!*config->dns_addr4_str_list) {
+			l_error("Can't parse the profile [IPv4].DNSList "
+				"setting as a string list");
+			goto invalid_format;
+		}
+	}
+
+	if (l_settings_get_value(profile, "IPv4", "IPRange")) {
+		char **ip_range = l_settings_get_string_list(profile, "IPv4",
+								"IPRange", ',');
+		int i;
+		uint32_t netmask;
+
+		if (!static_addr4 || !config->prefix_len4) {
+			l_error("[IPv4].IPRange only makes sense in an AP "
+				"profile if a static local address and "
+				"netmask have also been specified");
+			l_strv_free(ip_range);
+			goto invalid_format;
+		}
+
+		if (!ip_range || l_strv_length(ip_range) != 2) {
+			l_error("Can't parse the profile [IPv4].IPRange "
+				"setting as two address strings");
+			l_strv_free(ip_range);
+			goto invalid_format;
+		}
+
+		netmask = util_netmask_from_prefix(config->prefix_len4);
+
+		for (i = 0; i < 2; i++) {
+			struct in_addr range_addr;
+
+			if (inet_aton(ip_range[i], &range_addr) != 1) {
+				l_error("Can't parse address in "
+					"[IPv4].IPRange[%i]", i + 1);
+				goto invalid_format;
+			}
+
+			if ((static_addr4 ^ ntohl(range_addr.s_addr)) &
+					netmask) {
+				struct in_addr addr = { htonl(static_addr4) };
+
+				l_error("[IPv4].IPRange[%i] is not in the "
+					"%s/%i subnet", i + 1,
+					inet_ntoa(addr), config->prefix_len4);
+				goto invalid_format;
+			}
+
+			if (i == 0)
+				config->dhcp_start_addr4 =
+					ntohl(range_addr.s_addr);
+			else
+				config->dhcp_end_addr4 =
+					ntohl(range_addr.s_addr);
+		}
+	}
+
+	if (l_settings_get_value(profile, "IPv4", "Gateway")) {
+		L_AUTO_FREE_VAR(char *, gateway_str) =
+			l_settings_get_string(profile, "IPv4", "Gateway");
+		struct in_addr ia;
+
+		if (inet_pton(AF_INET, gateway_str, &ia) != 1) {
+			l_error("Can't parse the profile [IPv4].Gateway "
+				"setting");
+			goto invalid_format;
+		}
+
+		config->dhcp_gateway_addr4 = ntohl(ia.s_addr);
+	}
+
+	if (l_settings_get_uint(profile, "IPv4", "LeaseTime", &lease_time))
+		config->dhcp_lease_time = lease_time;
+
+	return config;
+
+invalid_format:
+	ap_config_free(config);
+	return NULL;
+}
+
 void ap_config_free(struct ap_config *config)
 {
 	if (unlikely(!config))
@@ -236,10 +401,8 @@ void ap_config_free(struct ap_config *config)
 	explicit_bzero(config->psk, sizeof(config->psk));
 	l_free(config->authorized_macs);
 	l_free(config->wsc_name);
-
-	if (config->profile)
-		l_free(config->profile);
-
+	l_strv_free(config->addr4_str_list);
+	l_strv_free(config->dns_addr4_str_list);
 	l_free(config);
 }
 
@@ -2394,233 +2557,6 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	}
 }
 
-static bool dhcp_load_settings(struct ap_state *ap, struct l_settings *settings)
-{
-	struct l_dhcp_server *server = ap->server;
-	struct in_addr ia;
-
-	L_AUTO_FREE_VAR(char *, netmask) = l_settings_get_string(settings,
-							"IPv4", "Netmask");
-	L_AUTO_FREE_VAR(char *, gateway) = l_settings_get_string(settings,
-							"IPv4", "Gateway");
-	char **dns = l_settings_get_string_list(settings, "IPv4",
-							"DNSList", ',');
-	char **ip_range = l_settings_get_string_list(settings, "IPv4",
-							"IPRange", ',');
-	unsigned int lease_time;
-	bool ret = false;
-
-	if (!l_settings_get_uint(settings, "IPv4", "LeaseTime", &lease_time))
-		lease_time = 0;
-
-	if (gateway && !l_dhcp_server_set_gateway(server, gateway)) {
-		l_error("[IPv4].Gateway value error");
-		goto error;
-	}
-
-	if (dns && !l_dhcp_server_set_dns(server, dns)) {
-		l_error("[IPv4].DNSList value error");
-		goto error;
-	}
-
-	if (netmask && !l_dhcp_server_set_netmask(server, netmask)) {
-		l_error("[IPv4].Netmask value error");
-		goto error;
-	}
-
-	if (ip_range) {
-		if (l_strv_length(ip_range) != 2) {
-			l_error("Two addresses expected in [IPv4].IPRange");
-			goto error;
-		}
-
-		if (!l_dhcp_server_set_ip_range(server, ip_range[0],
-							ip_range[1])) {
-			l_error("Error setting IP range from [IPv4].IPRange");
-			goto error;
-		}
-	}
-
-	if (lease_time && !l_dhcp_server_set_lease_time(server, lease_time)) {
-		l_error("[IPv4].LeaseTime value error");
-		goto error;
-	}
-
-	if (netmask && inet_pton(AF_INET, netmask, &ia) > 0)
-		ap->ip_prefix = __builtin_popcountl(ia.s_addr);
-	else
-		ap->ip_prefix = 24;
-
-	ret = true;
-
-error:
-	l_strv_free(dns);
-	l_strv_free(ip_range);
-	return ret;
-}
-
-/*
- * This will determine the IP being used for DHCP. The IP will be automatically
- * set to ap->own_ip.
- *
- * The address to set (or keep) is determined in this order:
- * 1. Address defined in provisioning file
- * 2. Address already set on interface
- * 3. Address in IP pool.
- *
- * Returns:  0 if an IP was successfully selected and needs to be set
- *          -EALREADY if an IP was already set on the interface
- *          -EEXIST if the IP pool ran out of IP's
- *          -EINVAL if there was an error.
- */
-static int ap_setup_dhcp(struct ap_state *ap, struct l_settings *settings)
-{
-	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
-	struct in_addr ia;
-	uint32_t address = 0;
-	int ret = -EINVAL;
-
-	ap->server = l_dhcp_server_new(ifindex);
-	if (!ap->server) {
-		l_error("Failed to create DHCP server on %u", ifindex);
-		return -EINVAL;;
-	}
-
-	if (getenv("IWD_DHCP_DEBUG"))
-		l_dhcp_server_set_debug(ap->server, do_debug,
-							"[DHCPv4 SERV] ", NULL);
-
-	/* get the current address if there is one */
-	if (l_net_get_address(ifindex, &ia) && ia.s_addr != 0)
-		address = ia.s_addr;
-
-	if (ap->config->profile) {
-		char *addr;
-
-		addr = l_settings_get_string(settings, "IPv4", "Address");
-		if (addr) {
-			if (inet_pton(AF_INET, addr, &ia) < 0)
-				goto free_addr;
-
-			/* Is a matching address already set on interface? */
-			if (ia.s_addr == address)
-				ret = -EALREADY;
-			else
-				ret = 0;
-		} else if (address) {
-			/* No address in config, but interface has one set */
-			addr = l_strdup(inet_ntoa(ia));
-			ret = -EALREADY;
-		} else
-			goto free_addr;
-
-		/* Set the remaining DHCP options in config file */
-		if (!dhcp_load_settings(ap, settings)) {
-			ret = -EINVAL;
-			goto free_addr;
-		}
-
-		if (!l_dhcp_server_set_ip_address(ap->server, addr)) {
-			ret = -EINVAL;
-			goto free_addr;
-		}
-
-		ap->own_ip = l_strdup(addr);
-
-free_addr:
-		l_free(addr);
-
-		return ret;
-	} else if (address) {
-		/* No config file and address is already set */
-		ap->own_ip = l_strdup(inet_ntoa(ia));
-
-		return -EALREADY;
-	} else if (pool.used) {
-		/* No config file, no address set. Use IP pool */
-		ap->own_ip = ip_pool_get();
-		if (!ap->own_ip) {
-			l_error("No more IP's in pool, cannot start AP on %u",
-					ifindex);
-			return -EEXIST;
-		}
-
-		ap->use_ip_pool = true;
-		ap->ip_prefix = pool.prefix;
-
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-static int ap_load_profile_and_dhcp(struct ap_state *ap, bool *wait_dhcp)
-{
-	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
-	char *passphrase;
-	struct l_settings *settings = NULL;
-	int err = -EINVAL;
-
-	/* No profile or DHCP settings */
-	if (!ap->config->profile && !pool.used)
-		return 0;
-
-	if (ap->config->profile) {
-		settings = l_settings_new();
-
-		if (!l_settings_load_from_file(settings, ap->config->profile))
-			goto cleanup;
-
-		passphrase = l_settings_get_string(settings, "Security",
-							"Passphrase");
-		if (passphrase) {
-			if (strlen(passphrase) > 63) {
-				l_error("[Security].Passphrase must not exceed "
-						"63 characters");
-				l_free(passphrase);
-				goto cleanup;
-			}
-
-			strcpy(ap->config->passphrase, passphrase);
-			l_free(passphrase);
-		}
-
-		if (!l_settings_has_group(settings, "IPv4")) {
-			*wait_dhcp = false;
-			err = 0;
-			goto cleanup;
-		}
-	}
-
-	err = ap_setup_dhcp(ap, settings);
-	if (err == 0) {
-		/* Address change required */
-		ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
-					ap->ip_prefix, ap->own_ip,
-					broadcast_from_ip(ap->own_ip),
-					ap_ifaddr4_added_cb, ap, NULL);
-
-		if (!ap->rtnl_add_cmd) {
-			l_error("Failed to add IPv4 address");
-			err = -EIO;
-			goto cleanup;
-		}
-
-		ap->cleanup_ip = true;
-
-		*wait_dhcp = true;
-		err = 0;
-	/* Selected address already set, continue normally */
-	} else if (err == -EALREADY) {
-		*wait_dhcp = false;
-		err = 0;
-	}
-
-cleanup:
-	l_settings_free(settings);
-	return err;
-}
-
 /*
  * Start a simple independent WPA2 AP on given netdev.
  *
@@ -2642,7 +2578,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	struct l_genl_msg *cmd;
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
 	int err = -EINVAL;
-	bool wait_on_address = false;
 
 	if (err_out)
 		*err_out = err;
@@ -2650,7 +2585,7 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	if (L_WARN_ON(!config->ssid))
 		return NULL;
 
-	if (L_WARN_ON(!config->profile && !config->passphrase[0] &&
+	if (L_WARN_ON(!config->passphrase[0] &&
 			util_mem_is_zero(config->psk, sizeof(config->psk))))
 		return NULL;
 
@@ -2661,16 +2596,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	ap->ops = ops;
 	ap->user_data = user_data;
 
-	/*
-	 * This both loads a profile if required and loads DHCP settings either
-	 * by the profile itself or the IP pool (or does nothing in the case
-	 * of a profile-less configuration). wait_on_address will be set true
-	 * if an address change is required.
-	 */
-	err = ap_load_profile_and_dhcp(ap, &wait_on_address);
-	if (err < 0)
-		goto error;
-
 	if (!config->channel)
 		/* TODO: Start a Get Survey to decide the channel */
 		config->channel = 6;
@@ -2752,13 +2677,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	if (!ap->mlme_watch)
 		l_error("Registering for MLME notification failed");
 
-	if (wait_on_address) {
-		if (err_out)
-			*err_out = 0;
-
-		return ap;
-	}
-
 	cmd = ap_build_cmd_start_ap(ap);
 	if (!cmd)
 		goto error;
@@ -3113,10 +3031,9 @@ static struct l_dbus_message *ap_dbus_start_profile(struct l_dbus *dbus,
 	if (!l_dbus_message_get_arguments(message, "s", &ssid))
 		return dbus_error_invalid_args(message);
 
-	config = l_new(struct ap_config, 1);
-	config->ssid = l_strdup(ssid);
-	/* This tells ap_start to pull settings from a profile on disk */
-	config->profile = storage_get_path("ap/%s.ap", ssid);
+	config = ap_config_load_profile(ssid);
+	if (!config)
+		return dbus_error_invalid_format(message);
 
 	ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if);
 	if (!ap_if->ap)
diff --git a/src/ap.h b/src/ap.h
index dc57a0bb..cad00846 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -64,7 +64,14 @@ struct ap_config {
 	char *wsc_name;
 	struct wsc_primary_device_type wsc_primary_device_type;
 
-	char *profile;
+	bool disable_netconfig4;
+	char **addr4_str_list;
+	char **dns_addr4_str_list;
+	uint8_t prefix_len4;
+	uint32_t dhcp_start_addr4;
+	uint32_t dhcp_end_addr4;
+	uint32_t dhcp_gateway_addr4;
+	unsigned int dhcp_lease_time;
 
 	bool no_cck_rates : 1;
 };
@@ -74,6 +81,7 @@ struct ap_ops {
 				void *user_data);
 };
 
+struct ap_config *ap_config_load_profile(const char *ssid);
 void ap_config_free(struct ap_config *config);
 
 struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 02/10] ap: Add missing ap_config_free() in error path
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 22:21   ` Denis Kenzior
  2021-03-05 15:09 ` [PATCH 03/10] ap: Refactor address pool logic Andrew Zaborowski
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 599 bytes --]

---
 src/ap.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/ap.c b/src/ap.c
index 677ba8fa..589f6059 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -3036,8 +3036,10 @@ static struct l_dbus_message *ap_dbus_start_profile(struct l_dbus *dbus,
 		return dbus_error_invalid_format(message);
 
 	ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if);
-	if (!ap_if->ap)
+	if (!ap_if->ap) {
+		ap_config_free(config);
 		return dbus_error_from_errno(err, message);
+	}
 
 	ap_if->pending = l_dbus_message_ref(message);
 	return NULL;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 03/10] ap: Refactor address pool logic
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 02/10] ap: Add missing ap_config_free() in error path Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 04/10] ap: Add subnet address selection logic Andrew Zaborowski
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 6119 bytes --]

Deprecate the global [General].APRanges setting in favour of
[IPv4].APAddressPool with an extended syntax, but keep backwards
compatibility.  Drop the existing address pool creation code.  The
new APAddressPool setting has the same syntax as the profile-local
[IPv4].Address.  [IPv4].Address is loaded as ap->config->addr4_str_list
and, when missing, ap_start will fall back to the global setting so
there'll be common code to handle both settings.
---
 src/ap.c | 153 +++++++++++++++----------------------------------------
 1 file changed, 40 insertions(+), 113 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 589f6059..fdb4b454 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -86,7 +86,6 @@ struct ap_state {
 	bool started : 1;
 	bool gtk_set : 1;
 	bool cleanup_ip : 1;
-	bool use_ip_pool : 1;
 };
 
 struct sta_state {
@@ -115,99 +114,11 @@ struct ap_wsc_pbc_probe_record {
 	uint64_t timestamp;
 };
 
-struct ap_ip_pool {
-	uint32_t start;
-	uint32_t end;
-	uint8_t prefix;
-
-	/* Fist/last valid subnet */
-	uint8_t sub_start;
-	uint8_t sub_end;
-
-	struct l_uintset *used;
-};
-
-struct ap_ip_pool pool;
+static bool netconfig_enabled;
+static char **global_addr_strs;
 static uint32_t netdev_watch;
 struct l_netlink *rtnl;
 
-/*
- * Creates pool of IPs which AP intefaces can use. Each call to ip_pool_get
- * will advance the subnet +1 so there are no IP conflicts between AP
- * interfaces
- */
-static bool ip_pool_create(const char *ip_prefix)
-{
-	if (!util_ip_prefix_tohl(ip_prefix, &pool.prefix, &pool.start,
-					&pool.end, NULL))
-		return false;
-
-	if (pool.prefix > 24) {
-		l_error("APRanges prefix must 24 or less (%u used)",
-				pool.prefix);
-		memset(&pool, 0, sizeof(pool));
-		return false;
-	}
-
-	/*
-	 * Find the number of subnets we can use, this will dictate the number
-	 * of AP interfaces that can be created (when using DHCP)
-	 */
-	pool.sub_start = (pool.start & 0x0000ff00) >> 8;
-	pool.sub_end = (pool.end & 0x0000ff00) >> 8;
-
-	pool.used = l_uintset_new_from_range(pool.sub_start, pool.sub_end);
-
-	return true;
-}
-
-static char *ip_pool_get()
-{
-	uint32_t ip;
-	struct in_addr ia;
-	uint8_t next_subnet = (uint8_t)l_uintset_find_unused_min(pool.used);
-
-	/* This shouldn't happen */
-	if (next_subnet < pool.sub_start || next_subnet > pool.sub_end)
-		return NULL;
-
-	l_uintset_put(pool.used, next_subnet);
-
-	ip = pool.start;
-	ip &= 0xffff00ff;
-	ip |= (next_subnet << 8);
-
-	ia.s_addr = htonl(ip);
-	return l_strdup(inet_ntoa(ia));
-}
-
-static bool ip_pool_put(const char *address)
-{
-	struct in_addr ia;
-	uint32_t ip;
-	uint8_t subnet;
-
-	if (inet_aton(address, &ia) < 0)
-		return false;
-
-	ip = ntohl(ia.s_addr);
-
-	subnet = (ip & 0x0000ff00) >> 8;
-
-	if (subnet < pool.sub_start || subnet > pool.sub_end)
-		return false;
-
-	return l_uintset_take(pool.used, subnet);
-}
-
-static void ip_pool_destroy()
-{
-	if (pool.used)
-		l_uintset_free(pool.used);
-
-	memset(&pool, 0, sizeof(pool));
-}
-
 static const char *broadcast_from_ip(const char *ip)
 {
 	struct in_addr ia;
@@ -2677,6 +2588,10 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	if (!ap->mlme_watch)
 		l_error("Registering for MLME notification failed");
 
+	if (netconfig_enabled && !ap->config->disable_netconfig4) {
+		/* TODO: Start netconfig */
+	}
+
 	cmd = ap_build_cmd_start_ap(ap);
 	if (!cmd)
 		goto error;
@@ -3235,7 +3150,6 @@ static void ap_netdev_watch(struct netdev *netdev,
 static int ap_init(void)
 {
 	const struct l_settings *settings = iwd_get_config();
-	bool dhcp_enable;
 
 	netdev_watch = netdev_watch_add(ap_netdev_watch, NULL, NULL);
 
@@ -3246,31 +3160,44 @@ static int ap_init(void)
 			ap_diagnostic_interface_destroy, false);
 
 	/*
-	 * Reusing [General].EnableNetworkConfiguration as a switch to enable
-	 * DHCP server. If no value is found or it is false do not create a
-	 * DHCP server.
+	 * Enable network configuration and DHCP only if
+	 * [General].EnableNetworkConfiguration is true.
 	 */
 	if (!l_settings_get_bool(settings, "General",
-				"EnableNetworkConfiguration", &dhcp_enable))
-		dhcp_enable = false;
+					"EnableNetworkConfiguration",
+					&netconfig_enabled))
+		netconfig_enabled = false;
 
-	if (dhcp_enable) {
-		L_AUTO_FREE_VAR(char *, ip_prefix);
-
-		ip_prefix = l_settings_get_string(settings, "General",
-							"APRanges");
-		/*
-		 * In this case its assumed the user only cares about station
-		 * netconfig so we let ap_init pass but DHCP will not be
-		 * enabled.
-		 */
-		if (!ip_prefix) {
-			l_warn("[General].APRanges must be set for DHCP");
-			return 0;
+	if (netconfig_enabled) {
+		if (l_settings_get_value(settings, "IPv4", "APAddressPool")) {
+			global_addr_strs = l_settings_get_string_list(settings,
+								"IPv4",
+								"APAddressPool",
+								',');
+			if (!global_addr_strs || !*global_addr_strs) {
+				l_error("Can't parse the [IPv4].APAddressPool "
+					"setting as a string list");
+				l_strv_free(global_addr_strs);
+				global_addr_strs = NULL;
+			}
+		} else if (l_settings_get_value(settings,
+						"General", "APRanges")) {
+			global_addr_strs = l_settings_get_string_list(settings,
+								"General",
+								"APRanges",
+								',');
+			if (!global_addr_strs || !*global_addr_strs) {
+				l_error("Can't parse the [General].APRanges "
+					"setting as a string list");
+				l_strv_free(global_addr_strs);
+				global_addr_strs = NULL;
+			}
 		}
 
-		if (!ip_pool_create(ip_prefix))
-			return -EINVAL;
+		/* Fall back to 192.168.0.0/16 */
+		if (!global_addr_strs)
+			global_addr_strs =
+				l_strv_append(NULL, "192.168.0.0/16");
 	}
 
 	rtnl = iwd_get_rtnl();
@@ -3283,7 +3210,7 @@ static void ap_exit(void)
 	netdev_watch_remove(netdev_watch);
 	l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
 
-	ip_pool_destroy();
+	l_strv_free(global_addr_strs);
 }
 
 IWD_MODULE(ap, ap_init, ap_exit)
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 04/10] ap: Add subnet address selection logic
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 02/10] ap: Add missing ap_config_free() in error path Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 03/10] ap: Refactor address pool logic Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 22:35   ` Denis Kenzior
  2021-03-05 15:09 ` [PATCH 05/10] ap: Dump addreses in use and assign IP/netmask Andrew Zaborowski
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 9179 bytes --]

Add the ap_select_addr4 function to select a random subnet of requested
size from an address space defined by a string list (for use with the
AP profile [IPv4].Address and the global [IPv4].APAddressPool settings),
avoiding those that conflict with subnets from a second list.  We take
care to give a similar weight to all subnets contained in the specified
ranges regardless of how many ranges contain each, basically so that
overlapping ranges don't affect the probabilities.

This is added as src/ap-addr.c because it's pretty self-contained.
---
 Makefile.am   |   3 +-
 src/ap-addr.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ap.h      |  10 +++
 3 files changed, 250 insertions(+), 1 deletion(-)
 create mode 100644 src/ap-addr.c

diff --git a/Makefile.am b/Makefile.am
index 6ec9fd62..bdd190fc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -217,7 +217,8 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
 					src/knownnetworks.c \
 					src/rfkill.h src/rfkill.c \
 					src/ft.h src/ft.c \
-					src/ap.h src/ap.c src/adhoc.c \
+					src/ap.h src/ap.c src/ap-addr.c \
+					src/adhoc.c \
 					src/sae.h src/sae.c \
 					src/nl80211util.h src/nl80211util.c \
 					src/nl80211cmd.h src/nl80211cmd.c \
diff --git a/src/ap-addr.c b/src/ap-addr.c
new file mode 100644
index 00000000..4356eda9
--- /dev/null
+++ b/src/ap-addr.c
@@ -0,0 +1,238 @@
+/*
+ *
+ *  Wireless daemon for Linux
+ *
+ *  Copyright (C) 2021  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ell/ell.h>
+
+#include "src/util.h"
+#include "src/mpdu.h"
+#include "src/wscutil.h"
+#include "src/netdev.h"
+#include "src/ap.h"
+
+struct ap_ip_range {
+	uint32_t start;
+	uint32_t end;
+};
+
+static int ap_ip_range_compare(const void *a, const void *b, void *user_data)
+{
+	const struct ap_ip_range *range_a = a;
+	const struct ap_ip_range *range_b = b;
+
+	return range_a->start > range_b->start ? 1 : -1;
+}
+
+/*
+ * Append any address ranges within an input start/end range which contain
+ * at least one full subnet and don't intersect with any subnets already in
+ * use.  This may result in the input range being split into multiple ranges
+ * of different sizes or being skipped altogether.
+ * All inputs must be rounded to the subnet boundary and the @used queue
+ * sorted by the subnet start address.
+ */
+static void ap_append_range(struct l_queue *to, const struct ap_ip_range *range,
+				struct l_queue *used, const char *str)
+{
+	const struct l_queue_entry *entry = l_queue_get_entries(used);
+	const struct ap_ip_range *used_range = entry ? entry->data : NULL;
+	uint32_t start = range->start;
+	bool print = true;
+
+	while (range->end > start) {
+		while (used_range && used_range->end <= start) {
+			entry = entry->next;
+			used_range = entry ? entry->data : NULL;
+		}
+
+		/* No more used ranges that intersect with @start/@range->end */
+		if (!used_range || range->end <= used_range->start) {
+			struct ap_ip_range *sub = l_new(struct ap_ip_range, 1);
+
+			sub->start = start;
+			sub->end = range->end;
+			l_queue_push_tail(to, sub);
+			l_queue_insert(used, l_memdup(sub, sizeof(*sub)),
+					ap_ip_range_compare, NULL);
+			return;
+		}
+
+		if (print) {
+			l_debug("Address spec %s intersects with at least one "
+				"subnet already in use on the system or "
+				"specified in the settings", str);
+			print = false;
+		}
+
+		/* Now we know @used_range is non-NULL and intersects */
+		if (start < used_range->start) {
+			struct ap_ip_range *sub = l_new(struct ap_ip_range, 1);
+
+			sub->start = start;
+			sub->end = used_range->start;
+			l_queue_push_tail(to, sub);
+			l_queue_insert(used, l_memdup(sub, sizeof(*sub)),
+					ap_ip_range_compare, NULL);
+		}
+
+		/* Skip to the start of the next subnet */
+		start = used_range->end;
+	}
+}
+
+/*
+ * Select a subnet and a host address from a defined space.
+ *
+ * Returns:  0 when an address was selected and written to *out_addr,
+ *          -EEXIST if all available subnet addresses are in use,
+ *          -EINVAL if there was a different error.
+ */
+int ap_select_addr4(const char **addr_str_list, uint8_t prefix,
+			const struct l_queue *used_addr_dump, uint32_t ifindex,
+			uint32_t *out_addr)
+{
+	uint32_t total = 0;
+	uint32_t selected;
+	unsigned int i;
+	uint32_t subnet_size = 1 << (32 - prefix);
+	uint32_t host_mask = subnet_size - 1;
+	uint32_t subnet_mask = ~host_mask;
+	uint32_t host_addr = 0;
+	struct l_queue *ranges = l_queue_new();
+	struct l_queue *used = l_queue_new();
+	struct in_addr ia;
+	const struct l_queue_entry *entry;
+	int err = -EINVAL;
+
+	/* Build a sorted list of used/unavailable subnets */
+	for (entry = l_queue_get_entries((struct l_queue *) used_addr_dump);
+			entry; entry = entry->next) {
+		const struct ap_rtnl_addr4_record *rec = entry->data;
+		struct ap_ip_range *range;
+
+		/*
+		 * Ignore the subnet(s) set on the target interface, we'll be
+		 * overwriting it if everything goes well.
+		 */
+		if (rec->ifindex == ifindex)
+			continue;
+
+		if (!rec->addr || !rec->prefix_len)
+			continue;
+
+		range = l_new(struct ap_ip_range, 1);
+		range->start = rec->addr & subnet_mask;
+		range->end = (range->start + (1 << (32 - rec->prefix_len)) +
+				subnet_size - 1) & subnet_mask;
+		l_queue_insert(used, range, ap_ip_range_compare, NULL);
+	}
+
+	/* Build the list of available subnets */
+
+	/* Check for the static IP syntax: Address=<IP> */
+	if (l_strv_length((char **) addr_str_list) == 1 &&
+			inet_pton(AF_INET, *addr_str_list, &ia) == 1) {
+		struct ap_ip_range range;
+
+		host_addr = ntohl(ia.s_addr);
+		range.start = host_addr & subnet_mask;
+		range.end = range.start + subnet_size;
+		ap_append_range(ranges, &range, used, *addr_str_list);
+		goto check_avail;
+	}
+
+	for (i = 0; addr_str_list[i]; i++) {
+		struct ap_ip_range range;
+		uint32_t addr;
+		uint8_t addr_prefix;
+
+		if (!util_ip_prefix_tohl(addr_str_list[i], &addr_prefix, &addr,
+						NULL, NULL)) {
+			l_error("Can't parse %s as a subnet address",
+				addr_str_list[i]);
+			goto cleanup;
+		}
+
+		if (addr_prefix > prefix) {
+			l_debug("Address spec %s smaller than requested "
+				"subnet (prefix len %i)", addr_str_list[i],
+				prefix);
+			continue;
+		}
+
+		range.start = addr & subnet_mask;
+		range.end = range.start + (1 << (32 - addr_prefix));
+		ap_append_range(ranges, &range, used, addr_str_list[i]);
+	}
+
+check_avail:
+	if (l_queue_isempty(ranges)) {
+		l_error("No IP subnets available for new Access Point after "
+			"eliminating those already in use on the system");
+		err = -EEXIST;
+		goto cleanup;
+	}
+
+	if (host_addr)
+		goto done;
+
+	/* Count available @prefix-sized subnets */
+	for (entry = l_queue_get_entries(ranges); entry; entry = entry->next) {
+		struct ap_ip_range *range = entry->data;
+
+		total += (range->end - range->start) >> (32 - prefix);
+	}
+
+	selected = l_getrandom_uint32() % total;
+
+	/* Find the @selected'th @prefix-sized subnet */
+	for (entry = l_queue_get_entries(ranges);; entry = entry->next) {
+		struct ap_ip_range *range = entry->data;
+		uint32_t count = (range->end - range->start) >> (32 - prefix);
+
+		if (selected < count) {
+			host_addr = range->start + (selected << (32 - prefix));
+			break;
+		}
+
+		selected -= count;
+	}
+
+	if ((host_addr & 0xff) == 0)
+		host_addr += 1;
+
+done:
+	err = 0;
+	*out_addr = host_addr;
+
+cleanup:
+	l_queue_destroy(ranges, l_free);
+	l_queue_destroy(used, l_free);
+	return err;
+}
diff --git a/src/ap.h b/src/ap.h
index cad00846..d776b27f 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -95,3 +95,13 @@ bool ap_station_disconnect(struct ap_state *ap, const uint8_t *mac,
 				enum mmpdu_reason_code reason);
 
 bool ap_push_button(struct ap_state *ap);
+
+struct ap_rtnl_addr4_record {
+	uint32_t ifindex;
+	uint32_t addr;
+	uint8_t prefix_len;
+};
+
+int ap_select_addr4(const char **addr_str_list, uint8_t prefix,
+			const struct l_queue *used_addr_dump, uint32_t ifindex,
+			uint32_t *out_addr);
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 05/10] ap: Dump addreses in use and assign IP/netmask
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 04/10] ap: Add subnet address selection logic Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 06/10] ap: Set up the DHCP server Andrew Zaborowski
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 9281 bytes --]

During AP start dump addresses in use by all netdevs, handle the subnet
address and netmask settings ap->config to select the final IP/netmask
and set the on the interface if not already set.
---
 src/ap.c | 271 +++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 202 insertions(+), 69 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index fdb4b454..df3715a2 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -79,9 +79,10 @@ struct ap_state {
 	struct l_queue *sta_states;
 
 	struct l_dhcp_server *server;
+	uint32_t addr4;
 	uint32_t rtnl_add_cmd;
-	char *own_ip;
-	unsigned int ip_prefix;
+	uint32_t rtnl_dump_cmd;
+	struct l_queue *rtnl_addr4_dump;
 
 	bool started : 1;
 	bool gtk_set : 1;
@@ -119,23 +120,6 @@ static char **global_addr_strs;
 static uint32_t netdev_watch;
 struct l_netlink *rtnl;
 
-static const char *broadcast_from_ip(const char *ip)
-{
-	struct in_addr ia;
-	uint32_t bcast;
-
-	if (inet_aton(ip, &ia) != 1)
-		return NULL;
-
-	bcast = ntohl(ia.s_addr);
-	bcast &= 0xffffff00;
-	bcast |= 0x000000ff;
-
-	ia.s_addr = htonl(bcast);
-
-	return inet_ntoa(ia);
-}
-
 #define AP_DEFAULT_IPV4_PREFIX_LEN 28
 
 struct ap_config *ap_config_load_profile(const char *ssid)
@@ -386,30 +370,35 @@ static void ap_reset(struct ap_state *ap)
 	if (ap->rates)
 		l_uintset_free(ap->rates);
 
-	ap_config_free(ap->config);
-	ap->config = NULL;
-
 	l_queue_destroy(ap->wsc_pbc_probes, l_free);
 
 	ap->started = false;
 
 	/* Delete IP if one was set by IWD */
-	if (ap->cleanup_ip)
-		l_rtnl_ifaddr4_delete(rtnl, netdev_get_ifindex(netdev),
-					ap->ip_prefix, ap->own_ip,
-					broadcast_from_ip(ap->own_ip),
-					NULL, NULL, NULL);
+	if (ap->cleanup_ip) {
+		struct in_addr ia;
+		char ip_str[16];
+		char broadcast_str[16];
 
-	if (ap->own_ip) {
-		/* Release IP from pool if used */
-		if (ap->use_ip_pool)
-			ip_pool_put(ap->own_ip);
+		ia.s_addr = htonl(ap->addr4);
+		strcpy(ip_str, inet_ntoa(ia));
+		ia.s_addr = htonl(ap->addr4 | ~util_netmask_from_prefix(
+						ap->config->prefix_len4));
+		strcpy(broadcast_str, inet_ntoa(ia));
 
-		l_free(ap->own_ip);
+		l_rtnl_ifaddr4_delete(rtnl, netdev_get_ifindex(netdev),
+					ap->config->prefix_len4, ip_str,
+					broadcast_str, NULL, NULL, NULL);
 	}
 
+	ap_config_free(ap->config);
+	ap->config = NULL;
+
 	if (ap->server)
 		l_dhcp_server_stop(ap->server);
+
+	l_queue_destroy(ap->rtnl_addr4_dump, l_free);
+	ap->rtnl_addr4_dump = NULL;
 }
 
 static void ap_del_station(struct sta_state *sta, uint16_t reason,
@@ -2246,34 +2235,41 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
 	return cmd;
 }
 
-static void ap_ifaddr4_added_cb(int error, uint16_t type, const void *data,
-				uint32_t len, void *user_data)
+static bool ap_start_send(struct ap_state *ap)
 {
-	struct ap_state *ap = user_data;
-	struct l_genl_msg *cmd;
-
-	ap->rtnl_add_cmd = 0;
+	struct l_genl_msg *cmd = ap_build_cmd_start_ap(ap);
 
-	if (error) {
-		l_error("Failed to set IP address");
-		goto error;
+	if (!cmd) {
+		l_error("ap_build_cmd_start_ap failed");
+		return false;
 	}
 
-	cmd = ap_build_cmd_start_ap(ap);
-	if (!cmd)
-		goto error;
-
 	ap->start_stop_cmd_id = l_genl_family_send(ap->nl80211, cmd,
 							ap_start_cb, ap, NULL);
 	if (!ap->start_stop_cmd_id) {
+		l_error("AP_START l_genl_family_send failed");
 		l_genl_msg_unref(cmd);
-		goto error;
+		return false;
 	}
 
-	return;
+	return true;
+}
 
-error:
-	ap_start_failed(ap);
+static void ap_ifaddr4_added_cb(int error, uint16_t type, const void *data,
+				uint32_t len, void *user_data)
+{
+	struct ap_state *ap = user_data;
+
+	ap->rtnl_add_cmd = 0;
+
+	if (error) {
+		l_error("Failed to set IP address");
+		ap_start_failed(ap);
+		return;
+	}
+
+	if (!ap_start_send(ap))
+		ap_start_failed(ap);
 }
 
 static bool ap_parse_new_station_ies(const void *data, uint16_t len,
@@ -2468,6 +2464,146 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	}
 }
 
+static bool ap_addr4_match_ifindex(const void *a, const void *b)
+{
+	const struct ap_rtnl_addr4_record *rec = a;
+	const uint32_t *ifindex = b;
+
+	return rec->ifindex == *ifindex && rec->addr && rec->prefix_len <= 30;
+}
+
+static bool ap_addr4_match_subnet(const void *a, const void *b)
+{
+	const struct ap_rtnl_addr4_record *rec = a;
+	const struct ap_state *ap = b;
+	uint32_t netmask = util_netmask_from_prefix(rec->prefix_len);
+
+	return rec->prefix_len <= ap->config->prefix_len4 &&
+		((rec->addr ^ ap->addr4) & netmask) == 0;
+}
+
+static void ap_netconfig_start(struct ap_state *ap)
+{
+	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
+	int ret;
+	struct in_addr ia;
+	char addr_str[16];
+	char broadcast_str[16];
+
+	if (!ap->config->prefix_len4)
+		ap->config->prefix_len4 = AP_DEFAULT_IPV4_PREFIX_LEN;
+
+	/*
+	 * The address pool specified for this AP has the priority (if any),
+	 * next is the address currently set on the interface (if any) and
+	 * last is the global AP address pool (APAddressPool setting).
+	 */
+	if (ap->config->addr4_str_list)
+		ret = ap_select_addr4((const char **)
+					ap->config->addr4_str_list,
+					ap->config->prefix_len4,
+					ap->rtnl_addr4_dump, ifindex,
+					&ap->addr4);
+	else {
+		const struct ap_rtnl_addr4_record *match =
+			l_queue_find(ap->rtnl_addr4_dump,
+					ap_addr4_match_ifindex,
+					&ifindex);
+		if (match) {
+			ap->addr4 = match->addr;
+			ap->config->prefix_len4 = match->prefix_len;
+			goto set_up_dhcp;
+		}
+
+		ret = ap_select_addr4((const char **) global_addr_strs,
+					ap->config->prefix_len4,
+					ap->rtnl_addr4_dump, ifindex,
+					&ap->addr4);
+	}
+
+	if (ret)
+		goto error;
+
+set_up_dhcp:
+	/* TODO */
+
+	/* See if the selected subnet is already set on the interface */
+	if (l_queue_find(ap->rtnl_addr4_dump, ap_addr4_match_subnet, ap)) {
+		/* Selected address already set, continue normally */
+		ap->cleanup_ip = false;
+
+		if (!ap_start_send(ap))
+			goto error;
+
+		return;
+	}
+
+	ia.s_addr = htonl(ap->addr4);
+	strcpy(addr_str, inet_ntoa(ia));
+	ia.s_addr = htonl(ap->addr4 |
+			~util_netmask_from_prefix(ap->config->prefix_len4));
+	strcpy(broadcast_str, inet_ntoa(ia));
+
+	ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
+					ap->config->prefix_len4,
+					addr_str, broadcast_str,
+					ap_ifaddr4_added_cb, ap, NULL);
+	if (!ap->rtnl_add_cmd) {
+		l_error("Failed to add the IPv4 address");
+		goto error;
+	}
+
+	ap->cleanup_ip = true;
+	return;
+
+error:
+	ap_start_failed(ap);
+}
+
+static void ap_ifaddr4_dump_cb(int error,
+				uint16_t type, const void *data,
+				uint32_t len, void *user_data)
+{
+	struct ap_state *ap = user_data;
+	const struct ifaddrmsg *ifa = data;
+	char *ip_str;
+	struct in_addr ia;
+	struct ap_rtnl_addr4_record *addr;
+
+	if (error) {
+		l_error("Error getting existing IPv4 addresses on AP iface");
+		return;
+	}
+
+	if (type != RTM_NEWADDR || ifa->ifa_prefixlen < 1)
+		return;
+
+	l_rtnl_ifaddr4_extract(ifa, len, NULL, &ip_str, NULL);
+	inet_aton(ip_str, &ia);
+	l_free(ip_str);
+
+	addr = l_new(struct ap_rtnl_addr4_record, 1);
+	addr->ifindex = ifa->ifa_index;
+	addr->prefix_len = ifa->ifa_prefixlen;
+	addr->addr = ntohl(ia.s_addr);
+
+	if (!ap->rtnl_addr4_dump)
+		ap->rtnl_addr4_dump = l_queue_new();
+
+	l_queue_push_tail(ap->rtnl_addr4_dump, addr);
+}
+
+static void ap_ifaddr4_dump_destroy_cb(void *user_data)
+{
+	struct ap_state *ap = user_data;
+
+	if (!ap->rtnl_dump_cmd)
+		return;
+
+	ap->rtnl_dump_cmd = 0;
+	ap_netconfig_start(ap);
+}
+
 /*
  * Start a simple independent WPA2 AP on given netdev.
  *
@@ -2486,12 +2622,10 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 {
 	struct ap_state *ap;
 	struct wiphy *wiphy = netdev_get_wiphy(netdev);
-	struct l_genl_msg *cmd;
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
-	int err = -EINVAL;
 
 	if (err_out)
-		*err_out = err;
+		*err_out = -EINVAL;
 
 	if (L_WARN_ON(!config->ssid))
 		return NULL;
@@ -2589,29 +2723,28 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 		l_error("Registering for MLME notification failed");
 
 	if (netconfig_enabled && !ap->config->disable_netconfig4) {
-		/* TODO: Start netconfig */
-	}
-
-	cmd = ap_build_cmd_start_ap(ap);
-	if (!cmd)
-		goto error;
+		ap->rtnl_dump_cmd = l_rtnl_ifaddr4_dump(rtnl,
+						ap_ifaddr4_dump_cb, ap,
+						ap_ifaddr4_dump_destroy_cb);
+		if (!ap->rtnl_dump_cmd) {
+			if (err_out)
+				*err_out = -EIO;
+
+			l_error("Sending the IPv4 addr dump req failed");
+			goto error;
+		}
 
-	ap->start_stop_cmd_id = l_genl_family_send(ap->nl80211, cmd,
-							ap_start_cb, ap, NULL);
-	if (!ap->start_stop_cmd_id) {
-		l_genl_msg_unref(cmd);
-		goto error;
+		return ap;
 	}
 
-	if (err_out)
-		*err_out = 0;
+	if (ap_start_send(ap)) {
+		if (err_out)
+			*err_out = 0;
 
-	return ap;
+		return ap;
+	}
 
 error:
-	if (err_out)
-		*err_out = err;
-
 	ap->config = NULL;
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 06/10] ap: Set up the DHCP server
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 05/10] ap: Dump addreses in use and assign IP/netmask Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 07/10] ap: Move the DHCP server freeing to ap_reset Andrew Zaborowski
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3851 bytes --]

Create the DHCP server instance, set the settings from ap->config using
l_dhcp_server_* setters.
---
 src/ap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 79 insertions(+), 7 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index df3715a2..1b856c23 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -78,7 +78,7 @@ struct ap_state {
 	uint16_t last_aid;
 	struct l_queue *sta_states;
 
-	struct l_dhcp_server *server;
+	struct l_dhcp_server *dhcp;
 	uint32_t addr4;
 	uint32_t rtnl_add_cmd;
 	uint32_t rtnl_dump_cmd;
@@ -394,8 +394,8 @@ static void ap_reset(struct ap_state *ap)
 	ap_config_free(ap->config);
 	ap->config = NULL;
 
-	if (ap->server)
-		l_dhcp_server_stop(ap->server);
+	if (ap->dhcp)
+		l_dhcp_server_stop(ap->dhcp);
 
 	l_queue_destroy(ap->rtnl_addr4_dump, l_free);
 	ap->rtnl_addr4_dump = NULL;
@@ -2142,7 +2142,7 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
 		goto failed;
 	}
 
-	if (ap->server && !l_dhcp_server_start(ap->server)) {
+	if (ap->dhcp && !l_dhcp_server_start(ap->dhcp)) {
 		l_error("DHCP server failed to start");
 		goto failed;
 	}
@@ -2464,6 +2464,77 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	}
 }
 
+static bool ap_setup_dhcp(struct ap_state *ap)
+{
+	struct in_addr ia;
+	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
+
+	ap->dhcp = l_dhcp_server_new(ifindex);
+	if (!ap->dhcp) {
+		l_error("Failed to create DHCP server on %u", ifindex);
+		return false;
+	}
+
+	if (getenv("IWD_DHCP_DEBUG"))
+		l_dhcp_server_set_debug(ap->dhcp, do_debug,
+					"[DHCPv4 SERV] ", NULL);
+
+	ia.s_addr = htonl(ap->addr4);
+
+	if (!l_dhcp_server_set_ip_address(ap->dhcp, inet_ntoa(ia))) {
+		l_error("l_dhcp_server_set_ip_address failed");
+		return false;
+	}
+
+	ia.s_addr = htonl(util_netmask_from_prefix(ap->config->prefix_len4));
+
+	if (!l_dhcp_server_set_netmask(ap->dhcp, inet_ntoa(ia))) {
+		l_error("l_dhcp_server_set_netmask failed");
+		return false;
+	}
+
+	if (ap->config->dhcp_gateway_addr4) {
+		ia.s_addr = htonl(util_netmask_from_prefix(
+					ap->config->dhcp_gateway_addr4));
+
+		if (!l_dhcp_server_set_gateway(ap->dhcp, inet_ntoa(ia))) {
+			l_error("l_dhcp_server_set_gateway failed");
+			return false;
+		}
+	}
+
+	if (ap->config->dns_addr4_str_list &&
+			!l_dhcp_server_set_dns(ap->dhcp,
+					ap->config->dns_addr4_str_list)) {
+		l_error("l_dhcp_server_set_dns failed");
+		return false;
+	}
+
+	if (ap->config->dhcp_start_addr4) {
+		char start_str[16];
+		char end_str[16];
+
+		ia.s_addr = htonl(ap->config->dhcp_start_addr4);
+		strcpy(start_str, inet_ntoa(ia));
+		ia.s_addr = htonl(ap->config->dhcp_end_addr4);
+		strcpy(end_str, inet_ntoa(ia));
+
+		if (!l_dhcp_server_set_ip_range(ap->dhcp, start_str, end_str)) {
+			l_error("l_dhcp_server_set_ip_range failed");
+			return false;
+		}
+	}
+
+	if (ap->config->dhcp_lease_time &&
+			!l_dhcp_server_set_lease_time(ap->dhcp,
+						ap->config->dhcp_lease_time)) {
+		l_error("l_dhcp_server_set_lease_time failed");
+		return false;
+	}
+
+	return true;
+}
+
 static bool ap_addr4_match_ifindex(const void *a, const void *b)
 {
 	const struct ap_rtnl_addr4_record *rec = a;
@@ -2525,7 +2596,8 @@ static void ap_netconfig_start(struct ap_state *ap)
 		goto error;
 
 set_up_dhcp:
-	/* TODO */
+	if (!ap_setup_dhcp(ap))
+		goto error;
 
 	/* See if the selected subnet is already set on the interface */
 	if (l_queue_find(ap->rtnl_addr4_dump, ap_addr4_match_subnet, ap)) {
@@ -2852,8 +2924,8 @@ void ap_free(struct ap_state *ap)
 {
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
-	if (ap->server)
-		l_dhcp_server_destroy(ap->server);
+	if (ap->dhcp)
+		l_dhcp_server_destroy(ap->dhcp);
 	l_free(ap);
 }
 
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 07/10] ap: Move the DHCP server freeing to ap_reset
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 06/10] ap: Set up the DHCP server Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 08/10] ap: Send a specific error message on async AP start failure Andrew Zaborowski
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 885 bytes --]

Some callers of ap_reset(ap)/l_free(ap) would not call
l_dhcp_server_destroy() causing some likely leaks.  It's easier to call
it directly from inside ap_reset().
---
 src/ap.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 1b856c23..c9eec2a7 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -394,8 +394,10 @@ static void ap_reset(struct ap_state *ap)
 	ap_config_free(ap->config);
 	ap->config = NULL;
 
-	if (ap->dhcp)
-		l_dhcp_server_stop(ap->dhcp);
+	if (ap->dhcp) {
+		l_dhcp_server_destroy(ap->dhcp);
+		ap->dhcp = NULL;
+	}
 
 	l_queue_destroy(ap->rtnl_addr4_dump, l_free);
 	ap->rtnl_addr4_dump = NULL;
@@ -2924,8 +2926,6 @@ void ap_free(struct ap_state *ap)
 {
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
-	if (ap->dhcp)
-		l_dhcp_server_destroy(ap->dhcp);
 	l_free(ap);
 }
 
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 08/10] ap: Send a specific error message on async AP start failure
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 07/10] ap: Move the DHCP server freeing to ap_reset Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 09/10] autotests: Update APRanges usage in testAP Andrew Zaborowski
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 4424 bytes --]

We generate the DBus error reply type from the errno only when
ap_start() was failing synchronously, now also send the errno through
the callbacks so that we can also return a specific DBus reply when
failing asynchronously.  Thea AP autotest relies on receiving the
AlreadyExists DBus error.
---
 src/ap.c | 42 +++++++++++++++++++++++++-----------------
 src/ap.h |  4 ++++
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index c9eec2a7..8d58e66f 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -2123,9 +2123,11 @@ static void do_debug(const char *str, void *user_data)
 	l_info("%s%s", prefix, str);
 }
 
-static void ap_start_failed(struct ap_state *ap)
+static void ap_start_failed(struct ap_state *ap, int err)
 {
-	ap->ops->handle_event(AP_EVENT_START_FAILED, NULL, ap->user_data);
+	struct ap_event_start_failed_data data = { err };
+
+	ap->ops->handle_event(AP_EVENT_START_FAILED, &data, ap->user_data);
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
 
@@ -2140,22 +2142,17 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
 
 	if (l_genl_msg_get_error(msg) < 0) {
 		l_error("START_AP failed: %i", l_genl_msg_get_error(msg));
-
-		goto failed;
+		ap_start_failed(ap, l_genl_msg_get_error(msg));
+		return;
 	}
 
 	if (ap->dhcp && !l_dhcp_server_start(ap->dhcp)) {
 		l_error("DHCP server failed to start");
-		goto failed;
+		ap_start_failed(ap, -EINVAL);
 	}
 
 	ap->started = true;
 	ap->ops->handle_event(AP_EVENT_STARTED, NULL, ap->user_data);
-
-	return;
-
-failed:
-	ap_start_failed(ap);
 }
 
 static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
@@ -2266,12 +2263,12 @@ static void ap_ifaddr4_added_cb(int error, uint16_t type, const void *data,
 
 	if (error) {
 		l_error("Failed to set IP address");
-		ap_start_failed(ap);
+		ap_start_failed(ap, error);
 		return;
 	}
 
 	if (!ap_start_send(ap))
-		ap_start_failed(ap);
+		ap_start_failed(ap, -EIO);
 }
 
 static bool ap_parse_new_station_ies(const void *data, uint16_t len,
@@ -2442,10 +2439,12 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	switch (l_genl_msg_get_command(msg)) {
 	case NL80211_CMD_STOP_AP:
 		if (ap->start_stop_cmd_id) {
+			struct ap_event_start_failed_data data = { -ECANCELED };
+
 			l_genl_family_cancel(ap->nl80211,
 						ap->start_stop_cmd_id);
 			ap->start_stop_cmd_id = 0;
-			ap->ops->handle_event(AP_EVENT_START_FAILED, NULL,
+			ap->ops->handle_event(AP_EVENT_START_FAILED, &data,
 						ap->user_data);
 		} else if (ap->started) {
 			ap->started = false;
@@ -2598,16 +2597,20 @@ static void ap_netconfig_start(struct ap_state *ap)
 		goto error;
 
 set_up_dhcp:
-	if (!ap_setup_dhcp(ap))
+	if (!ap_setup_dhcp(ap)) {
+		ret = -EIO;
 		goto error;
+	}
 
 	/* See if the selected subnet is already set on the interface */
 	if (l_queue_find(ap->rtnl_addr4_dump, ap_addr4_match_subnet, ap)) {
 		/* Selected address already set, continue normally */
 		ap->cleanup_ip = false;
 
-		if (!ap_start_send(ap))
+		if (!ap_start_send(ap)) {
+			ret = -EIO;
 			goto error;
+		}
 
 		return;
 	}
@@ -2624,6 +2627,7 @@ set_up_dhcp:
 					ap_ifaddr4_added_cb, ap, NULL);
 	if (!ap->rtnl_add_cmd) {
 		l_error("Failed to add the IPv4 address");
+		ret = -EIO;
 		goto error;
 	}
 
@@ -2631,7 +2635,7 @@ set_up_dhcp:
 	return;
 
 error:
-	ap_start_failed(ap);
+	ap_start_failed(ap, ret);
 }
 
 static void ap_ifaddr4_dump_cb(int error,
@@ -3004,13 +3008,17 @@ static void ap_if_event_func(enum ap_event_type type, const void *event_data,
 
 	switch (type) {
 	case AP_EVENT_START_FAILED:
+	{
+		const struct ap_event_start_failed_data *data = event_data;
+
 		if (L_WARN_ON(!ap_if->pending))
 			break;
 
-		reply = dbus_error_failed(ap_if->pending);
+		reply = dbus_error_from_errno(data->error, ap_if->pending);
 		dbus_pending_reply(&ap_if->pending, reply);
 		ap_if->ap = NULL;
 		break;
+	}
 
 	case AP_EVENT_STARTED:
 		if (L_WARN_ON(!ap_if->pending))
diff --git a/src/ap.h b/src/ap.h
index d776b27f..0a92f430 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -34,6 +34,10 @@ enum ap_event_type {
 	AP_EVENT_PBC_MODE_EXIT,
 };
 
+struct ap_event_start_failed_data {
+	int error;
+};
+
 struct ap_event_station_added_data {
 	const uint8_t *mac;
 	const uint8_t *rsn_ie;
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 09/10] autotests: Update APRanges usage in testAP
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 08/10] ap: Send a specific error message on async AP start failure Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 15:09 ` [PATCH 10/10] doc: Update AP settings in iwd.ap and iwd.config(5) Andrew Zaborowski
  2021-03-05 22:19 ` [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Denis Kenzior
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2087 bytes --]

[General].APRange is now [IPv4].APAddressPool and the netmask is changed
from 23 to 27 bits to make the test correctly assert that only two
default-sized subnets are allowed by IWD simultaneously (default has
changed from 24 to 28 bits)
---
 autotests/testAP/dhcp/main.conf |  4 +++-
 autotests/testAP/dhcp_test.py   | 12 +++++++++---
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/autotests/testAP/dhcp/main.conf b/autotests/testAP/dhcp/main.conf
index 667de23c..62ae782d 100644
--- a/autotests/testAP/dhcp/main.conf
+++ b/autotests/testAP/dhcp/main.conf
@@ -3,4 +3,6 @@ DisableMacAddressRandomization=true
 
 [General]
 EnableNetworkConfiguration=true
-APRanges=192.168.80.0/23
+
+[IPv4]
+APAddressPool=192.168.80.0/27
diff --git a/autotests/testAP/dhcp_test.py b/autotests/testAP/dhcp_test.py
index bf52fb28..e69c4465 100644
--- a/autotests/testAP/dhcp_test.py
+++ b/autotests/testAP/dhcp_test.py
@@ -58,8 +58,14 @@ class Test(unittest.TestCase):
             testutil.test_iface_operstate(dev2.name)
             testutil.test_ifaces_connected(dev1.name, dev2.name, group=False)
 
-            testutil.test_ip_address_match(dev1.name, "192.168.80.1")
-            testutil.test_ip_address_match(dev2.name, "192.168.80.2")
+            try:
+                testutil.test_ip_address_match(dev1.name, "192.168.80.1")
+                testutil.test_ip_address_match(dev2.name, "192.168.80.2")
+                ip = "192.168.80.1"
+            except:
+                testutil.test_ip_address_match(dev1.name, "192.168.80.17")
+                testutil.test_ip_address_match(dev2.name, "192.168.80.18")
+                ip = "192.168.80.17"
 
             wd.unregister_psk_agent(psk_agent)
 
@@ -76,7 +82,7 @@ class Test(unittest.TestCase):
             # got initially.
             dev4.start_ap('TestAP4', 'Password4')
 
-            testutil.test_ip_address_match(dev4.name, "192.168.80.1")
+            testutil.test_ip_address_match(dev4.name, ip)
 
         finally:
             dev1.stop_ap()
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 10/10] doc: Update AP settings in iwd.ap and iwd.config(5)
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (7 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 09/10] autotests: Update APRanges usage in testAP Andrew Zaborowski
@ 2021-03-05 15:09 ` Andrew Zaborowski
  2021-03-05 22:19 ` [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Denis Kenzior
  9 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 15:09 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 8138 bytes --]

Update the AP-mode address logic documentation, change some of the
wording and add some references.
---
 src/iwd.ap.rst     | 69 +++++++++++++++++++++++++++++-----------------
 src/iwd.config.rst | 31 +++++++++++++--------
 2 files changed, 63 insertions(+), 37 deletions(-)

diff --git a/src/iwd.ap.rst b/src/iwd.ap.rst
index 52312855..e2ec01e6 100644
--- a/src/iwd.ap.rst
+++ b/src/iwd.ap.rst
@@ -3,7 +3,7 @@
 ============
 
 --------------------------------------
-Configuration of IWD access point
+Configuration of IWD access points
 --------------------------------------
 
 :Author: James Prestwood <prestwoj@gmail.com>
@@ -25,19 +25,21 @@ Description of access point provisioning files.
 DESCRIPTION
 ===========
 
-An access point provisioning files define the configuration of an IWD access
+An access point provisioning file defines the configuration of an IWD access
 point. These files live in *$STATE_DIRECTORY*/ap (/var/lib/iwd/ap by default).
+They are read when the `net.connman.iwd.AccessPoint.StartProfile(ssid)` DBus
+method is used.
 
 FILE FORMAT
 ===========
 
-See *iwd.network* for details on the file format.
+See *iwd.network* for details on the settings file syntax.
 
 SETTINGS
 ========
 
 The settings are split into several categories.  Each category has a group
-associated with it and described in separate tables below.
+associated with it and is described in the corresponding table below.
 
 Network Authentication Settings
 -------------------------------
@@ -54,13 +56,15 @@ configuration.
    * - Passphrase
      - 8..63 character string
 
-       Passphrase to be used with this access point.
+       WPA PSK Passphrase to be used with this access point.
 
-DHCP Server Settings
---------------------
+IPv4 Network Configuration
+--------------------------
 
-The group ``[IPv4]`` contains settings for IWD's built in DHCP server. All
-settings are optional.
+The group ``[IPv4]`` contains settings for IWD's built-in DHCP server. All
+settings are optional.  They're used if network configuration was enabled as
+described in ``iwd.config(5)``.  Omitting the ``[IPv4]`` group disables
+network configuration and the DHCP server for this access point.
 
 .. list-table::
    :header-rows: 0
@@ -68,41 +72,56 @@ settings are optional.
    :widths: 20 80
 
    * - Address
-     - IP Address of AP
-
-       Optional address for the DHCP server/access point. If provided this
-       address will be set on the AP interface and any other DHCP server options
-       will be derived from this address, unless they are overriden inside the
-       AP profile. If [IPv4].Address is not provided and no IP address is set
-       on the interface prior to calling StartProfile the IP pool will be used.
+     - Local IP address or a comma-separated list of prefix-notation addresses
+
+       Optional local address pool for the access point and the DHCP server.
+       If a single address is provided this address will be set on the AP
+       interface and any other DHCP server options will be derived from it,
+       unless they are overriden by other settings.  If a list of addresses and
+       prefix lengths is given (in the `<IP>/<prefix-len>` format), a single
+       subnet address will be selected from the available space each time this
+       profile is started.  The subnet size will still be based on the
+       ``[IPv4].Netmask`` setting.  If ``[IPv4].Address`` is not provided and
+       no IP address is set on the interface prior to calling `StartProfile`
+       the value of the main.conf ``[IPv4].APAddressPool`` setting will be
+       inherited, which in turn defaults to 192.168.0.0/16.
+
+       For example, if ``[IPv4].Netmask`` is set the 255.255.255.0 and this
+       setting, or the global ``APAddressPool`` fallback, is set to
+       ``192.168.0.0/16, 10.0.0.0/22``, IWD will select one of the 256 subnets
+       with addresses in the 192.168.<0-255>.0/24 range or one of the 4 subnets
+       with addresses in the 10.0.<0-3>.0/24 range, allowing 270 possible
+       subnets.  Defining an address pool larger than the subnet may be
+       desired if other interfaces on the system use dynamically assigned
+       addresses giving IWD a chance to avoid conflicts.
 
    * - Gateway
      - IP Address of gateway
 
-       IP address of gateway. This will inherit from [IPv4].Address if not
-       provided.
+       IP address of the gateway to be advertised by DHCP. This will fall back
+       to the local IP address if not provided.
 
    * - Netmask
-     - Netmask of DHCP server
+     - Local netmask of the AP
 
-       This will be generated from [IPv4].Address if not provided.
+       Defaults to a 28-bit netmask if not provided.
 
    * - DNSList
-     - List of DNS servers
+     - List of DNS servers as a comma-separated IP address list
 
        A list of DNS servers which will be advertised by the DHCP server. If
        not provided no DNS servers will be sent by the DHCP server.
 
    * - LeaseTime
-     - Time limit for DHCP leases
+     - Time limit for DHCP leases in seconds
 
        Override the default lease time.
 
    * - IPRange
-     - Range of IPs to use for the DHCP server
+     - Range of IPs given as two addresses separated by a comma
 
-       If not provided a default range will be chosen which is the DHCP server
-       address + 1 to 254.
+       From and to addresses of the range assigned to clients through DHCP.
+       If not provided the range from local address + 1 to .254 will be used.
 
 SEE ALSO
 ========
diff --git a/src/iwd.config.rst b/src/iwd.config.rst
index 478bd616..e3dc2827 100644
--- a/src/iwd.config.rst
+++ b/src/iwd.config.rst
@@ -67,23 +67,13 @@ The group ``[General]`` contains general settings.
        obtain the dynamic addresses from the network through the built-in
        DHCP client.
 
-       This also enables DHCP server when in AP mode when either
-       [General].APRanges is set or an AP profile is being used.
+       This also enables network configuration and the DHCP server when in AP
+       mode and the AP profile being activated does does not override it.
 
        The network configuration feature is disabled by default.  See
        ``[Network]`` settings for additional settings related to network
        configuration.
 
-   * - APRanges
-     - Values: <IP in prefix notation>
-
-       Sets the range of IP's used for DHCP server (AP mode). The IP should be
-       in prefix notation e.g. 192.168.1.0/24. AP's which are started in a
-       profile-less configuration will use this pool of IP's to set the AP's
-       interface address as well as default DHCP server options. Each AP will
-       get a new subnet from the range and clients will be addressed in that
-       subnet to avoid IP conflicts if multiple AP's are started.
-
    * - UseDefaultInterface
      - Values: true, **false**
 
@@ -312,6 +302,23 @@ No modification from defaults is normally required.
        prevent **iwd** from roaming properly, but can be useful for networks
        operating under extremely low rssi levels where roaming isn't possible.
 
+IPv4
+----
+
+The group ``[IPv4]`` contains settings related to IPv4 address configuration.
+
+   * - APAddressPool
+     - Values: comma-separated list of prefix-notation IPv4 address strings
+
+       Defines the space of IPs used for the Access Point-mode subnet addresses
+       and the DHCP server.  Defaults to 192.168.0.0/16.  The prefix length
+       decides the size of the pool from which an address is selected but the
+       actual subnet size (netmask) is based on the AP profile being activated
+       and defaults to 28 bits.  The AP profile's ``[IPv4].Address`` setting
+       overrides the global value set here.  Setting a too small address space
+       will limit the number of access points that can be running
+       simultaneously on different interfaces.
+
 SEE ALSO
 ========
 
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax
  2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
                   ` (8 preceding siblings ...)
  2021-03-05 15:09 ` [PATCH 10/10] doc: Update AP settings in iwd.ap and iwd.config(5) Andrew Zaborowski
@ 2021-03-05 22:19 ` Denis Kenzior
  2021-03-05 23:51   ` Andrew Zaborowski
  9 siblings, 1 reply; 16+ messages in thread
From: Denis Kenzior @ 2021-03-05 22:19 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 7577 bytes --]

Hi Andrew,

On 3/5/21 9:09 AM, Andrew Zaborowski wrote:
> After the profile support was added some settings were passed to
> ap_start() through the ap_config struct and others were loaded inside
> ap_start() based on the profile path.  Instead make profiles transparent
> to ap_start():
> 
> * don't pass the profile path to ap_start(),
> * add storage for the profile settings directly to ap_config,

This is ok, but I would actually load them directly into the l_dhcp_server object

> * load and validate the settings from the profile file directly
>    in the caller (ap_dbus_start_profile).
> 
> Now any user of the ap_start() api can set those values and the
> difference between .Start and .StartProfile is only that one loads the
> extra settings from the profile whlie the other relies on the defaults.

typo 'whlie'

> 
> Temporarily drop most of the DHCP setup code as it needs to be rewritten
> to account for the new setting syntax and setup order, this happens in
> the subsequent commits in steps.
> 
> While I'm rewriting the profile loading I also expand the [IPv4].Address
> syntax to allow a new format: a list of <IP>/<prefix_len> -form strings
> that define the address space for subnet address selection.

Not ideal, but ok..

> ---
>   src/ap.c | 425 ++++++++++++++++++++++---------------------------------
>   src/ap.h |  10 +-
>   2 files changed, 180 insertions(+), 255 deletions(-)
> 
> diff --git a/src/ap.c b/src/ap.c
> index 2042cde6..677ba8fa 100644
> --- a/src/ap.c
> +++ b/src/ap.c
> @@ -225,6 +225,171 @@ static const char *broadcast_from_ip(const char *ip)
>   	return inet_ntoa(ia);
>   }
>   
> +#define AP_DEFAULT_IPV4_PREFIX_LEN 28
> +
> +struct ap_config *ap_config_load_profile(const char *ssid)
> +{
> +	L_AUTO_FREE_VAR(char *, profile_path) =
> +		storage_get_path("ap/%s.ap", ssid);
> +	L_AUTO_FREE_OBJ(l_settings, profile) = l_settings_new();

So I can't apply this until the ell part is figured out.  Way to live on 
bleeding edge :)

> +	L_AUTO_FREE_VAR(char *, passphrase) = NULL;
> +	struct ap_config *config;
> +	uint32_t static_addr4 = 0;
> +	unsigned int lease_time;
> +
> +	if (!l_settings_load_from_file(profile, profile_path))
> +		return NULL;
> +
> +	passphrase = l_settings_get_string(profile, "Security",
> +							"Passphrase");
> +	if (!passphrase) {
> +		l_error("[Security].Passphrase not found in "
> +			"AP profile");

Hmm, don't we have open network profiles?

> +		return NULL;
> +	}
> +
> +	/* Full checks done later in crypto_passphrase_is_valid() */
> +	if (strlen(passphrase) > 63) {

I wonder why we would want to delay this?  Also, where does this check actually 
happen?  I can't seem to find this in this patch set?  Or do you mean somewhere 
inside ap_start when we try to generate the PSK?

I would actually run the full sanity check here...

> +		l_error("[Security].Passphrase must not exceed "
> +			"63 characters");
> +		return NULL;
> +	}
> +
> +	config = l_new(struct ap_config, 1);
> +	config->ssid = l_strdup(ssid);
> +	strcpy(config->passphrase, passphrase);
> +
> +	if (!l_settings_has_group(profile, "IPv4")) {
> +		config->disable_netconfig4 = true;
> +		return config;
> +	}
> +
> +	if (l_settings_get_value(profile, "IPv4", "Address")) {
> +		struct in_addr ia;
> +
> +		config->addr4_str_list = l_settings_get_string_list(profile,
> +								"IPv4",
> +								"Address", ',');
> +		if (!config->addr4_str_list || !*config->addr4_str_list) {
> +			l_error("Can't parse the profile [IPv4].Address "
> +				"setting as a string list");
> +			goto invalid_format;
> +		}
> +
> +		/* Check for the static IP syntax: Address=<IP> */
> +		if (l_strv_length((char **) config->addr4_str_list) == 1 &&
> +				inet_pton(AF_INET, *config->addr4_str_list,
> +						&ia) == 1)
> +			static_addr4 = ntohl(ia.s_addr);
> +	}
> +
> +	if (l_settings_get_value(profile, "IPv4", "Netmask")) {
> +		L_AUTO_FREE_VAR(char *, netmask_str) =
> +			l_settings_get_string(profile, "IPv4", "Netmask");
> +		struct in_addr ia;
> +
> +		if (inet_pton(AF_INET, netmask_str, &ia) != 1) {
> +			l_error("Can't parse the profile [IPv4].Netmask "
> +				"setting");
> +			goto invalid_format;
> +		}
> +
> +		config->prefix_len4 = __builtin_popcount(ia.s_addr);
> +
> +		if (ntohl(ia.s_addr) !=
> +				util_netmask_from_prefix(config->prefix_len4)) {
> +			l_error("Invalid profile [IPv4].Netmask value");
> +			goto invalid_format;
> +		}
> +	}
> +
> +	if (l_settings_get_value(profile, "IPv4", "DNSList")) {
> +		config->dns_addr4_str_list = l_settings_get_string_list(
> +								profile, "IPv4",
> +								"DNSList", ',');
> +		if (!config->dns_addr4_str_list ||
> +				!*config->dns_addr4_str_list) {
> +			l_error("Can't parse the profile [IPv4].DNSList "
> +				"setting as a string list");
> +			goto invalid_format;
> +		}
> +	}
> +
> +	if (l_settings_get_value(profile, "IPv4", "IPRange")) {
> +		char **ip_range = l_settings_get_string_list(profile, "IPv4",
> +								"IPRange", ',');
> +		int i;
> +		uint32_t netmask;
> +
> +		if (!static_addr4 || !config->prefix_len4) {
> +			l_error("[IPv4].IPRange only makes sense in an AP "
> +				"profile if a static local address and "
> +				"netmask have also been specified");
> +			l_strv_free(ip_range);
> +			goto invalid_format;
> +		}
> +
> +		if (!ip_range || l_strv_length(ip_range) != 2) {
> +			l_error("Can't parse the profile [IPv4].IPRange "
> +				"setting as two address strings");
> +			l_strv_free(ip_range);
> +			goto invalid_format;
> +		}
> +
> +		netmask = util_netmask_from_prefix(config->prefix_len4);
> +
> +		for (i = 0; i < 2; i++) {
> +			struct in_addr range_addr;
> +
> +			if (inet_aton(ip_range[i], &range_addr) != 1) {
> +				l_error("Can't parse address in "
> +					"[IPv4].IPRange[%i]", i + 1);
> +				goto invalid_format;
> +			}
> +
> +			if ((static_addr4 ^ ntohl(range_addr.s_addr)) &
> +					netmask) {
> +				struct in_addr addr = { htonl(static_addr4) };
> +
> +				l_error("[IPv4].IPRange[%i] is not in the "
> +					"%s/%i subnet", i + 1,
> +					inet_ntoa(addr), config->prefix_len4);
> +				goto invalid_format;
> +			}
> +
> +			if (i == 0)
> +				config->dhcp_start_addr4 =
> +					ntohl(range_addr.s_addr);
> +			else
> +				config->dhcp_end_addr4 =
> +					ntohl(range_addr.s_addr);
> +		}
> +	}
> +
> +	if (l_settings_get_value(profile, "IPv4", "Gateway")) {
> +		L_AUTO_FREE_VAR(char *, gateway_str) =
> +			l_settings_get_string(profile, "IPv4", "Gateway");
> +		struct in_addr ia;
> +
> +		if (inet_pton(AF_INET, gateway_str, &ia) != 1) {
> +			l_error("Can't parse the profile [IPv4].Gateway "
> +				"setting");
> +			goto invalid_format;
> +		}
> +
> +		config->dhcp_gateway_addr4 = ntohl(ia.s_addr);
> +	}
> +
> +	if (l_settings_get_uint(profile, "IPv4", "LeaseTime", &lease_time))
> +		config->dhcp_lease_time = lease_time;

So as mentioned above, there's really no harm in just building the l_dhcp_server 
object as you go here.  Nothing actually happens until l_dhcp_server_start is 
called, so most of the settings can be filled in without needing this 
intermediate storage.  You might need to also tell the caller whether an address 
needs to be allocated.

> +
> +	return config;
> +
> +invalid_format:
> +	ap_config_free(config);
> +	return NULL;
> +}
> +
>   void ap_config_free(struct ap_config *config)
>   {
>   	if (unlikely(!config))

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 02/10] ap: Add missing ap_config_free() in error path
  2021-03-05 15:09 ` [PATCH 02/10] ap: Add missing ap_config_free() in error path Andrew Zaborowski
@ 2021-03-05 22:21   ` Denis Kenzior
  0 siblings, 0 replies; 16+ messages in thread
From: Denis Kenzior @ 2021-03-05 22:21 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 183 bytes --]

Hi Andrew,

On 3/5/21 9:09 AM, Andrew Zaborowski wrote:
> ---
>   src/ap.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 

Applied, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 04/10] ap: Add subnet address selection logic
  2021-03-05 15:09 ` [PATCH 04/10] ap: Add subnet address selection logic Andrew Zaborowski
@ 2021-03-05 22:35   ` Denis Kenzior
  2021-03-05 23:28     ` Andrew Zaborowski
  0 siblings, 1 reply; 16+ messages in thread
From: Denis Kenzior @ 2021-03-05 22:35 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 427 bytes --]

Hi Andrew,

> +		/*
> +		 * Ignore the subnet(s) set on the target interface, we'll be
> +		 * overwriting it if everything goes well.
> +		 */
> +		if (rec->ifindex == ifindex)
> +			continue;

Ok, I'm a bit confused here.  I thought we're adding on to the address list, not 
flushing the addresses?  So shouldn't any addresses set on the target interface 
still be taken into account as 'used'?

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 04/10] ap: Add subnet address selection logic
  2021-03-05 22:35   ` Denis Kenzior
@ 2021-03-05 23:28     ` Andrew Zaborowski
  2021-03-08 20:41       ` Denis Kenzior
  0 siblings, 1 reply; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 23:28 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 1587 bytes --]

On Fri, 5 Mar 2021 at 23:35, Denis Kenzior <denkenz@gmail.com> wrote:
> > +             /*
> > +              * Ignore the subnet(s) set on the target interface, we'll be
> > +              * overwriting it if everything goes well.
> > +              */
> > +             if (rec->ifindex == ifindex)
> > +                     continue;
>
> Ok, I'm a bit confused here.  I thought we're adding on to the address list, not
> flushing the addresses?  So shouldn't any addresses set on the target interface
> still be taken into account as 'used'?

So the comment is actually wrong as we're currently not overwriting
(as in replacing) the addresses.  But keeping extra address space
assigned to that interface is not an issue.  In the following patch I
iterate over all the existing addresses on the interface and I only
add the new subnet if none of the existing subnet addresses fully
covers the new subnet.  So e.g. if the selected subnet is 10.0.5.0/24
and the target interface has 10.0.0.0/16, I wouldn't add the new
address because AFAIU it wouldn't make any difference.

So the 10.0.0.0/16 subnet doesn't make the 10.0.<0-255>.0/24 subnets
unavailable for us if it's on the same interface.  If we did add the
10.0.5.0/24 subnet anyway, it wouldn't create a routing conflict, i.e.
it wouldn't be simultaneously on two interfaces.

By the way I think it's be a good idea to flush the existing
addresses, I just don't want to tackle that simultaneously with this
patchset, and I think it may not be worth our time being such a small
feature.

Best regards

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax
  2021-03-05 22:19 ` [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Denis Kenzior
@ 2021-03-05 23:51   ` Andrew Zaborowski
  0 siblings, 0 replies; 16+ messages in thread
From: Andrew Zaborowski @ 2021-03-05 23:51 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 4245 bytes --]

On Fri, 5 Mar 2021 at 23:19, Denis Kenzior <denkenz@gmail.com> wrote:
> On 3/5/21 9:09 AM, Andrew Zaborowski wrote:
> > After the profile support was added some settings were passed to
> > ap_start() through the ap_config struct and others were loaded inside
> > ap_start() based on the profile path.  Instead make profiles transparent
> > to ap_start():
> >
> > * don't pass the profile path to ap_start(),
> > * add storage for the profile settings directly to ap_config,
>
> This is ok, but I would actually load them directly into the l_dhcp_server object

So the idea is that struct ap_config is part of the public API.  Right
now only P2P uses it but the idea was that it can be mapped directly
to a config file, that's why we didn't add callbacks or other
complicated structures to it.

I guess P2P could create an l_dhcp_server object and store it the
ap_config object but that would limit what we can do with struct
ap_config.

Also each of the 3 callers of ap_start() would need to know whether
netconfig is enabled, do the address selection (hopefully
consistently) and create their own l_dhcp_server object.

>
> > * load and validate the settings from the profile file directly
> >    in the caller (ap_dbus_start_profile).
> >
> > Now any user of the ap_start() api can set those values and the
> > difference between .Start and .StartProfile is only that one loads the
> > extra settings from the profile whlie the other relies on the defaults.
>
> typo 'whlie'
>
> >
> > Temporarily drop most of the DHCP setup code as it needs to be rewritten
> > to account for the new setting syntax and setup order, this happens in
> > the subsequent commits in steps.
> >
> > While I'm rewriting the profile loading I also expand the [IPv4].Address
> > syntax to allow a new format: a list of <IP>/<prefix_len> -form strings
> > that define the address space for subnet address selection.
>
> Not ideal, but ok..

I couldn't think of a different way to split the changes.

>
> > ---
> >   src/ap.c | 425 ++++++++++++++++++++++---------------------------------
> >   src/ap.h |  10 +-
> >   2 files changed, 180 insertions(+), 255 deletions(-)
> >
> > diff --git a/src/ap.c b/src/ap.c
> > index 2042cde6..677ba8fa 100644
> > --- a/src/ap.c
> > +++ b/src/ap.c
> > @@ -225,6 +225,171 @@ static const char *broadcast_from_ip(const char *ip)
> >       return inet_ntoa(ia);
> >   }
> >
> > +#define AP_DEFAULT_IPV4_PREFIX_LEN 28
> > +
> > +struct ap_config *ap_config_load_profile(const char *ssid)
> > +{
> > +     L_AUTO_FREE_VAR(char *, profile_path) =
> > +             storage_get_path("ap/%s.ap", ssid);
> > +     L_AUTO_FREE_OBJ(l_settings, profile) = l_settings_new();
>
> So I can't apply this until the ell part is figured out.  Way to live on
> bleeding edge :)

This has served as a test and as a usage example and I assumed
(correctly) that I'd have a few more chances to drop it ;)

>
> > +     L_AUTO_FREE_VAR(char *, passphrase) = NULL;
> > +     struct ap_config *config;
> > +     uint32_t static_addr4 = 0;
> > +     unsigned int lease_time;
> > +
> > +     if (!l_settings_load_from_file(profile, profile_path))
> > +             return NULL;
> > +
> > +     passphrase = l_settings_get_string(profile, "Security",
> > +                                                     "Passphrase");
> > +     if (!passphrase) {
> > +             l_error("[Security].Passphrase not found in "
> > +                     "AP profile");
>
> Hmm, don't we have open network profiles?

So I think we did have open profiles (because profile loading didn't
strictly require [Security].Passphrase) but we had no open AP
implementation ;)

>
> > +             return NULL;
> > +     }
> > +
> > +     /* Full checks done later in crypto_passphrase_is_valid() */
> > +     if (strlen(passphrase) > 63) {
>
> I wonder why we would want to delay this?  Also, where does this check actually
> happen?  I can't seem to find this in this patch set?  Or do you mean somewhere
> inside ap_start when we try to generate the PSK?

Yep, in crypto_psk_from_passphrase()

>
> I would actually run the full sanity check here...

Ok.

Best regards

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 04/10] ap: Add subnet address selection logic
  2021-03-05 23:28     ` Andrew Zaborowski
@ 2021-03-08 20:41       ` Denis Kenzior
  0 siblings, 0 replies; 16+ messages in thread
From: Denis Kenzior @ 2021-03-08 20:41 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 2097 bytes --]

Hi Andrew,

On 3/5/21 5:28 PM, Andrew Zaborowski wrote:
> On Fri, 5 Mar 2021 at 23:35, Denis Kenzior <denkenz@gmail.com> wrote:
>>> +             /*
>>> +              * Ignore the subnet(s) set on the target interface, we'll be
>>> +              * overwriting it if everything goes well.
>>> +              */
>>> +             if (rec->ifindex == ifindex)
>>> +                     continue;
>>
>> Ok, I'm a bit confused here.  I thought we're adding on to the address list, not
>> flushing the addresses?  So shouldn't any addresses set on the target interface
>> still be taken into account as 'used'?
> 
> So the comment is actually wrong as we're currently not overwriting
> (as in replacing) the addresses.  But keeping extra address space
> assigned to that interface is not an issue.  In the following patch I
> iterate over all the existing addresses on the interface and I only
> add the new subnet if none of the existing subnet addresses fully
> covers the new subnet.  So e.g. if the selected subnet is 10.0.5.0/24
> and the target interface has 10.0.0.0/16, I wouldn't add the new
> address because AFAIU it wouldn't make any difference.
> 
> So the 10.0.0.0/16 subnet doesn't make the 10.0.<0-255>.0/24 subnets
> unavailable for us if it's on the same interface.  If we did add the
> 10.0.5.0/24 subnet anyway, it wouldn't create a routing conflict, i.e.
> it wouldn't be simultaneously on two interfaces.
Okay...

> 
> By the way I think it's be a good idea to flush the existing
> addresses, I just don't want to tackle that simultaneously with this
> patchset, and I think it may not be worth our time being such a small
> feature.

Flushing addresses should be dead-simple to implement.  So, if I were doing 
this, I'd wipe all the addresses from ifindex, dump the other interfaces (to 
check for conflicts) and pick from either the profile address pool or the global 
one.  Would be simpler, probably more fool-proof and eliminate the extra 
parameters (that require 2 paragraphs to explain) from ap_select_addr4().

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2021-03-08 20:41 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-05 15:09 [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 02/10] ap: Add missing ap_config_free() in error path Andrew Zaborowski
2021-03-05 22:21   ` Denis Kenzior
2021-03-05 15:09 ` [PATCH 03/10] ap: Refactor address pool logic Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 04/10] ap: Add subnet address selection logic Andrew Zaborowski
2021-03-05 22:35   ` Denis Kenzior
2021-03-05 23:28     ` Andrew Zaborowski
2021-03-08 20:41       ` Denis Kenzior
2021-03-05 15:09 ` [PATCH 05/10] ap: Dump addreses in use and assign IP/netmask Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 06/10] ap: Set up the DHCP server Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 07/10] ap: Move the DHCP server freeing to ap_reset Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 08/10] ap: Send a specific error message on async AP start failure Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 09/10] autotests: Update APRanges usage in testAP Andrew Zaborowski
2021-03-05 15:09 ` [PATCH 10/10] doc: Update AP settings in iwd.ap and iwd.config(5) Andrew Zaborowski
2021-03-05 22:19 ` [PATCH 01/10] ap: Load profile before calling ap_start, extend Address syntax Denis Kenzior
2021-03-05 23:51   ` Andrew Zaborowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.