All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/15] netconfig: Fix address format validation
@ 2022-06-16  0:02 Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
                   ` (14 more replies)
  0 siblings, 15 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Drop the wrong negation in the error check.  Check that there are no extra
characters after prefix length suffix.  Reset errno 0 before the strtoul
call, as recommended by the manpage.
---
 src/netconfig.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/netconfig.c b/src/netconfig.c
index 2ab03b90..4a70b0ca 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -515,6 +515,7 @@ static struct l_rtnl_address *netconfig_get_static6_address(
 {
 	L_AUTO_FREE_VAR(char *, ip);
 	char *p;
+	char *endp;
 	struct l_rtnl_address *ret;
 	uint32_t prefix_len = 128;
 
@@ -530,8 +531,9 @@ static struct l_rtnl_address *netconfig_get_static6_address(
 	if (*++p == '\0')
 		goto no_prefix_len;
 
-	prefix_len = strtoul(p, NULL, 10);
-	if (!unlikely(errno == EINVAL || errno == ERANGE ||
+	errno = 0;
+	prefix_len = strtoul(p, &endp, 10);
+	if (unlikely(*endp != '\0' || errno ||
 			!prefix_len || prefix_len > 128)) {
 		l_error("netconfig: Invalid prefix '%s' provided in network"
 				" configuration file", p);
-- 
2.34.1


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

* [PATCH 02/15] storage: Log a message on network file parse errors
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-17 19:09   ` Denis Kenzior
  2022-06-17 19:09   ` Denis Kenzior
  2022-06-16  0:02 ` [PATCH 03/15] station: Move netconfig_reset() to common path Andrew Zaborowski
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Most users of storage_network_open don't log errors when the function
returns a NULL and fall back to defaults (empty l_settings).
storage_network_open() itself only logs errors if the flie is encrypted.
Now also log an error when l_settings_load_from_file() fails to help track
down potential syntax errors.
---
 src/storage.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/storage.c b/src/storage.c
index 2100d08d..77be36c8 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -597,8 +597,10 @@ struct l_settings *storage_network_open(enum security type, const char *ssid)
 
 	settings = l_settings_new();
 
-	if (!l_settings_load_from_file(settings, path))
+	if (!l_settings_load_from_file(settings, path)) {
+		l_error("Error loading %s", path);
 		goto error;
+	}
 
 	if (type != SECURITY_NONE && !storage_decrypt(settings, path, ssid))
 		goto error;
-- 
2.34.1


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

