All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 1/6] util: add util_ip_prefix_tohl
@ 2020-10-26 18:05 James Prestwood
  2020-10-26 18:05 ` [PATCH v5 2/6] unit: add test for util_ip_prefix_tohl James Prestwood
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

Parses an IP prefix notation string into prefix, start, end, and
netmask. All values are returned in host order.
---
 src/util.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/util.h |  3 ++
 2 files changed, 98 insertions(+)

diff --git a/src/util.c b/src/util.c
index 13b01c81..5dc5f15d 100644
--- a/src/util.c
+++ b/src/util.c
@@ -28,6 +28,8 @@
 #include <stdio.h>
 #include <sys/uio.h>
 #include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <ell/ell.h>
 
@@ -218,3 +220,96 @@ const char *util_get_username(const char *identity)
 
 	return identity;
 }
+
+static bool is_prefix_valid(uint32_t ip, unsigned int prefix)
+{
+	int i;
+
+	for (i = 31 - prefix; i >= 0; i--) {
+		if (ip & (1 << i))
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * Parse a prefix notation IP string (e.g. A.B.C.D/E) into an IP range and
+ * netmask. All returned IP addresses/mask will be in host order. The start/end
+ * IP will only include the usable IP range where the last octet is not zero or
+ * 255.
+ */
+bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
+				uint32_t *start_out, uint32_t *end_out,
+				uint32_t *mask_out)
+{
+	struct in_addr ia;
+	int i;
+	unsigned int prefix;
+	char no_prefix[16];
+	char *endp;
+	uint32_t start_ip;
+	uint32_t end_ip;
+	uint32_t netmask = 0xffffffff;
+	int len = strlen(ip) - 1;
+	bool found = false;
+	uint8_t dots = 0;
+
+	/* Count dots and find prefix offset */
+	for (i = 0; i <= len; i++) {
+		if (ip[i] == '.')
+			dots++;
+		else if (ip[i] == '/') {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found || dots != 3)
+		return false;
+
+	memcpy(no_prefix, ip, i);
+	no_prefix[i] = '\0';
+
+	/* Check if IP preceeding prefix is valid */
+	if (inet_aton(no_prefix, &ia) < 0 || ia.s_addr == 0)
+		return false;
+
+	prefix = strtol(ip + i + 1, &endp, 0);
+	if (*endp != '\0')
+		return false;
+
+	if (prefix < 1 || prefix > 31)
+		return false;
+
+	start_ip = ntohl(ia.s_addr);
+
+	if (!is_prefix_valid(start_ip, prefix))
+		return false;
+
+	/* Usable range is start + 1 .. end - 1 */
+	start_ip += 1;
+
+	/* Calculate end IP and netmask */
+	end_ip = start_ip;
+	for (i = 31 - prefix; i >= 0; i--) {
+		end_ip |= (1 << i);
+		netmask &= ~(1 << i);
+	}
+
+	end_ip -= 1;
+
+	if (prefix_out)
+		*prefix_out = prefix;
+
+	if (start_out)
+		*start_out = start_ip;
+
+	if (end_out)
+		*end_out = end_ip;
+
+	if (mask_out)
+		*mask_out = netmask;
+
+	return true;
+}
diff --git a/src/util.h b/src/util.h
index 2679c117..e6b4747f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -107,4 +107,7 @@ static inline uint32_t util_secure_fill_with_msb(uint32_t val)
 	return (uint32_t) (val >> (sizeof(val)*8 - 1)) * 0xFFFFFFFF;
 }
 
+bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix, uint32_t *start_out,
+				uint32_t *end_out, uint32_t *mask_out);
+
 #endif /* __UTIL_H */
-- 
2.26.2

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

* [PATCH v5 2/6] unit: add test for util_ip_prefix_tohl
  2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
@ 2020-10-26 18:05 ` James Prestwood
  2020-10-26 18:05 ` [PATCH v5 3/6] ap: add error out param to ap_start James Prestwood
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

---
 unit/test-util.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/unit/test-util.c b/unit/test-util.c
index f091f2c2..09af925e 100644
--- a/unit/test-util.c
+++ b/unit/test-util.c
@@ -27,6 +27,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <ell/ell.h>
 
 #include "src/util.h"
@@ -115,6 +117,64 @@ static void get_username_test(const void *data)
 	assert(strcmp(test, "username") == 0);
 }
 
+static void ip_prefix_test(const void *data)
+{
+	unsigned int i;
+	char *invalid[] = {
+		"192.168.0.0", /* Not prefix notation */
+		"192.168./22", /* Incomplete notation */
+		"192.168.0.1/255", /* Too long prefix */
+		"192.168.0.1/0", /* Too short prefix */
+		"192.168.0.1/16", /* Invalid prefix */
+		"192.168.1.2.3/24", /* IP too long */
+	};
+
+	struct {
+		char *ip_prefix;
+		uint8_t prefix;
+		char *start;
+		char *end;
+		char *mask;
+	} valid[] = {
+		{"192.168.80.0/22", 22, "192.168.80.1",
+				"192.168.83.254", "255.255.252.0"},
+		{"192.168.128.0/20", 20, "192.168.128.1",
+				"192.168.143.254", "255.255.240.0"},
+		{"192.168.0.0/25", 25, "192.168.0.1",
+				"192.168.0.126", "255.255.255.128"},
+		{"192.168.0.0/29", 29, "192.168.0.1",
+				"192.168.0.6", "255.255.255.248"},
+		{"192.168.0.128/25", 25, "192.168.0.129",
+				"192.168.0.254", "255.255.255.128"},
+	};
+
+	for (i = 0; i < L_ARRAY_SIZE(invalid); i++)
+		assert(!util_ip_prefix_tohl(invalid[i], NULL, NULL,
+						NULL, NULL));
+
+	for (i = 0; i < L_ARRAY_SIZE(valid); i++) {
+		uint8_t prefix;
+		uint32_t start;
+		uint32_t end;
+		uint32_t mask;
+		struct in_addr ia;
+
+		assert(util_ip_prefix_tohl(valid[i].ip_prefix,
+						&prefix, &start, &end, &mask));
+
+		assert(valid[i].prefix == prefix);
+
+		ia.s_addr = htonl(start);
+		assert(strcmp(inet_ntoa(ia), valid[i].start) == 0);
+
+		ia.s_addr = htonl(end);
+		assert(strcmp(inet_ntoa(ia), valid[i].end) == 0);
+
+		ia.s_addr = htonl(mask);
+		assert(strcmp(inet_ntoa(ia), valid[i].mask) == 0);
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	l_test_init(&argc, &argv);
@@ -122,6 +182,7 @@ int main(int argc, char *argv[])
 	l_test_add("/util/ssid_to_utf8/", ssid_to_utf8, ssid_samples);
 	l_test_add("/util/get_domain/", get_domain_test, NULL);
 	l_test_add("/util/get_username/", get_username_test, NULL);
+	l_test_add("/util/ip_prefix/", ip_prefix_test, NULL);
 
 	return l_test_run();
 }
-- 
2.26.2

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

* [PATCH v5 3/6] ap: add error out param to ap_start
  2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
  2020-10-26 18:05 ` [PATCH v5 2/6] unit: add test for util_ip_prefix_tohl James Prestwood
