All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/8] ip-pool: Track IPv4 addresses in use
@ 2021-05-28  0:47 Andrew Zaborowski
  2021-05-28  0:47 ` [PATCH 2/8] ip-pool: Add subnet address selection logic Andrew Zaborowski
                   ` (8 more replies)
  0 siblings, 9 replies; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

Add the ip-pool submodule that tracks IPv4 addresses in use on the
system for use when selecting the address for a new AP.  l_rtnl_address
is used internally because if we're going to return l_rtnl_address
objects it would be misleading if we didn't fill in all of their
properties like flags etc.
---
 Makefile.am   |   1 +
 src/ip-pool.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ip-pool.h |  23 +++++++
 3 files changed, 199 insertions(+)
 create mode 100644 src/ip-pool.c
 create mode 100644 src/ip-pool.h

diff --git a/Makefile.am b/Makefile.am
index 68035e46..083bc95a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -243,6 +243,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
 					src/eap-wsc.c src/eap-wsc.h \
 					src/wscutil.h src/wscutil.c \
 					src/diagnostic.h src/diagnostic.c \
+					src/ip-pool.h src/ip-pool.c \
 					$(eap_sources) \
 					$(builtin_sources)
 
diff --git a/src/ip-pool.c b/src/ip-pool.c
new file mode 100644
index 00000000..51b3ef40
--- /dev/null
+++ b/src/ip-pool.c
@@ -0,0 +1,175 @@
+/*
+ *
+ *  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/iwd.h"
+#include "src/module.h"
+#include "src/netdev.h"
+#include "src/ip-pool.h"
+
+struct ip_pool_addr4_record {
+	uint32_t ifindex;
+	struct l_rtnl_address *addr;
+};
+
+static struct l_queue *used_addr4_list;
+static struct l_netlink *rtnl;
+
+static void ip_pool_addr4_record_free(void *data)
+{
+	struct ip_pool_addr4_record *rec = data;
+
+	l_rtnl_address_free(rec->addr);
+	l_free(rec);
+}
+
+static bool ip_pool_addr4_match_ifindex(const void *a, const void *b)
+{
+	const struct ip_pool_addr4_record *addr = a;
+
+	return addr->ifindex == L_PTR_TO_UINT(b);
+}
+
+struct l_rtnl_address *ip_pool_get_addr4(uint32_t ifindex)
+{
+	const struct ip_pool_addr4_record *rec =
+		l_queue_find(used_addr4_list, ip_pool_addr4_match_ifindex,
+				L_UINT_TO_PTR(ifindex));
+
+	return rec ? l_rtnl_address_dup(rec->addr) : 0;
+}
+
+static bool ip_pool_addr4_match_free(void *data, void *user_data)
+{
+	const struct ip_pool_addr4_record *a = data;
+	const struct ip_pool_addr4_record *b = user_data;
+	char a_addr_str[INET_ADDRSTRLEN];
+	char b_addr_str[INET_ADDRSTRLEN];
+
+	if (a->ifindex != b->ifindex ||
+			l_rtnl_address_get_prefix_length(a->addr) !=
+			l_rtnl_address_get_prefix_length(b->addr))
+		return false;
+
+	if (!l_rtnl_address_get_address(a->addr, a_addr_str) ||
+			!l_rtnl_address_get_address(b->addr, b_addr_str) ||
+			strcmp(a_addr_str, b_addr_str))
+		return false;
+
+	ip_pool_addr4_record_free(data);
+	return true;
+}
+
+static void ip_pool_addr_notify(uint16_t type, const void *data, uint32_t len,
+				void *user_data)
+{
+	const struct ifaddrmsg *ifa = data;
+	struct l_rtnl_address *addr;
+
+	if (ifa->ifa_family != AF_INET || ifa->ifa_prefixlen < 1)
+		return;
+
+	len -= NLMSG_ALIGN(sizeof(struct ifaddrmsg));
+
+	addr = l_rtnl_ifaddr_extract(ifa, len);
+	if (!addr)
+		return;
+
+	if (type == RTM_NEWADDR) {
+		struct ip_pool_addr4_record *rec;
+
+		rec = l_new(struct ip_pool_addr4_record, 1);
+		rec->ifindex = ifa->ifa_index;
+		rec->addr = l_steal_ptr(addr);
+		l_queue_push_tail(used_addr4_list, rec);
+	} else if (type == RTM_DELADDR) {
+		struct ip_pool_addr4_record rec;
+
+		rec.ifindex = ifa->ifa_index;
+		rec.addr = addr;
+
+		l_queue_foreach_remove(used_addr4_list,
+					ip_pool_addr4_match_free, &rec);
+	}
+
+	l_rtnl_address_free(addr);
+}
+
+static void ip_pool_addr4_dump_cb(int error,
+					uint16_t type, const void *data,
+					uint32_t len, void *user_data)
+{
+	if (error) {
+		l_error("addr4_dump_cb: %s (%i)", strerror(-error), -error);
+		return;
+	}
+
+	ip_pool_addr_notify(type, data, len, user_data);
+}
+
+static int ip_pool_init(void)
+{
+	const struct l_settings *settings = iwd_get_config();
+	bool netconfig_enabled;
+
+	if (!l_settings_get_bool(settings, "General",
+				"EnableNetworkConfiguration",
+				&netconfig_enabled))
+		netconfig_enabled = false;
+
+	if (!netconfig_enabled)
+		return 0;
+
+	rtnl = iwd_get_rtnl();
+
+	if (!l_netlink_register(rtnl, RTNLGRP_IPV4_IFADDR,
+				ip_pool_addr_notify, NULL, NULL)) {
+		l_error("Failed to register for RTNL link notifications");
+		return -EIO;
+	}
+
+	if (!l_rtnl_ifaddr4_dump(rtnl, ip_pool_addr4_dump_cb, NULL, NULL)) {
+		l_error("Sending the IPv4 addr dump req failed");
+		return -EIO;
+	}
+
+	used_addr4_list = l_queue_new();
+	return 0;
+}
+
+static void ip_pool_exit(void)
+{
+	l_queue_destroy(used_addr4_list, ip_pool_addr4_record_free);
+	used_addr4_list = NULL;
+}
+
+IWD_MODULE(ip_pool, ip_pool_init, ip_pool_exit)
diff --git a/src/ip-pool.h b/src/ip-pool.h
new file mode 100644
index 00000000..f3c3011d
--- /dev/null
+++ b/src/ip-pool.h
@@ -0,0 +1,23 @@
+/*
+ *
+ *  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
+ *
+ */
+
+struct l_rtnl_address *ip_pool_get_addr4(uint32_t ifindex);
-- 
2.27.0

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

* [PATCH 2/8] ip-pool: Add subnet address selection logic
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:13   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 3/8] ap: Refactor DHCP settings loading Andrew Zaborowski
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

Add the ip_pool_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 subnets that conflict with subnets in use.  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 (debatable.)
---
 src/ip-pool.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ip-pool.h |   2 +
 2 files changed, 208 insertions(+)

diff --git a/src/ip-pool.c b/src/ip-pool.c
index 51b3ef40..c8a70628 100644
--- a/src/ip-pool.c
+++ b/src/ip-pool.c
@@ -41,9 +41,215 @@ struct ip_pool_addr4_record {
 	struct l_rtnl_address *addr;
 };
 
+struct ip_pool_addr4_range {
+	uint32_t start;
+	uint32_t end;
+};
+
 static struct l_queue *used_addr4_list;
 static struct l_netlink *rtnl;
 