* [PATCH 03/15] station: Move netconfig_reset() to common path
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-17 19:11   ` Denis Kenzior
  2022-06-16  0:02 ` [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available Andrew Zaborowski
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

To avoid repetition, call netconfig_reset in
station_reset_connection_state.
---
 src/station.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/station.c b/src/station.c
index 7c88f266..e5972269 100644
--- a/src/station.c
+++ b/src/station.c
@@ -1623,6 +1623,9 @@ static void station_reset_connection_state(struct station *station)
 
 	station_roam_state_clear(station);
 
+	if (station->netconfig)
+		netconfig_reset(station->netconfig);
+
 	/* Refresh the ordered network list */
 	network_rank_update(station->connected_network, false);
 	l_queue_remove(station->networks_sorted, station->connected_network);
@@ -1655,9 +1658,6 @@ static void station_disassociated(struct station *station)
 {
 	l_debug("%u", netdev_get_ifindex(station->netdev));
 
-	if (station->netconfig)
-		netconfig_reset(station->netconfig);
-
 	station_reset_connection_state(station);
 
 	station_enter_state(station, STATION_STATE_DISCONNECTED);
@@ -3096,9 +3096,6 @@ static void station_disconnect_onconnect(struct station *station,
 		return;
 	}
 
-	if (station->netconfig)
-		netconfig_reset(station->netconfig);
-
 	station_reset_connection_state(station);
 
 	station_enter_state(station, STATION_STATE_DISCONNECTING);
@@ -3402,9 +3399,6 @@ int station_disconnect(struct station *station)
 	if (!station->connected_bss)
 		return -ENOTCONN;
 
-	if (station->netconfig)
-		netconfig_reset(station->netconfig);
-
 	/*
 	 * If the disconnect somehow fails we won't know if we're still
 	 * connected so we may as well indicate now that we're no longer
-- 
2.34.1


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

* [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 03/15] station: Move netconfig_reset() to common path Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-17 19:13   ` Denis Kenzior
  2022-06-16  0:02 ` [PATCH 05/15] testrunner: Fix parsing for some arguments Andrew Zaborowski
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

---
 monitor/nlmon.c | 55 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 38 insertions(+), 17 deletions(-)

diff --git a/monitor/nlmon.c b/monitor/nlmon.c
index 34c5eed6..ff23140d 100644
--- a/monitor/nlmon.c
+++ b/monitor/nlmon.c
@@ -50,6 +50,7 @@
 #include "linux/nl80211.h"
 
 #include "ell/useful.h"
+#include "ell/netlink-private.h"
 #include "src/ie.h"
 #include "src/mpdu.h"
 #include "src/eapol.h"
@@ -5332,15 +5333,7 @@ static const struct attr_entry rekey_table[] = {
 	{ }
 };
 
-#define NLA_OK(nla,len)         ((len) >= (int) sizeof(struct nlattr) && \
-				(nla)->nla_len >= sizeof(struct nlattr) && \
-				(nla)->nla_len <= (len))
-#define NLA_NEXT(nla,attrlen)	((attrlen) -= NLA_ALIGN((nla)->nla_len), \
-				(struct nlattr*)(((char*)(nla)) + \
-				NLA_ALIGN((nla)->nla_len)))
-
-#define NLA_LENGTH(len)		(NLA_ALIGN(sizeof(struct nlattr)) + (len))
-#define NLA_DATA(nla)		((void*)(((char*)(nla)) + NLA_LENGTH(0)))
+#undef NLA_PAYLOAD
 #define NLA_PAYLOAD(nla)	((int)((nla)->nla_len - NLA_LENGTH(0)))
 
 static void print_supported_commands(unsigned int level, const char *label,
@@ -6835,7 +6828,7 @@ static void netlink_str(char *str, size_t size,
 	}
 }
 
-static void print_message(struct nlmon *nlmon, const struct timeval *tv,
+static bool print_message(struct nlmon *nlmon, const struct timeval *tv,
 						enum msg_type type,
 						uint16_t flags, int status,
 						uint8_t cmd, uint8_t version,
@@ -6848,11 +6841,11 @@ static void print_message(struct nlmon *nlmon, const struct timeval *tv,
 	bool out = false;
 
 	if (nlmon->nowiphy && (cmd == NL80211_CMD_NEW_WIPHY))
-		return;
+		return false;
 
 	if (nlmon->noscan && ((cmd == NL80211_CMD_NEW_SCAN_RESULTS) ||
 			(cmd == NL80211_CMD_TRIGGER_SCAN)))
-		return;
+		return false;
 
 	switch (type) {
 	case MSG_REQUEST:
@@ -6911,6 +6904,8 @@ static void print_message(struct nlmon *nlmon, const struct timeval *tv,
 			print_field("Status: %d", status);
 		break;
 	}
+
+	return true;
 }
 
 struct nlmon_req_match {
@@ -6978,12 +6973,17 @@ static void nlmon_message(struct nlmon *nlmon, const struct timeval *tv,
 			enum msg_type type;
 			struct nlmsgerr *err;
 			int status;
+			bool print;
+			const char *err_str = NULL;
+			uint32_t err_offset = -1U;
 
 			switch (nlmsg->nlmsg_type) {
 			case NLMSG_ERROR:
 				type = MSG_RESPONSE;
 				err = NLMSG_DATA(nlmsg);
 				status = -err->error;
+				netlink_parse_ext_ack_error(nlmsg, &err_str,
+								&err_offset);
 				break;
 			case NLMSG_DONE:
 				type = MSG_COMPLETE;
@@ -6994,10 +6994,18 @@ static void nlmon_message(struct nlmon *nlmon, const struct timeval *tv,
 			}
 
 			store_message(nlmon, tv, nlmsg);
-			print_message(nlmon, tv, type, nlmsg->nlmsg_flags, status,
+			print = print_message(nlmon, tv, type,
+						nlmsg->nlmsg_flags, status,
 						req->cmd, req->version,
 						NULL, sizeof(status));
 			nlmon_req_free(req);
+
+			if (err_str && print)
+				print_field("Extended error: %s", err_str);
+
+			if (err_offset != -1U && print)
+				print_field("Offending element offset: %i "
+						"bytes", (int) err_offset);
 		}
 		return;
 	}
@@ -7795,6 +7803,8 @@ static void print_nlmsg(const struct timeval *tv, const struct nlmsghdr *nlmsg)
 {
 	struct nlmsgerr *err;
 	int status;
+	const char *err_str = NULL;
+	uint32_t err_offset = -1U;
 
 	print_nlmsghdr(tv, nlmsg);
 
@@ -7802,11 +7812,22 @@ static void print_nlmsg(const struct timeval *tv, const struct nlmsghdr *nlmsg)
 	case NLMSG_ERROR:
 		err = NLMSG_DATA(nlmsg);
 		status = err->error;
-		if (status < 0)
-			print_field("Error: %d (%s)",
-						status, strerror(-status));
-		else
+		if (status >= 0) {
 			print_field("ACK: %d", status);
+			break;
+		}
+
+		print_field("Error: %d (%s)", status, strerror(-status));
+
+		netlink_parse_ext_ack_error(nlmsg, &err_str, &err_offset);
+
+		if (err_str)
+			print_field("Extended error: %s", err_str);
+
+		if (err_offset != -1U)
+			print_field("Offending element offset: %i bytes",
+					(int) err_offset);
+
 		break;
 
 	case NLMSG_DONE:
-- 
2.34.1


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

* [PATCH 05/15] testrunner: Fix parsing for some arguments
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-22 20:58   ` Denis Kenzior
  2022-06-16  0:02 ` [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value Andrew Zaborowski
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Currently the parameter values reach run-tests by first being parsed by
runner.py's RunnerArgParser, then the resulting object members being
encoded as a commandline string, then as environment variables, then the
environment being converted to a python string list and passed to
RunnerCoreArgParser again.  Where argument names (like --sub-tests) had
dashes, the object members had underscores (.sub_tests), this wasn't
taken into account when building the python string list from environment
variables so convert all underscores to dashes and hope that all the
names match now.

Additionally some arguments used nargs='1' or nargs='*' which resulted
in their python values becoming lists.  They were converted back to command
line arguments such as: --sub_tests ['static_test.py'], and when parsed
by RunnerCoreArgParser again, the values ended up being lists of lists.
In all three cases it seems the actual user of the parsed value actually
expects a single string with comma-separated substrings in it so just drop
the nargs= uses.
---
 tools/runner.py | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/tools/runner.py b/tools/runner.py
index 2ce26de0..b018a0ad 100644
--- a/tools/runner.py
+++ b/tools/runner.py
@@ -108,7 +108,7 @@ class RunnerCoreArgParse(ArgumentParser):
 				help='Enables iwmon output to file')
 		self.add_argument('--sub-tests', '-S',
 				metavar='<subtests>',
-				type=str, nargs=1, help='List of subtests to run',
+				type=str, help='List of subtests to run',
 				default=None, dest='sub_tests')
 		self.add_argument('--result', '-e',
 				type=os.path.abspath,
@@ -131,8 +131,6 @@ class RunnerCoreArgParse(ArgumentParser):
 		auto_unit_group.add_argument('--unit-tests', '-U',
 				metavar='<tests>',
 				type=str,
-				nargs='?',
-				const='*',
 				help='List of unit tests to run',
 				dest='unit_tests')
 
@@ -141,7 +139,6 @@ class RunnerCoreArgParse(ArgumentParser):
 		valgrind_gdb_group.add_argument('--gdb', '-g',
 				metavar='<exec>',
 				type=str,
-				nargs=1,
 				help='Run gdb on specified executable',
 				dest='gdb')
 		valgrind_gdb_group.add_argument('--valgrind', '-V',
@@ -156,7 +153,7 @@ class RunnerCoreArgParse(ArgumentParser):
 
 		options = []
 		for k, v in os.environ.items():
-			options.append('--' + k)
+			options.append('--' + k.replace('_', '-'))
 			options.append(v)
 
 		return self.parse_known_args(args=options, namespace=RunnerNamespace())[0]
-- 
2.34.1


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

* [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 05/15] testrunner: Fix parsing for some arguments Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-17 19:15   ` Denis Kenzior
  2022-06-16  0:02 ` [PATCH 07/15] autotests: Drop unused file+directory Andrew Zaborowski
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

---
 tools/utils.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tools/utils.py b/tools/utils.py
index d16f221f..3cee9a22 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -405,6 +405,9 @@ class Namespace:
 		if Process.is_verbose('iwd-acd'):
 			env['IWD_ACD_DEBUG'] = '1'
 
+		if Process.is_verbose('iwd-rtnl'):
+			env['IWD_RTNL_DEBUG'] = '1'
+
 		return self.start_process(args, env=env)
 
 	@staticmethod
-- 
2.34.1


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

* [PATCH 07/15] autotests: Drop unused file+directory
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 08/15] autotests: Validate netmasks in testNetconfig, add utility Andrew Zaborowski
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

---
 autotests/testNetconfig/storage/ssidTKIP.psk | 3 ---
 1 file changed, 3 deletions(-)
 delete mode 100644 autotests/testNetconfig/storage/ssidTKIP.psk

diff --git a/autotests/testNetconfig/storage/ssidTKIP.psk b/autotests/testNetconfig/storage/ssidTKIP.psk
deleted file mode 100644
index f15724a3..00000000
--- a/autotests/testNetconfig/storage/ssidTKIP.psk
+++ /dev/null
@@ -1,3 +0,0 @@
-[IPv4]
-Address=192.168.1.10
-Gateway=192.168.1.1
-- 
2.34.1


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

* [PATCH 08/15] autotests: Validate netmasks in testNetconfig, add utility
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 07/15] autotests: Drop unused file+directory Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 09/15] autotests: Ensure storage_dir exists, clean up Andrew Zaborowski
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Extend test_ip_address_match to support IPv6 and to test the
netmask/prefix length while it reads the local address since those are
retrieved using the same API.

Modify testNetconfig to validate the prefix lengths, change the prefix
lengths to be less common values (not 24 bits for IPv4 or 64 for IPv6),
minor cleanup.
---
 autotests/testNetconfig/connection_test.py | 12 ++--
 autotests/testNetconfig/dhcpd-v6.conf      |  2 +-
 autotests/testNetconfig/dhcpd.conf         |  8 +--
 autotests/testNetconfig/ssidTKIP.psk       |  1 +
 autotests/testNetconfig/static_test.py     |  6 +-
 autotests/util/testutil.py                 | 75 ++++++++++++++++++----
 6 files changed, 79 insertions(+), 25 deletions(-)

diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py
index 672efb72..0334267b 100644
--- a/autotests/testNetconfig/connection_test.py
+++ b/autotests/testNetconfig/connection_test.py
@@ -11,7 +11,7 @@ from iwd import NetworkType
 from hostapd import HostapdCLI
 import testutil
 from config import ctx
-import os, time
+import os
 import subprocess
 
 class Test(unittest.TestCase):
@@ -19,8 +19,9 @@ class Test(unittest.TestCase):
     def test_connection_success(self):
         def check_addr(device):
             try:
-                subprocess.check_output('ip addr show ' + device.name + \
-                            ' | grep \'inet6 3ffe:501:ffff:100::\'', shell=True)
+                # DHCPv6 addresses always have a prefix length of 128 bits, the actual
+                # subnet's prefix length is in the route.
+                testutil.test_ip_address_match(device.name, '3ffe:501:ffff:100::1', 128, 112)
             except:
                 return False
 
@@ -49,6 +50,7 @@ class Test(unittest.TestCase):
         testutil.test_iface_operstate()
         testutil.test_ifaces_connected()
 
+        testutil.test_ip_address_match(device.name, '192.168.1.10', 17, 24)
         ctx.non_block_wait(check_addr, 10, device,
                             exception=Exception("IPv6 address was not set"))
 
@@ -78,14 +80,14 @@ class Test(unittest.TestCase):
         # TODO: This could be moved into test-runner itself if other tests ever
         #       require this functionality (p2p, FILS, etc.). Since its simple
         #       enough it can stay here for now.
-        ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.255.0',
+        ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.128.0',
                             'dev', hapd.ifname,]).wait()
         ctx.start_process(['touch', '/tmp/dhcpd.leases']).wait()
         cls.dhcpd_pid = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf',
                                             '-lf', '/tmp/dhcpd.leases',
                                             hapd.ifname], cleanup=remove_lease4)
 
-        ctx.start_process(['ip', 'addr', 'add', '3ffe:501:ffff:100::1/64',
+        ctx.start_process(['ip', 'addr', 'add', '3ffe:501:ffff:100::1/72',
                             'dev', hapd.ifname]).wait()
         ctx.start_process(['touch', '/tmp/dhcpd6.leases']).wait()
         cls.dhcpd6_pid = ctx.start_process(['dhcpd', '-6', '-f', '-cf', '/tmp/dhcpd-v6.conf',
diff --git a/autotests/testNetconfig/dhcpd-v6.conf b/autotests/testNetconfig/dhcpd-v6.conf
index 7a22cc65..917ee8a3 100644
--- a/autotests/testNetconfig/dhcpd-v6.conf
+++ b/autotests/testNetconfig/dhcpd-v6.conf
@@ -1,4 +1,4 @@
-subnet6 3ffe:501:ffff:100::/64
+subnet6 3ffe:501:ffff:100::/72
  {
   option dhcp6.name-servers 3ffe:501:ffff:100::1;
   range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::20;
diff --git a/autotests/testNetconfig/dhcpd.conf b/autotests/testNetconfig/dhcpd.conf
index 0c4fe9b9..ea9957a3 100644
--- a/autotests/testNetconfig/dhcpd.conf
+++ b/autotests/testNetconfig/dhcpd.conf
@@ -1,14 +1,14 @@
 default-lease-time 600;         # 10 minutes
 max-lease-time 7200;            # 2  hours
 
-option broadcast-address 192.168.1.255;
+option broadcast-address 192.168.127.255;
 option routers 192.168.1.254;
-option subnet-mask 255.255.255.0;
+option subnet-mask 255.255.128.0;
 
-subnet 192.168.1.0 netmask 255.255.255.0
+subnet 192.168.0.0 netmask 255.255.128.0
  {
   option routers 192.168.1.1;
-  option subnet-mask 255.255.255.0;
+  option subnet-mask 255.255.128.0;
   option domain-name-servers 192.168.1.1;
   range 192.168.1.10 192.168.1.20;
   range 192.168.1.100 192.168.1.200;
diff --git a/autotests/testNetconfig/ssidTKIP.psk b/autotests/testNetconfig/ssidTKIP.psk
index 64e84f12..eea59cda 100644
--- a/autotests/testNetconfig/ssidTKIP.psk
+++ b/autotests/testNetconfig/ssidTKIP.psk
@@ -1,5 +1,6 @@
 [IPv4]
 Address=192.168.1.10
+Netmask=255.255.255.128
 Gateway=192.168.1.1
 
 [Settings]
diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py
index bba2c644..e82dbaeb 100644
--- a/autotests/testNetconfig/static_test.py
+++ b/autotests/testNetconfig/static_test.py
@@ -45,7 +45,7 @@ class Test(unittest.TestCase):
         testutil.test_iface_operstate()
         testutil.test_ifaces_connected()
 
-        testutil.test_ip_address_match(dev1.name, '192.168.1.10')
+        testutil.test_ip_address_match(dev1.name, '192.168.1.10', 25)
 
         ordered_network = dev2.get_ordered_network('ssidTKIP')
 
@@ -79,9 +79,9 @@ class Test(unittest.TestCase):
 
         hapd = HostapdCLI()
         # TODO: This could be moved into test-runner itself if other tests ever
-        #       require this functionality (p2p, FILS, etc.). Since its simple
+        #       require this functionality (p2p, FILS, etc.). Since it's simple
         #       enough it can stay here for now.
-        ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.255.0',
+        ctx.start_process(['ip', 'addr','add', '192.168.1.1/255.255.128.0',
                             'dev', hapd.ifname]).wait()
         ctx.start_process(['touch', '/tmp/dhcpd.leases']).wait()
         cls.dhcpd_pid = ctx.start_process(['dhcpd', '-f', '-cf', '/tmp/dhcpd.conf',
diff --git a/autotests/util/testutil.py b/autotests/util/testutil.py
index 01c42654..99cd58f0 100644
--- a/autotests/util/testutil.py
+++ b/autotests/util/testutil.py
@@ -4,6 +4,7 @@ import socket
 import fcntl
 import struct
 import select
+import codecs
 
 import iwd
 from config import ctx
@@ -131,6 +132,7 @@ def test_ifaces_connected(if0=None, if1=None, group=True, expect_fail=False):
 
 SIOCGIFFLAGS = 0x8913
 SIOCGIFADDR = 0x8915
+SIOCGIFNETMASK = 0x891b
 IFF_UP = 1 << 0
 IFF_RUNNING = 1 << 6
 
@@ -156,19 +158,68 @@ def test_iface_operstate(intf=None):
     ctx.non_block_wait(_test_operstate, 10, intf,
                         exception=Exception(intf + ' operstate wrong'))
 
-def test_ip_address_match(intf, ip):
-    try:
-        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])
-    except OSError as e:
-        if e.errno != 99 or ip != None:
-            raise Exception('SIOCGIFADDR failed with %d' % e.errno)
-
-        return
+def get_addrs6(ifname):
+    f = open('/proc/net/if_inet6', 'r')
+    lines = f.readlines()
+    f.close()
+    for line in lines:
+        addr_str, _, plen, _, _, addr_ifname = line.split()
+        if ifname is not None and addr_ifname != ifname:
+            continue
+
+        yield (codecs.decode(addr_str, 'hex'), int(plen, 16), addr_ifname)
+
+def test_ip_address_match(intf, expected_addr_str, expected_plen=None, match_plen=None):
+    def mask_addr(addr, plen):
+        if plen is None or len(addr) * 8 <= plen:
+            return addr
+        bytelen = int(plen / 8)
+        return addr[0:bytelen] + bytes([addr[bytelen] & (0xff00 >> (plen & 7))]) + b'\0' * (len(addr) - bytelen - 1)
+    if expected_addr_str is not None:
+        try:
+            expected_addr = socket.inet_pton(socket.AF_INET, expected_addr_str)
+            family = socket.AF_INET
+        except OSError as e:
+            try:
+                expected_addr = socket.inet_pton(socket.AF_INET6, expected_addr_str)
+                family = socket.AF_INET6
+            except OSError as e2:
+                raise e2 from None
+        expected_addr = mask_addr(expected_addr, match_plen)
+    else:
+        expected_addr = None
+        family = socket.AF_INET
 
-    if ip != addr:
-        raise Exception('IP for %s did not match %s (was %s)' % (intf, ip, addr))
+    if family == socket.AF_INET:
+        try:
+            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+            out = fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', intf.encode('utf-8')))
+            actual_addr = mask_addr(out[20:24], match_plen)
+            out = fcntl.ioctl(s.fileno(), SIOCGIFNETMASK, struct.pack('256s', intf.encode('utf-8')))
+            actual_plen = sum([sum([(byte >> bit) & 1 for bit in range(0, 8)]) for byte in out[20:24]]) # count bits
+        except OSError as e:
+            if e.errno == 99 and expected_addr is None:
+                return
+
+            raise Exception('SIOCGIFADDR/SIOCGIFNETMASK failed with %d' % e.errno)
+    else:
+        # The "get" ioctls don't work for IPv6, netdevice(7) recommends reading /proc/net instead,
+        # which on the other hand works *only* for IPv6
+        actual_addr = None
+        actual_plen = None
+        for addr, plen, _ in get_addrs6(intf):
+            actual_addr = mask_addr(addr, match_plen)
+            actual_plen = plen
+            if actual_addr == expected_addr:
+                break
+
+    if expected_addr != actual_addr:
+        raise Exception('IP for %s did not match %s (was %s)' %
+                        (intf, expected_addr_str, socket.inet_ntop(family, actual_addr)))
+
+    if expected_plen is not None and expected_plen != actual_plen:
+        raise Exception('Prefix Length for %s did not match %i (was %i)' %
+                        (intf, expected_plen, actual_plen))
 
 def test_ip_connected(tup0, tup1):
     ip0, ns0 = tup0
-- 
2.34.1


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

* [PATCH 09/15] autotests: Ensure storage_dir exists, clean up
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 08/15] autotests: Validate netmasks in testNetconfig, add utility Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 10/15] autotests: In testNetconfig add static IPv6, add comments Andrew Zaborowski
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

In iwd.py make sure all the static methods that touch IWD storage take the
storage_dir parameter instead of hardcoding IWD_STORAGE_DIR, and make
sure that parameter is actually used.

Create the directory if it doesn't exist before copying files into it.
This fixes a problem in testNetconfig where

`IWD.copy_to_storage('ssidTKIP.psk', '/tmp/storage')`

would result in /tmp/storage being created as a file, rather than a
directory containing a file, and resulting in IWD failing to start with:

`Failed to create /tmp/storage`

runner.py creates /tmp/iwd but that doesn't account for IWD sessions
with a custom storage dir path.
---
 autotests/testNetconfig/static_test.py |  2 +-
 autotests/util/iwd.py                  | 39 ++++++++++++++++----------
 2 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py
index e82dbaeb..8d5710c1 100644
--- a/autotests/testNetconfig/static_test.py
+++ b/autotests/testNetconfig/static_test.py
@@ -91,7 +91,7 @@ class Test(unittest.TestCase):
 
     @classmethod
     def tearDownClass(cls):
-        IWD.clear_storage()
+        IWD.clear_storage(storage_dir='/tmp/storage')
         cls.dhcpd_pid.kill()
 
 if __name__ == '__main__':
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index 6cee1e07..f609e37e 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -1086,7 +1086,7 @@ class IWD(AsyncOpAbstract):
     psk_agent = None
 
     def __init__(self, start_iwd_daemon = False, iwd_config_dir = '/tmp',
-                            iwd_storage_dir = '/tmp/iwd', namespace=ctx):
+                            iwd_storage_dir = IWD_STORAGE_DIR, namespace=ctx):
         self.namespace = namespace
         self._bus = namespace.get_bus()
 
@@ -1197,40 +1197,49 @@ class IWD(AsyncOpAbstract):
         os.system('rm -rf ' + storage_dir + '/ap/*')
 
     @staticmethod
-    def create_in_storage(file_name, file_content):
-        fo = open(IWD_STORAGE_DIR + '/' + file_name, 'w')
+    def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR):
+        fo = open(storage_dir + '/' + file_name, 'w')
 
         fo.write(file_content)
         fo.close()
 
+    @staticmethod
+    def _ensure_storage_dir_exists(storage_dir):
+        if not os.path.exists(storage_dir):
+            os.mkdir(storage_dir)
+
     @staticmethod
     def copy_to_storage(source, storage_dir=IWD_STORAGE_DIR, name=None):
         import shutil
 
         assert not os.path.isabs(source)
 
+        target = storage_dir
         if name:
-            storage_dir += '/%s' % name
+            target += '/%s' % name
 
-        shutil.copy(source, storage_dir)
+        IWD._ensure_storage_dir_exists(storage_dir)
+        shutil.copy(source, target)
 
     @staticmethod
-    def copy_to_hotspot(source):
-        if not os.path.exists(IWD_STORAGE_DIR + "/hotspot"):
-            os.mkdir(IWD_STORAGE_DIR + "/hotspot")
+    def copy_to_hotspot(source, storage_dir=IWD_STORAGE_DIR):
+        IWD._ensure_storage_dir_exists(storage_dir)
+
+        if not os.path.exists(storage_dir + "/hotspot"):
+            os.mkdir(storage_dir + "/hotspot")
 
-        IWD.copy_to_storage(source, IWD_STORAGE_DIR + "/hotspot")
+        IWD.copy_to_storage(source, storage_dir + "/hotspot")
 
     @staticmethod
-    def copy_to_ap(source):
-        if not os.path.exists(IWD_STORAGE_DIR + "/ap"):
-            os.mkdir(IWD_STORAGE_DIR + "/ap")
+    def copy_to_ap(source, storage_dir=IWD_STORAGE_DIR):
+        if not os.path.exists(storage_dir + "/ap"):
+            os.mkdir(storage_dir + "/ap")
 
-        IWD.copy_to_storage(source, IWD_STORAGE_DIR + '/ap/')
+        IWD.copy_to_storage(source, storage_dir + '/ap/')
 
     @staticmethod
-    def remove_from_storage(file_name):
-        os.system('rm -rf ' + IWD_STORAGE_DIR + '/\'' + file_name + '\'')
+    def remove_from_storage(file_name, storage_dir=IWD_STORAGE_DIR):
+        os.system('rm -rf ' + storage_dir + '/\'' + file_name + '\'')
 
     def list_devices(self, wait_to_appear = 0, max_wait = 50, p2p = False):
         if not wait_to_appear:
-- 
2.34.1


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

* [PATCH 10/15] autotests: In testNetconfig add static IPv6, add comments
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (7 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 09/15] autotests: Ensure storage_dir exists, clean up Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 11/15] autotests: In testNetconfig verify routes created Andrew Zaborowski
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

In static_test.py add IPv6.  Add comments on what we're actually testing
since it wasn't very clear.  After the expected ACD conflict detection,
succeed if either the lost address was removed or the client disconnected
from the AP since this seems like a correct action for netconfig to
implement.
---
 autotests/testNetconfig/ssidTKIP.psk   |  7 +++++++
 autotests/testNetconfig/static_test.py | 11 ++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/autotests/testNetconfig/ssidTKIP.psk b/autotests/testNetconfig/ssidTKIP.psk
index eea59cda..72a71ce9 100644
--- a/autotests/testNetconfig/ssidTKIP.psk
+++ b/autotests/testNetconfig/ssidTKIP.psk
@@ -1,7 +1,14 @@
 [IPv4]
+# Use a different netmask than DHCP on purpose, but use the same address
+# as DHCP would assign us to produce a conflict and test ACD
 Address=192.168.1.10
 Netmask=255.255.255.128
 Gateway=192.168.1.1
 
+[IPv6]
+# Use a different subnet than DHCP on purpose
+Address=3ffe:501:ffff:200::10/80
+Gateway=3ffe:501:ffff:200::1
+
 [Settings]
 AutoConnect=false
diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py
index 8d5710c1..a3b294a2 100644
--- a/autotests/testNetconfig/static_test.py
+++ b/autotests/testNetconfig/static_test.py
@@ -16,6 +16,7 @@ import os
 class Test(unittest.TestCase):
 
     def test_connection_success(self):
+        # Use a non-default storage_dir for one of the instances, the default for the other one
         wd = IWD(True, iwd_storage_dir='/tmp/storage')
 
         ns0 = ctx.get_namespace('ns0')
@@ -46,19 +47,27 @@ class Test(unittest.TestCase):
         testutil.test_ifaces_connected()
 
         testutil.test_ip_address_match(dev1.name, '192.168.1.10', 25)
+        testutil.test_ip_address_match(dev1.name, '3ffe:501:ffff:200::10', 80)
 
         ordered_network = dev2.get_ordered_network('ssidTKIP')
 
         condition = 'not obj.connected'
         wd_ns0.wait_for_object_condition(ordered_network.network_object, condition)
 
+        # Connect to the same network from a dynamically configured client.  The
+        # DHCP server doesn't know (even though dev1 announced itself) that
+        # 192.168.1.10 is already in use and if it assigns dev2 the lowest
+        # available address, that's going to be 192.168.1.10.  dev1's ACD
+        # implementation should then stop using this address.
         ordered_network.network_object.connect()
 
         condition = 'obj.state == DeviceState.connected'
         wd_ns0.wait_for_object_condition(dev2, condition)
 
         wd.wait(1)
-        testutil.test_ip_address_match(dev1.name, None)
+        # Check dev1 is now disconnected or without its IPv4 address
+        if dev1.state == iwd.DeviceState.connected:
+            testutil.test_ip_address_match(dev1.name, None)
 
         dev1.disconnect()
         dev2.disconnect()
-- 
2.34.1


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

* [PATCH 11/15] autotests: In testNetconfig verify routes created
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (8 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 10/15] autotests: In testNetconfig add static IPv6, add comments Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 12/15] autotests: Verify DNS entries added from DHCP/static Andrew Zaborowski
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Check that the right set of routes is being added for IPv4 and IPv6.
Chane gateway addresses to differ from the AP or dhcpd addresses.
---
 autotests/testNetconfig/connection_test.py | 39 +++++++++++++--
 autotests/testNetconfig/ssidTKIP.psk       |  4 +-
 autotests/testNetconfig/static_test.py     | 23 +++++++++
 autotests/util/testutil.py                 | 57 ++++++++++++++++++++++
 4 files changed, 118 insertions(+), 5 deletions(-)

diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py
index 0334267b..db3327ef 100644
--- a/autotests/testNetconfig/connection_test.py
+++ b/autotests/testNetconfig/connection_test.py
@@ -12,7 +12,7 @@ from hostapd import HostapdCLI
 import testutil
 from config import ctx
 import os
-import subprocess
+import socket
 
 class Test(unittest.TestCase):
 
@@ -54,6 +54,29 @@ class Test(unittest.TestCase):
         ctx.non_block_wait(check_addr, 10, device,
                             exception=Exception("IPv6 address was not set"))
 
+        ifname = str(device.name)
+        router_ll_addr = [addr for addr, _, _ in testutil.get_addrs6(self.hapd.ifname) if addr[0:2] == b'\xfe\x80'][0]
+        # Since we're in an isolated VM with freshly created interfaces we know any routes
+        # will have been created by IWD and don't have to allow for pre-existing routes
+        # in the table.
+        # Flags: 1=RTF_UP, 2=RTF_GATEWAY
+        expected_routes4 = {
+                testutil.RouteInfo(gw=socket.inet_pton(socket.AF_INET, '192.168.1.1'),
+                    flags=3, ifname=ifname),
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET, '192.168.0.0'), plen=17,
+                    flags=1, ifname=ifname)
+            }
+        expected_routes6 = {
+                # Default router
+                testutil.RouteInfo(gw=router_ll_addr, flags=3, ifname=ifname),
+                # On-link prefix
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:100::'), plen=72,
+                    flags=1, ifname=ifname),
+            }
+        self.maxDiff = None
+        self.assertEqual(expected_routes4, set(testutil.get_routes4(ifname)))
+        self.assertEqual(expected_routes6, set(testutil.get_routes6(ifname)))
+
         device.disconnect()
 
         condition = 'not obj.connected'
@@ -77,6 +100,7 @@ class Test(unittest.TestCase):
                 pass
 
         hapd = HostapdCLI()
+        cls.hapd = hapd
         # TODO: This could be moved into test-runner itself if other tests ever
         #       require this functionality (p2p, FILS, etc.). Since its simple
         #       enough it can stay here for now.
@@ -94,9 +118,18 @@ class Test(unittest.TestCase):
                                             '-lf', '/tmp/dhcpd6.leases',
                                             hapd.ifname], cleanup=remove_lease6)
         ctx.start_process(['sysctl', 'net.ipv6.conf.' + hapd.ifname + '.forwarding=1']).wait()
-        # Tell clients to use DHCPv6
+        # Send out Router Advertisements telling clients to use DHCPv6.
+        # Note trying to send the RAs from the router's global IPv6 address by adding a
+        # "AdvRASrcAddress { 3ffe:501:ffff:100::1; };" line will fail because the client
+        # and the router interfaces are in the same namespace and Linux won't allow routes
+        # with a non-link-local gateway address that is present on another interface in the
+        # same namespace.
         config = open('/tmp/radvd.conf', 'w')
-        config.write('interface ' + hapd.ifname + ' { AdvSendAdvert on; AdvManagedFlag on; };')
+        config.write('interface ' + hapd.ifname + ''' {
+            AdvSendAdvert on;
+            AdvManagedFlag on;
+            prefix 3ffe:501:ffff:100::/72 { AdvAutonomous off; };
+            };''')
         config.close()
         cls.radvd_pid = ctx.start_process(['radvd', '-n', '-d5', '-p', '/tmp/radvd.pid', '-C', '/tmp/radvd.conf'])
 
diff --git a/autotests/testNetconfig/ssidTKIP.psk b/autotests/testNetconfig/ssidTKIP.psk
index 72a71ce9..e371c97f 100644
--- a/autotests/testNetconfig/ssidTKIP.psk
+++ b/autotests/testNetconfig/ssidTKIP.psk
@@ -3,12 +3,12 @@
 # as DHCP would assign us to produce a conflict and test ACD
 Address=192.168.1.10
 Netmask=255.255.255.128
-Gateway=192.168.1.1
+Gateway=192.168.1.3
 
 [IPv6]
 # Use a different subnet than DHCP on purpose
 Address=3ffe:501:ffff:200::10/80
-Gateway=3ffe:501:ffff:200::1
+Gateway=3ffe:501:ffff:200::3
 
 [Settings]
 AutoConnect=false
diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py
index a3b294a2..cd147894 100644
--- a/autotests/testNetconfig/static_test.py
+++ b/autotests/testNetconfig/static_test.py
@@ -12,6 +12,7 @@ from hostapd import HostapdCLI
 import testutil
 from config import ctx
 import os
+import socket
 
 class Test(unittest.TestCase):
 
@@ -49,6 +50,28 @@ class Test(unittest.TestCase):
         testutil.test_ip_address_match(dev1.name, '192.168.1.10', 25)
         testutil.test_ip_address_match(dev1.name, '3ffe:501:ffff:200::10', 80)
 
+        ifname = str(dev1.name)
+        # Since we're in an isolated VM with freshly created interfaces we know any routes
+        # will have been created by IWD and don't have to allow for pre-existing routes
+        # in the table.
+        # Flags: 1=RTF_UP, 2=RTF_GATEWAY
+        expected_routes4 = {
+                testutil.RouteInfo(gw=socket.inet_pton(socket.AF_INET, '192.168.1.3'),
+                    flags=3, ifname=ifname),
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET, '192.168.1.0'), plen=25,
+                    flags=1, ifname=ifname)
+            }
+        expected_routes6 = {
+                testutil.RouteInfo(gw=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:200::3'),
+                    flags=3, ifname=ifname),
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:200::'), plen=80,
+                    flags=1, ifname=ifname),
+            }
+
+        self.maxDiff = None
+        self.assertEqual(expected_routes4, set(testutil.get_routes4(ifname)))
+        self.assertEqual(expected_routes6, set(testutil.get_routes6(ifname)))
+
         ordered_network = dev2.get_ordered_network('ssidTKIP')
 
         condition = 'not obj.connected'
diff --git a/autotests/util/testutil.py b/autotests/util/testutil.py
index 99cd58f0..eae4dd89 100644
--- a/autotests/util/testutil.py
+++ b/autotests/util/testutil.py
@@ -5,6 +5,7 @@ import fcntl
 import struct
 import select
 import codecs
+import collections
 
 import iwd
 from config import ctx
@@ -230,3 +231,59 @@ def test_ip_connected(tup0, tup1):
         ns1.start_process(['ping', '-c', '5', '-i', '0.2', ip0], check=True)
     except:
         raise Exception('Could not ping between %s and %s' % (ip0, ip1))
+
+RouteInfo = collections.namedtuple('RouteInfo', 'dst plen gw flags ifname',
+        defaults=(None, None, None, 0, ''))
+
+def get_routes4(ifname=None):
+    f = open('/proc/net/route', 'r')
+    lines = f.readlines()
+    f.close()
+    for line in lines[1:]: # Skip header line
+        route_ifname, dst_str, gw_str, flags, ref_cnt, use_cnt, metric, mask_str, \
+                mtu = line.strip().split(maxsplit=8)
+        if ifname is not None and route_ifname != ifname:
+            continue
+
+        dst = codecs.decode(dst_str, 'hex')[::-1]
+        mask = int(mask_str, 16)
+        plen = sum([(mask >> bit) & 1 for bit in range(0, 32)]) # count bits
+        gw = codecs.decode(gw_str, 'hex')[::-1]
+
+        if dst == b'\0\0\0\0':
+            dst = None
+            plen = None
+        if gw == b'\0\0\0\0':
+            gw = None
+        yield RouteInfo(dst, plen, gw, int(flags, 16), route_ifname)
+
+def get_routes6(ifname=None):
+    f = open('/proc/net/ipv6_route', 'r')
+    lines = f.readlines()
+    f.close()
+    for line in lines:
+        dst_str, dst_plen_str, src_str, src_plen_str, gw_str, metric, ref_cnt, \
+                use_cnt, flags, route_ifname = line.strip().split(maxsplit=9)
+        if ifname is not None and route_ifname != ifname:
+            continue
+
+        dst = codecs.decode(dst_str, 'hex')
+        plen = int(dst_plen_str, 16)
+        gw = codecs.decode(gw_str, 'hex')
+
+        if dst[0] == 0xff or dst[:2] == b'\xfe\x80': # Skip link-local and multicast
+            continue
+
+        # Skip RTN_LOCAL-type routes, we don't need to validate them since they're added by
+        # the kernel and we can't simply add them to the expected list (the list that we
+        # validate against) because they're added a short time after an address (due to DAD?)
+        # and would create race conditions
+        if int(flags, 16) & (1 << 31):
+            continue
+
+        if dst == b'\0' * 16:
+            dst = None
+            plen = None
+        if gw == b'\0' * 16:
+            gw = None
+        yield RouteInfo(dst, plen, gw, int(flags, 16) & 0xf, route_ifname)
-- 
2.34.1


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

* [PATCH 12/15] autotests: Verify DNS entries added from DHCP/static
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (9 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 11/15] autotests: In testNetconfig verify routes created Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 13/15][RFC] netconfig: Switch to l_netconfig Andrew Zaborowski
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Add a fake resolvconf executable to verify that the right nameserver
addresses were actually committed by iwd.  Again use unique nameserver
addresses to reduce the possibility that the test succeeds by pure luck.
---
 autotests/testNetconfig/connection_test.py | 15 ++++++++++++++-
 autotests/testNetconfig/dhcpd-v6.conf      |  2 +-
 autotests/testNetconfig/dhcpd.conf         |  2 +-
 autotests/testNetconfig/main.conf          |  2 +-
 autotests/testNetconfig/resolvconf         |  2 ++
 autotests/testNetconfig/ssidTKIP.psk       |  2 ++
 autotests/testNetconfig/static_test.py     | 15 ++++++++++++++-
 7 files changed, 35 insertions(+), 5 deletions(-)
 create mode 100755 autotests/testNetconfig/resolvconf

diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py
index db3327ef..4b05c745 100644
--- a/autotests/testNetconfig/connection_test.py
+++ b/autotests/testNetconfig/connection_test.py
@@ -77,6 +77,14 @@ class Test(unittest.TestCase):
         self.assertEqual(expected_routes4, set(testutil.get_routes4(ifname)))
         self.assertEqual(expected_routes6, set(testutil.get_routes6(ifname)))
 
+        rclog = open('/tmp/resolvconf.log', 'r')
+        entries = rclog.readlines()
+        rclog.close()
+        expected_rclog = ['-a wlan1.dns\n', 'nameserver 192.168.1.2\n', 'nameserver 3ffe:501:ffff:100::2\n']
+        # Every real resolvconf -a run overwrites the previous settings.  Check the last three lines
+        # of our log since we care about the end result here.
+        self.assertEqual(expected_rclog, entries[-3:])
+
         device.disconnect()
 
         condition = 'not obj.connected'
@@ -133,6 +141,10 @@ class Test(unittest.TestCase):
         config.close()
         cls.radvd_pid = ctx.start_process(['radvd', '-n', '-d5', '-p', '/tmp/radvd.pid', '-C', '/tmp/radvd.conf'])
 
+        cls.orig_path = os.environ['PATH']
+        os.environ['PATH'] = '/tmp/test-bin:' + os.environ['PATH']
+        IWD.copy_to_storage('resolvconf', '/tmp/test-bin')
+
     @classmethod
     def tearDownClass(cls):
         IWD.clear_storage()
@@ -142,7 +154,8 @@ class Test(unittest.TestCase):
         cls.dhcpd6_pid = None
         ctx.stop_process(cls.radvd_pid)
         cls.radvd_pid = None
-        os.remove('/tmp/radvd.conf')
+        os.system('rm -rf /tmp/radvd.conf /tmp/resolvconf.log /tmp/test-bin')
+        os.environ['PATH'] = cls.orig_path
 
 if __name__ == '__main__':
     unittest.main(exit=True)
diff --git a/autotests/testNetconfig/dhcpd-v6.conf b/autotests/testNetconfig/dhcpd-v6.conf
index 917ee8a3..45057ab8 100644
--- a/autotests/testNetconfig/dhcpd-v6.conf
+++ b/autotests/testNetconfig/dhcpd-v6.conf
@@ -1,6 +1,6 @@
 subnet6 3ffe:501:ffff:100::/72
  {
-  option dhcp6.name-servers 3ffe:501:ffff:100::1;
+  option dhcp6.name-servers 3ffe:501:ffff:100::2;
   range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::20;
   range6 3ffe:501:ffff:100::100 3ffe:501:ffff:100::200;
  }
diff --git a/autotests/testNetconfig/dhcpd.conf b/autotests/testNetconfig/dhcpd.conf
index ea9957a3..d8a9d24c 100644
--- a/autotests/testNetconfig/dhcpd.conf
+++ b/autotests/testNetconfig/dhcpd.conf
@@ -9,7 +9,7 @@ subnet 192.168.0.0 netmask 255.255.128.0
  {
   option routers 192.168.1.1;
   option subnet-mask 255.255.128.0;
-  option domain-name-servers 192.168.1.1;
+  option domain-name-servers 192.168.1.2;
   range 192.168.1.10 192.168.1.20;
   range 192.168.1.100 192.168.1.200;
  }
diff --git a/autotests/testNetconfig/main.conf b/autotests/testNetconfig/main.conf
index 1ffe2be1..8641a390 100644
--- a/autotests/testNetconfig/main.conf
+++ b/autotests/testNetconfig/main.conf
@@ -3,4 +3,4 @@ EnableNetworkConfiguration=true
 
 [Network]
 EnableIPv6=true
-NameResolvingService=none
+NameResolvingService=resolvconf
diff --git a/autotests/testNetconfig/resolvconf b/autotests/testNetconfig/resolvconf
new file mode 100755
index 00000000..7c56c616
--- /dev/null
+++ b/autotests/testNetconfig/resolvconf
@@ -0,0 +1,2 @@
+#! /bin/sh
+(echo "$*" ; [ "$1" = -a ] && cat) >> /tmp/resolvconf.log 2> /dev/null
diff --git a/autotests/testNetconfig/ssidTKIP.psk b/autotests/testNetconfig/ssidTKIP.psk
index e371c97f..032e2185 100644
--- a/autotests/testNetconfig/ssidTKIP.psk
+++ b/autotests/testNetconfig/ssidTKIP.psk
@@ -4,11 +4,13 @@
 Address=192.168.1.10
 Netmask=255.255.255.128
 Gateway=192.168.1.3
+DNS=192.168.1.4
 
 [IPv6]
 # Use a different subnet than DHCP on purpose
 Address=3ffe:501:ffff:200::10/80
 Gateway=3ffe:501:ffff:200::3
+DNS=3ffe:501:ffff:200::4
 
 [Settings]
 AutoConnect=false
diff --git a/autotests/testNetconfig/static_test.py b/autotests/testNetconfig/static_test.py
index cd147894..ac525b28 100644
--- a/autotests/testNetconfig/static_test.py
+++ b/autotests/testNetconfig/static_test.py
@@ -72,6 +72,14 @@ class Test(unittest.TestCase):
         self.assertEqual(expected_routes4, set(testutil.get_routes4(ifname)))
         self.assertEqual(expected_routes6, set(testutil.get_routes6(ifname)))
 
+        rclog = open('/tmp/resolvconf.log', 'r')
+        entries = rclog.readlines()
+        rclog.close()
+        expected_rclog = ['-a wlan1.dns\n', 'nameserver 192.168.1.4\n', 'nameserver 3ffe:501:ffff:200::4\n']
+        # Every resolvconf -a run overwrites the previous settings.  Check the last three lines
+        # of the log since we care about the end result here.
+        self.assertEqual(expected_rclog, entries[-3:])
+
         ordered_network = dev2.get_ordered_network('ssidTKIP')
 
         condition = 'not obj.connected'
@@ -121,10 +129,15 @@ class Test(unittest.TestCase):
                                             hapd.ifname], cleanup=remove_lease)
         IWD.copy_to_storage('ssidTKIP.psk', '/tmp/storage')
 
+        cls.orig_path = os.environ['PATH']
+        os.environ['PATH'] = '/tmp/test-bin:' + os.environ['PATH']
+        IWD.copy_to_storage('resolvconf', '/tmp/test-bin')
+
     @classmethod
     def tearDownClass(cls):
-        IWD.clear_storage(storage_dir='/tmp/storage')
         cls.dhcpd_pid.kill()
+        os.system('rm -rf /tmp/resolvconf.log /tmp/test-bin /tmp/storage')
+        os.environ['PATH'] = cls.orig_path
 
 if __name__ == '__main__':
     unittest.main(exit=True)
-- 
2.34.1


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

* [PATCH 13/15][RFC] netconfig: Switch to l_netconfig
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (10 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 12/15] autotests: Verify DNS entries added from DHCP/static Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-16  0:02 ` [PATCH 14/15] autotests: Add bad netconfig settings scenario Andrew Zaborowski
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Posting this for early review only.

This leaves IWD's netconfig.h mostly unchanged but the implementation is
switched over to use l_netconfig.

The struct netconfig declaration is nearly empty now but contains the
resolver client, the data received from FILS and some state that mirrors
l_netconfig state and could be replaced by using l_netconfig getters for
the settings which have only setters at this time.

A minor behaviour change is that a netconfig failure, such as DHCP error
or timeout, would now cause station to signal an DBus error and continue
with autoconnect instead of being stuck in netconfig.
---
 Makefile.am     |    6 +-
 src/netconfig.c | 1685 +++++++++++------------------------------------
 src/netconfig.h |    1 +
 src/station.c   |  134 ++--
 4 files changed, 497 insertions(+), 1329 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 80377613..bcb313d7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,7 +62,8 @@ ell_headers = ell/util.h \
 			ell/icmp6.h \
 			ell/dhcp6.h \
 			ell/acd.h \
-			ell/cleanup.h
+			ell/cleanup.h \
+			ell/netconfig.h
 
 ell_sources = ell/private.h \
 			ell/missing.h \
@@ -141,7 +142,8 @@ ell_sources = ell/private.h \
 			ell/icmp6-private.h \
 			ell/dhcp6-lease.c \
 			ell/dhcp6-transport.c \
-			ell/acd.c
+			ell/acd.c \
+			ell/netconfig.c
 
 ell_shared = ell/useful.h ell/asn1-private.h
 
diff --git a/src/netconfig.c b/src/netconfig.c
index 4a70b0ca..2f41b79f 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -53,23 +53,16 @@
 #include "src/sysfs.h"
 
 struct netconfig {
-	uint32_t ifindex;
-	struct l_dhcp_client *dhcp_client;
-	struct l_dhcp6_client *dhcp6_client;
-	uint8_t rtm_protocol;
-	uint8_t rtm_v6_protocol;
-	struct l_rtnl_address *v4_address;
-	struct l_rtnl_address *v6_address;
-	char **dns4_overrides;
-	char **dns6_overrides;
-	char **dns4_list;
-	char **dns6_list;
+	struct l_netconfig *nc;
+	struct netdev *netdev;
+
 	char *mdns;
 	struct ie_fils_ip_addr_response_info *fils_override;
-	char *v4_gateway_str;
-	char *v6_gateway_str;
-	char *v4_domain;
-	char **v6_domains;
+	bool enabled[2];
+	bool static_config[2];
+	bool gateway_overridden[2];
+	bool dns_overridden[2];
+	bool connected[2];
 
 	const struct l_settings *active_settings;
 
@@ -77,17 +70,12 @@ struct netconfig {
 	void *user_data;
 
 	struct resolve *resolve;
-
-	struct l_acd *acd;
-
-	uint32_t addr4_add_cmd_id;
-	uint32_t addr6_add_cmd_id;
-	uint32_t route4_add_gateway_cmd_id;
-	uint32_t route6_add_cmd_id;
 };
 
+/* 0 for AF_INET, 1 for AF_INET6 */
+#define INDEX_FOR_AF(af)	((af) != AF_INET)
+
 static struct l_netlink *rtnl;
-static struct l_queue *netconfig_list;
 
 /*
  * Routing priority offset, configurable in main.conf. The route with lower
@@ -105,172 +93,65 @@ static void do_debug(const char *str, void *user_data)
 
 static void netconfig_free_settings(struct netconfig *netconfig)
 {
-	l_rtnl_address_free(netconfig->v4_address);
-	netconfig->v4_address = NULL;
-	l_rtnl_address_free(netconfig->v6_address);
-	netconfig->v6_address = NULL;
-
-	l_strfreev(netconfig->dns4_overrides);
-	netconfig->dns4_overrides = NULL;
-	l_strfreev(netconfig->dns6_overrides);
-	netconfig->dns6_overrides = NULL;
+	netconfig->enabled[0] = true;
+	netconfig->enabled[1] = false;
+	netconfig->static_config[0] = false;
+	netconfig->static_config[1] = false;
+	netconfig->gateway_overridden[0] = false;
+	netconfig->gateway_overridden[1] = false;
+	netconfig->dns_overridden[0] = false;
+	netconfig->dns_overridden[1] = false;
+	l_netconfig_reset_config(netconfig->nc);
 
 	l_free(netconfig->mdns);
 	netconfig->mdns = NULL;
+
+	l_free(l_steal_ptr(netconfig->fils_override));
 }
 
 static void netconfig_free(void *data)
 {
 	struct netconfig *netconfig = data;
 
-	l_dhcp_client_destroy(netconfig->dhcp_client);
-	l_dhcp6_client_destroy(netconfig->dhcp6_client);
-
+	l_netconfig_destroy(netconfig->nc);
 	l_free(netconfig);
 }
 
-static struct netconfig *netconfig_find(uint32_t ifindex)
-{
-	const struct l_queue_entry *entry;
-
-	for (entry = l_queue_get_entries(netconfig_list); entry;
-							entry = entry->next) {
-		struct netconfig *netconfig = entry->data;
-
-		if (netconfig->ifindex != ifindex)
-			continue;
-
-		return netconfig;
-	}
-
-	return NULL;
-}
-
-static inline char *netconfig_ipv4_to_string(uint32_t addr)
+static bool netconfig_addr_to_str(uint8_t af, const void *v4_addr,
+					const void *v6_addr, char *out_str,
+					bool *out_is_zero)
 {
-	struct in_addr in_addr = { .s_addr = addr };
-	char *addr_str = l_malloc(INET_ADDRSTRLEN);
+	const void *addr = (af == AF_INET ? v4_addr : v6_addr);
+	uint8_t bytes = (af == AF_INET ? 4 : 16);
 
-	if (L_WARN_ON(unlikely(!inet_ntop(AF_INET, &in_addr, addr_str,
-						INET_ADDRSTRLEN)))) {
-		l_free(addr_str);
-		return NULL;
+	if (l_memeqzero(addr, bytes)) {
+		*out_is_zero = true;
+		return true;
 	}
 
-	return addr_str;
-}
-
-static inline char *netconfig_ipv6_to_string(const uint8_t *addr)
-{
-	struct in6_addr in6_addr;
-	char *addr_str = l_malloc(INET6_ADDRSTRLEN);
-
-	memcpy(in6_addr.s6_addr, addr, 16);
+	*out_is_zero = false;
 
-	if (L_WARN_ON(unlikely(!inet_ntop(AF_INET6, &in6_addr, addr_str,
-						INET6_ADDRSTRLEN)))) {
-		l_free(addr_str);
-		return NULL;
-	}
+	if (L_WARN_ON(!inet_ntop(af, addr, out_str, INET6_ADDRSTRLEN)))
+		return false;
 
-	return addr_str;
+	return true;
 }
 
 static bool netconfig_use_fils_addr(struct netconfig *netconfig, int af)
 {
-	if ((af == AF_INET ? netconfig->rtm_protocol :
-				netconfig->rtm_v6_protocol) != RTPROT_DHCP)
+	if (!netconfig->enabled[INDEX_FOR_AF(af)])
 		return false;
 
-	if (!netconfig->fils_override)
-		return false;
-
-	if (af == AF_INET)
-		return !!netconfig->fils_override->ipv4_addr;
-
-	return !l_memeqzero(netconfig->fils_override->ipv6_addr, 16);
-}
-
-static bool netconfig_use_fils_gateway(struct netconfig *netconfig, int af)
-{
-	if ((af == AF_INET ? netconfig->rtm_protocol :
-				netconfig->rtm_v6_protocol) != RTPROT_DHCP)
+	if (netconfig->static_config[INDEX_FOR_AF(af)])
 		return false;
 
 	if (!netconfig->fils_override)
 		return false;
 
 	if (af == AF_INET)
-		return !!netconfig->fils_override->ipv4_gateway;
-
-	return !l_memeqzero(netconfig->fils_override->ipv6_gateway, 16);
-}
-
-static char **netconfig_get_dns_list(struct netconfig *netconfig, int af,
-					const uint8_t **out_dns_mac)
-{
-	const struct ie_fils_ip_addr_response_info *fils =
-		netconfig->fils_override;
-
-	if (af == AF_INET) {
-		const struct l_dhcp_lease *lease;
-
-		if (netconfig->dns4_overrides)
-			return l_strv_copy(netconfig->dns4_overrides);
-
-		if (netconfig->rtm_protocol != RTPROT_DHCP)
-			return NULL;
-
-		if (fils && fils->ipv4_dns) {
-			char **dns_list = l_new(char *, 2);
-
-			if (!l_memeqzero(fils->ipv4_dns_mac, 6) &&
-					out_dns_mac &&
-					util_ip_subnet_match(
-							fils->ipv4_prefix_len,
-							&fils->ipv4_addr,
-							&fils->ipv4_dns))
-				*out_dns_mac = fils->ipv4_dns_mac;
-
-			dns_list[0] = netconfig_ipv4_to_string(fils->ipv4_dns);
-			return dns_list;
-		}
-
-		lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
-		if (!lease)
-			return NULL;
-
-		return l_dhcp_lease_get_dns(lease);
-	} else {
-		const struct l_dhcp6_lease *lease;
-
-		if (netconfig->dns6_overrides)
-			return l_strv_copy(netconfig->dns6_overrides);
-
-		if (netconfig->rtm_v6_protocol != RTPROT_DHCP)
-			return NULL;
-
-		if (fils && !l_memeqzero(fils->ipv6_dns, 16)) {
-			char **dns_list = l_new(char *, 2);
-
-			if (!l_memeqzero(fils->ipv6_dns_mac, 6) &&
-					out_dns_mac &&
-					util_ip_subnet_match(
-							fils->ipv6_prefix_len,
-							fils->ipv6_addr,
-							fils->ipv6_dns))
-				*out_dns_mac = fils->ipv6_dns_mac;
-
-			dns_list[0] = netconfig_ipv6_to_string(fils->ipv6_dns);
-			return dns_list;
-		}
-
-		lease = l_dhcp6_client_get_lease(netconfig->dhcp6_client);
-		if (!lease)
-			return NULL;
+		return !!netconfig->fils_override->ipv4_addr;
 
-		return l_dhcp6_lease_get_dns(lease);
-	}
+	return !l_memeqzero(netconfig->fils_override->ipv6_addr, 16);
 }
 
 static void netconfig_set_neighbor_entry_cb(int error,
@@ -282,150 +163,28 @@ static void netconfig_set_neighbor_entry_cb(int error,
 			strerror(-error), error);
 }
 
-static void netconfig_set_dns(struct netconfig *netconfig)
+static void netconfig_dns_list_update(struct netconfig *netconfig)
 {
-	if (!netconfig->dns4_list && !netconfig->dns6_list)
-		return;
-
-	if (netconfig->dns4_list && netconfig->dns6_list) {
-		unsigned int n_entries4 = l_strv_length(netconfig->dns4_list);
-		unsigned int n_entries6 = l_strv_length(netconfig->dns6_list);
-		char **dns_list = l_malloc(sizeof(char *) *
-					(n_entries4 + n_entries6 + 1));
+	_auto_(l_strv_free) char **dns_list =
+		l_netconfig_get_dns_list(netconfig->nc);
 
-		memcpy(dns_list, netconfig->dns4_list,
-			sizeof(char *) * n_entries4);
-		memcpy(dns_list + n_entries4, netconfig->dns6_list,
-			sizeof(char *) * (n_entries6 + 1));
+	if (netconfig->resolve && dns_list)
 		resolve_set_dns(netconfig->resolve, dns_list);
-		l_free(dns_list);
-		return;
-	}
-
-	resolve_set_dns(netconfig->resolve,
-			netconfig->dns4_list ?: netconfig->dns6_list);
-}
-
-static bool netconfig_dns_list_update(struct netconfig *netconfig, uint8_t af)
-{
-	const uint8_t *fils_dns_mac = NULL;
-	char ***dns_list_ptr = af == AF_INET ?
-		&netconfig->dns4_list : &netconfig->dns6_list;
-	char **new_dns_list = netconfig_get_dns_list(netconfig, af,
-							&fils_dns_mac);
-
-	if (l_strv_eq(*dns_list_ptr, new_dns_list)) {
-		l_strv_free(new_dns_list);
-		return false;
-	}
-
-	l_strv_free(*dns_list_ptr);
-	*dns_list_ptr = new_dns_list;
-
-	if (fils_dns_mac) {
-		const struct ie_fils_ip_addr_response_info *fils =
-			netconfig->fils_override;
-		const void *dns_ip = af == AF_INET ?
-			(const void *) &fils->ipv4_dns :
-			(const void *) &fils->ipv6_dns;
-
-		if (!l_rtnl_neighbor_set_hwaddr(rtnl, netconfig->ifindex, af,
-						dns_ip, fils_dns_mac, 6,
-						netconfig_set_neighbor_entry_cb,
-						NULL, NULL))
-			l_debug("l_rtnl_neighbor_set_hwaddr failed");
-	}
-
-	return true;
 }
 
-static void append_domain(char **domains, unsigned int *n_domains,
-				size_t max, char *domain)
+static void netconfig_domains_update(struct netconfig *netconfig)
 {
-	unsigned int i;
+	_auto_(l_strv_free) char **domains =
+		l_netconfig_get_domain_names(netconfig->nc);
 
-	if (*n_domains == max)
-		return;
-
-	for (i = 0; i < *n_domains; i++)
-		if (!strcmp(domains[i], domain))
-			return;
-
-	domains[*n_domains] = domain;
-	*n_domains += 1;
-}
-
-static void netconfig_set_domains(struct netconfig *netconfig)
-{
-	char *domains[31];
-	unsigned int n_domains = 0;
-	char **p;
-
-	memset(domains, 0, sizeof(domains));
-
-	append_domain(domains, &n_domains,
-			L_ARRAY_SIZE(domains) - 1, netconfig->v4_domain);
-
-	for (p = netconfig->v6_domains; p && *p; p++)
-		append_domain(domains, &n_domains,
-				L_ARRAY_SIZE(domains) - 1, *p);
-
-	resolve_set_domains(netconfig->resolve, domains);
-}
-
-static bool netconfig_domains_update(struct netconfig *netconfig, uint8_t af)
-{
-	bool changed = false;
-
-	if (af == AF_INET) {
-		/* Allow to override the DHCP domain name with setting entry. */
-		char *v4_domain = l_settings_get_string(
-						netconfig->active_settings,
-						"IPv4", "DomainName");
-
-		if (!v4_domain && netconfig->rtm_protocol == RTPROT_DHCP) {
-			const struct l_dhcp_lease *lease =
-				l_dhcp_client_get_lease(netconfig->dhcp_client);
-
-			if (lease)
-				v4_domain = l_dhcp_lease_get_domain_name(lease);
-		}
-
-		if (l_streq0(v4_domain, netconfig->v4_domain))
-			l_free(v4_domain);
-		else {
-			l_free(netconfig->v4_domain);
-			netconfig->v4_domain = v4_domain;
-			changed = true;
-		}
-	} else {
-		char **v6_domains = NULL;
-
-		if (netconfig->rtm_v6_protocol == RTPROT_DHCP) {
-			const struct l_dhcp6_lease *lease =
-				l_dhcp6_client_get_lease(
-						netconfig->dhcp6_client);
-
-			if (lease)
-				v6_domains = l_dhcp6_lease_get_domains(lease);
-		}
-
-		if (l_strv_eq(netconfig->v6_domains, v6_domains))
-			l_strv_free(v6_domains);
-		else {
-			l_strv_free(netconfig->v6_domains);
-			netconfig->v6_domains = v6_domains;
-			changed = true;
-		}
-	}
-
-	return changed;
+	if (netconfig->resolve && domains)
+		resolve_set_domains(netconfig->resolve, domains);
 }
 
 static struct l_rtnl_address *netconfig_get_static4_address(
 				const struct l_settings *active_settings)
 {
-	struct l_rtnl_address *ifaddr = NULL;
+	_auto_(l_rtnl_address_free) struct l_rtnl_address *ifaddr = NULL;
 	L_AUTO_FREE_VAR(char *, ip) = NULL;
 	L_AUTO_FREE_VAR(char *, netmask) = NULL;
 	struct in_addr in_addr;
@@ -433,13 +192,22 @@ static struct l_rtnl_address *netconfig_get_static4_address(
 	uint32_t prefix_len;
 
 	ip = l_settings_get_string(active_settings, "IPv4", "Address");
-	if (!ip)
+	if (unlikely(!ip)) {
+		l_error("netconfig: Can't load IPv4.Address");
+		return NULL;
+	}
+
+	if (l_settings_has_key(active_settings, "IPv4", "Netmask") &&
+			!(netmask = l_settings_get_string(active_settings,
+								"IPv4",
+								"Netmask"))) {
+		l_error("netconfig: Can't load IPv4.Netmask");
 		return NULL;
+	}
 
-	netmask = l_settings_get_string(active_settings, "IPv4", "Netmask");
 	if (netmask) {
 		if (inet_pton(AF_INET, netmask, &in_addr) != 1) {
-			l_error("netconfig: Can't parse IPv4 Netmask");
+			l_error("netconfig: Can't parse IPv4.Netmask");
 			return NULL;
 		}
 
@@ -447,14 +215,14 @@ static struct l_rtnl_address *netconfig_get_static4_address(
 
 		if (ntohl(in_addr.s_addr) !=
 				util_netmask_from_prefix(prefix_len)) {
-			l_error("netconfig: Invalid IPv4 Netmask");
+			l_error("netconfig: Invalid IPv4.Netmask");
 			return NULL;
 		}
 	} else
 		prefix_len = 24;
 
 	ifaddr = l_rtnl_address_new(ip, prefix_len);
-	if (!ifaddr) {
+	if (!ifaddr || l_rtnl_address_get_family(ifaddr) != AF_INET) {
 		l_error("netconfig: Unable to parse IPv4.Address");
 		return NULL;
 	}
@@ -462,52 +230,10 @@ static struct l_rtnl_address *netconfig_get_static4_address(
 	broadcast = l_settings_get_string(active_settings, "IPv4", "Broadcast");
 	if (broadcast && !l_rtnl_address_set_broadcast(ifaddr, broadcast)) {
 		l_error("netconfig: Unable to parse IPv4.Broadcast");
-		l_rtnl_address_free(ifaddr);
 		return NULL;
 	}
 
-	l_rtnl_address_set_noprefixroute(ifaddr, true);
-	return ifaddr;
-}
-
-static char *netconfig_ipv4_get_gateway(struct netconfig *netconfig,
-					const uint8_t **out_mac)
-{
-	const struct l_dhcp_lease *lease;
-	char *gateway;
-	const struct ie_fils_ip_addr_response_info *fils =
-		netconfig->fils_override;
-
-	switch (netconfig->rtm_protocol) {
-	case RTPROT_STATIC:
-		gateway = l_settings_get_string(netconfig->active_settings,
-							"IPv4", "Gateway");
-		if (!gateway)
-			gateway = l_settings_get_string(
-						netconfig->active_settings,
-						"IPv4", "gateway");
-
-		return gateway;
-
-	case RTPROT_DHCP:
-		if (netconfig_use_fils_gateway(netconfig, AF_INET)) {
-			gateway = netconfig_ipv4_to_string(fils->ipv4_gateway);
-
-			if (gateway && out_mac &&
-					!l_memeqzero(fils->ipv4_gateway_mac, 6))
-				*out_mac = fils->ipv4_gateway_mac;
-
-			return gateway;
-		}
-
-		lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
-		if (!lease)
-			return NULL;
-
-		return l_dhcp_lease_get_gateway(lease);
-	}
-
-	return NULL;
+	return l_steal_ptr(ifaddr);
 }
 
 static struct l_rtnl_address *netconfig_get_static6_address(
@@ -516,12 +242,14 @@ static struct l_rtnl_address *netconfig_get_static6_address(
 	L_AUTO_FREE_VAR(char *, ip);
 	char *p;
 	char *endp;
-	struct l_rtnl_address *ret;
-	uint32_t prefix_len = 128;
+	_auto_(l_rtnl_address_free) struct l_rtnl_address *ret = NULL;
+	uint32_t prefix_len = 64;
 
 	ip = l_settings_get_string(active_settings, "IPv6", "Address");
-	if (!ip)
+	if (unlikely(!ip)) {
+		l_error("netconfig: Can't load IPv6.Address");
 		return NULL;
+	}
 
 	p = strrchr(ip, '/');
 	if (!p)
@@ -535,100 +263,38 @@ static struct l_rtnl_address *netconfig_get_static6_address(
 	prefix_len = strtoul(p, &endp, 10);
 	if (unlikely(*endp != '\0' || errno ||
 			!prefix_len || prefix_len > 128)) {
-		l_error("netconfig: Invalid prefix '%s' provided in network"
-				" configuration file", p);
+		l_error("netconfig: Invalid prefix '%s' is provided in network"
+			" configuration file", p);
 		return NULL;
 	}
 
 no_prefix_len:
 	ret = l_rtnl_address_new(ip, prefix_len);
-	if (!ret)
-		l_error("netconfig: Invalid IPv6 address %s is "
-				"provided in network configuration file.", ip);
-
-	return ret;
-}
-
-static struct l_rtnl_route *netconfig_get_static6_gateway(
-						struct netconfig *netconfig,
-						char **out_str,
-						const uint8_t **out_mac)
-{
-	L_AUTO_FREE_VAR(char *, gateway);
-	struct l_rtnl_route *ret;
-	const uint8_t *mac = NULL;
-
-	gateway = l_settings_get_string(netconfig->active_settings,
-						"IPv6", "Gateway");
-	if (!gateway && netconfig_use_fils_gateway(netconfig, AF_INET6)) {
-		gateway = netconfig_ipv6_to_string(
-					netconfig->fils_override->ipv6_gateway);
-
-		if (!l_memeqzero(netconfig->fils_override->ipv6_gateway_mac, 6))
-			mac = netconfig->fils_override->ipv6_gateway_mac;
-	} else if (!gateway)
+	if (!ret || l_rtnl_address_get_family(ret) != AF_INET6) {
+		l_error("netconfig: Invalid IPv6 address %s is provided in "
+			"network configuration file.", ip);
 		return NULL;
-
-	ret = l_rtnl_route_new_gateway(gateway);
-	if (!ret) {
-		l_error("netconfig: Invalid IPv6 gateway address %s is "
-			"provided in network configuration file.",
-			gateway);
-		return ret;
 	}
 
-	l_rtnl_route_set_priority(ret, ROUTE_PRIORITY_OFFSET);
-	l_rtnl_route_set_protocol(ret, RTPROT_STATIC);
-	*out_str = l_steal_ptr(gateway);
-	*out_mac = mac;
-
-	return ret;
+	return l_steal_ptr(ret);
 }
 
-static struct l_rtnl_address *netconfig_get_dhcp4_address(
-						struct netconfig *netconfig)
-{
-	const struct l_dhcp_lease *lease =
-			l_dhcp_client_get_lease(netconfig->dhcp_client);
-	L_AUTO_FREE_VAR(char *, ip) = NULL;
-	L_AUTO_FREE_VAR(char *, broadcast) = NULL;
-	uint32_t prefix_len;
-	struct l_rtnl_address *ret;
-
-	if (L_WARN_ON(!lease))
-		return NULL;
-
-	ip = l_dhcp_lease_get_address(lease);
-	broadcast = l_dhcp_lease_get_broadcast(lease);
-
-	prefix_len = l_dhcp_lease_get_prefix_length(lease);
-	if (!prefix_len)
-		prefix_len = 24;
-
-	ret = l_rtnl_address_new(ip, prefix_len);
-	if (!ret)
-		return ret;
-
-	if (broadcast)
-		l_rtnl_address_set_broadcast(ret, broadcast);
-
-	l_rtnl_address_set_noprefixroute(ret, true);
-	return ret;
-}
-
-static void netconfig_gateway_to_arp(struct netconfig *netconfig)
+static void netconfig_dhcp_gateway_to_arp(struct netconfig *netconfig)
 {
+	struct l_dhcp_client *dhcp = l_netconfig_get_dhcp_client(netconfig->nc);
 	const struct l_dhcp_lease *lease;
 	_auto_(l_free) char *server_id = NULL;
 	_auto_(l_free) char *gw = NULL;
 	const uint8_t *server_mac;
 	struct in_addr in_gw;
+	uint32_t ifindex = netdev_get_ifindex(netconfig->netdev);
 
 	/* Can only do this for DHCP in certain network setups */
-	if (netconfig->rtm_protocol != RTPROT_DHCP)
+	if (netconfig->static_config[INDEX_FOR_AF(AF_INET)] ||
+			netconfig->fils_override)
 		return;
 
-	lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
+	lease = l_dhcp_client_get_lease(dhcp);
 	if (!lease)
 		return;
 
@@ -642,844 +308,379 @@ static void netconfig_gateway_to_arp(struct netconfig *netconfig)
 	l_debug("Gateway MAC is known, setting into ARP cache");
 	in_gw.s_addr = l_dhcp_lease_get_gateway_u32(lease);
 
-	if (!l_rtnl_neighbor_set_hwaddr(rtnl, netconfig->ifindex, AF_INET,
+	if (!l_rtnl_neighbor_set_hwaddr(rtnl, ifindex, AF_INET,
 					&in_gw, server_mac, ETH_ALEN,
 					netconfig_set_neighbor_entry_cb, NULL,
 					NULL))
 		l_debug("l_rtnl_neighbor_set_hwaddr failed");
 }
 
-static void netconfig_ifaddr_added(struct netconfig *netconfig,
-					const struct ifaddrmsg *ifa,
-					uint32_t len)
-{
-	L_AUTO_FREE_VAR(char *, label) = NULL;
-	L_AUTO_FREE_VAR(char *, ip) = NULL;
-	L_AUTO_FREE_VAR(char *, broadcast) = NULL;
-
-	l_rtnl_ifaddr4_extract(ifa, len, &label, &ip, &broadcast);
-	l_debug("%s: ifaddr %s/%u broadcast %s", label,
-					ip, ifa->ifa_prefixlen, broadcast);
-}
-
-static void netconfig_ifaddr_deleted(struct netconfig *netconfig,
-					const struct ifaddrmsg *ifa,
-					uint32_t len)
-{
-	L_AUTO_FREE_VAR(char *, ip);
-
-	l_rtnl_ifaddr4_extract(ifa, len, NULL, &ip, NULL);
-	l_debug("ifaddr %s/%u", ip, ifa->ifa_prefixlen);
-}
-
-static void netconfig_ifaddr_notify(uint16_t type, const void *data,
-						uint32_t len, void *user_data)
+static void netconfig_commit_fils_macs(struct netconfig *netconfig, uint8_t af)
 {
-	const struct ifaddrmsg *ifa = data;
-	struct netconfig *netconfig;
-	uint32_t bytes;
+	const struct ie_fils_ip_addr_response_info *fils =
+		netconfig->fils_override;
+	const void *addr;
+	const void *hwaddr;
+	size_t addr_len = (af == AF_INET ? 4 : 16);
+	uint32_t ifindex = netdev_get_ifindex(netconfig->netdev);
 
-	netconfig = netconfig_find(ifa->ifa_index);
-	if (!netconfig)
-		/* Ignore the interfaces which aren't managed by iwd. */
+	if (!fils)
 		return;
 
-	bytes = len - NLMSG_ALIGN(sizeof(struct ifaddrmsg));
+	/*
+	 * Attempt to use the gateway/DNS MAC addressed received from the AP
+	 * by writing the mapping directly into the netdev's ARP table so as
+	 * to save one data frame roundtrip before first IP connections are
+	 * established.  This is very low-priority but print error messages
+	 * just because they may indicate bigger problems.
+	 */
 
-	switch (type) {
-	case RTM_NEWADDR:
-		netconfig_ifaddr_added(netconfig, ifa, bytes);
-		break;
-	case RTM_DELADDR:
-		netconfig_ifaddr_deleted(netconfig, ifa, bytes);
-		break;
-	}
-}
+	addr = (af == AF_INET ? (void *) &fils->ipv4_gateway :
+			(void *) &fils->ipv6_gateway);
+	hwaddr = (af == AF_INET ?
+			&fils->ipv4_gateway_mac : &fils->ipv6_gateway_mac);
 
-static void netconfig_ifaddr_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
-{
-	if (error) {
-		l_error("netconfig: ifaddr command failure. "
-				"Error %d: %s", error, strerror(-error));
-		return;
-	}
+	if (!l_memeqzero(addr, addr_len) && !l_memeqzero(hwaddr, ETH_ALEN) &&
+			unlikely(!l_rtnl_neighbor_set_hwaddr(rtnl, ifindex, af,
+						addr, hwaddr, ETH_ALEN,
+						netconfig_set_neighbor_entry_cb,
+						NULL, NULL)))
+		l_debug("l_rtnl_neighbor_set_hwaddr(%s, gateway) failed",
+			af == AF_INET ? "AF_INET" : "AF_INET6");
 
-	if (type != RTM_NEWADDR)
-		return;
+	addr = (af == AF_INET ? (void *) &fils->ipv4_dns :
+			(void *) &fils->ipv6_dns);
+	hwaddr = (af == AF_INET ? &fils->ipv4_dns_mac : &fils->ipv6_dns_mac);
 
-	netconfig_ifaddr_notify(type, data, len, user_data);
+	if (!l_memeqzero(addr, addr_len) && !l_memeqzero(hwaddr, ETH_ALEN) &&
+			unlikely(!l_rtnl_neighbor_set_hwaddr(rtnl, ifindex, af,
+						addr, hwaddr, ETH_ALEN,
+						netconfig_set_neighbor_entry_cb,
+						NULL, NULL)))
+		l_debug("l_rtnl_neighbor_set_hwaddr(%s, DNS) failed",
+			af == AF_INET ? "AF_INET" : "AF_INET6");
 }
 
-static void netconfig_ifaddr_ipv6_added(struct netconfig *netconfig,
-					const struct ifaddrmsg *ifa,
-					uint32_t len)
+static void netconfig_event_handler(struct l_netconfig *nc, uint8_t family,
+					enum l_netconfig_event event,
+					void *user_data)
 {
-	struct in6_addr in6;
-	L_AUTO_FREE_VAR(char *, ip) = NULL;
-
-	if (ifa->ifa_flags & IFA_F_TENTATIVE)
-		return;
-
-	l_rtnl_ifaddr6_extract(ifa, len, &ip);
-
-	l_debug("ifindex %u: ifaddr %s/%u", netconfig->ifindex,
-			ip, ifa->ifa_prefixlen);
-
-	if (netconfig->rtm_v6_protocol != RTPROT_DHCP ||
-			netconfig_use_fils_addr(netconfig, AF_INET6))
-		return;
-
-	inet_pton(AF_INET6, ip, &in6);
-	if (!IN6_IS_ADDR_LINKLOCAL(&in6))
-		return;
-
-	l_dhcp6_client_set_link_local_address(netconfig->dhcp6_client, ip);
-
-	if (l_dhcp6_client_start(netconfig->dhcp6_client))
-		return;
+	struct netconfig *netconfig = user_data;
 
-	l_error("netconfig: Failed to start DHCPv6 client for "
-			"interface %u", netconfig->ifindex);
-}
+	l_debug("l_netconfig event %d", event);
 
-static void netconfig_ifaddr_ipv6_deleted(struct netconfig *netconfig,
-						const struct ifaddrmsg *ifa,
-						uint32_t len)
-{
-	L_AUTO_FREE_VAR(char *, ip);
+	l_netconfig_apply_rtnl(nc);
 
-	l_rtnl_ifaddr6_extract(ifa, len, &ip);
-	l_debug("ifindex %u: ifaddr %s/%u", netconfig->ifindex,
-			ip, ifa->ifa_prefixlen);
-}
+	switch (event) {
+	case L_NETCONFIG_EVENT_CONFIGURE:
+		netconfig_dns_list_update(netconfig);
+		netconfig_domains_update(netconfig);
 
-static void netconfig_ifaddr_ipv6_notify(uint16_t type, const void *data,
-						uint32_t len, void *user_data)
-{
-	const struct ifaddrmsg *ifa = data;
-	struct netconfig *netconfig;
-	uint32_t bytes;
+		if (family == AF_INET &&
+				!netconfig->static_config[INDEX_FOR_AF(family)])
+			netconfig_dhcp_gateway_to_arp(netconfig);
 
-	netconfig = netconfig_find(ifa->ifa_index);
-	if (!netconfig)
-		/* Ignore the interfaces which aren't managed by iwd. */
-		return;
+		if (!netconfig->connected[INDEX_FOR_AF(family)] &&
+				netconfig_use_fils_addr(netconfig, family))
+			netconfig_commit_fils_macs(netconfig, family);
 
-	bytes = len - NLMSG_ALIGN(sizeof(struct ifaddrmsg));
+		if (family == AF_INET &&
+				!netconfig->connected[INDEX_FOR_AF(family)] &&
+				netconfig->notify)
+			netconfig->notify(NETCONFIG_EVENT_CONNECTED,
+						netconfig->user_data);
 
-	switch (type) {
-	case RTM_NEWADDR:
-		netconfig_ifaddr_ipv6_added(netconfig, ifa, bytes);
-		break;
-	case RTM_DELADDR:
-		netconfig_ifaddr_ipv6_deleted(netconfig, ifa, bytes);
+		netconfig->connected[INDEX_FOR_AF(family)] = true;
 		break;
-	}
-}
 
-static void netconfig_ifaddr_ipv6_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
-{
-	if (error) {
-		l_error("netconfig: ifaddr IPv6 command failure. "
-				"Error %d: %s", error, strerror(-error));
-		return;
-	}
-
-	if (type != RTM_NEWADDR)
-		return;
-
-	netconfig_ifaddr_ipv6_notify(type, data, len, user_data);
-}
-
-static void netconfig_route_generic_cb(int error, uint16_t type,
-					const void *data, uint32_t len,
-					void *user_data)
-{
-	if (error) {
-		l_error("netconfig: Failed to add route. Error %d: %s",
-						error, strerror(-error));
-		return;
-	}
-}
-
-static void netconfig_route_add_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
-{
-	struct netconfig *netconfig = user_data;
-
-	netconfig->route4_add_gateway_cmd_id = 0;
-
-	if (error) {
-		l_error("netconfig: Failed to add route. Error %d: %s",
-						error, strerror(-error));
-		return;
-	}
-
-	if (!netconfig->notify)
-		return;
-
-	netconfig->notify(NETCONFIG_EVENT_CONNECTED, netconfig->user_data);
-	netconfig->notify = NULL;
-}
+	case L_NETCONFIG_EVENT_UPDATE:
+	case L_NETCONFIG_EVENT_UNCONFIGURE:
+		break;
 
-static void netconfig_route6_add_cb(int error, uint16_t type,
-					const void *data, uint32_t len,
-					void *user_data)
-{
-	struct netconfig *netconfig = user_data;
+	case L_NETCONFIG_EVENT_FAILED:
+		/*
+		 * l_netconfig might have emitted an UNCONFIGURE before this
+		 * but now it tells us it's given up on (re)establishing the
+		 * IP setup.
+		 */
+		if (family == AF_INET && netconfig->notify)
+			netconfig->notify(NETCONFIG_EVENT_FAILED,
+						netconfig->user_data);
 
-	netconfig->route6_add_cmd_id = 0;
+		break;
 
-	if (error) {
-		l_error("netconfig: Failed to add route. Error %d: %s",
-						error, strerror(-error));
-		return;
+	default:
+		l_error("netconfig: Received unsupported l_netconfig event: %d",
+									event);
 	}
 }
 
-static bool netconfig_ipv4_subnet_route_install(struct netconfig *netconfig)
+static bool netconfig_load_dns(struct netconfig *netconfig,
+				const struct l_settings *active_settings,
+				const char *group_name, uint8_t family)
 {
-	struct in_addr in_addr;
-	char ip[INET_ADDRSTRLEN];
-	char network[INET_ADDRSTRLEN];
-	unsigned int prefix_len =
-		l_rtnl_address_get_prefix_length(netconfig->v4_address);
-
-	if (!l_rtnl_address_get_address(netconfig->v4_address, ip) ||
-			inet_pton(AF_INET, ip, &in_addr) < 1)
-		return false;
+	_auto_(l_strv_free) char **dns_str_list = NULL;
 
-	in_addr.s_addr = in_addr.s_addr &
-				htonl(0xFFFFFFFFLU << (32 - prefix_len));
+	if (!l_settings_has_key(active_settings, group_name, "DNS"))
+		return true;
 
-	if (!inet_ntop(AF_INET, &in_addr, network, INET_ADDRSTRLEN))
+	dns_str_list = l_settings_get_string_list(active_settings,
+							group_name, "DNS", ' ');
+	if (unlikely(!dns_str_list)) {
+		l_error("netconfig: Can't load [%s].DNS", group_name);
 		return false;
+	}
 
-	if (!l_rtnl_route4_add_connected(rtnl, netconfig->ifindex,
-						prefix_len, network, ip,
-						netconfig->rtm_protocol,
-						netconfig_route_generic_cb,
-						netconfig, NULL)) {
-		l_error("netconfig: Failed to add subnet route.");
+	if (unlikely(!l_netconfig_set_dns_override(netconfig->nc, family,
+							dns_str_list))) {
+		l_error("netconfig: l_netconfig_set_dns_override(%s) failed",
+			family == AF_INET ? "AF_INET" : "AF_INET6");
 		return false;
 	}
 
+	netconfig->dns_overridden[INDEX_FOR_AF(family)] = true;
 	return true;
 }
 
-static bool netconfig_ipv4_gateway_route_install(struct netconfig *netconfig)
+static bool netconfig_load_gateway(struct netconfig *netconfig,
+				const struct l_settings *active_settings,
+				const char *group_name, uint8_t family)
 {
-	L_AUTO_FREE_VAR(char *, gateway) = NULL;
-	const uint8_t *gateway_mac = NULL;
-	struct in_addr in_addr;
-	char ip[INET_ADDRSTRLEN];
-
-	gateway = netconfig_ipv4_get_gateway(netconfig, &gateway_mac);
-	if (!gateway) {
-		l_debug("No gateway obtained from %s.",
-				netconfig->rtm_protocol == RTPROT_STATIC ?
-				"setting file" : "DHCPv4 lease");
-
-		if (netconfig->notify) {
-			netconfig->notify(NETCONFIG_EVENT_CONNECTED,
-						netconfig->user_data);
-			netconfig->notify = NULL;
-		}
+	_auto_(l_free) char *gateway_str = NULL;
 
+	if (!l_settings_has_key(active_settings, group_name, "Gateway"))
 		return true;
-	}
 
-	if (!l_rtnl_address_get_address(netconfig->v4_address, ip) ||
-			inet_pton(AF_INET, ip, &in_addr) < 1)
+	gateway_str = l_settings_get_string(active_settings, group_name,
+						"Gateway");
+	if (unlikely(!gateway_str)) {
+		l_error("netconfig: Can't load [%s].Gateway", group_name);
 		return false;
+	}
 
-	netconfig->route4_add_gateway_cmd_id =
-		l_rtnl_route4_add_gateway(rtnl, netconfig->ifindex, gateway, ip,
-						ROUTE_PRIORITY_OFFSET,
-						netconfig->rtm_protocol,
-						netconfig_route_add_cmd_cb,
-						netconfig, NULL);
-	if (!netconfig->route4_add_gateway_cmd_id) {
-		l_error("netconfig: Failed to add route for: %s gateway.",
-								gateway);
-
+	if (unlikely(!l_netconfig_set_gateway_override(netconfig->nc, family,
+							gateway_str))) {
+		l_error("netconfig: l_netconfig_set_gateway_override(%s) "
+			"failed", family == AF_INET ? "AF_INET" : "AF_INET6");
 		return false;
 	}
 
-	/*
-	 * Attempt to use the gateway MAC address received from the AP by
-	 * writing the mapping directly into the netdev's ARP table so as
-	 * to save one data frame roundtrip before first IP connections
-	 * are established.  This is very low-priority but print error
-	 * messages just because they may indicate bigger problems.
-	 */
-	if (gateway_mac && !l_rtnl_neighbor_set_hwaddr(rtnl, netconfig->ifindex,
-					AF_INET,
-					&netconfig->fils_override->ipv4_gateway,
-					gateway_mac, 6,
-					netconfig_set_neighbor_entry_cb, NULL,
-					NULL))
-		l_debug("l_rtnl_neighbor_set_hwaddr failed");
-
+	netconfig->gateway_overridden[INDEX_FOR_AF(family)] = true;
 	return true;
 }
 
-static void netconfig_ipv4_ifaddr_add_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
-{
-	struct netconfig *netconfig = user_data;
-
-	netconfig->addr4_add_cmd_id = 0;
-
-	if (error && error != -EEXIST) {
-		l_error("netconfig: Failed to add IP address. "
-				"Error %d: %s", error, strerror(-error));
-		return;
-	}
-
-	netconfig_gateway_to_arp(netconfig);
-
-	if (!netconfig_ipv4_subnet_route_install(netconfig) ||
-			!netconfig_ipv4_gateway_route_install(netconfig))
-		return;
-
-	netconfig_set_dns(netconfig);
-	netconfig_set_domains(netconfig);
-}
-
-static void netconfig_ipv6_ifaddr_add_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
-{
-	struct netconfig *netconfig = user_data;
-	struct l_rtnl_route *gateway;
-	const uint8_t *gateway_mac;
-
-	netconfig->addr6_add_cmd_id = 0;
-
-	if (error && error != -EEXIST) {
-		l_error("netconfig: Failed to add IPv6 address. "
-				"Error %d: %s", error, strerror(-error));
-		return;
-	}
-
-	gateway = netconfig_get_static6_gateway(netconfig,
-						&netconfig->v6_gateway_str,
-						&gateway_mac);
-	if (gateway) {
-		netconfig->route6_add_cmd_id = l_rtnl_route_add(rtnl,
-							netconfig->ifindex,
-							gateway,
-							netconfig_route6_add_cb,
-							netconfig, NULL);
-		L_WARN_ON(unlikely(!netconfig->route6_add_cmd_id));
-		l_rtnl_route_free(gateway);
-
-		if (gateway_mac && !l_rtnl_neighbor_set_hwaddr(rtnl,
-					netconfig->ifindex, AF_INET6,
-					netconfig->fils_override->ipv6_gateway,
-					gateway_mac, 6,
-					netconfig_set_neighbor_entry_cb, NULL,
-					NULL))
-			l_debug("l_rtnl_neighbor_set_hwaddr failed");
-	}
-
-	netconfig_set_dns(netconfig);
-	netconfig_set_domains(netconfig);
-}
-
-static void netconfig_ifaddr_del_cmd_cb(int error, uint16_t type,
-						const void *data, uint32_t len,
-						void *user_data)
+bool netconfig_load_settings(struct netconfig *netconfig,
+				const struct l_settings *active_settings)
 {
-	if (error == -ENODEV)
-		/* The device is unplugged, we are done. */
-		return;
-
-	if (!error)
-		/*
-		 * The kernel removes all of the routes associated with the
-		 * deleted IP on its own. There is no need to explicitly remove
-		 * them.
-		 */
-		return;
+	bool send_hostname = false;
+	char hostname[HOST_NAME_MAX + 1];
+	_auto_(l_free) char *mdns = NULL;
+	bool success = true;
+	bool static_ipv4 = false;
+	bool static_ipv6 = false;
+	bool enable_ipv4 = true;
+	bool enable_ipv6 = ipv6_enabled;
 
-	l_error("netconfig: Failed to delete IP address. "
-				"Error %d: %s", error, strerror(-error));
-}
+	netconfig_free_settings(netconfig);
 
-static void netconfig_ipv4_dhcp_event_handler(struct l_dhcp_client *client,
-						enum l_dhcp_client_event event,
-						void *userdata)
-{
-	struct netconfig *netconfig = userdata;
+	/*
+	 * Note we try to print errors and continue validating the
+	 * configuration until we've gone through all the settings so
+	 * as to make fixing the settings more efficient for the user.
+	 */
 
-	l_debug("DHCPv4 event %d", event);
+	if (l_settings_has_key(active_settings, "IPv4", "Address")) {
+		_auto_(l_rtnl_address_free) struct l_rtnl_address *addr =
+			netconfig_get_static4_address(active_settings);
 
-	switch (event) {
-	case L_DHCP_CLIENT_EVENT_IP_CHANGED:
-		L_WARN_ON(!l_rtnl_ifaddr_delete(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ifaddr_del_cmd_cb,
-					netconfig, NULL));
-		/* Fall through. */
-	case L_DHCP_CLIENT_EVENT_LEASE_OBTAINED:
-	{
-		char *gateway_str;
-		struct l_rtnl_address *address;
-
-		gateway_str = netconfig_ipv4_get_gateway(netconfig, NULL);
-		if (l_streq0(netconfig->v4_gateway_str, gateway_str))
-			l_free(gateway_str);
-		else {
-			l_free(netconfig->v4_gateway_str);
-			netconfig->v4_gateway_str = gateway_str;
+		if (unlikely(!addr)) {
+			success = false;
+			goto ipv6_addr;
 		}
 
-		address = netconfig_get_dhcp4_address(netconfig);
-		l_rtnl_address_free(netconfig->v4_address);
-		netconfig->v4_address = address;
-
-		if (!netconfig->v4_address) {
-			l_error("netconfig: Failed to obtain IP addresses from "
-							"DHCPv4 lease.");
-			return;
+		if (!l_netconfig_set_static_addr(netconfig->nc, AF_INET,
+							addr)) {
+			l_error("netconfig: l_netconfig_set_static_addr("
+				"AF_INET) failed");
+			success = false;
+			goto ipv6_addr;
 		}
 
-		netconfig_dns_list_update(netconfig, AF_INET);
-		netconfig_domains_update(netconfig, AF_INET);
-
-		L_WARN_ON(!(netconfig->addr4_add_cmd_id =
-				l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ipv4_ifaddr_add_cmd_cb,
-					netconfig, NULL)));
-		break;
-	}
-	case L_DHCP_CLIENT_EVENT_LEASE_RENEWED:
-		break;
-	case L_DHCP_CLIENT_EVENT_LEASE_EXPIRED:
-		L_WARN_ON(!l_rtnl_ifaddr_delete(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ifaddr_del_cmd_cb,
-					netconfig, NULL));
-		l_rtnl_address_free(netconfig->v4_address);
-		netconfig->v4_address = NULL;
-		l_free(l_steal_ptr(netconfig->v4_gateway_str));
-
-		/* Fall through. */
-	case L_DHCP_CLIENT_EVENT_NO_LEASE:
-		/*
-		 * The requested address is no longer available, try to restart
-		 * the client.
-		 */
-		if (!l_dhcp_client_start(client))
-			l_error("netconfig: Failed to re-start DHCPv4 client "
-					"for interface %u", netconfig->ifindex);
-
-		break;
-	default:
-		l_error("netconfig: Received unsupported DHCPv4 event: %d",
-									event);
+		static_ipv4 = true;
 	}
-}
 
-static void netconfig_dhcp6_event_handler(struct l_dhcp6_client *client,
-						enum l_dhcp6_client_event event,
-						void *userdata)
-{
-	struct netconfig *netconfig = userdata;
+ipv6_addr:
+	if (l_settings_has_key(active_settings, "IPv6", "Address")) {
+		_auto_(l_rtnl_address_free) struct l_rtnl_address *addr =
+			netconfig_get_static6_address(active_settings);
 
-	switch (event) {
-	case L_DHCP6_CLIENT_EVENT_IP_CHANGED:
-	case L_DHCP6_CLIENT_EVENT_LEASE_OBTAINED:
-	case L_DHCP6_CLIENT_EVENT_LEASE_RENEWED:
-	{
-		const struct l_dhcp6_lease *lease =
-			l_dhcp6_client_get_lease(netconfig->dhcp6_client);
-		_auto_(l_free) char *addr_str =
-			l_dhcp6_lease_get_address(lease);
-		struct l_rtnl_address *address;
-		struct l_icmp6_client *icmp6 =
-			l_dhcp6_client_get_icmp6(netconfig->dhcp6_client);
-		const struct l_icmp6_router *router =
-			l_icmp6_client_get_router(icmp6);
-		char *gateway_str = l_icmp6_router_get_address(router);
-
-		if (l_streq0(netconfig->v6_gateway_str, gateway_str))
-			l_free(gateway_str);
-		else {
-			l_free(netconfig->v6_gateway_str);
-			netconfig->v6_gateway_str = gateway_str;
+		if (unlikely(!addr)) {
+			success = false;
+			goto gateway;
 		}
 
-		address = l_rtnl_address_new(addr_str,
-					l_dhcp6_lease_get_prefix_length(lease));
-		l_rtnl_address_free(netconfig->v6_address);
-		netconfig->v6_address = address;
+		if (!l_netconfig_set_static_addr(netconfig->nc, AF_INET6,
+							addr)) {
+			l_error("netconfig: l_netconfig_set_static_addr("
+				"AF_INET6) failed");
+			success = false;
+			goto gateway;
+		}
 
-		netconfig_dns_list_update(netconfig, AF_INET6);
-		netconfig_domains_update(netconfig, AF_INET6);
-		netconfig_set_dns(netconfig);
-		netconfig_set_domains(netconfig);
-		break;
-	}
-	case L_DHCP6_CLIENT_EVENT_LEASE_EXPIRED:
-		l_debug("Lease for interface %u expired", netconfig->ifindex);
-		netconfig_dns_list_update(netconfig, AF_INET6);
-		netconfig_domains_update(netconfig, AF_INET6);
-		netconfig_set_dns(netconfig);
-		netconfig_set_domains(netconfig);
-		l_rtnl_address_free(netconfig->v6_address);
-		netconfig->v6_address = NULL;
-		l_free(l_steal_ptr(netconfig->v6_gateway_str));
-
-		/* Fall through */
-	case L_DHCP6_CLIENT_EVENT_NO_LEASE:
-		if (!l_dhcp6_client_start(netconfig->dhcp6_client))
-			l_error("netconfig: Failed to re-start DHCPv6 client "
-					"for interface %u", netconfig->ifindex);
-		break;
+		static_ipv6 = true;
 	}
-}
 
-static void netconfig_remove_v4_address(struct netconfig *netconfig)
-{
-	if (!netconfig->v4_address)
-		return;
+gateway:
+	if (!netconfig_load_gateway(netconfig, active_settings,
+					"IPv4", AF_INET))
+		success = false;
 
-	L_WARN_ON(!l_rtnl_ifaddr_delete(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ifaddr_del_cmd_cb,
-					netconfig, NULL));
-	l_rtnl_address_free(netconfig->v4_address);
-	netconfig->v4_address = NULL;
-}
+	if (!netconfig_load_gateway(netconfig, active_settings,
+					"IPv6", AF_INET6))
+		success = false;
 
-static void netconfig_reset_v4(struct netconfig *netconfig)
-{
-	if (netconfig->rtm_protocol) {
-		netconfig_remove_v4_address(netconfig);
-
-		l_strv_free(l_steal_ptr(netconfig->dns4_overrides));
-		l_strv_free(l_steal_ptr(netconfig->dns4_list));
-
-		l_dhcp_client_stop(netconfig->dhcp_client);
-		netconfig->rtm_protocol = 0;
-
-		l_acd_destroy(netconfig->acd);
-		netconfig->acd = NULL;
+	if (!netconfig_load_dns(netconfig, active_settings, "IPv4", AF_INET))
+		success = false;
 
-		l_free(l_steal_ptr(netconfig->v4_gateway_str));
+	if (!netconfig_load_dns(netconfig, active_settings, "IPv6", AF_INET6))
+		success = false;
 
-		l_free(l_steal_ptr(netconfig->v4_domain));
+	if (l_settings_has_key(active_settings, "IPv6", "Enabled") &&
+			!l_settings_get_bool(active_settings, "IPv6", "Enabled",
+						&enable_ipv6)) {
+		l_error("netconfig: Can't load IPv6.Enabled");
+		success = false;
+		goto send_hostname;
 	}
-}
-
-static void netconfig_ipv4_acd_event(enum l_acd_event event, void *user_data)
-{
-	struct netconfig *netconfig = user_data;
 
-	switch (event) {
-	case L_ACD_EVENT_AVAILABLE:
-		L_WARN_ON(!(netconfig->addr4_add_cmd_id =
-				l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ipv4_ifaddr_add_cmd_cb,
-					netconfig, NULL)));
-		return;
-	case L_ACD_EVENT_CONFLICT:
-		/*
-		 * Conflict found, no IP was actually set so just free/unset
-		 * anything we set prior to starting ACD.
-		 */
-		l_error("netconfig: statically configured address conflict!");
-		l_rtnl_address_free(netconfig->v4_address);
-		netconfig->v4_address = NULL;
-		netconfig->rtm_protocol = 0;
-		break;
-	case L_ACD_EVENT_LOST:
-		/*
-		 * Set IP but lost it some time after. Full (IPv4) reset in this
-		 * case.
-		 */
-		l_error("netconfig: statically configured address was lost");
-		netconfig_remove_v4_address(netconfig);
-		break;
+	if (!l_netconfig_set_family_enabled(netconfig->nc, AF_INET,
+						enable_ipv4) ||
+			!l_netconfig_set_family_enabled(netconfig->nc, AF_INET6,
+							enable_ipv6)) {
+		l_error("netconfig: l_netconfig_set_family_enabled() failed");
+		success = false;
 	}
-}
-
-static bool netconfig_ipv4_select_and_install(struct netconfig *netconfig)
-{
-	struct netdev *netdev = netdev_find(netconfig->ifindex);
-	bool set_address = (netconfig->rtm_protocol == RTPROT_STATIC);
-
-	if (netconfig_use_fils_addr(netconfig, AF_INET)) {
-		L_AUTO_FREE_VAR(char *, addr_str) = netconfig_ipv4_to_string(
-					netconfig->fils_override->ipv4_addr);
-		uint8_t prefix_len = netconfig->fils_override->ipv4_prefix_len;
-
-		if (unlikely(!addr_str))
-			return false;
 
-		netconfig->v4_address = l_rtnl_address_new(addr_str,
-								prefix_len);
-		if (L_WARN_ON(!netconfig->v4_address))
-			return false;
-
-		l_rtnl_address_set_noprefixroute(netconfig->v4_address, true);
-		set_address = true;
-
-		/*
-		 * TODO: If netconfig->fils_override->ipv4_lifetime is set,
-		 * start a timeout to renew the address using FILS IP Address
-		 * Assignment or perhaps just start the DHCP client at that
-		 * time.
-		 */
+send_hostname:
+	if (l_settings_has_key(active_settings, "IPv4", "SendHostname") &&
+			!l_settings_get_bool(active_settings, "IPv4",
+						"SendHostname",
+						&send_hostname)) {
+		l_error("netconfig: Can't load [IPv4].SendHostname");
+		success = false;
+		goto mdns;
 	}
 
-	if (set_address) {
-		char ip[INET6_ADDRSTRLEN];
-
-		if (L_WARN_ON(!netconfig->v4_address ||
-					!l_rtnl_address_get_address(
-							netconfig->v4_address,
-							ip)))
-			return false;
-
-		netconfig_dns_list_update(netconfig, AF_INET);
-		netconfig_domains_update(netconfig, AF_INET);
-
-		netconfig->acd = l_acd_new(netconfig->ifindex);
-		l_acd_set_event_handler(netconfig->acd,
-					netconfig_ipv4_acd_event, netconfig,
-					NULL);
-		if (getenv("IWD_ACD_DEBUG"))
-			l_acd_set_debug(netconfig->acd, do_debug,
-					"[ACD] ", NULL);
-
-		if (!l_acd_start(netconfig->acd, ip)) {
-			l_error("failed to start ACD, continuing anyways");
-			l_acd_destroy(netconfig->acd);
-			netconfig->acd = NULL;
-
-			L_WARN_ON(!(netconfig->addr4_add_cmd_id =
-				l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
-					netconfig->v4_address,
-					netconfig_ipv4_ifaddr_add_cmd_cb,
-					netconfig, NULL)));
-		}
-
-		return true;
+	if (send_hostname && gethostname(hostname, sizeof(hostname)) != 0) {
+		/* Warning only */
+		l_warn("netconfig: Unable to get hostname. "
+			"Error %d: %s", errno, strerror(errno));
+		goto mdns;
 	}
 
-	l_dhcp_client_set_address(netconfig->dhcp_client, ARPHRD_ETHER,
-					netdev_get_address(netdev), ETH_ALEN);
-
-	if (l_dhcp_client_start(netconfig->dhcp_client))
-		return true;
-
-	l_error("netconfig: Failed to start DHCPv4 client for interface %u",
-							netconfig->ifindex);
-	return false;
-}
-
-static bool netconfig_ipv6_select_and_install(struct netconfig *netconfig)
-{
-	struct netdev *netdev = netdev_find(netconfig->ifindex);
-
-	if (netconfig->rtm_v6_protocol == RTPROT_UNSPEC) {
-		l_debug("IPV6 configuration disabled");
-		return true;
+	if (send_hostname &&
+			!l_netconfig_set_hostname(netconfig->nc, hostname)) {
+		l_error("netconfig: l_netconfig_set_hostname() failed");
+		success = false;
+		goto mdns;
 	}
 
-	sysfs_write_ipv6_setting(netdev_get_name(netdev), "disable_ipv6", "0");
-
-	if (netconfig_use_fils_addr(netconfig, AF_INET6)) {
-		uint8_t prefix_len = netconfig->fils_override->ipv6_prefix_len;
-		L_AUTO_FREE_VAR(char *, addr_str) = netconfig_ipv6_to_string(
-					netconfig->fils_override->ipv6_addr);
-
-		if (unlikely(!addr_str))
-			return false;
-
-		netconfig->v6_address = l_rtnl_address_new(addr_str,
-								prefix_len);
-		if (L_WARN_ON(unlikely(!netconfig->v6_address)))
-			return false;
-
-		l_rtnl_address_set_noprefixroute(netconfig->v6_address, true);
-
-		/*
-		 * TODO: If netconfig->fils_override->ipv6_lifetime is set,
-		 * start a timeout to renew the address using FILS IP Address
-		 * Assignment or perhaps just start the DHCP client at that
-		 * time.
-		 */
+mdns:
+	if (l_settings_has_key(active_settings, "Network", "MulticastDNS") &&
+			!(mdns = l_settings_get_string(active_settings,
+							"Network",
+							"MulticastDNS"))) {
+		l_error("netconfig: Can't load Network.MulticastDNS");
+		success = false;
 	}
 
-	if (netconfig->v6_address) {
-		netconfig_dns_list_update(netconfig, AF_INET6);
-
-		L_WARN_ON(!(netconfig->addr6_add_cmd_id =
-			l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
-					netconfig->v6_address,
-					netconfig_ipv6_ifaddr_add_cmd_cb,
-					netconfig, NULL)));
-		return true;
+	if (!l_netconfig_check_config(netconfig->nc)) {
+		l_error("netconfig: Invalid configuration");
+		success = false;
 	}
 
-	/* DHCPv6 or RA, update MAC */
-	l_dhcp6_client_set_address(netconfig->dhcp6_client, ARPHRD_ETHER,
-					netdev_get_address(netdev), ETH_ALEN);
-
-	return true;
-}
-
-static int validate_dns_list(int family, char **dns_list)
-{
-	unsigned int n_valid = 0;
-	struct in_addr in_addr;
-	struct in6_addr in6_addr;
-	char **p;
-
-	for (p = dns_list; *p; p++) {
-		int r;
-
-		if (family == AF_INET)
-			r = inet_pton(AF_INET, *p, &in_addr);
-		else if (family == AF_INET6)
-			r = inet_pton(AF_INET6, *p, &in6_addr);
-		else
-			r = -EAFNOSUPPORT;
-
-		if (r > 0) {
-			n_valid += 1;
-			continue;
-		}
-
-		l_error("netconfig: Invalid DNS address '%s'.", *p);
-		return -EINVAL;
+	if (success) {
+		netconfig->active_settings = active_settings;
+		netconfig->static_config[INDEX_FOR_AF(AF_INET)] = static_ipv4;
+		netconfig->static_config[INDEX_FOR_AF(AF_INET6)] = static_ipv6;
+		netconfig->enabled[INDEX_FOR_AF(AF_INET)] = enable_ipv4;
+		netconfig->enabled[INDEX_FOR_AF(AF_INET6)] = enable_ipv6;
+		netconfig->mdns = l_steal_ptr(mdns);
+		return true;
 	}
 
-	return n_valid;
+	l_netconfig_reset_config(netconfig->nc);
+	return false;
 }
 
-bool netconfig_load_settings(struct netconfig *netconfig,
-				const struct l_settings *active_settings)
+static bool netconfig_load_fils_settings(struct netconfig *netconfig,
+						uint8_t af)
 {
-	_auto_(l_free) char *mdns = NULL;
-	bool send_hostname;
-	bool v6_enabled;
-	char hostname[HOST_NAME_MAX + 1];
-	_auto_(l_strv_free) char **dns4_overrides = NULL;
-	_auto_(l_strv_free) char **dns6_overrides = NULL;
-	_auto_(l_rtnl_address_free) struct l_rtnl_address *v4_address = NULL;
-	_auto_(l_rtnl_address_free) struct l_rtnl_address *v6_address = NULL;
-
-	dns4_overrides = l_settings_get_string_list(active_settings,
-							"IPv4", "DNS", ' ');
-	if (dns4_overrides) {
-		int r = validate_dns_list(AF_INET, dns4_overrides);
-
-		if (unlikely(r <= 0)) {
-			l_strfreev(dns4_overrides);
-			dns4_overrides = NULL;
-
-			if (r < 0)
-				return false;
-		}
-
-		if (r == 0)
-			l_error("netconfig: Empty IPv4.DNS entry, skipping...");
-	}
+	struct ie_fils_ip_addr_response_info *fils = netconfig->fils_override;
+	char addr_str[INET6_ADDRSTRLEN];
+	char gw_addr_str[INET6_ADDRSTRLEN];
+	char dns_addr_str[INET6_ADDRSTRLEN];
+	_auto_(l_rtnl_address_free) struct l_rtnl_address *rtnl_addr = NULL;
+	bool is_zero = false;
+	uint8_t prefix_len;
 
-	dns6_overrides = l_settings_get_string_list(active_settings,
-							"IPv6", "DNS", ' ');
+	if (!netconfig_addr_to_str(af, &fils->ipv4_addr, &fils->ipv6_addr,
+					addr_str, &is_zero) || is_zero)
+		return is_zero;
 
-	if (dns6_overrides) {
-		int r = validate_dns_list(AF_INET6, dns6_overrides);
+	prefix_len = (af == AF_INET ? fils->ipv4_prefix_len :
+			fils->ipv6_prefix_len);
 
-		if (unlikely(r <= 0)) {
-			l_strfreev(dns6_overrides);
-			dns6_overrides = NULL;
+	if (L_WARN_ON(!(rtnl_addr = l_rtnl_address_new(addr_str, prefix_len))))
+		return false;
 
-			if (r < 0)
-				return false;
-		}
+	if (L_WARN_ON(!l_netconfig_set_static_addr(netconfig->nc, af,
+							rtnl_addr)))
+		return false;
 
-		if (r == 0)
-			l_error("netconfig: Empty IPv6.DNS entry, skipping...");
-	}
+	/*
+	 * Done with local address, move on to gateway and DNS.
+	 *
+	 * Since load_settings is called early, generally before the actual
+	 * connection setup starts, and load_fils_settings is called after
+	 * 802.11 Authentication & Association, we need to check if either
+	 * the gateway or DNS settings were overridden in load_settings so
+	 * as not to overwrite the user-provided values.  Values received
+	 * with FILS are expected to have the same weight as those from
+	 * DHCP/SLAAC.
+	 *
+	 * TODO: If netconfig->fils_override->ipv{4,6}_lifetime is set,
+	 * start a timeout to renew the address using FILS IP Address
+	 * Assignment or perhaps just start the DHCP client after that
+	 * time.
+	 *
+	 * TODO: validate gateway and/or DNS on local subnet, link-local,
+	 * etc.?
+	 */
 
-	if (!l_settings_get_bool(active_settings,
-					"IPv4", "SendHostname", &send_hostname))
-		send_hostname = false;
+	if (!netconfig_addr_to_str(af, &fils->ipv4_gateway, &fils->ipv6_gateway,
+					gw_addr_str, &is_zero))
+		return false;
 
-	if (send_hostname) {
-		if (gethostname(hostname, sizeof(hostname)) != 0) {
-			l_warn("netconfig: Unable to get hostname. "
-					"Error %d: %s", errno, strerror(errno));
-			send_hostname = false;
-		}
-	}
+	if (!netconfig->gateway_overridden[INDEX_FOR_AF(af)] && !is_zero &&
+			L_WARN_ON(!l_netconfig_set_gateway_override(
+								netconfig->nc,
+								af,
+								gw_addr_str)))
+		return false;
 
-	mdns = l_settings_get_string(active_settings,
-					"Network", "MulticastDNS");
+	if (!netconfig_addr_to_str(af, &fils->ipv4_dns, &fils->ipv6_dns,
+					dns_addr_str, &is_zero))
+		return is_zero;
 
-	if (l_settings_has_key(active_settings, "IPv4", "Address")) {
-		v4_address = netconfig_get_static4_address(active_settings);
+	if (!netconfig->dns_overridden[INDEX_FOR_AF(af)] && !is_zero) {
+		char *dns_list[2] = { dns_addr_str, NULL };
 
-		if (unlikely(!v4_address)) {
-			l_error("netconfig: Can't parse IPv4 address");
+		if (L_WARN_ON(!l_netconfig_set_dns_override(netconfig->nc,
+								af, dns_list)))
 			return false;
-		}
 	}
 
-	if (!l_settings_get_bool(active_settings, "IPv6",
-					"Enabled", &v6_enabled))
-		v6_enabled = ipv6_enabled;
-
-	if (l_settings_has_key(active_settings, "IPv6", "Address")) {
-		v6_address = netconfig_get_static6_address(active_settings);
-
-		if (unlikely(!v6_address)) {
-			l_error("netconfig: Can't parse IPv6 address");
-			return false;
-		}
-	}
-
-	/* No more validation steps for now, commit new values */
-	netconfig->rtm_protocol = v4_address ? RTPROT_STATIC : RTPROT_DHCP;
-
-	if (!v6_enabled)
-		netconfig->rtm_v6_protocol = RTPROT_UNSPEC;
-	else if (v6_address)
-		netconfig->rtm_v6_protocol = RTPROT_STATIC;
-	else
-		netconfig->rtm_v6_protocol = RTPROT_DHCP;
-
-	if (send_hostname)
-		l_dhcp_client_set_hostname(netconfig->dhcp_client, hostname);
-
-	netconfig_free_settings(netconfig);
-
-	if (netconfig->rtm_protocol == RTPROT_STATIC)
-		netconfig->v4_address = l_steal_ptr(v4_address);
-
-	if (netconfig->rtm_v6_protocol == RTPROT_STATIC)
-		netconfig->v6_address = l_steal_ptr(v6_address);
-
-	netconfig->active_settings = active_settings;
-	netconfig->dns4_overrides = l_steal_ptr(dns4_overrides);
-	netconfig->dns6_overrides = l_steal_ptr(dns6_overrides);
-	netconfig->mdns = l_steal_ptr(mdns);
 	return true;
 }
 
@@ -1489,10 +690,15 @@ bool netconfig_configure(struct netconfig *netconfig,
 	netconfig->notify = notify;
 	netconfig->user_data = user_data;
 
-	if (unlikely(!netconfig_ipv4_select_and_install(netconfig)))
+	if (netconfig_use_fils_addr(netconfig, AF_INET) &&
+			!netconfig_load_fils_settings(netconfig, AF_INET))
 		return false;
 
-	if (unlikely(!netconfig_ipv6_select_and_install(netconfig)))
+	if (netconfig_use_fils_addr(netconfig, AF_INET6) &&
+			!netconfig_load_fils_settings(netconfig, AF_INET6))
+		return false;
+
+	if (unlikely(!l_netconfig_start(netconfig->nc)))
 		return false;
 
 	resolve_set_mdns(netconfig->resolve, netconfig->mdns);
@@ -1509,14 +715,23 @@ bool netconfig_reconfigure(struct netconfig *netconfig, bool set_arp_gw)
 	 * lost or delayed.  Try to force the gateway into the ARP cache
 	 * to alleviate this
 	 */
-	if (set_arp_gw)
-		netconfig_gateway_to_arp(netconfig);
+	if (set_arp_gw) {
+		netconfig_dhcp_gateway_to_arp(netconfig);
 
-	if (netconfig->rtm_protocol == RTPROT_DHCP) {
+		if (netconfig->connected[INDEX_FOR_AF(AF_INET)] &&
+				netconfig_use_fils_addr(netconfig, AF_INET))
+			netconfig_commit_fils_macs(netconfig, AF_INET);
+
+		if (netconfig->connected[INDEX_FOR_AF(AF_INET6)] &&
+				netconfig_use_fils_addr(netconfig, AF_INET6))
+			netconfig_commit_fils_macs(netconfig, AF_INET6);
+	}
+
+	if (!netconfig->static_config[INDEX_FOR_AF(AF_INET)]) {
 		/* TODO l_dhcp_client sending a DHCP inform request */
 	}
 
-	if (netconfig->rtm_v6_protocol == RTPROT_DHCP) {
+	if (!netconfig->static_config[INDEX_FOR_AF(AF_INET6)]) {
 		/* TODO l_dhcp_v6_client sending a DHCP inform request */
 	}
 
@@ -1525,64 +740,27 @@ bool netconfig_reconfigure(struct netconfig *netconfig, bool set_arp_gw)
 
 bool netconfig_reset(struct netconfig *netconfig)
 {
-	struct netdev *netdev = netdev_find(netconfig->ifindex);
-
-	if (netconfig->route4_add_gateway_cmd_id) {
-		l_netlink_cancel(rtnl, netconfig->route4_add_gateway_cmd_id);
-		netconfig->route4_add_gateway_cmd_id = 0;
-	}
+	l_netconfig_stop(netconfig->nc);
 
-	if (netconfig->route6_add_cmd_id) {
-		l_netlink_cancel(rtnl, netconfig->route6_add_cmd_id);
-		netconfig->route6_add_cmd_id = 0;
-	}
-
-	if (netconfig->addr4_add_cmd_id) {
-		l_netlink_cancel(rtnl, netconfig->addr4_add_cmd_id);
-		netconfig->addr4_add_cmd_id = 0;
-	}
-
-	if (netconfig->addr6_add_cmd_id) {
-		l_netlink_cancel(rtnl, netconfig->addr6_add_cmd_id);
-		netconfig->addr6_add_cmd_id = 0;
-	}
-
-	if (netconfig->rtm_protocol || netconfig->rtm_v6_protocol)
-		resolve_revert(netconfig->resolve);
-
-	netconfig_reset_v4(netconfig);
-
-	if (netconfig->rtm_v6_protocol) {
-		l_rtnl_address_free(netconfig->v6_address);
-		netconfig->v6_address = NULL;
-
-		l_strv_free(l_steal_ptr(netconfig->dns6_overrides));
-		l_strv_free(l_steal_ptr(netconfig->dns6_list));
-
-		l_dhcp6_client_stop(netconfig->dhcp6_client);
-		netconfig->rtm_v6_protocol = 0;
-
-		sysfs_write_ipv6_setting(netdev_get_name(netdev),
-						"disable_ipv6", "1");
-
-		l_free(l_steal_ptr(netconfig->v6_gateway_str));
-
-		l_strv_free(l_steal_ptr(netconfig->v6_domains));
-	}
+	resolve_revert(netconfig->resolve);
 
-	l_free(l_steal_ptr(netconfig->fils_override));
+	netconfig->connected[0] = false;
+	netconfig->connected[1] = false;
 
+	netconfig_free_settings(netconfig);
 	return true;
 }
 
 char *netconfig_get_dhcp_server_ipv4(struct netconfig *netconfig)
 {
+	struct l_dhcp_client *client =
+		l_netconfig_get_dhcp_client(netconfig->nc);
 	const struct l_dhcp_lease *lease;
 
-	if (!netconfig->dhcp_client)
+	if (!client)
 		return NULL;
 
-	lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
+	lease = l_dhcp_client_get_lease(client);
 	if (!lease)
 		return NULL;
 
@@ -1598,15 +776,16 @@ bool netconfig_get_fils_ip_req(struct netconfig *netconfig,
 	 * configuration (usually DHCP).  If we're configured with static
 	 * values return false to mean the IE should not be sent.
 	 */
-	if (netconfig->rtm_protocol != RTPROT_DHCP &&
-			netconfig->rtm_v6_protocol != RTPROT_DHCP)
+	if (netconfig->static_config[0] && netconfig->static_config[1])
 		return false;
 
 	memset(info, 0, sizeof(*info));
-	info->ipv4 = (netconfig->rtm_protocol == RTPROT_DHCP);
-	info->ipv6 = (netconfig->rtm_v6_protocol == RTPROT_DHCP);
-	info->dns = (info->ipv4 && !netconfig->dns4_overrides) ||
-		(info->ipv6 && !netconfig->dns6_overrides);
+	info->ipv4 = !netconfig->static_config[INDEX_FOR_AF(AF_INET)];
+	info->ipv6 = !netconfig->static_config[INDEX_FOR_AF(AF_INET6)];
+	info->dns = (info->ipv4 &&
+			!netconfig->dns_overridden[INDEX_FOR_AF(AF_INET)]) ||
+		(info->ipv6 &&
+			!netconfig->dns_overridden[INDEX_FOR_AF(AF_INET)]);
 
 	return true;
 }
@@ -1622,28 +801,17 @@ struct netconfig *netconfig_new(uint32_t ifindex)
 {
 	struct netdev *netdev = netdev_find(ifindex);
 	struct netconfig *netconfig;
-	struct l_icmp6_client *icmp6;
 	const char *debug_level = NULL;
 	int dhcp_priority = L_LOG_INFO;
+	struct l_dhcp6_client *dhcp6;
 
-	if (!netconfig_list)
-		return NULL;
-
-	l_debug("Starting netconfig for interface: %d", ifindex);
-
-	netconfig = netconfig_find(ifindex);
-	if (netconfig)
-		return netconfig;
+	l_debug("Creating netconfig for interface: %d", ifindex);
 
 	netconfig = l_new(struct netconfig, 1);
-	netconfig->ifindex = ifindex;
+	netconfig->nc = l_netconfig_new(ifindex);
+	netconfig->netdev = netdev;
 	netconfig->resolve = resolve_new(ifindex);
 
-	netconfig->dhcp_client = l_dhcp_client_new(ifindex);
-	l_dhcp_client_set_event_handler(netconfig->dhcp_client,
-					netconfig_ipv4_dhcp_event_handler,
-					netconfig, NULL);
-
 	debug_level = getenv("IWD_DHCP_DEBUG");
 	if (debug_level != NULL) {
 		if (!strcmp("debug", debug_level))
@@ -1658,42 +826,28 @@ struct netconfig *netconfig_new(uint32_t ifindex)
 			dhcp_priority = L_LOG_DEBUG;
 	}
 
-	l_dhcp_client_set_debug(netconfig->dhcp_client, do_debug,
-					"[DHCPv4] ", NULL, dhcp_priority);
-
-	netconfig->dhcp6_client = l_dhcp6_client_new(ifindex);
-	l_dhcp6_client_set_event_handler(netconfig->dhcp6_client,
-						netconfig_dhcp6_event_handler,
-						netconfig, NULL);
-	l_dhcp6_client_set_lla_randomized(netconfig->dhcp6_client, true);
-	l_dhcp6_client_set_nodelay(netconfig->dhcp6_client, true);
-	l_dhcp6_client_set_rtnl(netconfig->dhcp6_client, rtnl);
+	l_netconfig_set_event_handler(netconfig->nc, netconfig_event_handler,
+					netconfig, NULL);
 
-	if (getenv("IWD_DHCP_DEBUG"))
-		l_dhcp6_client_set_debug(netconfig->dhcp6_client, do_debug,
-							"[DHCPv6] ", NULL);
+	l_dhcp_client_set_debug(l_netconfig_get_dhcp_client(netconfig->nc),
+				do_debug, "[DHCPv4] ", NULL, dhcp_priority);
 
-	icmp6 = l_dhcp6_client_get_icmp6(netconfig->dhcp6_client);
-	l_icmp6_client_set_rtnl(icmp6, rtnl);
-	l_icmp6_client_set_route_priority(icmp6, ROUTE_PRIORITY_OFFSET);
+	dhcp6 = l_netconfig_get_dhcp6_client(netconfig->nc);
+	l_dhcp6_client_set_lla_randomized(dhcp6, true);
+	l_dhcp6_client_set_nodelay(dhcp6, true);
 
-	l_queue_push_tail(netconfig_list, netconfig);
+	if (debug_level)
+		l_dhcp6_client_set_debug(dhcp6, do_debug, "[DHCPv6] ", NULL);
 
-	sysfs_write_ipv6_setting(netdev_get_name(netdev), "accept_ra", "0");
-	sysfs_write_ipv6_setting(netdev_get_name(netdev), "disable_ipv6", "1");
+	l_netconfig_set_route_priority(netconfig->nc, ROUTE_PRIORITY_OFFSET);
 
 	return netconfig;
 }
 
 void netconfig_destroy(struct netconfig *netconfig)
 {
-	if (!netconfig_list)
-		return;
-
 	l_debug("");
 
-	l_queue_remove(netconfig_list, netconfig);
-
 	netconfig_reset(netconfig);
 	resolve_free(netconfig->resolve);
 	netconfig_free(netconfig);
@@ -1710,43 +864,8 @@ bool netconfig_enabled(void)
 
 static int netconfig_init(void)
 {
-	uint32_t r;
-
-	if (netconfig_list)
-		return -EALREADY;
-
 	rtnl = iwd_get_rtnl();
 
-	r = l_netlink_register(rtnl, RTNLGRP_IPV4_IFADDR,
-					netconfig_ifaddr_notify, NULL, NULL);
-	if (!r) {
-		l_error("netconfig: Failed to register for RTNL link address"
-							" notifications.");
-		goto error;
-	}
-
-	r = l_rtnl_ifaddr4_dump(rtnl, netconfig_ifaddr_cmd_cb, NULL, NULL);
-	if (!r) {
-		l_error("netconfig: Failed to get addresses from RTNL link.");
-		goto error;
-	}
-
-	r = l_netlink_register(rtnl, RTNLGRP_IPV6_IFADDR,
-				netconfig_ifaddr_ipv6_notify, NULL, NULL);
-	if (!r) {
-		l_error("netconfig: Failed to register for RTNL link IPv6 "
-					"address notifications.");
-		goto error;
-	}
-
-	r = l_rtnl_ifaddr6_dump(rtnl, netconfig_ifaddr_ipv6_cmd_cb, NULL,
-									NULL);
-	if (!r) {
-		l_error("netconfig: Failed to get IPv6 addresses from RTNL"
-								" link.");
-		goto error;
-	}
-
 	if (!l_settings_get_uint(iwd_get_config(), "Network",
 							"RoutePriorityOffset",
 							&ROUTE_PRIORITY_OFFSET))
@@ -1757,24 +876,12 @@ static int netconfig_init(void)
 					&ipv6_enabled))
 		ipv6_enabled = false;
 
-	netconfig_list = l_queue_new();
-
 	return 0;
-
-error:
-	rtnl = NULL;
-
-	return r;
 }
 
 static void netconfig_exit(void)
 {
-	if (!netconfig_list)
-		return;
-
 	rtnl = NULL;
-
-	l_queue_destroy(netconfig_list, netconfig_free);
 }
 
 IWD_MODULE(netconfig, netconfig_init, netconfig_exit)
diff --git a/src/netconfig.h b/src/netconfig.h
index c9ac6f8f..b42e9cc8 100644
--- a/src/netconfig.h
+++ b/src/netconfig.h
@@ -26,6 +26,7 @@ struct ie_fils_ip_addr_response_info;
 
 enum netconfig_event {
 	NETCONFIG_EVENT_CONNECTED,
+	NETCONFIG_EVENT_FAILED,
 };
 
 typedef void (*netconfig_notify_func_t)(enum netconfig_event event,
diff --git a/src/station.c b/src/station.c
index e5972269..e02b68f8 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2011,6 +2011,73 @@ delayed_retry:
 	station_roam_retry(station);
 }
 
+static void station_connect_failed(struct station *station, int error,
+					bool during_eapol)
+{
+	bool continue_autoconnect;
+
+	if (station->connect_pending) {
+		struct l_dbus_message *reply;
+
+		if (error == -ECANCELED)
+			reply = dbus_error_aborted(station->connect_pending);
+		else
+			reply = dbus_error_failed(station->connect_pending);
+
+		dbus_pending_reply(&station->connect_pending, reply);
+	}
+
+	if (error == -ECANCELED)
+		return;
+
+	continue_autoconnect = station->state == STATION_STATE_CONNECTING_AUTO;
+
+	if (station->state == STATION_STATE_CONNECTING)
+		network_connect_failed(station->connected_network,
+								during_eapol);
+
+	station_reset_connection_state(station);
+	station_enter_state(station, STATION_STATE_DISCONNECTED);
+
+	if (continue_autoconnect) {
+		if (station_autoconnect_next(station) < 0) {
+			l_debug("Nothing left on autoconnect list");
+			station_enter_state(station,
+					STATION_STATE_AUTOCONNECT_FULL);
+		}
+
+		return;
+	}
+
+	if (station->autoconnect)
+		station_enter_state(station, STATION_STATE_AUTOCONNECT_QUICK);
+}
+
+static void station_disconnect_on_error_cb(struct netdev *netdev, bool success,
+					void *user_data)
+{
+	struct station *station = user_data;
+	bool continue_autoconnect;
+
+	station_enter_state(station, STATION_STATE_DISCONNECTED);
+
+	continue_autoconnect = station->state == STATION_STATE_CONNECTING_AUTO;
+
+	if (continue_autoconnect) {
+		if (station_autoconnect_next(station) < 0) {
+			l_debug("Nothing left on autoconnect list");
+			station_enter_state(station,
+					STATION_STATE_AUTOCONNECT_FULL);
+		}
+
+		return;
+	}
+
+	if (station->autoconnect)
+		station_enter_state(station, STATION_STATE_AUTOCONNECT_QUICK);
+}
+
+
 static void station_netconfig_event_handler(enum netconfig_event event,
 							void *user_data)
 {
@@ -2019,7 +2086,32 @@ static void station_netconfig_event_handler(enum netconfig_event event,
 	switch (event) {
 	case NETCONFIG_EVENT_CONNECTED:
 		station_enter_state(station, STATION_STATE_CONNECTED);
+		break;
+	case NETCONFIG_EVENT_FAILED:
+		if (station->connect_pending) {
+			struct l_dbus_message *reply = dbus_error_failed(
+						station->connect_pending);
 
+			dbus_pending_reply(&station->connect_pending, reply);
+		}
+
+		if (L_IN_SET(station->state, STATION_STATE_CONNECTING,
+				STATION_STATE_CONNECTING_AUTO))
+			network_connect_failed(station->connected_network,
+						false);
+
+		/*
+		 * TODO: if in STATION_STATE_CONNECTING_AUTO, continue with
+		 * the previous autoconnect list after disconnect completes.
+		 */
+
+		netdev_disconnect(station->netdev,
+					station_disconnect_on_error_cb,
+					station);
+
+		station_reset_connection_state(station);
+
+		station_enter_state(station, STATION_STATE_DISCONNECTING);
 		break;
 	default:
 		l_error("station: Unsupported netconfig event: %d.", event);
@@ -2899,7 +2991,6 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
 					void *event_data, void *user_data)
 {
 	struct station *station = user_data;
-	bool continue_autoconnect;
 
 	l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
 
@@ -2925,43 +3016,10 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
 		break;
 	}
 
-	if (station->connect_pending) {
-		struct l_dbus_message *reply;
-
-		if (result == NETDEV_RESULT_ABORTED)
-			reply = dbus_error_aborted(station->connect_pending);
-		else
-			reply = dbus_error_failed(station->connect_pending);
-
-		dbus_pending_reply(&station->connect_pending, reply);
-	}
-
-	if (result == NETDEV_RESULT_ABORTED)
-		return;
-
-	continue_autoconnect = station->state == STATION_STATE_CONNECTING_AUTO;
-
-	if (station->state == STATION_STATE_CONNECTING) {
-		bool during_eapol = result == NETDEV_RESULT_HANDSHAKE_FAILED;
-		network_connect_failed(station->connected_network,
-								during_eapol);
-	}
-
-	station_reset_connection_state(station);
-	station_enter_state(station, STATION_STATE_DISCONNECTED);
-
-	if (continue_autoconnect) {
-		if (station_autoconnect_next(station) < 0) {
-			l_debug("Nothing left on autoconnect list");
-			station_enter_state(station,
-					STATION_STATE_AUTOCONNECT_FULL);
-		}
-
-		return;
-	}
-
-	if (station->autoconnect)
-		station_enter_state(station, STATION_STATE_AUTOCONNECT_QUICK);
+	station_connect_failed(station,
+				result == NETDEV_RESULT_ABORTED ?
+					-ECANCELED : -EIO,
+				result == NETDEV_RESULT_HANDSHAKE_FAILED);
 }
 
 static void station_disconnect_event(struct station *station, void *event_data)
-- 
2.34.1


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

* [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (11 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 13/15][RFC] netconfig: Switch to l_netconfig Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-21 21:04   ` James Prestwood
  2022-06-16  0:02 ` [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs Andrew Zaborowski
  2022-06-17 19:04 ` [PATCH 01/15] netconfig: Fix address format validation Denis Kenzior
  14 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

---
 autotests/testNetconfig/connection_test.py | 30 +++++++++++++++++++++-
 autotests/testNetconfig/mismatch.psk       |  7 +++++
 2 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 autotests/testNetconfig/mismatch.psk

diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py
index 4b05c745..837b19ec 100644
--- a/autotests/testNetconfig/connection_test.py
+++ b/autotests/testNetconfig/connection_test.py
@@ -92,6 +92,35 @@ class Test(unittest.TestCase):
 
         wd.unregister_psk_agent(psk_agent)
 
+    def test_addr_type_error(self):
+        IWD.copy_to_storage('mismatch.psk', name='ssidTKIP.psk')
+        wd = IWD(True)
+
+        psk_agent = PSKAgent("secret123")
+        wd.register_psk_agent(psk_agent)
+
+        devices = wd.list_devices(1)
+        device = devices[0]
+
+        ordered_network = device.get_ordered_network('ssidTKIP')
+
+        self.assertEqual(ordered_network.type, NetworkType.psk)
+
+        condition = 'not obj.connected'
+        wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+        # Ideally we want to check that this errors out at an early phase, especially
+        # before any radio operations, due to netconfig setting validation, but this is
+        # tricky to ensure.  Also ideally this would happen even before asking the user
+        # for the passphrase which is easier to ensure in the tests, but currently IWD
+        # asks for the PSK before control reaches station.c.
+        self.assertRaises(iwd.InvalidArgumentsEx, ordered_network.network_object.connect)
+
+        wd.unregister_psk_agent(psk_agent)
+
+    def tearDown(self):
+        IWD.clear_storage()
+
     @classmethod
     def setUpClass(cls):
         def remove_lease4():
@@ -147,7 +176,6 @@ class Test(unittest.TestCase):
 
     @classmethod
     def tearDownClass(cls):
-        IWD.clear_storage()
         ctx.stop_process(cls.dhcpd_pid)
         cls.dhcpd_pid = None
         ctx.stop_process(cls.dhcpd6_pid)
diff --git a/autotests/testNetconfig/mismatch.psk b/autotests/testNetconfig/mismatch.psk
new file mode 100644
index 00000000..f8920b7c
--- /dev/null
+++ b/autotests/testNetconfig/mismatch.psk
@@ -0,0 +1,7 @@
+[IPv6]
+# IPv4 addresses where IPv6 are expected
+Address=192.168.1.10
+Gateway=192.168.1.1
+
+[Settings]
+AutoConnect=false
-- 
2.34.1


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

* [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (12 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 14/15] autotests: Add bad netconfig settings scenario Andrew Zaborowski
@ 2022-06-16  0:02 ` Andrew Zaborowski
  2022-06-22 23:36   ` Denis Kenzior
  2022-06-27 18:12   ` Denis Kenzior
  2022-06-17 19:04 ` [PATCH 01/15] netconfig: Fix address format validation Denis Kenzior
  14 siblings, 2 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-16  0:02 UTC (permalink / raw)
  To: iwd

Verify that IWD with NetworkConfigurationEnabled creates the off-link
routes for Route Information Options in the Router Advertisements.
Sending this last because these options are not supported by current
netconfig code and the test should fail without the l_netconfig patch.
---
 autotests/testNetconfig/connection_test.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/autotests/testNetconfig/connection_test.py b/autotests/testNetconfig/connection_test.py
index 837b19ec..9c1d2efa 100644
--- a/autotests/testNetconfig/connection_test.py
+++ b/autotests/testNetconfig/connection_test.py
@@ -72,6 +72,15 @@ class Test(unittest.TestCase):
                 # On-link prefix
                 testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:100::'), plen=72,
                     flags=1, ifname=ifname),
+                # Router for an off-link prefix, medium preference
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:300::'), plen=64,
+                    gw=router_ll_addr, flags=3, ifname=ifname),
+                # Router for an off-link prefix, high preference
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:400::'), plen=65,
+                    gw=router_ll_addr, flags=3, ifname=ifname),
+                # Router for an off-link prefix, low preference
+                testutil.RouteInfo(dst=socket.inet_pton(socket.AF_INET6, '3ffe:501:ffff:500::'), plen=66,
+                    gw=router_ll_addr, flags=3, ifname=ifname)
             }
         self.maxDiff = None
         self.assertEqual(expected_routes4, set(testutil.get_routes4(ifname)))
@@ -166,6 +175,9 @@ class Test(unittest.TestCase):
             AdvSendAdvert on;
             AdvManagedFlag on;
             prefix 3ffe:501:ffff:100::/72 { AdvAutonomous off; };
+            route 3ffe:501:ffff:300::/64 {};
+            route 3ffe:501:ffff:400::/65 { AdvRoutePreference low; };
+            route 3ffe:501:ffff:500::/66 { AdvRoutePreference high; };
             };''')
         config.close()
         cls.radvd_pid = ctx.start_process(['radvd', '-n', '-d5', '-p', '/tmp/radvd.pid', '-C', '/tmp/radvd.conf'])
-- 
2.34.1


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

* Re: [PATCH 01/15] netconfig: Fix address format validation
  2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
                   ` (13 preceding siblings ...)
  2022-06-16  0:02 ` [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs Andrew Zaborowski
@ 2022-06-17 19:04 ` Denis Kenzior
  14 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:04 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Drop the wrong negation in the error check.  Check that there are no extra
> characters after prefix length suffix.  Reset errno 0 before the strtoul
> call, as recommended by the manpage.
> ---
>   src/netconfig.c | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
> 

Applied, thanks.

Regards,
-Denis


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

* Re: [PATCH 02/15] storage: Log a message on network file parse errors
  2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
@ 2022-06-17 19:09   ` Denis Kenzior
  2022-06-17 19:09   ` Denis Kenzior
  1 sibling, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:09 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Most users of storage_network_open don't log errors when the function
> returns a NULL and fall back to defaults (empty l_settings).
> storage_network_open() itself only logs errors if the flie is encrypted.
> Now also log an error when l_settings_load_from_file() fails to help track
> down potential syntax errors.
> ---
>   src/storage.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 

Hmm, I'm still not sure logging l_errors inside storage.c is the right approach. 
  I see this crept in when encrypted profiles were merged.

Anyway, I went ahead and applied this, but I wonder if we need to fix this to 
return proper error codes at some point.

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 02/15] storage: Log a message on network file parse errors
  2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
  2022-06-17 19:09   ` Denis Kenzior
@ 2022-06-17 19:09   ` Denis Kenzior
  1 sibling, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:09 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Most users of storage_network_open don't log errors when the function
> returns a NULL and fall back to defaults (empty l_settings).
> storage_network_open() itself only logs errors if the flie is encrypted.
> Now also log an error when l_settings_load_from_file() fails to help track
> down potential syntax errors.
> ---
>   src/storage.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 

Hmm, I'm still not sure logging l_errors inside storage.c is the right approach. 
  I see this crept in when encrypted profiles were merged.

Anyway, I went ahead and applied this, but I wonder if we need to fix this to 
return proper error codes at some point.

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 03/15] station: Move netconfig_reset() to common path
  2022-06-16  0:02 ` [PATCH 03/15] station: Move netconfig_reset() to common path Andrew Zaborowski
@ 2022-06-17 19:11   ` Denis Kenzior
  0 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:11 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> To avoid repetition, call netconfig_reset in
> station_reset_connection_state.
> ---
>   src/station.c | 12 +++---------
>   1 file changed, 3 insertions(+), 9 deletions(-)
> 

Applied, thanks.

Regards,
-Denis


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

* Re: [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available
  2022-06-16  0:02 ` [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available Andrew Zaborowski
@ 2022-06-17 19:13   ` Denis Kenzior
  0 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:13 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> ---
>   monitor/nlmon.c | 55 ++++++++++++++++++++++++++++++++++---------------
>   1 file changed, 38 insertions(+), 17 deletions(-)
> 
> diff --git a/monitor/nlmon.c b/monitor/nlmon.c
> index 34c5eed6..ff23140d 100644
> --- a/monitor/nlmon.c
> +++ b/monitor/nlmon.c
> @@ -50,6 +50,7 @@
>   #include "linux/nl80211.h"
>   
>   #include "ell/useful.h"
> +#include "ell/netlink-private.h"

I'm pretty sure this breaks shared-ell builds as netlink-private.h is not an 
installed header.

You'd need to expose this function into public API.

Regards,
-Denis

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

* Re: [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value
  2022-06-16  0:02 ` [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value Andrew Zaborowski
@ 2022-06-17 19:15   ` Denis Kenzior
  0 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-17 19:15 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> ---
>   tools/utils.py | 3 +++
>   1 file changed, 3 insertions(+)
> 

Patch 6-7 applied, thanks.

Regards,
-Denis


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

* Re: [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-16  0:02 ` [PATCH 14/15] autotests: Add bad netconfig settings scenario Andrew Zaborowski
@ 2022-06-21 21:04   ` James Prestwood
  2022-06-21 21:17     ` Andrew Zaborowski
  0 siblings, 1 reply; 30+ messages in thread
From: James Prestwood @ 2022-06-21 21:04 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On Thu, 2022-06-16 at 02:02 +0200, Andrew Zaborowski wrote:
> ---
>  autotests/testNetconfig/connection_test.py | 30 +++++++++++++++++++++-
>  autotests/testNetconfig/mismatch.psk       |  7 +++++
>  2 files changed, 36 insertions(+), 1 deletion(-)
>  create mode 100644 autotests/testNetconfig/mismatch.psk
> 
> diff --git a/autotests/testNetconfig/connection_test.py
> b/autotests/testNetconfig/connection_test.py
> index 4b05c745..837b19ec 100644
> --- a/autotests/testNetconfig/connection_test.py
> +++ b/autotests/testNetconfig/connection_test.py
> @@ -92,6 +92,35 @@ class Test(unittest.TestCase):
>  
>          wd.unregister_psk_agent(psk_agent)
>  
> +    def test_addr_type_error(self):
> +        IWD.copy_to_storage('mismatch.psk', name='ssidTKIP.psk')
> +        wd = IWD(True)
> +
> +        psk_agent = PSKAgent("secret123")
> +        wd.register_psk_agent(psk_agent)
> +
> +        devices = wd.list_devices(1)
> +        device = devices[0]
> +
> +        ordered_network = device.get_ordered_network('ssidTKIP')
> +
> +        self.assertEqual(ordered_network.type, NetworkType.psk)
> +
> +        condition = 'not obj.connected'
> +        wd.wait_for_object_condition(ordered_network.network_object,
> condition)
> +
> +        # Ideally we want to check that this errors out at an early
> phase, especially
> +        # before any radio operations, due to netconfig setting
> validation, but this is
> +        # tricky to ensure.  Also ideally this would happen even
> before asking the user
> +        # for the passphrase which is easier to ensure in the tests,
> but currently IWD
> +        # asks for the PSK before control reaches station.c.
> +        self.assertRaises(iwd.InvalidArgumentsEx,
> ordered_network.network_object.connect)
> +
> +        wd.unregister_psk_agent(psk_agent)
> +
> +    def tearDown(self):
> +        IWD.clear_storage()
> +
>      @classmethod
>      def setUpClass(cls):
>          def remove_lease4():
> @@ -147,7 +176,6 @@ class Test(unittest.TestCase):
>  
>      @classmethod
>      def tearDownClass(cls):
> -        IWD.clear_storage()
>          ctx.stop_process(cls.dhcpd_pid)
>          cls.dhcpd_pid = None
>          ctx.stop_process(cls.dhcpd6_pid)
> diff --git a/autotests/testNetconfig/mismatch.psk
> b/autotests/testNetconfig/mismatch.psk
> new file mode 100644
> index 00000000..f8920b7c
> --- /dev/null
> +++ b/autotests/testNetconfig/mismatch.psk
> @@ -0,0 +1,7 @@
> +[IPv6]
> +# IPv4 addresses where IPv6 are expected
> +Address=192.168.1.10
> +Gateway=192.168.1.1
> +
> +[Settings]
> +AutoConnect=false

Does this happen to depend on patch 13/15? I applied all the other
autotest changes but this one fails to pass testNetconfig. The
assertRaises() call is what fails for me.

Thanks,
James


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

* Re: [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-21 21:04   ` James Prestwood
@ 2022-06-21 21:17     ` Andrew Zaborowski
  2022-06-22 20:59       ` Denis Kenzior
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-21 21:17 UTC (permalink / raw)
  To: James Prestwood; +Cc: iwd

On Tue, 21 Jun 2022 at 23:04, James Prestwood <prestwoj@gmail.com> wrote:
> Does this happen to depend on patch 13/15? I applied all the other
> autotest changes but this one fails to pass testNetconfig. The
> assertRaises() call is what fails for me.

It does, sorry I didn't make this clear.  Both 14 and 15 depend on 13/15.

Best regards

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

* Re: [PATCH 05/15] testrunner: Fix parsing for some arguments
  2022-06-16  0:02 ` [PATCH 05/15] testrunner: Fix parsing for some arguments Andrew Zaborowski
@ 2022-06-22 20:58   ` Denis Kenzior
  0 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-22 20:58 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Currently the parameter values reach run-tests by first being parsed by
> runner.py's RunnerArgParser, then the resulting object members being
> encoded as a commandline string, then as environment variables, then the
> environment being converted to a python string list and passed to
> RunnerCoreArgParser again.  Where argument names (like --sub-tests) had
> dashes, the object members had underscores (.sub_tests), this wasn't
> taken into account when building the python string list from environment
> variables so convert all underscores to dashes and hope that all the
> names match now.
> 
> Additionally some arguments used nargs='1' or nargs='*' which resulted
> in their python values becoming lists.  They were converted back to command
> line arguments such as: --sub_tests ['static_test.py'], and when parsed
> by RunnerCoreArgParser again, the values ended up being lists of lists.
> In all three cases it seems the actual user of the parsed value actually
> expects a single string with comma-separated substrings in it so just drop
> the nargs= uses.
> ---
>   tools/runner.py | 7 ++-----
>   1 file changed, 2 insertions(+), 5 deletions(-)
> 

Patches 5,8-12 applied, thanks.

Regards,
-Denis


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

* Re: [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-21 21:17     ` Andrew Zaborowski
@ 2022-06-22 20:59       ` Denis Kenzior
  2022-06-22 23:26         ` Andrew Zaborowski
  0 siblings, 1 reply; 30+ messages in thread
From: Denis Kenzior @ 2022-06-22 20:59 UTC (permalink / raw)
  To: Andrew Zaborowski, James Prestwood; +Cc: iwd

Hi Andrew,

On 6/21/22 16:17, Andrew Zaborowski wrote:
> On Tue, 21 Jun 2022 at 23:04, James Prestwood <prestwoj@gmail.com> wrote:
>> Does this happen to depend on patch 13/15? I applied all the other
>> autotest changes but this one fails to pass testNetconfig. The
>> assertRaises() call is what fails for me.
> 
> It does, sorry I didn't make this clear.  Both 14 and 15 depend on 13/15.
> 

Can this test be made applicable to the current netconfig implementation?  In 
other words, does it really need to depend on patch 13?  Same with 15?

Regards,
-Denis

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

* Re: [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-22 20:59       ` Denis Kenzior
@ 2022-06-22 23:26         ` Andrew Zaborowski
  2022-06-22 23:28           ` Denis Kenzior
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-22 23:26 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: James Prestwood, iwd

Hi Denis,

On Wed, 22 Jun 2022 at 23:02, Denis Kenzior <denkenz@gmail.com> wrote:
> On 6/21/22 16:17, Andrew Zaborowski wrote:
> > On Tue, 21 Jun 2022 at 23:04, James Prestwood <prestwoj@gmail.com> wrote:
> >> Does this happen to depend on patch 13/15? I applied all the other
> >> autotest changes but this one fails to pass testNetconfig. The
> >> assertRaises() call is what fails for me.
> >
> > It does, sorry I didn't make this clear.  Both 14 and 15 depend on 13/15.
> >
>
> Can this test be made applicable to the current netconfig implementation?  In
> other words, does it really need to depend on patch 13?  Same with 15?

Since this tests things that are fixed by switching to l_netconfig,
the test doesn't need any changes but src/netconfig.c does (that's
what patch 13 does), but it's safe to hold on with this.

Best regards

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

* Re: [PATCH 14/15] autotests: Add bad netconfig settings scenario
  2022-06-22 23:26         ` Andrew Zaborowski
@ 2022-06-22 23:28           ` Denis Kenzior
  0 siblings, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-22 23:28 UTC (permalink / raw)
  To: Andrew Zaborowski; +Cc: James Prestwood, iwd

Hi Andrew,

> Since this tests things that are fixed by switching to l_netconfig,
> the test doesn't need any changes but src/netconfig.c does (that's

Shouldn't we fix the current netconfig implementation?  And then apply this test?

> what patch 13 does), but it's safe to hold on with this.

It might take us a bit of time until the switch to l_netconfig happens.  So 
fixing these small issues might be a good idea in the interim.

Regards,
-Denis

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

* Re: [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs
  2022-06-16  0:02 ` [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs Andrew Zaborowski
@ 2022-06-22 23:36   ` Denis Kenzior
  2022-06-23  0:34     ` Andrew Zaborowski
  2022-06-27 18:12   ` Denis Kenzior
  1 sibling, 1 reply; 30+ messages in thread
From: Denis Kenzior @ 2022-06-22 23:36 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Verify that IWD with NetworkConfigurationEnabled creates the off-link
> routes for Route Information Options in the Router Advertisements.
> Sending this last because these options are not supported by current
> netconfig code and the test should fail without the l_netconfig patch.

So is this true?  Didn't ell commit:
9c5cbf5484b2 ("icmp6: Parse extra options") add off-link route setting directly 
to l_icmp6?  Shouldn't this work with the current (upstream) netconfig 
implementation?

Regards,
-Denis

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

* Re: [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs
  2022-06-22 23:36   ` Denis Kenzior
@ 2022-06-23  0:34     ` Andrew Zaborowski
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Zaborowski @ 2022-06-23  0:34 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: iwd

Hi Denis,

On Thu, 23 Jun 2022 at 01:52, Denis Kenzior <denkenz@gmail.com> wrote:
> On 6/15/22 19:02, Andrew Zaborowski wrote:
> > Verify that IWD with NetworkConfigurationEnabled creates the off-link
> > routes for Route Information Options in the Router Advertisements.
> > Sending this last because these options are not supported by current
> > netconfig code and the test should fail without the l_netconfig patch.
>
> So is this true?  Didn't ell commit:
> 9c5cbf5484b2 ("icmp6: Parse extra options") add off-link route setting directly
> to l_icmp6?  Shouldn't this work with the current (upstream) netconfig
> implementation?

True, looking at the code this should work together with the
l_icmp6_client_set_rtnl().

Best regards

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

* Re: [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs
  2022-06-16  0:02 ` [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs Andrew Zaborowski
  2022-06-22 23:36   ` Denis Kenzior
@ 2022-06-27 18:12   ` Denis Kenzior
  1 sibling, 0 replies; 30+ messages in thread
From: Denis Kenzior @ 2022-06-27 18:12 UTC (permalink / raw)
  To: Andrew Zaborowski, iwd

Hi Andrew,

On 6/15/22 19:02, Andrew Zaborowski wrote:
> Verify that IWD with NetworkConfigurationEnabled creates the off-link
> routes for Route Information Options in the Router Advertisements.
> Sending this last because these options are not supported by current
> netconfig code and the test should fail without the l_netconfig patch.
> ---
>   autotests/testNetconfig/connection_test.py | 12 ++++++++++++
>   1 file changed, 12 insertions(+)
> 

I went ahead and applied this patch with a slight amendment of the commit 
description.

Regards,
-Denis


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

end of thread, other threads:[~2022-06-27 18:25 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-16  0:02 [PATCH 01/15] netconfig: Fix address format validation Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 02/15] storage: Log a message on network file parse errors Andrew Zaborowski
2022-06-17 19:09   ` Denis Kenzior
2022-06-17 19:09   ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 03/15] station: Move netconfig_reset() to common path Andrew Zaborowski
2022-06-17 19:11   ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 04/15] monitor: Print netlink errors from Extended ACKs if available Andrew Zaborowski
2022-06-17 19:13   ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 05/15] testrunner: Fix parsing for some arguments Andrew Zaborowski
2022-06-22 20:58   ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 06/15] test-runner: Support iwd-rtnl as a --verbose value Andrew Zaborowski
2022-06-17 19:15   ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 07/15] autotests: Drop unused file+directory Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 08/15] autotests: Validate netmasks in testNetconfig, add utility Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 09/15] autotests: Ensure storage_dir exists, clean up Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 10/15] autotests: In testNetconfig add static IPv6, add comments Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 11/15] autotests: In testNetconfig verify routes created Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 12/15] autotests: Verify DNS entries added from DHCP/static Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 13/15][RFC] netconfig: Switch to l_netconfig Andrew Zaborowski
2022-06-16  0:02 ` [PATCH 14/15] autotests: Add bad netconfig settings scenario Andrew Zaborowski
2022-06-21 21:04   ` James Prestwood
2022-06-21 21:17     ` Andrew Zaborowski
2022-06-22 20:59       ` Denis Kenzior
2022-06-22 23:26         ` Andrew Zaborowski
2022-06-22 23:28           ` Denis Kenzior
2022-06-16  0:02 ` [PATCH 15/15] autotests: In testNetconfig verify routes from RIOs Andrew Zaborowski
2022-06-22 23:36   ` Denis Kenzior
2022-06-23  0:34     ` Andrew Zaborowski
2022-06-27 18:12   ` Denis Kenzior
2022-06-17 19:04 ` [PATCH 01/15] netconfig: Fix address format validation 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.