@ 2020-10-26 18:05 ` James Prestwood
  2020-10-26 19:30   ` Denis Kenzior
  2020-10-26 18:05 ` [PATCH v5 4/6] ap: add support for DHCPv4 server James Prestwood
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

This allows the caller to extract a bit more information about what
exactly went wrong.
---
 src/ap.c  | 18 +++++++++++++++---
 src/ap.h  |  3 ++-
 src/p2p.c |  2 +-
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 3c4ae907..b10b65ae 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -2033,12 +2033,17 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
  * to by its members (which also can't be static).
  */
 struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
-				const struct ap_ops *ops, void *user_data)
+				const struct ap_ops *ops, int *err_out,
+				void *user_data)
 {
 	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;
 
 	if (L_WARN_ON(!config->ssid))
 		return NULL;
@@ -2146,9 +2151,15 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 		goto error;
 	}
 
+	if (err_out)
+		*err_out = 0;
+
 	return ap;
 
 error:
+	if (err_out)
+		*err_out = err;
+
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
 	l_free(ap);
@@ -2382,6 +2393,7 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
 	struct ap_if_data *ap_if = user_data;
 	const char *ssid, *wpa2_passphrase;
 	struct ap_config *config;
+	int err;
 
 	if (ap_if->ap && ap_if->ap->started)
 		return dbus_error_already_exists(message);
@@ -2398,10 +2410,10 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
 	l_strlcpy(config->passphrase, wpa2_passphrase,
 			sizeof(config->passphrase));
 
-	ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, ap_if);
+	ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if);
 	if (!ap_if->ap) {
 		ap_config_free(config);
-		return dbus_error_invalid_args(message);
+		return dbus_error_from_errno(err, message);
 	}
 
 	ap_if->pending = l_dbus_message_ref(message);
diff --git a/src/ap.h b/src/ap.h
index a39797aa..1f2d438a 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -74,7 +74,8 @@ struct ap_ops {
 void ap_config_free(struct ap_config *config);
 
 struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
-				const struct ap_ops *ops, void *user_data);
+				const struct ap_ops *ops, int *err,
+				void *user_data);
 void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
 			void *user_data);
 void ap_free(struct ap_state *ap);
diff --git a/src/p2p.c b/src/p2p.c
index 417ce3d6..6932033c 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -754,7 +754,7 @@ static void p2p_group_start(struct p2p_device *dev)
 	dev->capability.group_caps |= P2P_GROUP_CAP_GO;
 	dev->capability.group_caps |= P2P_GROUP_CAP_GROUP_FORMATION;
 
-	dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, dev);
+	dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, NULL, dev);
 	if (!dev->group) {
 		ap_config_free(config);
 		p2p_connect_failed(dev);
-- 
2.26.2

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

* [PATCH v5 4/6] ap: add support for DHCPv4 server
  2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
  2020-10-26 18:05 ` [PATCH v5 2/6] unit: add test for util_ip_prefix_tohl James Prestwood
  2020-10-26 18:05 ` [PATCH v5 3/6] ap: add error out param to ap_start James Prestwood
@ 2020-10-26 18:05 ` James Prestwood
  2020-10-26 18:23   ` James Prestwood
  2020-10-26 18:05 ` [PATCH v5 5/6] auto-t: testutil: add test_ip_address_match James Prestwood
  2020-10-26 18:05 ` [PATCH v5 6/6] auto-t: add AP test with DHCP server James Prestwood
  4 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

The DHCP server can be enabled by enabling network configuration
with [General].EnableNetworkConfiguration. If an IP is not set
on the interface before the AP is started a valid IP range must
also be provided under [General].APRanges in IP prefix format e.g.

[General]
EnableNetworkConfiguration=true
APRanges=192.168.1.1/24

Each AP started will get assigned a new subnet within the range
specified by APRanges as to not conflict with other AP interfaces.
If there are no subnets left in the pool when an AP is started
it will fail with -EEXIST. Any AP's that are stopped will release
their subnet back into the pool to be used with other APs.

The DHCP IP pool will be automatically chosen by the ELL DHCP
implementation (+1 the AP's IP to *.254). The remaining DHCP
settings will be defaults chosen by ELL (DNS, lease time, etc).
---
 src/ap.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 252 insertions(+), 6 deletions(-)

v5:
 * Added IP pool rather than hard coded IP address

diff --git a/src/ap.c b/src/ap.c
index b10b65ae..17877ddd 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -26,6 +26,8 @@
 
 #include <errno.h>
 #include <linux/if_ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <ell/ell.h>
 
@@ -74,6 +76,10 @@ struct ap_state {
 	uint16_t last_aid;
 	struct l_queue *sta_states;
 
+	struct l_dhcp_server *server;
+	uint32_t rtnl_add_cmd;
+	char *own_ip;
+
 	bool started : 1;
 	bool gtk_set : 1;
 };
@@ -104,7 +110,99 @@ struct ap_wsc_pbc_probe_record {
 	uint64_t timestamp;
 };
 
+struct ap_ip_pool {
+	uint32_t start;
+	uint32_t end;
+	char *mask;
+	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 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)
+{
+	struct in_addr ia;
+	uint32_t mask;
+
+	if (!util_ip_prefix_tohl(ip_prefix, &pool.prefix, &pool.start,
+					&pool.end, &mask))
+		return false;
+
+	ia.s_addr = htonl(mask);
+	pool.mask = l_strdup(inet_ntoa(ia));
+
+	/*
+	 * 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);
+
+	if (pool.mask)
+		l_free(pool.mask);
+}
 
 void ap_config_free(struct ap_config *config)
 {
@@ -181,6 +279,9 @@ static void ap_reset(struct ap_state *ap)
 	if (ap->start_stop_cmd_id)
 		l_genl_family_cancel(ap->nl80211, ap->start_stop_cmd_id);
 
+	if (ap->rtnl_add_cmd)
+		l_netlink_cancel(rtnl, ap->rtnl_add_cmd);
+
 	l_queue_destroy(ap->sta_states, ap_sta_free);
 
 	if (ap->rates)
@@ -192,6 +293,14 @@ static void ap_reset(struct ap_state *ap)
 	l_queue_destroy(ap->wsc_pbc_probes, l_free);
 
 	ap->started = false;
+
+	if (ap->own_ip) {
+		ip_pool_put(ap->own_ip);
+		l_free(ap->own_ip);
+	}
+
+	if (ap->server)
+		l_dhcp_server_stop(ap->server);
 }
 
 static void ap_del_station(struct sta_state *sta, uint16_t reason,
@@ -1900,6 +2009,22 @@ static void ap_deauth_cb(const struct mmpdu_header *hdr, const void *body,
 	ap_sta_free(sta);
 }
 
+static void do_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	l_info("%s%s", prefix, str);
+}
+
+static void ap_start_failed(struct ap_state *ap)
+{
+	ap->ops->handle_event(AP_EVENT_START_FAILED, NULL, ap->user_data);
+	ap_reset(ap);
+	l_genl_family_free(ap->nl80211);
+
+	l_free(ap);
+}
+
 static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
 {
 	struct ap_state *ap = user_data;
@@ -1909,16 +2034,26 @@ 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));
 
-		ap->ops->handle_event(AP_EVENT_START_FAILED, NULL,
-					ap->user_data);
-		ap_reset(ap);
-		l_genl_family_free(ap->nl80211);
-		l_free(ap);
-		return;
+		goto failed;
+	}
+
+	/*
+	 * TODO: Add support for provisioning files where user could configure
+	 * specific DHCP sever settings.
+	 */
+
+	if (ap->server && !l_dhcp_server_start(ap->server)) {
+		l_error("DHCP server failed to start");
+		goto failed;
 	}
 
 	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)
@@ -1989,6 +2124,37 @@ 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)
+{
+	struct ap_state *ap = user_data;
+	struct l_genl_msg *cmd;
+
+	ap->rtnl_add_cmd = 0;
+
+	if (error) {
+		l_error("Failed to set IP address");
+		goto error;
+	}
+
+	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_genl_msg_unref(cmd);
+		goto error;
+	}
+
+	return;
+
+error:
+	ap_start_failed(ap);
+}
+
+
 static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
 {
 	struct ap_state *ap = user_data;
@@ -2040,6 +2206,8 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	struct wiphy *wiphy = netdev_get_wiphy(netdev);
 	struct l_genl_msg *cmd;
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
+	uint32_t ifindex = netdev_get_ifindex(netdev);
+	struct in_addr ia;
 	int err = -EINVAL;
 
 	if (err_out)
@@ -2140,6 +2308,53 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
 	if (!ap->mlme_watch)
 		l_error("Registering for MLME notification failed");
 
+	/* No IP pool initialized, DHCP is not being used */
+	if (!pool.used)
+		goto done;
+
+	ap->server = l_dhcp_server_new(ifindex);
+	if (!ap->server) {
+		l_error("Failed to create DHCP server on %u", ifindex);
+		goto error;
+	}
+
+	if (getenv("IWD_DHCP_DEBUG"))
+		l_dhcp_server_set_debug(ap->server, do_debug,
+							"[DHCPv4 SERV] ", NULL);
+
+	/*
+	 * If there is no IP set on the interface use one from the IP pool
+	 * defined in main.conf.
+	 */
+	if (!l_net_get_address(ifindex, &ia) || ia.s_addr == 0) {
+		ap->own_ip = ip_pool_get();
+
+		if (!ap->own_ip) {
+			l_error("No more IP's in pool, cannot start AP on %u",
+					ifindex);
+			err = -EEXIST;
+			goto error;
+		}
+
+		ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
+					pool.prefix, ap->own_ip, pool.mask,
+					ap_ifaddr4_added_cb, ap, NULL);
+
+		if (!ap->rtnl_add_cmd) {
+			l_error("Failed to add IPv4 address");
+			goto error;
+		}
+
+
+		if (*err_out)
+			*err_out = 0;
+
+		/* Finish starting AP in added callback */
+		return ap;
+	}
+	/* Else honor the IP set on the interface prior to calling Start() */
+
+done:
 	cmd = ap_build_cmd_start_ap(ap);
 	if (!cmd)
 		goto error;
@@ -2160,6 +2375,7 @@ error:
 	if (err_out)
 		*err_out = err;
 
+	ap->config = NULL;
 	ap_reset(ap);
 	l_genl_family_free(ap->nl80211);
 	l_free(ap);
@@ -2266,6 +2482,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);
 	l_free(ap);
 }
 
@@ -2542,11 +2760,37 @@ static void ap_netdev_watch(struct netdev *netdev,
 
 static int ap_init(void)
 {
+	const struct l_settings *settings = iwd_get_config();
+	bool dhcp_enable = false;
+
 	netdev_watch = netdev_watch_add(ap_netdev_watch, NULL, NULL);
 
 	l_dbus_register_interface(dbus_get_bus(), IWD_AP_INTERFACE,
 			ap_setup_interface, ap_destroy_interface, 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.
+	 */
+	l_settings_get_bool(settings, "General", "EnableNetworkConfiguration",
+				&dhcp_enable);
+
+	if (dhcp_enable) {
+		L_AUTO_FREE_VAR(char *, ip_prefix);
+
+		ip_prefix = l_settings_get_string(settings, "General",
+							"APRanges");
+		if (!ip_prefix) {
+			l_error("[General].APRanges must be set for DHCP");
+			return -EINVAL;
+		}
+
+		ip_pool_create(ip_prefix);
+
+		rtnl = iwd_get_rtnl();
+	}
+
 	return 0;
 }
 
@@ -2554,6 +2798,8 @@ static void ap_exit(void)
 {
 	netdev_watch_remove(netdev_watch);
 	l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
+
+	ip_pool_destroy();
 }
 
 IWD_MODULE(ap, ap_init, ap_exit)
-- 
2.26.2

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

* [PATCH v5 5/6] auto-t: testutil: add test_ip_address_match
  2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
                   ` (2 preceding siblings ...)
  2020-10-26 18:05 ` [PATCH v5 4/6] ap: add support for DHCPv4 server James Prestwood
@ 2020-10-26 18:05 ` James Prestwood
  2020-10-26 18:05 ` [PATCH v5 6/6] auto-t: add AP test with DHCP server James Prestwood
  4 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

Checks that the the IP address for a given interface matches
---
 autotests/util/testutil.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/autotests/util/testutil.py b/autotests/util/testutil.py
index f9cfbf29..faa81f65 100644
--- a/autotests/util/testutil.py
+++ b/autotests/util/testutil.py
@@ -125,6 +125,7 @@ def test_ifaces_connected(if0=None, if1=None, group=True):
             raise e
 
 SIOCGIFFLAGS = 0x8913
+SIOCGIFADDR = 0x8915
 IFF_UP = 1 << 0
 IFF_RUNNING = 1 << 6
 
@@ -143,3 +144,11 @@ def test_iface_operstate(intf=None):
             raise Exception(intf + ' operstate wrong')
     finally:
         sock.close()
+
+def test_ip_address_match(intf, ip):
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    addr = fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', intf.encode('utf-8')))
+    addr = socket.inet_ntoa(addr[20:24])
+
+    if ip != addr:
+        raise Exception('IP for %s did not match %s (was %s)' % (intf, ip, addr))
-- 
2.26.2

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

* [PATCH v5 6/6] auto-t: add AP test with DHCP server
  2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
                   ` (3 preceding siblings ...)
  2020-10-26 18:05 ` [PATCH v5 5/6] auto-t: testutil: add test_ip_address_match James Prestwood
@ 2020-10-26 18:05 ` James Prestwood
  4 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:05 UTC (permalink / raw
  To: iwd

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

The existing AP tests needed to be modified to start IWD from
python since the DHCP test uses a different main.conf.

Also removed some stale hw.conf options that are no longer used.
---
 autotests/testAP/connection_test.py |  2 +-
 autotests/testAP/dhcp/main.conf     |  6 ++
 autotests/testAP/dhcp_test.py       | 93 +++++++++++++++++++++++++++++
 autotests/testAP/failure_test.py    |  2 +-
 autotests/testAP/hw.conf            |  5 +-
 5 files changed, 103 insertions(+), 5 deletions(-)
 create mode 100644 autotests/testAP/dhcp/main.conf
 create mode 100644 autotests/testAP/dhcp_test.py

diff --git a/autotests/testAP/connection_test.py b/autotests/testAP/connection_test.py
index c3744c55..754ce952 100644
--- a/autotests/testAP/connection_test.py
+++ b/autotests/testAP/connection_test.py
@@ -38,7 +38,7 @@ class Test(unittest.TestCase):
         wd.wait_for_object_condition(ordered_network.network_object, condition)
 
     def test_connection_success(self):
-        wd = IWD()
+        wd = IWD(True)
 
         dev1, dev2 = wd.list_devices(2)
 
diff --git a/autotests/testAP/dhcp/main.conf b/autotests/testAP/dhcp/main.conf
new file mode 100644
index 00000000..667de23c
--- /dev/null
+++ b/autotests/testAP/dhcp/main.conf
@@ -0,0 +1,6 @@
+[Scan]
+DisableMacAddressRandomization=true
+
+[General]
+EnableNetworkConfiguration=true
+APRanges=192.168.80.0/23
diff --git a/autotests/testAP/dhcp_test.py b/autotests/testAP/dhcp_test.py
new file mode 100644
index 00000000..bf52fb28
--- /dev/null
+++ b/autotests/testAP/dhcp_test.py
@@ -0,0 +1,93 @@
+#! /usr/bin/python3
+
+import unittest
+import sys, os
+
+import iwd
+from iwd import IWD
+from iwd import PSKAgent
+from iwd import NetworkType
+from config import ctx
+import testutil
+
+class Test(unittest.TestCase):
+    def test_connection_success(self):
+        wd = IWD(True, '/tmp/dhcp')
+
+        # dev1, dev3, and dev4 are all AP's
+        # The configured IP range only supports 2 subnets, so dev4 should fail
+        # to start AP.
+        dev1, dev2, dev3, dev4 = wd.list_devices(4)
+
+        dev1.start_ap('TestAP2', "Password2")
+        dev3.start_ap('TestAP3', 'Password3')
+
+        with self.assertRaises(iwd.AlreadyExistsEx):
+            dev4.start_ap('TestAP4', 'Password4')
+
+        try:
+            condition = 'not obj.scanning'
+            wd.wait_for_object_condition(dev2, condition)
+            dev2.scan()
+            condition = 'obj.scanning'
+            wd.wait_for_object_condition(dev2, condition)
+            condition = 'not obj.scanning'
+            wd.wait_for_object_condition(dev2, condition)
+
+            ordered_networks = dev2.get_ordered_networks()
+
+            networks = { n.name: n for n in ordered_networks }
+            self.assertEqual(networks['TestAP2'].type, NetworkType.psk)
+
+            psk_agent = PSKAgent('Password2')
+            wd.register_psk_agent(psk_agent)
+
+            try:
+                dev2.disconnect()
+
+                condition = 'not obj.connected'
+                wd.wait_for_object_condition(dev2, condition)
+            except:
+                pass
+
+            networks['TestAP2'].network_object.connect()
+
+            condition = 'obj.state == DeviceState.connected'
+            wd.wait_for_object_condition(dev2, condition)
+
+            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")
+
+            wd.unregister_psk_agent(psk_agent)
+
+            dev2.disconnect()
+
+            condition = 'not obj.connected'
+            wd.wait_for_object_condition(networks['TestAP2'].network_object,
+                                         condition)
+
+            # This should release the IP */
+            dev1.stop_ap()
+
+            # This should now succeed and the IP should match the old IP dev1
+            # got initially.
+            dev4.start_ap('TestAP4', 'Password4')
+
+            testutil.test_ip_address_match(dev4.name, "192.168.80.1")
+
+        finally:
+            dev1.stop_ap()
+
+    @classmethod
+    def setUpClass(cls):
+        pass
+
+    @classmethod
+    def tearDownClass(cls):
+        IWD.clear_storage()
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
diff --git a/autotests/testAP/failure_test.py b/autotests/testAP/failure_test.py
index 4959802f..fc5cb6a7 100644
--- a/autotests/testAP/failure_test.py
+++ b/autotests/testAP/failure_test.py
@@ -39,7 +39,7 @@ class Test(unittest.TestCase):
         wd.wait_for_object_condition(ordered_network.network_object, condition)
 
     def test_connection_failure(self):
-        wd = IWD()
+        wd = IWD(True)
 
         dev1, dev2 = wd.list_devices(2)
 
diff --git a/autotests/testAP/hw.conf b/autotests/testAP/hw.conf
index 96a2bbb1..bac128b2 100644
--- a/autotests/testAP/hw.conf
+++ b/autotests/testAP/hw.conf
@@ -1,7 +1,6 @@
 [SETUP]
-num_radios=3
-max_test_exec_interval_sec=40
-tmpfs_extra_stuff=main.conf
+num_radios=5
+start_iwd=0
 
 [HOSTAPD]
 rad0=psk-ccmp.conf
-- 
2.26.2

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

* Re: [PATCH v5 4/6] ap: add support for DHCPv4 server
  2020-10-26 18:05 ` [PATCH v5 4/6] ap: add support for DHCPv4 server James Prestwood
@ 2020-10-26 18:23   ` James Prestwood
  0 siblings, 0 replies; 8+ messages in thread
From: James Prestwood @ 2020-10-26 18:23 UTC (permalink / raw
  To: iwd

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

On Mon, 2020-10-26 at 11:05 -0700, James Prestwood wrote:
> The DHCP server can be enabled by enabling network configuration
> with [General].EnableNetworkConfiguration. If an IP is not set
> on the interface before the AP is started a valid IP range must
> also be provided under [General].APRanges in IP prefix format e.g.
> 
> [General]
> EnableNetworkConfiguration=true
> APRanges=192.168.1.1/24
> 
> Each AP started will get assigned a new subnet within the range
> specified by APRanges as to not conflict with other AP interfaces.
> If there are no subnets left in the pool when an AP is started
> it will fail with -EEXIST. Any AP's that are stopped will release
> their subnet back into the pool to be used with other APs.
> 
> The DHCP IP pool will be automatically chosen by the ELL DHCP
> implementation (+1 the AP's IP to *.254). The remaining DHCP
> settings will be defaults chosen by ELL (DNS, lease time, etc).
> ---
>  src/ap.c | 258
> +++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 252 insertions(+), 6 deletions(-)
> 
> v5:
>  * Added IP pool rather than hard coded IP address
> 
> diff --git a/src/ap.c b/src/ap.c
> index b10b65ae..17877ddd 100644
> --- a/src/ap.c
> +++ b/src/ap.c
> @@ -26,6 +26,8 @@
>  
>  #include <errno.h>
>  #include <linux/if_ether.h>
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
>  
>  #include <ell/ell.h>
>  
> @@ -74,6 +76,10 @@ struct ap_state {
>  	uint16_t last_aid;
>  	struct l_queue *sta_states;
>  
> +	struct l_dhcp_server *server;
> +	uint32_t rtnl_add_cmd;
> +	char *own_ip;
> +
>  	bool started : 1;
>  	bool gtk_set : 1;
>  };
> @@ -104,7 +110,99 @@ struct ap_wsc_pbc_probe_record {
>  	uint64_t timestamp;
>  };
>  
> +struct ap_ip_pool {
> +	uint32_t start;
> +	uint32_t end;
> +	char *mask;
> +	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 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)
> +{
> +	struct in_addr ia;
> +	uint32_t mask;
> +
> +	if (!util_ip_prefix_tohl(ip_prefix, &pool.prefix, &pool.start,
> +					&pool.end, &mask))
> +		return false;
> +
> +	ia.s_addr = htonl(mask);
> +	pool.mask = l_strdup(inet_ntoa(ia));
> +
> +	/*
> +	 * 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);
> +
> +	if (pool.mask)
> +		l_free(pool.mask);
> +}
>  
>  void ap_config_free(struct ap_config *config)
>  {
> @@ -181,6 +279,9 @@ static void ap_reset(struct ap_state *ap)
>  	if (ap->start_stop_cmd_id)
>  		l_genl_family_cancel(ap->nl80211, ap-
> >start_stop_cmd_id);
>  
> +	if (ap->rtnl_add_cmd)
> +		l_netlink_cancel(rtnl, ap->rtnl_add_cmd);
> +
>  	l_queue_destroy(ap->sta_states, ap_sta_free);
>  
>  	if (ap->rates)
> @@ -192,6 +293,14 @@ static void ap_reset(struct ap_state *ap)
>  	l_queue_destroy(ap->wsc_pbc_probes, l_free);
>  
>  	ap->started = false;
> +
> +	if (ap->own_ip) {
> +		ip_pool_put(ap->own_ip);
> +		l_free(ap->own_ip);
> +	}
> +
> +	if (ap->server)
> +		l_dhcp_server_stop(ap->server);
>  }
>  
>  static void ap_del_station(struct sta_state *sta, uint16_t reason,
> @@ -1900,6 +2009,22 @@ static void ap_deauth_cb(const struct
> mmpdu_header *hdr, const void *body,
>  	ap_sta_free(sta);
>  }
>  
> +static void do_debug(const char *str, void *user_data)
> +{
> +	const char *prefix = user_data;
> +
> +	l_info("%s%s", prefix, str);
> +}
> +
> +static void ap_start_failed(struct ap_state *ap)
> +{
> +	ap->ops->handle_event(AP_EVENT_START_FAILED, NULL, ap-
> >user_data);
> +	ap_reset(ap);
> +	l_genl_family_free(ap->nl80211);
> +
> +	l_free(ap);
> +}
> +
>  static void ap_start_cb(struct l_genl_msg *msg, void *user_data)
>  {
>  	struct ap_state *ap = user_data;
> @@ -1909,16 +2034,26 @@ 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));
>  
> -		ap->ops->handle_event(AP_EVENT_START_FAILED, NULL,
> -					ap->user_data);
> -		ap_reset(ap);
> -		l_genl_family_free(ap->nl80211);
> -		l_free(ap);
> -		return;
> +		goto failed;
> +	}
> +
> +	/*
> +	 * TODO: Add support for provisioning files where user could
> configure
> +	 * specific DHCP sever settings.
> +	 */
> +
> +	if (ap->server && !l_dhcp_server_start(ap->server)) {
> +		l_error("DHCP server failed to start");
> +		goto failed;
>  	}
>  
>  	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)
> @@ -1989,6 +2124,37 @@ 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)
> +{
> +	struct ap_state *ap = user_data;
> +	struct l_genl_msg *cmd;
> +
> +	ap->rtnl_add_cmd = 0;
> +
> +	if (error) {
> +		l_error("Failed to set IP address");
> +		goto error;
> +	}
> +
> +	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_genl_msg_unref(cmd);
> +		goto error;
> +	}
> +
> +	return;
> +
> +error:
> +	ap_start_failed(ap);
> +}
> +
> +
>  static void ap_mlme_notify(struct l_genl_msg *msg, void *user_data)
>  {
>  	struct ap_state *ap = user_data;
> @@ -2040,6 +2206,8 @@ struct ap_state *ap_start(struct netdev
> *netdev, struct ap_config *config,
>  	struct wiphy *wiphy = netdev_get_wiphy(netdev);
>  	struct l_genl_msg *cmd;
>  	uint64_t wdev_id = netdev_get_wdev_id(netdev);
> +	uint32_t ifindex = netdev_get_ifindex(netdev);
> +	struct in_addr ia;
>  	int err = -EINVAL;
>  
>  	if (err_out)
> @@ -2140,6 +2308,53 @@ struct ap_state *ap_start(struct netdev
> *netdev, struct ap_config *config,
>  	if (!ap->mlme_watch)
>  		l_error("Registering for MLME notification failed");
>  
> +	/* No IP pool initialized, DHCP is not being used */
> +	if (!pool.used)
> +		goto done;
> +
> +	ap->server = l_dhcp_server_new(ifindex);
> +	if (!ap->server) {
> +		l_error("Failed to create DHCP server on %u", ifindex);
> +		goto error;
> +	}
> +
> +	if (getenv("IWD_DHCP_DEBUG"))
> +		l_dhcp_server_set_debug(ap->server, do_debug,
> +							"[DHCPv4 SERV]
> ", NULL);
> +
> +	/*
> +	 * If there is no IP set on the interface use one from the IP
> pool
> +	 * defined in main.conf.
> +	 */
> +	if (!l_net_get_address(ifindex, &ia) || ia.s_addr == 0) {
> +		ap->own_ip = ip_pool_get();
> +
> +		if (!ap->own_ip) {
> +			l_error("No more IP's in pool, cannot start AP
> on %u",
> +					ifindex);
> +			err = -EEXIST;
> +			goto error;
> +		}
> +
> +		ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
> +					pool.prefix, ap->own_ip,
> pool.mask,
> +					ap_ifaddr4_added_cb, ap, NULL);
> +
> +		if (!ap->rtnl_add_cmd) {
> +			l_error("Failed to add IPv4 address");
> +			goto error;
> +		}
> +
> +
> +		if (*err_out)

Should be checking 'err_out' not '*err_out'

> +			*err_out = 0;
> +
> +		/* Finish starting AP in added callback */
> +		return ap;
> +	}
> +	/* Else honor the IP set on the interface prior to calling
> Start() */
> +
> +done:
>  	cmd = ap_build_cmd_start_ap(ap);
>  	if (!cmd)
>  		goto error;
> @@ -2160,6 +2375,7 @@ error:
>  	if (err_out)
>  		*err_out = err;
>  
> +	ap->config = NULL;
>  	ap_reset(ap);
>  	l_genl_family_free(ap->nl80211);
>  	l_free(ap);
> @@ -2266,6 +2482,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);
>  	l_free(ap);
>  }
>  
> @@ -2542,11 +2760,37 @@ static void ap_netdev_watch(struct netdev
> *netdev,
>  
>  static int ap_init(void)
>  {
> +	const struct l_settings *settings = iwd_get_config();
> +	bool dhcp_enable = false;
> +
>  	netdev_watch = netdev_watch_add(ap_netdev_watch, NULL, NULL);
>  
>  	l_dbus_register_interface(dbus_get_bus(), IWD_AP_INTERFACE,
>  			ap_setup_interface, ap_destroy_interface,
> 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.
> +	 */
> +	l_settings_get_bool(settings, "General",
> "EnableNetworkConfiguration",
> +				&dhcp_enable);
> +
> +	if (dhcp_enable) {
> +		L_AUTO_FREE_VAR(char *, ip_prefix);
> +
> +		ip_prefix = l_settings_get_string(settings, "General",
> +							"APRanges");
> +		if (!ip_prefix) {
> +			l_error("[General].APRanges must be set for
> DHCP");
> +			return -EINVAL;
> +		}
> +
> +		ip_pool_create(ip_prefix);
> +
> +		rtnl = iwd_get_rtnl();
> +	}
> +
>  	return 0;
>  }
>  
> @@ -2554,6 +2798,8 @@ static void ap_exit(void)
>  {
>  	netdev_watch_remove(netdev_watch);
>  	l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
> +
> +	ip_pool_destroy();
>  }
>  
>  IWD_MODULE(ap, ap_init, ap_exit)

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

* Re: [PATCH v5 3/6] ap: add error out param to ap_start
  2020-10-26 18:05 ` [PATCH v5 3/6] ap: add error out param to ap_start James Prestwood
@ 2020-10-26 19:30   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2020-10-26 19:30 UTC (permalink / raw
  To: iwd

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

Hi James,

On 10/26/20 1:05 PM, James Prestwood wrote:
> This allows the caller to extract a bit more information about what
> exactly went wrong.
> ---
>   src/ap.c  | 18 +++++++++++++++---
>   src/ap.h  |  3 ++-
>   src/p2p.c |  2 +-
>   3 files changed, 18 insertions(+), 5 deletions(-)
> 

Applied, thanks.

Regards,
-Denis

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

end of thread, other threads:[~2020-10-26 19:30 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-10-26 18:05 [PATCH v5 1/6] util: add util_ip_prefix_tohl James Prestwood
2020-10-26 18:05 ` [PATCH v5 2/6] unit: add test for util_ip_prefix_tohl James Prestwood
2020-10-26 18:05 ` [PATCH v5 3/6] ap: add error out param to ap_start James Prestwood
2020-10-26 19:30   ` Denis Kenzior
2020-10-26 18:05 ` [PATCH v5 4/6] ap: add support for DHCPv4 server James Prestwood
2020-10-26 18:23   ` James Prestwood
2020-10-26 18:05 ` [PATCH v5 5/6] auto-t: testutil: add test_ip_address_match James Prestwood
2020-10-26 18:05 ` [PATCH v5 6/6] auto-t: add AP test with DHCP server James Prestwood

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.