+static int ip_pool_addr4_range_compare(const void *a, const void *b,
+					void *user_data)
+{
+	const struct ip_pool_addr4_range *range_a = a;
+	const struct ip_pool_addr4_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 ip_pool_append_range(struct l_queue *to,
+					const struct ip_pool_addr4_range *range,
+					struct l_queue *used, const char *str)
+{
+	const struct l_queue_entry *entry = l_queue_get_entries(used);
+	const struct ip_pool_addr4_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 ip_pool_addr4_range *sub =
+				l_new(struct ip_pool_addr4_range, 1);
+
+			sub->start = start;
+			sub->end = range->end;
+			l_queue_push_tail(to, sub);
+			l_queue_insert(used, l_memdup(sub, sizeof(*sub)),
+					ip_pool_addr4_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 ip_pool_addr4_range *sub =
+				l_new(struct ip_pool_addr4_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)),
+					ip_pool_addr4_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 ip_pool_select_addr4(const char **addr_str_list, uint8_t subnet_prefix_len,
+				struct l_rtnl_address **out_addr)
+{
+	uint32_t total = 0;
+	uint32_t selected;
+	unsigned int i;
+	uint32_t subnet_size = 1 << (32 - subnet_prefix_len);
+	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_addr4_list);
+			entry; entry = entry->next) {
+		const struct ip_pool_addr4_record *rec = entry->data;
+		struct ip_pool_addr4_range *range;
+		char addr_str[INET_ADDRSTRLEN];
+		uint32_t used_subnet_size = 1 <<
+			(32 - l_rtnl_address_get_prefix_length(rec->addr));
+
+		if (!l_rtnl_address_get_address(rec->addr, addr_str) ||
+				inet_aton(addr_str, &ia) < 0)
+			continue;
+
+		range = l_new(struct ip_pool_addr4_range, 1);
+		range->start = ntohl(ia.s_addr) & subnet_mask;
+		range->end = (range->start + used_subnet_size + subnet_size -
+				1) & subnet_mask;
+		l_queue_insert(used, range, ip_pool_addr4_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 ip_pool_addr4_range range;
+
+		host_addr = ntohl(ia.s_addr);
+		range.start = host_addr & subnet_mask;
+		range.end = range.start + subnet_size;
+		ip_pool_append_range(ranges, &range, used, *addr_str_list);
+		goto check_avail;
+	}
+
+	for (i = 0; addr_str_list[i]; i++) {
+		struct ip_pool_addr4_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 > subnet_prefix_len) {
+			l_debug("Address spec %s smaller than requested "
+				"subnet (prefix len %i)", addr_str_list[i],
+				subnet_prefix_len);
+			continue;
+		}
+
+		range.start = addr & subnet_mask;
+		range.end = range.start + (1 << (32 - addr_prefix));
+		ip_pool_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 @subnet_prefix_len-sized subnets */
+	for (entry = l_queue_get_entries(ranges); entry; entry = entry->next) {
+		struct ip_pool_addr4_range *range = entry->data;
+
+	total += (range->end - range->start) >>
+			(32 - subnet_prefix_len);
+	}
+
+	selected = l_getrandom_uint32() % total;
+
+	/* Find the @selected'th @subnet_prefix_len-sized subnet */
+	for (entry = l_queue_get_entries(ranges);; entry = entry->next) {
+		struct ip_pool_addr4_range *range = entry->data;
+		uint32_t count = (range->end - range->start) >>
+			(32 - subnet_prefix_len);
+
+		if (selected < count) {
+			host_addr = range->start +
+				(selected << (32 - subnet_prefix_len));
+			break;
+		}
+
+		selected -= count;
+	}
+
+	if ((host_addr & 0xff) == 0)
+		host_addr += 1;
+
+done:
+	err = 0;
+	ia.s_addr = htonl(host_addr);
+	*out_addr = l_rtnl_address_new(inet_ntoa(ia), subnet_prefix_len);
+
+cleanup:
+	l_queue_destroy(ranges, l_free);
+	l_queue_destroy(used, l_free);
+	return err;
+}
+
 static void ip_pool_addr4_record_free(void *data)
 {
 	struct ip_pool_addr4_record *rec = data;
diff --git a/src/ip-pool.h b/src/ip-pool.h
index f3c3011d..2c832610 100644
--- a/src/ip-pool.h
+++ b/src/ip-pool.h
@@ -20,4 +20,6 @@
  *
  */
 
+int ip_pool_select_addr4(const char **addr_str_list, uint8_t subnet_prefix_len,
+				struct l_rtnl_address **out_addr);
 struct l_rtnl_address *ip_pool_get_addr4(uint32_t ifindex);
-- 
2.27.0

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

* [PATCH 3/8] ap: Refactor DHCP settings loading
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
  2021-05-28  0:47 ` [PATCH 2/8] ip-pool: Add subnet address selection logic Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:10   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 4/8] ap: Refactor global address pool loading Andrew Zaborowski
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

Extend the [IPv4].Address setting's syntax to allow a new format: a list
of <IP>/<prefix_len> -form strings that define the address space from
which a subnet is selected.  Rewrite the DHCP settings loading with
other notable changes:

 * validate some of the settings more thoroughly,
 * name all netconfig-related ap_state members with the netconfig_
   prefix,
 * make sure we always call l_dhcp_server_set_netmask(),
 * allow netmasks other than 24-bit and change the default to 28 bits,
 * as requested avoid using the l_net_ ioctl-based functions although
   l_dhcp still uses them internally,
 * as requested avoid touching the ap_state members until the end of
   some functions so that on error they're basically a no-op (for
   readabaility).
---
 src/ap.c | 451 +++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 288 insertions(+), 163 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index fff7c664..33130f19 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -52,6 +52,7 @@
 #include "src/frame-xchg.h"
 #include "src/wscutil.h"
 #include "src/eap-wsc.h"
+#include "src/ip-pool.h"
 #include "src/ap.h"
 #include "src/storage.h"
 #include "src/diagnostic.h"
@@ -88,15 +89,15 @@ struct ap_state {
 	uint16_t last_aid;
 	struct l_queue *sta_states;
 
-	struct l_dhcp_server *server;
+	struct l_dhcp_server *netconfig_dhcp;
+	char *netconfig_addr4_str;
+	uint8_t netconfig_prefix_len4;
 	uint32_t rtnl_add_cmd;
-	char *own_ip;
-	unsigned int ip_prefix;
 
 	bool started : 1;
 	bool gtk_set : 1;
-	bool cleanup_ip : 1;
-	bool use_ip_pool : 1;
+	bool netconfig_set_addr4 : 1;
+	bool netconfig_use_ip_pool : 1;
 };
 
 struct sta_state {
@@ -181,7 +182,7 @@ static char *ip_pool_get()
 
 	/* This shouldn't happen */
 	if (next_subnet < pool.sub_start || next_subnet > pool.sub_end)
-		return NULL;
+		return 0;
 
 	l_uintset_put(pool.used, next_subnet);
 
@@ -321,23 +322,27 @@ static void ap_reset(struct ap_state *ap)
 	ap->started = false;
 
 	/* Delete IP if one was set by IWD */
-	if (ap->cleanup_ip)
+	if (ap->netconfig_set_addr4) {
 		l_rtnl_ifaddr4_delete(rtnl, netdev_get_ifindex(netdev),
-					ap->ip_prefix, ap->own_ip,
-					broadcast_from_ip(ap->own_ip),
-					NULL, NULL, NULL);
+				ap->netconfig_prefix_len4,
+				ap->netconfig_addr4_str,
+				broadcast_from_ip(ap->netconfig_addr4_str),
+				NULL, NULL, NULL);
+		ap->netconfig_set_addr4 = false;
+	}
 
-	if (ap->own_ip) {
+	if (ap->netconfig_use_ip_pool) {
 		/* Release IP from pool if used */
-		if (ap->use_ip_pool)
-			ip_pool_put(ap->own_ip);
-
-		l_free(ap->own_ip);
+		ip_pool_put(ap->netconfig_addr4_str);
+		ap->netconfig_use_ip_pool = false;
 	}
 
-	if (ap->server) {
-		l_dhcp_server_destroy(ap->server);
-		ap->server = NULL;
+	l_free(ap->netconfig_addr4_str);
+	ap->netconfig_addr4_str = NULL;
+
+	if (ap->netconfig_dhcp) {
+		l_dhcp_server_destroy(ap->netconfig_dhcp);
+		ap->netconfig_dhcp = NULL;
 	}
 }
 
@@ -2165,7 +2170,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->netconfig_dhcp && !l_dhcp_server_start(ap->netconfig_dhcp)) {
 		l_error("DHCP server failed to start");
 		goto failed;
 	}
@@ -2492,182 +2497,290 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	}
 }
 
-static bool dhcp_load_settings(struct ap_state *ap,
-				const struct l_settings *settings)
+#define AP_DEFAULT_IPV4_PREFIX_LEN 28
+
+static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
+				uint8_t prefix_len, const char *gateway_str,
+				const char **ip_range,
+				const char **dns_str_list,
+				unsigned int lease_time)
 {
-	struct l_dhcp_server *server = ap->server;
+	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
+	struct l_rtnl_address *existing_addr = ip_pool_get_addr4(ifindex);
+	struct l_rtnl_address *new_addr = NULL;
+	int ret;
 	struct in_addr ia;
+	struct l_dhcp_server *dhcp = NULL;
+	bool use_ip_pool = false;
+	bool r;
+	char addr_str_buf[INET_ADDRSTRLEN];
+
+	dhcp = l_dhcp_server_new(ifindex);
+	if (!dhcp) {
+		l_error("Failed to create DHCP server on ifindex %u", ifindex);
+		ret = -EIO;
+		goto cleanup;
+	}
 
-	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 (getenv("IWD_DHCP_DEBUG"))
+		l_dhcp_server_set_debug(dhcp, do_debug,
+					"[DHCPv4 SERV] ", NULL);
+
+	/*
+	 * The address pool specified for this AP (if any) has the priority,
+	 * next is the address currently set on the interface (if any) and
+	 * last is the global AP address pool (APRanges setting).
+	 */
+	if (addr_str_list) {
+		if (!prefix_len)
+			prefix_len = AP_DEFAULT_IPV4_PREFIX_LEN;
+
+		ret = ip_pool_select_addr4(addr_str_list, prefix_len,
+						&new_addr);
+	} else if (existing_addr &&
+			l_rtnl_address_get_prefix_length(existing_addr) <
+			31) {
+		if (!prefix_len)
+			prefix_len = l_rtnl_address_get_prefix_length(
+								existing_addr);
+
+		if (!l_rtnl_address_get_address(existing_addr, addr_str_buf)) {
+			ret = -EIO;
+			goto cleanup;
+		}
 
-	if (!l_settings_get_uint(settings, "IPv4", "LeaseTime", &lease_time))
-		lease_time = 0;
+		new_addr = l_rtnl_address_new(addr_str_buf, prefix_len);
+		ret = 0;
+	} else {
+		L_AUTO_FREE_VAR(char *, addr_str) = NULL;
 
-	if (gateway && !l_dhcp_server_set_gateway(server, gateway)) {
-		l_error("[IPv4].Gateway value error");
-		goto error;
+		if (prefix_len) {
+			l_error("[IPv4].Netmask can't be used without an "
+				"address");
+			ret = -EINVAL;
+			goto cleanup;
+		}
+
+		/* No config, no address set. Use IP pool */
+		addr_str = ip_pool_get();
+		if (addr_str) {
+			use_ip_pool = true;
+			prefix_len = pool.prefix;
+			new_addr = l_rtnl_address_new(addr_str, prefix_len);
+			ret = 0;
+		} else {
+			l_error("No more IP's in pool, cannot start AP on %u",
+					ifindex);
+			ret = -EEXIST;
+		}
 	}
 
-	if (dns && !l_dhcp_server_set_dns(server, dns)) {
-		l_error("[IPv4].DNSList value error");
-		goto error;
+	if (ret)
+		goto cleanup;
+
+	ret = -EIO;
+
+	/*
+	 * l_dhcp_server_start() would retrieve the current IPv4 from
+	 * the interface but set it anyway in case there are multiple
+	 * addresses, saves one ioctl too.
+	 */
+	if (!l_rtnl_address_get_address(new_addr, addr_str_buf)) {
+		l_error("l_rtnl_address_get_address failed");
+		goto cleanup;
 	}
 
-	if (netmask && !l_dhcp_server_set_netmask(server, netmask)) {
-		l_error("[IPv4].Netmask value error");
-		goto error;
+	if (!l_dhcp_server_set_ip_address(dhcp, addr_str_buf)) {
+		l_error("l_dhcp_server_set_ip_address failed");
+		goto cleanup;
+	}
+
+	ia.s_addr = htonl(util_netmask_from_prefix(prefix_len));
+
+	if (!l_dhcp_server_set_netmask(dhcp, inet_ntoa(ia))) {
+		l_error("l_dhcp_server_set_netmask failed");
+		goto cleanup;
+	}
+
+	if (gateway_str && !l_dhcp_server_set_gateway(dhcp, gateway_str)) {
+		l_error("l_dhcp_server_set_gateway failed");
+		goto cleanup;
 	}
 
 	if (ip_range) {
-		if (l_strv_length(ip_range) != 2) {
-			l_error("Two addresses expected in [IPv4].IPRange");
-			goto error;
+		r = l_dhcp_server_set_ip_range(dhcp, ip_range[0], ip_range[1]);
+		if (!r) {
+			l_error("l_dhcp_server_set_ip_range failed");
+			goto cleanup;
 		}
+	}
 
-		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 (dns_str_list) {
+		r = l_dhcp_server_set_dns(dhcp, (char **) dns_str_list);
+		if (!r) {
+			l_error("l_dhcp_server_set_dns failed");
+			goto cleanup;
 		}
 	}
 
-	if (lease_time && !l_dhcp_server_set_lease_time(server, lease_time)) {
-		l_error("[IPv4].LeaseTime value error");
-		goto error;
+	if (lease_time && !l_dhcp_server_set_lease_time(dhcp, lease_time)) {
+		l_error("l_dhcp_server_set_lease_time failed");
+		goto cleanup;
 	}
 
-	if (netmask && inet_pton(AF_INET, netmask, &ia) > 0)
-		ap->ip_prefix = __builtin_popcountl(ia.s_addr);
-	else
-		ap->ip_prefix = 24;
+	ap->netconfig_addr4_str = l_strdup(addr_str_buf);
+	ap->netconfig_prefix_len4 = prefix_len;
+	ap->netconfig_set_addr4 = true;
+	ap->netconfig_use_ip_pool = use_ip_pool;
+	ap->netconfig_dhcp = l_steal_ptr(dhcp);
+	ret = 0;
 
-	ret = true;
+	if (existing_addr && l_rtnl_address_get_prefix_length(existing_addr) >
+			prefix_len) {
+		char addr_str_buf2[INET_ADDRSTRLEN];
 
-error:
-	l_strv_free(dns);
-	l_strv_free(ip_range);
+		if (l_rtnl_address_get_address(existing_addr, addr_str_buf2) &&
+				!strcmp(addr_str_buf, addr_str_buf2))
+			ap->netconfig_set_addr4 = false;
+	}
+
+cleanup:
+	l_dhcp_server_destroy(dhcp);
+	l_rtnl_address_free(new_addr);
+	l_rtnl_address_free(existing_addr);
 	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 or
- *            IP configuration was not enabled,
- *          -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, const struct l_settings *settings)
+static int ap_load_ipv4(struct ap_state *ap, const struct l_settings *config)
 {
-	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
-	struct in_addr ia;
-	uint32_t address = 0;
-	L_AUTO_FREE_VAR(char *, addr) = NULL;
 	int ret = -EINVAL;
+	char **addr_str_list = NULL;
+	uint32_t static_addr = 0;
+	uint8_t prefix_len = 0;
+	char *gateway_str = NULL;
+	char **ip_range = NULL;
+	char **dns_str_list = NULL;
+	unsigned int lease_time = 0;
+	struct in_addr ia;
 
-	/* get the current address if there is one */
-	if (l_net_get_address(ifindex, &ia) && ia.s_addr != 0)
-		address = ia.s_addr;
+	if (!l_settings_has_group(config, "IPv4") || !pool.used)
+		return 0;
 
-	addr = l_settings_get_string(settings, "IPv4", "Address");
-	if (addr) {
-		if (inet_pton(AF_INET, addr, &ia) < 0)
-			return -EINVAL;
+	if (l_settings_has_key(config, "IPv4", "Address")) {
+		addr_str_list = l_settings_get_string_list(config, "IPv4",
+								"Address", ',');
+		if (!addr_str_list || !*addr_str_list) {
+			l_error("Can't parse the profile [IPv4].Address "
+				"setting as a string list");
+			goto done;
+		}
 
-		/* 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 if (pool.used) {
-		/* No config file, no address set. Use IP pool */
-		addr = ip_pool_get();
-		if (!addr) {
-			l_error("No more IP's in pool, cannot start AP on %u",
-					ifindex);
-			return -EEXIST;
+		/* Check for the static IP syntax: Address=<IP> */
+		if (l_strv_length(addr_str_list) == 1 &&
+				inet_pton(AF_INET, *addr_str_list, &ia) == 1)
+			static_addr = ntohl(ia.s_addr);
+	}
+
+	if (l_settings_has_key(config, "IPv4", "Netmask")) {
+		L_AUTO_FREE_VAR(char *, netmask_str) =
+			l_settings_get_string(config, "IPv4", "Netmask");
+
+		if (inet_pton(AF_INET, netmask_str, &ia) != 1) {
+			l_error("Can't parse the profile [IPv4].Netmask "
+				"setting");
+			goto done;
 		}
 
-		ap->use_ip_pool = true;
-		ap->ip_prefix = pool.prefix;
-		ret = 0;
-	} else
-		return -EALREADY;
+		prefix_len = __builtin_popcount(ia.s_addr);
 
-	ap->server = l_dhcp_server_new(ifindex);
-	if (!ap->server) {
-		l_error("Failed to create DHCP server on %u", ifindex);
-		return -EINVAL;
+		if (ntohl(ia.s_addr) != util_netmask_from_prefix(prefix_len)) {
+			l_error("Invalid profile [IPv4].Netmask value");
+			goto done;
+		}
 	}
 
-	if (getenv("IWD_DHCP_DEBUG"))
-		l_dhcp_server_set_debug(ap->server, do_debug,
-							"[DHCPv4 SERV] ", NULL);
+	if (l_settings_has_key(config, "IPv4", "Gateway")) {
+		gateway_str = l_settings_get_string(config, "IPv4", "Gateway");
+		if (!gateway_str) {
+			l_error("Invalid profile [IPv4].Gateway value");
+			goto done;
+		}
+	}
 
-	/* Set the remaining DHCP options in config file */
-	if (!dhcp_load_settings(ap, settings))
-		return -EINVAL;
+	if (l_settings_get_value(config, "IPv4", "IPRange")) {
+		int i;
+		uint32_t netmask;
+		uint8_t tmp_len = prefix_len ?: AP_DEFAULT_IPV4_PREFIX_LEN;
 
-	if (!l_dhcp_server_set_ip_address(ap->server, addr))
-		return -EINVAL;
+		ip_range = l_settings_get_string_list(config, "IPv4",
+							"IPRange", ',');
 
-	ap->own_ip = l_steal_ptr(addr);
-	return ret;
-}
+		if (!static_addr) {
+			l_error("[IPv4].IPRange only makes sense in an AP "
+				"profile if a static local address has also "
+				"been specified");
+			goto done;
+		}
 
-static int ap_load_dhcp(struct ap_state *ap, const struct l_settings *config,
-			bool *wait_dhcp)
-{
-	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
-	int err = -EINVAL;
+		if (!ip_range || l_strv_length(ip_range) != 2) {
+			l_error("Can't parse the profile [IPv4].IPRange "
+				"setting as two address strings");
+			goto done;
+		}
 
-	if (!l_settings_has_group(config, "IPv4"))
-		return 0;
+		netmask = util_netmask_from_prefix(tmp_len);
 
-	err = ap_setup_dhcp(ap, config);
-	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);
+		for (i = 0; i < 2; i++) {
+			struct in_addr range_addr;
 
-		if (!ap->rtnl_add_cmd) {
-			l_error("Failed to add IPv4 address");
-			return -EIO;
+			if (inet_aton(ip_range[i], &range_addr) != 1) {
+				l_error("Can't parse address in "
+					"[IPv4].IPRange[%i]", i + 1);
+				goto done;
+			}
+
+			if ((static_addr ^ ntohl(range_addr.s_addr)) &
+					netmask) {
+				ia.s_addr = htonl(static_addr);
+				l_error("[IPv4].IPRange[%i] is not in the "
+					"%s/%i subnet", i + 1, inet_ntoa(ia),
+					tmp_len);
+				goto done;
+			}
 		}
+	}
 
-		ap->cleanup_ip = true;
+	if (l_settings_has_key(config, "IPv4", "DNSList")) {
+		dns_str_list = l_settings_get_string_list(config, "IPv4",
+								"DNSList", ',');
+		if (!dns_str_list || !*dns_str_list) {
+			l_error("Can't parse the profile [IPv4].DNSList "
+				"setting as a string list");
+			goto done;
+		}
+	}
 
-		*wait_dhcp = true;
-		err = 0;
-	/* Selected address already set, continue normally */
-	} else if (err == -EALREADY) {
-		*wait_dhcp = false;
-		err = 0;
+	if (l_settings_has_key(config, "IPv4", "LeaseTime")) {
+		if (!l_settings_get_uint(config, "IPv4", "LeaseTime",
+						&lease_time) ||
+				lease_time < 1) {
+			l_error("Error parsing [IPv4].LeaseTime as a positive "
+				"integer");
+			goto done;
+		}
 	}
 
-	return err;
+	ret = ap_setup_netconfig4(ap, (const char **) addr_str_list, prefix_len,
+					gateway_str, (const char **) ip_range,
+					(const char **) dns_str_list,
+					lease_time);
+
+done:
+	l_strv_free(addr_str_list);
+	l_free(gateway_str);
+	l_strv_free(ip_range);
+	l_strv_free(dns_str_list);
+	return ret;
 }
 
 static bool ap_load_psk(struct ap_state *ap, const struct l_settings *config)
@@ -2722,7 +2835,7 @@ static bool ap_load_psk(struct ap_state *ap, const struct l_settings *config)
 }
 
 static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
-				bool *out_wait_dhcp, bool *out_cck_rates)
+				bool *out_cck_rates)
 {
 	size_t len;
 	L_AUTO_FREE_VAR(char *, strval) = NULL;
@@ -2748,11 +2861,12 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 		return -EINVAL;
 
 	/*
-	 * This loads DHCP settings either from the l_settings object or uses
-	 * the defaults. wait_on_address will be set true if an address change
-	 * is required.
+	 * This looks@the network configuration settings in @config and
+	 * relevant global settings and if it determines that netconfig is to
+	 * be enabled for the AP, it both creates the DHCP server object and
+	 * processes IP settings, applying the defaults where needed.
 	 */
-	err = ap_load_dhcp(ap, config, out_wait_dhcp);
+	err = ap_load_ipv4(ap, config);
 	if (err)
 		return err;
 
@@ -2868,15 +2982,15 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
 	struct ap_state *ap;
 	struct wiphy *wiphy = netdev_get_wiphy(netdev);
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
-	int err = -EINVAL;
-	bool wait_on_address = false;
+	int err;
 	bool cck_rates = true;
 
-	if (err_out)
-		*err_out = err;
+	if (L_WARN_ON(!config)) {
+		if (err_out)
+			*err_out = -EINVAL;
 
-	if (L_WARN_ON(!config))
 		return NULL;
+	}
 
 	ap = l_new(struct ap_state, 1);
 	ap->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME);
@@ -2884,7 +2998,7 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
 	ap->ops = ops;
 	ap->user_data = user_data;
 
-	err = ap_load_config(ap, config, &wait_on_address, &cck_rates);
+	err = ap_load_config(ap, config, &cck_rates);
 	if (err)
 		goto error;
 
@@ -2950,9 +3064,20 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
 	if (!ap->mlme_watch)
 		l_error("Registering for MLME notification failed");
 
-	if (wait_on_address) {
-		if (err_out)
-			*err_out = 0;
+	if (ap->netconfig_set_addr4) {
+		const char *broadcast_str =
+			broadcast_from_ip(ap->netconfig_addr4_str);
+
+		ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl,
+						netdev_get_ifindex(netdev),
+						ap->netconfig_prefix_len4,
+						ap->netconfig_addr4_str,
+						broadcast_str,
+						ap_ifaddr4_added_cb, ap, NULL);
+		if (!ap->rtnl_add_cmd) {
+			l_error("Failed to add the IPv4 address");
+			goto error;
+		}
 
 		return ap;
 	}
-- 
2.27.0

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

* [PATCH 4/8] ap: Refactor global address pool loading
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
  2021-05-28  0:47 ` [PATCH 2/8] ip-pool: Add subnet address selection logic Andrew Zaborowski
  2021-05-28  0:47 ` [PATCH 3/8] ap: Refactor DHCP settings loading Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:14   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 5/8] ap: Send a specific error message on async AP start failure Andrew Zaborowski
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

Deprecate the global [General].APRanges setting in favour of
[IPv4].APAddressPool with an extended (but backwards-compatible) syntax.
Drop the existing address pool creation code.

The new APAddressPool setting has the same syntax as the profile-local
[IPv4].Address setting and the subnet selection code will fall back
to the global setting if it's missing, this way we use common code to
handle both settings.
---
 src/ap.c | 185 +++++++++++++------------------------------------------
 1 file changed, 42 insertions(+), 143 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 33130f19..9ffccd04 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -97,7 +97,6 @@ struct ap_state {
 	bool started : 1;
 	bool gtk_set : 1;
 	bool netconfig_set_addr4 : 1;
-	bool netconfig_use_ip_pool : 1;
 };
 
 struct sta_state {
@@ -128,99 +127,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_addr4_strs;
 static uint32_t netdev_watch;
 static 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 0;
-
-	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;
@@ -331,12 +242,6 @@ static void ap_reset(struct ap_state *ap)
 		ap->netconfig_set_addr4 = false;
 	}
 
-	if (ap->netconfig_use_ip_pool) {
-		/* Release IP from pool if used */
-		ip_pool_put(ap->netconfig_addr4_str);
-		ap->netconfig_use_ip_pool = false;
-	}
-
 	l_free(ap->netconfig_addr4_str);
 	ap->netconfig_addr4_str = NULL;
 
@@ -2511,7 +2416,6 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
 	int ret;
 	struct in_addr ia;
 	struct l_dhcp_server *dhcp = NULL;
-	bool use_ip_pool = false;
 	bool r;
 	char addr_str_buf[INET_ADDRSTRLEN];
 
@@ -2552,27 +2456,11 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
 		new_addr = l_rtnl_address_new(addr_str_buf, prefix_len);
 		ret = 0;
 	} else {
-		L_AUTO_FREE_VAR(char *, addr_str) = NULL;
-
-		if (prefix_len) {
-			l_error("[IPv4].Netmask can't be used without an "
-				"address");
-			ret = -EINVAL;
-			goto cleanup;
-		}
+		if (!prefix_len)
+			prefix_len = AP_DEFAULT_IPV4_PREFIX_LEN;
 
-		/* No config, no address set. Use IP pool */
-		addr_str = ip_pool_get();
-		if (addr_str) {
-			use_ip_pool = true;
-			prefix_len = pool.prefix;
-			new_addr = l_rtnl_address_new(addr_str, prefix_len);
-			ret = 0;
-		} else {
-			l_error("No more IP's in pool, cannot start AP on %u",
-					ifindex);
-			ret = -EEXIST;
-		}
+		ret = ip_pool_select_addr4((const char **) global_addr4_strs,
+						prefix_len, &new_addr);
 	}
 
 	if (ret)
@@ -2631,7 +2519,6 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
 	ap->netconfig_addr4_str = l_strdup(addr_str_buf);
 	ap->netconfig_prefix_len4 = prefix_len;
 	ap->netconfig_set_addr4 = true;
-	ap->netconfig_use_ip_pool = use_ip_pool;
 	ap->netconfig_dhcp = l_steal_ptr(dhcp);
 	ret = 0;
 
@@ -2663,7 +2550,7 @@ static int ap_load_ipv4(struct ap_state *ap, const struct l_settings *config)
 	unsigned int lease_time = 0;
 	struct in_addr ia;
 
-	if (!l_settings_has_group(config, "IPv4") || !pool.used)
+	if (!l_settings_has_group(config, "IPv4") || !netconfig_enabled)
 		return 0;
 
 	if (l_settings_has_key(config, "IPv4", "Address")) {
@@ -3652,7 +3539,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);
 
@@ -3663,31 +3549,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;
-
-	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;
+					"EnableNetworkConfiguration",
+					&netconfig_enabled))
+		netconfig_enabled = false;
+
+	if (netconfig_enabled) {
+		if (l_settings_get_value(settings, "IPv4", "APAddressPool")) {
+			global_addr4_strs = l_settings_get_string_list(settings,
+								"IPv4",
+								"APAddressPool",
+								',');
+			if (!global_addr4_strs || !*global_addr4_strs) {
+				l_error("Can't parse the [IPv4].APAddressPool "
+					"setting as a string list");
+				l_strv_free(global_addr4_strs);
+				global_addr4_strs = NULL;
+			}
+		} else if (l_settings_get_value(settings,
+						"General", "APRanges")) {
+			global_addr4_strs = l_settings_get_string_list(settings,
+								"General",
+								"APRanges",
+								',');
+			if (!global_addr4_strs || !*global_addr4_strs) {
+				l_error("Can't parse the [General].APRanges "
+					"setting as a string list");
+				l_strv_free(global_addr4_strs);
+				global_addr4_strs = NULL;
+			}
 		}
 
-		if (!ip_pool_create(ip_prefix))
-			return -EINVAL;
+		/* Fall back to 192.168.0.0/16 */
+		if (!global_addr4_strs)
+			global_addr4_strs =
+				l_strv_append(NULL, "192.168.0.0/16");
 	}
 
 	rtnl = iwd_get_rtnl();
@@ -3700,7 +3599,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_addr4_strs);
 }
 
 IWD_MODULE(ap, ap_init, ap_exit)
-- 
2.27.0

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

* [PATCH 5/8] ap: Send a specific error message on async AP start failure
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2021-05-28  0:47 ` [PATCH 4/8] ap: Refactor global address pool loading Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:18   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 6/8] ap: Save AP address as l_rtnl_address Andrew Zaborowski
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 3543 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 | 32 ++++++++++++++++++--------------
 src/ap.h |  4 ++++
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 9ffccd04..430fdfa3 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -2054,9 +2054,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);
 
@@ -2071,22 +2073,18 @@ 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->netconfig_dhcp && !l_dhcp_server_start(ap->netconfig_dhcp)) {
 		l_error("DHCP server failed to start");
-		goto failed;
+		ap_start_failed(ap, -EINVAL);
+		return;
 	}
 
 	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)
@@ -2201,12 +2199,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,
@@ -2378,10 +2376,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;
@@ -3164,13 +3164,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 b0100f34..9a03d3ae 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -35,6 +35,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 *assoc_ies;
-- 
2.27.0

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

* [PATCH 6/8] ap: Save AP address as l_rtnl_address
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2021-05-28  0:47 ` [PATCH 5/8] ap: Send a specific error message on async AP start failure Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:20   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 7/8] autotests: Update APRanges usage in testAP Andrew Zaborowski
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

Change the char *addr_str and uint8_t prefix_len pair to an
l_rtnl_address object and use ell/rtnl.h utilities that use that
directly.  Extend broadcast_from_ip to handle prefix_len.
---
 src/ap.c | 43 +++++++++++++++++--------------------------
 1 file changed, 17 insertions(+), 26 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 430fdfa3..dc067352 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -90,8 +90,7 @@ struct ap_state {
 	struct l_queue *sta_states;
 
 	struct l_dhcp_server *netconfig_dhcp;
-	char *netconfig_addr4_str;
-	uint8_t netconfig_prefix_len4;
+	struct l_rtnl_address *netconfig_addr4;
 	uint32_t rtnl_add_cmd;
 
 	bool started : 1;
@@ -132,20 +131,15 @@ static char **global_addr4_strs;
 static uint32_t netdev_watch;
 static struct l_netlink *rtnl;
 
-static const char *broadcast_from_ip(const char *ip)
+static const char *broadcast_from_ip(const char *ip, uint8_t prefix_len)
 {
 	struct in_addr ia;
-	uint32_t bcast;
+	uint32_t netmask = util_netmask_from_prefix(prefix_len);
 
 	if (inet_aton(ip, &ia) != 1)
 		return NULL;
 
-	bcast = ntohl(ia.s_addr);
-	bcast &= 0xffffff00;
-	bcast |= 0x000000ff;
-
-	ia.s_addr = htonl(bcast);
-
+	ia.s_addr |= htonl(~netmask);
 	return inet_ntoa(ia);
 }
 
@@ -234,16 +228,12 @@ static void ap_reset(struct ap_state *ap)
 
 	/* Delete IP if one was set by IWD */
 	if (ap->netconfig_set_addr4) {
-		l_rtnl_ifaddr4_delete(rtnl, netdev_get_ifindex(netdev),
-				ap->netconfig_prefix_len4,
-				ap->netconfig_addr4_str,
-				broadcast_from_ip(ap->netconfig_addr4_str),
-				NULL, NULL, NULL);
+		l_rtnl_ifaddr_delete(rtnl, netdev_get_ifindex(netdev),
+					ap->netconfig_addr4, NULL, NULL, NULL);
 		ap->netconfig_set_addr4 = false;
 	}
 
-	l_free(ap->netconfig_addr4_str);
-	ap->netconfig_addr4_str = NULL;
+	l_rtnl_address_free(l_steal_ptr(ap->netconfig_addr4));
 
 	if (ap->netconfig_dhcp) {
 		l_dhcp_server_destroy(ap->netconfig_dhcp);
@@ -2454,6 +2444,13 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
 		}
 
 		new_addr = l_rtnl_address_new(addr_str_buf, prefix_len);
+
+		if (!l_rtnl_address_set_broadcast(new_addr,
+				broadcast_from_ip(addr_str_buf, prefix_len))) {
+			ret = -EIO;
+			goto cleanup;
+		}
+
 		ret = 0;
 	} else {
 		if (!prefix_len)
@@ -2516,9 +2513,8 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
 		goto cleanup;
 	}
 
-	ap->netconfig_addr4_str = l_strdup(addr_str_buf);
-	ap->netconfig_prefix_len4 = prefix_len;
 	ap->netconfig_set_addr4 = true;
+	ap->netconfig_addr4 = l_steal_ptr(new_addr);
 	ap->netconfig_dhcp = l_steal_ptr(dhcp);
 	ret = 0;
 
@@ -2952,14 +2948,9 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
 		l_error("Registering for MLME notification failed");
 
 	if (ap->netconfig_set_addr4) {
-		const char *broadcast_str =
-			broadcast_from_ip(ap->netconfig_addr4_str);
-
-		ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl,
+		ap->rtnl_add_cmd = l_rtnl_ifaddr_add(rtnl,
 						netdev_get_ifindex(netdev),
-						ap->netconfig_prefix_len4,
-						ap->netconfig_addr4_str,
-						broadcast_str,
+						ap->netconfig_addr4,
 						ap_ifaddr4_added_cb, ap, NULL);
 		if (!ap->rtnl_add_cmd) {
 			l_error("Failed to add the IPv4 address");
-- 
2.27.0

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

* [PATCH 7/8] autotests: Update APRanges usage in testAP
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2021-05-28  0:47 ` [PATCH 6/8] ap: Save AP address as l_rtnl_address Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-06-01 15:22   ` Denis Kenzior
  2021-05-28  0:47 ` [PATCH 8/8] doc: Update AP settings in iwd.ap(5) and iwd.config(5) Andrew Zaborowski
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 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] 19+ messages in thread

* [PATCH 8/8] doc: Update AP settings in iwd.ap(5) and iwd.config(5)
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2021-05-28  0:47 ` [PATCH 7/8] autotests: Update APRanges usage in testAP Andrew Zaborowski
@ 2021-05-28  0:47 ` Andrew Zaborowski
  2021-05-28 14:22 ` [PATCH 1/8] ip-pool: Track IPv4 addresses in use Denis Kenzior
  2021-06-01 15:06 ` Denis Kenzior
  8 siblings, 0 replies; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28  0:47 UTC (permalink / raw)
  To: iwd

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

---
 src/iwd.ap.rst     | 31 ++++++++++++++++++++++++-------
 src/iwd.config.rst | 29 ++++++++++++++++++++++++++---
 2 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/src/iwd.ap.rst b/src/iwd.ap.rst
index 51622509..5fa23179 100644
--- a/src/iwd.ap.rst
+++ b/src/iwd.ap.rst
@@ -104,14 +104,31 @@ is desired, the group header line must still be present:
    :widths: 20 80
 
    * - Address
-     - Local IP address
+     - 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 provided this addresss will be set on the AP interface and any other
-       DHCP server options will be derived from it, unless they are overridden
-       by other settings below.  If *Address* is not provided and no IP
-       address is set on the interface prior to calling `StartProfile`,  the IP
-       pool defined by the global ``[General].APRanges`` setting will be used.
+       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
+       if not overridden by other settings below.
+
+       If a list of addresses and prefix lengths is specified (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 is based on the ``[IPv4].Netmask`` setting.
+
+       If *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 to 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 desired subnet gives
+       IWD a chance to avoid conflicts if other interfaces on the system use
+       dynamically assigned addresses.
 
    * - Gateway
      - IP Address of gateway
@@ -122,7 +139,7 @@ is desired, the group header line must still be present:
    * - Netmask
      - 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 as a comma-separated IP address list
diff --git a/src/iwd.config.rst b/src/iwd.config.rst
index 60f5b5db..78ec284e 100644
--- a/src/iwd.config.rst
+++ b/src/iwd.config.rst
@@ -67,8 +67,8 @@ 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 not override it.
 
        The network configuration feature is disabled by default.  See
        ``[Network]`` settings for additional settings related to network
@@ -182,7 +182,7 @@ The group ``[General]`` contains general settings.
        then setting ``DisableANQP`` to ``false`` is recommended.
 
 Network
----------
+-------
 
 The group ``[Network]`` contains network configuration related settings.
 
@@ -319,6 +319,29 @@ 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 network configuration.
+
+.. list-table::
+   :header-rows: 0
+   :stub-columns: 0
+   :widths: 20 80
+   :align: left
+
+   * - APAddressPool
+     - Values: comma-separated list of prefix-notation IP 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] 19+ messages in thread

* Re: [PATCH 1/8] ip-pool: Track IPv4 addresses in use
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2021-05-28  0:47 ` [PATCH 8/8] doc: Update AP settings in iwd.ap(5) and iwd.config(5) Andrew Zaborowski
@ 2021-05-28 14:22 ` Denis Kenzior
  2021-05-28 23:53   ` Andrew Zaborowski
  2021-06-01 15:06 ` Denis Kenzior
  8 siblings, 1 reply; 19+ messages in thread
From: Denis Kenzior @ 2021-05-28 14:22 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Add the ip-pool submodule that tracks IPv4 addresses in use on the
> system for use when selecting the address for a new AP.  l_rtnl_address
> is used internally because if we're going to return l_rtnl_address
> objects it would be misleading if we didn't fill in all of their
> properties like flags etc.
> ---
>   Makefile.am   |   1 +
>   src/ip-pool.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/ip-pool.h |  23 +++++++
>   3 files changed, 199 insertions(+)
>   create mode 100644 src/ip-pool.c
>   create mode 100644 src/ip-pool.h
> 

rc/ip-pool.c: In function ‘ip_pool_get_addr4’:
src/ip-pool.c:68:15: error: implicit declaration of function 
‘l_rtnl_address_dup’; did you mean ‘l_rtnl_address_new’? 
[-Werror=implicit-function-declaration]
    68 |  return rec ? l_rtnl_address_dup(rec->addr) : 0;
       |               ^~~~~~~~~~~~~~~~~~
       |               l_rtnl_address_new
src/ip-pool.c:68:45: error: returning ‘int’ from a function with return type 
‘struct l_rtnl_address *’ makes pointer from integer without a cast 
[-Werror=int-conversion]
    68 |  return rec ? l_rtnl_address_dup(rec->addr) : 0;
       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~


Regards,
-Denis

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

* Re: [PATCH 1/8] ip-pool: Track IPv4 addresses in use
  2021-05-28 14:22 ` [PATCH 1/8] ip-pool: Track IPv4 addresses in use Denis Kenzior
@ 2021-05-28 23:53   ` Andrew Zaborowski
  0 siblings, 0 replies; 19+ messages in thread
From: Andrew Zaborowski @ 2021-05-28 23:53 UTC (permalink / raw)
  To: iwd

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

Hi Denis,

On Fri, 28 May 2021 at 16:22, Denis Kenzior <denkenz@gmail.com> wrote:
> On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> src/ip-pool.c:68:15: error: implicit declaration of function
> ‘l_rtnl_address_dup’; did you mean ‘l_rtnl_address_new’?

Oops, sure enough I forgot to send the ell patch..

Best regards

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

* Re: [PATCH 1/8] ip-pool: Track IPv4 addresses in use
  2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
                   ` (7 preceding siblings ...)
  2021-05-28 14:22 ` [PATCH 1/8] ip-pool: Track IPv4 addresses in use Denis Kenzior
@ 2021-06-01 15:06 ` Denis Kenzior
  8 siblings, 0 replies; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:06 UTC (permalink / raw)
  To: iwd

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

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Add the ip-pool submodule that tracks IPv4 addresses in use on the
> system for use when selecting the address for a new AP.  l_rtnl_address
> is used internally because if we're going to return l_rtnl_address
> objects it would be misleading if we didn't fill in all of their
> properties like flags etc.
> ---
>   Makefile.am   |   1 +
>   src/ip-pool.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/ip-pool.h |  23 +++++++
>   3 files changed, 199 insertions(+)
>   create mode 100644 src/ip-pool.c
>   create mode 100644 src/ip-pool.h
> 

I s/l_rtnl_address_dup/l_rtnl_address_clone to match ell changes and..

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 3/8] ap: Refactor DHCP settings loading
  2021-05-28  0:47 ` [PATCH 3/8] ap: Refactor DHCP settings loading Andrew Zaborowski
@ 2021-06-01 15:10   ` Denis Kenzior
  0 siblings, 0 replies; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:10 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Extend the [IPv4].Address setting's syntax to allow a new format: a list
> of <IP>/<prefix_len> -form strings that define the address space from
> which a subnet is selected.  Rewrite the DHCP settings loading with
> other notable changes:
> 
>   * validate some of the settings more thoroughly,
>   * name all netconfig-related ap_state members with the netconfig_
>     prefix,
>   * make sure we always call l_dhcp_server_set_netmask(),
>   * allow netmasks other than 24-bit and change the default to 28 bits,
>   * as requested avoid using the l_net_ ioctl-based functions although
>     l_dhcp still uses them internally,
>   * as requested avoid touching the ap_state members until the end of
>     some functions so that on error they're basically a no-op (for
>     readabaility).

I fixed this typo above and..

> ---
>   src/ap.c | 451 +++++++++++++++++++++++++++++++++++--------------------
>   1 file changed, 288 insertions(+), 163 deletions(-)
> 

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 2/8] ip-pool: Add subnet address selection logic
  2021-05-28  0:47 ` [PATCH 2/8] ip-pool: Add subnet address selection logic Andrew Zaborowski
@ 2021-06-01 15:13   ` Denis Kenzior
  0 siblings, 0 replies; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:13 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Add the ip_pool_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 subnets that conflict with subnets in use.  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 (debatable.)
> ---
>   src/ip-pool.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   src/ip-pool.h |   2 +
>   2 files changed, 208 insertions(+)
> 

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 4/8] ap: Refactor global address pool loading
  2021-05-28  0:47 ` [PATCH 4/8] ap: Refactor global address pool loading Andrew Zaborowski
@ 2021-06-01 15:14   ` Denis Kenzior
  2021-06-01 23:06     ` Andrew Zaborowski
  0 siblings, 1 reply; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:14 UTC (permalink / raw)
  To: iwd

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

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Deprecate the global [General].APRanges setting in favour of
> [IPv4].APAddressPool with an extended (but backwards-compatible) syntax.
> Drop the existing address pool creation code.
> 
> The new APAddressPool setting has the same syntax as the profile-local
> [IPv4].Address setting and the subnet selection code will fall back
> to the global setting if it's missing, this way we use common code to
> handle both settings.
> ---
>   src/ap.c | 185 +++++++++++++------------------------------------------
>   1 file changed, 42 insertions(+), 143 deletions(-)
> 

<snip>

> +	if (netconfig_enabled) {
> +		if (l_settings_get_value(settings, "IPv4", "APAddressPool")) {
> +			global_addr4_strs = l_settings_get_string_list(settings,
> +								"IPv4",
> +								"APAddressPool",
> +								',');
> +			if (!global_addr4_strs || !*global_addr4_strs) {
> +				l_error("Can't parse the [IPv4].APAddressPool "
> +					"setting as a string list");
> +				l_strv_free(global_addr4_strs);
> +				global_addr4_strs = NULL;
> +			}
> +		} else if (l_settings_get_value(settings,
> +						"General", "APRanges")) {
> +			global_addr4_strs = l_settings_get_string_list(settings,
> +								"General",
> +								"APRanges",
> +								',');

Do you want to print a warning that this setting is deprecated?

> +			if (!global_addr4_strs || !*global_addr4_strs) {
> +				l_error("Can't parse the [General].APRanges "
> +					"setting as a string list");
> +				l_strv_free(global_addr4_strs);
> +				global_addr4_strs = NULL;
> +			}
>   		}
>   

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 5/8] ap: Send a specific error message on async AP start failure
  2021-05-28  0:47 ` [PATCH 5/8] ap: Send a specific error message on async AP start failure Andrew Zaborowski
@ 2021-06-01 15:18   ` Denis Kenzior
  0 siblings, 0 replies; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:18 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> 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 | 32 ++++++++++++++++++--------------
>   src/ap.h |  4 ++++
>   2 files changed, 22 insertions(+), 14 deletions(-)
> 

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 6/8] ap: Save AP address as l_rtnl_address
  2021-05-28  0:47 ` [PATCH 6/8] ap: Save AP address as l_rtnl_address Andrew Zaborowski
@ 2021-06-01 15:20   ` Denis Kenzior
  2021-06-01 23:21     ` Andrew Zaborowski
  0 siblings, 1 reply; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:20 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> Change the char *addr_str and uint8_t prefix_len pair to an
> l_rtnl_address object and use ell/rtnl.h utilities that use that
> directly.  Extend broadcast_from_ip to handle prefix_len.
> ---
>   src/ap.c | 43 +++++++++++++++++--------------------------
>   1 file changed, 17 insertions(+), 26 deletions(-)
> 

<snip>

> @@ -132,20 +131,15 @@ static char **global_addr4_strs;
>   static uint32_t netdev_watch;
>   static struct l_netlink *rtnl;
>   
> -static const char *broadcast_from_ip(const char *ip)
> +static const char *broadcast_from_ip(const char *ip, uint8_t prefix_len)
>   {
>   	struct in_addr ia;
> -	uint32_t bcast;
> +	uint32_t netmask = util_netmask_from_prefix(prefix_len);
>   
>   	if (inet_aton(ip, &ia) != 1)
>   		return NULL;
>   
> -	bcast = ntohl(ia.s_addr);
> -	bcast &= 0xffffff00;
> -	bcast |= 0x000000ff;
> -
> -	ia.s_addr = htonl(bcast);
> -
> +	ia.s_addr |= htonl(~netmask);
>   	return inet_ntoa(ia);
>   }
>   

Okay, but don't you really want to use l_rtnl_address_set_broadcast(rtnl, NULL) 
instead?  Also, you may want to do this directly in ip-pool.c?

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 7/8] autotests: Update APRanges usage in testAP
  2021-05-28  0:47 ` [PATCH 7/8] autotests: Update APRanges usage in testAP Andrew Zaborowski
@ 2021-06-01 15:22   ` Denis Kenzior
  0 siblings, 0 replies; 19+ messages in thread
From: Denis Kenzior @ 2021-06-01 15:22 UTC (permalink / raw)
  To: iwd

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

Hi Andrew,

On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> [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(-)
> 

Patches 7 and 8 applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 4/8] ap: Refactor global address pool loading
  2021-06-01 15:14   ` Denis Kenzior
@ 2021-06-01 23:06     ` Andrew Zaborowski
  0 siblings, 0 replies; 19+ messages in thread
From: Andrew Zaborowski @ 2021-06-01 23:06 UTC (permalink / raw)
  To: iwd

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

Hi Denis,

On Tue, 1 Jun 2021 at 17:14, Denis Kenzior <denkenz@gmail.com> wrote:
> On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> > +             } else if (l_settings_get_value(settings,
> > +                                             "General", "APRanges")) {
> > +                     global_addr4_strs = l_settings_get_string_list(settings,
> > +                                                             "General",
> > +                                                             "APRanges",
> > +                                                             ',');
>
> Do you want to print a warning that this setting is deprecated?

Ok, will send a patch.

Best regards

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

* Re: [PATCH 6/8] ap: Save AP address as l_rtnl_address
  2021-06-01 15:20   ` Denis Kenzior
@ 2021-06-01 23:21     ` Andrew Zaborowski
  0 siblings, 0 replies; 19+ messages in thread
From: Andrew Zaborowski @ 2021-06-01 23:21 UTC (permalink / raw)
  To: iwd

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

Hi Denis,

On Tue, 1 Jun 2021 at 17:20, Denis Kenzior <denkenz@gmail.com> wrote:
> On 5/27/21 7:47 PM, Andrew Zaborowski wrote:
> > -static const char *broadcast_from_ip(const char *ip)
> > +static const char *broadcast_from_ip(const char *ip, uint8_t prefix_len)
> >   {
> >       struct in_addr ia;
> > -     uint32_t bcast;
> > +     uint32_t netmask = util_netmask_from_prefix(prefix_len);
> >
> >       if (inet_aton(ip, &ia) != 1)
> >               return NULL;
> >
> > -     bcast = ntohl(ia.s_addr);
> > -     bcast &= 0xffffff00;
> > -     bcast |= 0x000000ff;
> > -
> > -     ia.s_addr = htonl(bcast);
> > -
> > +     ia.s_addr |= htonl(~netmask);
> >       return inet_ntoa(ia);
> >   }
> >
>
> Okay, but don't you really want to use l_rtnl_address_set_broadcast(rtnl, NULL)
> instead?

Oh, I didn't know this existed, and apparently it's already done in
l_rtnl_address_new() so I can just drop this from ap.c.

>  Also, you may want to do this directly in ip-pool.c?

Considered that too but in this case we're creating a new l_rtnl_address.

Best regards

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

end of thread, other threads:[~2021-06-01 23:21 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-28  0:47 [PATCH 1/8] ip-pool: Track IPv4 addresses in use Andrew Zaborowski
2021-05-28  0:47 ` [PATCH 2/8] ip-pool: Add subnet address selection logic Andrew Zaborowski
2021-06-01 15:13   ` Denis Kenzior
2021-05-28  0:47 ` [PATCH 3/8] ap: Refactor DHCP settings loading Andrew Zaborowski
2021-06-01 15:10   ` Denis Kenzior
2021-05-28  0:47 ` [PATCH 4/8] ap: Refactor global address pool loading Andrew Zaborowski
2021-06-01 15:14   ` Denis Kenzior
2021-06-01 23:06     ` Andrew Zaborowski
2021-05-28  0:47 ` [PATCH 5/8] ap: Send a specific error message on async AP start failure Andrew Zaborowski
2021-06-01 15:18   ` Denis Kenzior
2021-05-28  0:47 ` [PATCH 6/8] ap: Save AP address as l_rtnl_address Andrew Zaborowski
2021-06-01 15:20   ` Denis Kenzior
2021-06-01 23:21     ` Andrew Zaborowski
2021-05-28  0:47 ` [PATCH 7/8] autotests: Update APRanges usage in testAP Andrew Zaborowski
2021-06-01 15:22   ` Denis Kenzior
2021-05-28  0:47 ` [PATCH 8/8] doc: Update AP settings in iwd.ap(5) and iwd.config(5) Andrew Zaborowski
2021-05-28 14:22 ` [PATCH 1/8] ip-pool: Track IPv4 addresses in use Denis Kenzior
2021-05-28 23:53   ` Andrew Zaborowski
2021-06-01 15:06 ` Denis Kenzior

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.