about summary refs log tree commit diff
path: root/src/core/platform
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
committerMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
commit1372848511cb896b80b51ed1a3e9606bd9816631 (patch)
tree674792b9385bdef935988894b45f06b2af39f88c /src/core/platform
parent40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff)
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/core/platform')
-rw-r--r--src/core/platform/nm-fake-platform.c47
-rw-r--r--src/core/platform/nm-fake-platform.h2
-rw-r--r--src/core/platform/tests/meson.build2
-rw-r--r--src/core/platform/tests/monitor.c165
-rw-r--r--src/core/platform/tests/test-address.c6
-rw-r--r--src/core/platform/tests/test-cleanup.c4
-rw-r--r--src/core/platform/tests/test-common.c985
-rw-r--r--src/core/platform/tests/test-common.h190
-rw-r--r--src/core/platform/tests/test-link.c275
-rw-r--r--src/core/platform/tests/test-platform-general.c932
-rw-r--r--src/core/platform/tests/test-route.c397
-rw-r--r--src/core/platform/tests/test-tc.c8
12 files changed, 2192 insertions, 821 deletions
diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c
index a1ca5434..c92d9aef 100644
--- a/src/core/platform/nm-fake-platform.c
+++ b/src/core/platform/nm-fake-platform.c
@@ -170,7 +170,7 @@ link_get(NMPlatform *platform, int ifindex)
     if (idx >= priv->links->len)
         goto not_found;
 
-    device = &g_array_index(priv->links, NMFakePlatformLink, idx);
+    device = &nm_g_array_index(priv->links, NMFakePlatformLink, idx);
     if (!device->obj)
         goto not_found;
 
@@ -234,8 +234,7 @@ link_add_pre(NMPlatform *platform,
 
     g_assert(!name || strlen(name) < IFNAMSIZ);
 
-    g_array_set_size(priv->links, priv->links->len + 1);
-    device  = &g_array_index(priv->links, NMFakePlatformLink, priv->links->len - 1);
+    device  = nm_g_array_append_new(priv->links, NMFakePlatformLink);
     ifindex = priv->links->len;
 
     memset(device, 0, sizeof(*device));
@@ -272,7 +271,7 @@ link_add_pre(NMPlatform *platform,
         g_assert(address_len == 0);
 
     device->obj        = o;
-    device->ip6_lladdr = *nmtst_inet6_from_string(ip6_lladdr);
+    device->ip6_lladdr = nmtst_inet6_from_string(ip6_lladdr);
 
     return device;
 }
@@ -980,7 +979,7 @@ ipx_address_delete(NMPlatform   *platform,
                 || (plen && address->plen != *plen)
                 || (peer_addr
                     && (((peer_addr_i ^ address->peer_address)
-                         & _nm_utils_ip4_prefix_to_netmask(address->plen))
+                         & nm_ip4_addr_netmask_from_prefix(address->plen))
                         != 0)))
                 continue;
         } else {
@@ -1093,10 +1092,7 @@ object_delete(NMPlatform *platform, const NMPObject *obj)
 }
 
 static int
-ip_route_add(NMPlatform              *platform,
-             NMPNlmFlags              flags,
-             int                      addr_family,
-             const NMPlatformIPRoute *route)
+ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack)
 {
     NMDedupMultiIter                iter;
     nm_auto_nmpobj NMPObject       *obj = NULL;
@@ -1110,23 +1106,29 @@ ip_route_add(NMPlatform              *platform,
     NMPlatformIPRoute              *r           = NULL;
     NMPlatformIP4Route             *r4          = NULL;
     NMPlatformIP6Route             *r6          = NULL;
+    int                             addr_family;
     gboolean                        has_same_weak_id;
     gboolean                        only_dirty;
     guint16                         nlmsgflags;
 
-    g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
+    g_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack),
+                       NMP_OBJECT_TYPE_IP4_ROUTE,
+                       NMP_OBJECT_TYPE_IP6_ROUTE));
+
+    addr_family = NMP_OBJECT_GET_ADDR_FAMILY(obj_stack);
 
     flags = NM_FLAGS_UNSET(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE);
 
     /* currently, only replace is implemented. */
     g_assert(flags == NMP_NLM_FLAG_REPLACE);
 
-    obj = nmp_object_new(addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE
-                                                : NMP_OBJECT_TYPE_IP6_ROUTE,
-                         (const NMPlatformObject *) route);
-    r = NMP_OBJECT_CAST_IP_ROUTE(obj);
+    if (NMP_OBJECT_GET_TYPE(obj_stack) == NMP_OBJECT_TYPE_IP4_ROUTE
+        && obj_stack->ip4_route.n_nexthops == 0 && obj_stack->ip4_route.ifindex > 0)
+        obj_stack->ip4_route.n_nexthops = 1;
 
-    nm_platform_ip_route_normalize(addr_family, r);
+    obj = nmp_object_clone(obj_stack, FALSE);
+
+    r = NMP_OBJECT_CAST_IP_ROUTE(obj);
 
     switch (addr_family) {
     case AF_INET:
@@ -1151,8 +1153,8 @@ ip_route_add(NMPlatform              *platform,
                                  &o) {
             if (addr_family == AF_INET) {
                 const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE(o);
-                guint32 n = nm_utils_ip4_address_clear_host_address(item->network, item->plen);
-                guint32 g = nm_utils_ip4_address_clear_host_address(r4->gateway, item->plen);
+                guint32 n = nm_ip4_addr_clear_host_address(item->network, item->plen);
+                guint32 g = nm_ip4_addr_clear_host_address(r4->gateway, item->plen);
 
                 if (r->ifindex == item->ifindex && n == g) {
                     has_route_to_gw = TRUE;
@@ -1162,21 +1164,21 @@ ip_route_add(NMPlatform              *platform,
                 const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE(o);
 
                 if (r->ifindex == item->ifindex
-                    && nm_utils_ip6_address_same_prefix(&r6->gateway, &item->network, item->plen)) {
+                    && nm_ip6_addr_same_prefix(&r6->gateway, &item->network, item->plen)) {
                     has_route_to_gw = TRUE;
                     break;
                 }
             }
         }
         if (!has_route_to_gw) {
-            char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+            char sbuf[NM_INET_ADDRSTRLEN];
 
             if (addr_family == AF_INET) {
                 nm_log_warn(
                     LOGD_PLATFORM,
                     "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable",
                     r->ifindex,
-                    _nm_utils_inet4_ntop(r4->network, sbuf),
+                    nm_inet4_ntop(r4->network, sbuf),
                     r->plen,
                     r->metric);
             } else {
@@ -1184,7 +1186,7 @@ ip_route_add(NMPlatform              *platform,
                     LOGD_PLATFORM,
                     "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable",
                     r->ifindex,
-                    _nm_utils_inet6_ntop(&r6->network, sbuf),
+                    nm_inet6_ntop(&r6->network, sbuf),
                     r->plen,
                     r->metric);
             }
@@ -1230,6 +1232,7 @@ ip_route_add(NMPlatform              *platform,
                                               obj,
                                               FALSE,
                                               nlmsgflags,
+                                              TRUE,
                                               &obj_old,
                                               &obj_new,
                                               &obj_replace,
@@ -1292,7 +1295,7 @@ finalize(GObject *object)
 
     g_hash_table_unref(priv->options);
     for (i = 0; i < priv->links->len; i++) {
-        NMFakePlatformLink *device = &g_array_index(priv->links, NMFakePlatformLink, i);
+        NMFakePlatformLink *device = &nm_g_array_index(priv->links, NMFakePlatformLink, i);
 
         nm_clear_pointer(&device->obj, nmp_object_unref);
     }
diff --git a/src/core/platform/nm-fake-platform.h b/src/core/platform/nm-fake-platform.h
index 8f8571fa..88fd3243 100644
--- a/src/core/platform/nm-fake-platform.h
+++ b/src/core/platform/nm-fake-platform.h
@@ -10,7 +10,7 @@
 
 #define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type())
 #define NM_FAKE_PLATFORM(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform))
 #define NM_FAKE_PLATFORM_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass))
 #define NM_IS_FAKE_PLATFORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FAKE_PLATFORM))
diff --git a/src/core/platform/tests/meson.build b/src/core/platform/tests/meson.build
index 8824ed2f..252c6840 100644
--- a/src/core/platform/tests/meson.build
+++ b/src/core/platform/tests/meson.build
@@ -13,7 +13,7 @@ test_units = [
   ['test-nmp-object', 'test-nmp-object.c', test_c_flags, default_test_timeout],
   ['test-platform-general', 'test-platform-general.c', test_c_flags, default_test_timeout],
   ['test-route-fake', 'test-route.c', test_fake_c_flags, default_test_timeout],
-  ['test-route-linux', 'test-route.c', test_linux_c_flags, default_test_timeout],
+  ['test-route-linux', 'test-route.c', test_linux_c_flags, 900],
   ['test-tc-linux', 'test-tc.c', test_linux_c_flags, default_test_timeout],
 ]
 
diff --git a/src/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c
index 8d0e0107..c83192bb 100644
--- a/src/core/platform/tests/monitor.c
+++ b/src/core/platform/tests/monitor.c
@@ -8,6 +8,8 @@
 #include <stdlib.h>
 #include <syslog.h>
 
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "libnm-platform/nm-linux-platform.h"
 #include "libnm-platform/nmp-object.h"
 
@@ -17,23 +19,45 @@ NMTST_DEFINE();
 
 static struct {
     gboolean persist;
+    char    *state_file;
 } global_opt = {
     .persist = TRUE,
 };
 
+const char *const *const ip_argv_rule6 = NM_MAKE_STRV("ip", "-d", "-6", "rule");
+
+static const struct {
+    NMPObjectType      obj_type;
+    const char *const *ip_argv;
+} dump_obj_types[] = {
+    {NMP_OBJECT_TYPE_LINK, NM_MAKE_STRV("ip", "-d", "link")},
+    {NMP_OBJECT_TYPE_IP4_ADDRESS, NM_MAKE_STRV("ip", "-d", "-4", "address")},
+    {NMP_OBJECT_TYPE_IP6_ADDRESS, NM_MAKE_STRV("ip", "-d", "-6", "address")},
+    {NMP_OBJECT_TYPE_IP4_ROUTE, NM_MAKE_STRV("ip", "-d", "-4", "route")},
+    {NMP_OBJECT_TYPE_IP6_ROUTE, NM_MAKE_STRV("ip", "-d", "-6", "route")},
+    {NMP_OBJECT_TYPE_ROUTING_RULE, NM_MAKE_STRV("ip", "-d", "-4", "rule")},
+};
+
 static gboolean
 read_argv(int *argc, char ***argv)
 {
     GOptionContext *context;
     GOptionEntry    options[] = {
-           {"no-persist",
+        {"no-persist",
             'P',
             G_OPTION_FLAG_REVERSE,
             G_OPTION_ARG_NONE,
             &global_opt.persist,
             "Exit after processing netlink messages",
             NULL},
-           {0},
+        {"state-file",
+            'S',
+            0,
+            G_OPTION_ARG_FILENAME,
+            &global_opt.state_file,
+            "Dump the platform cache to this file",
+            "FILE"},
+        {0},
     };
     gs_free_error GError *error = NULL;
 
@@ -63,6 +87,123 @@ mptcp_addr_dump(NMPlatform *platform)
 
 /*****************************************************************************/
 
+static void
+_dump_state(NMPlatform *platform, const char *state_file)
+{
+    nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE);
+    nm_auto_unref_gdatetime GDateTime *time_datetime = NULL;
+    gs_free char                      *time_str      = NULL;
+    int                                i_obj_type;
+
+    if (!state_file)
+        return;
+
+    time_datetime = g_date_time_new_now_local();
+    time_str      = g_date_time_format(time_datetime, "%C%y-%m-%dT%H:%M:%S.%f");
+
+    nm_log_dbg(LOGD_PLATFORM, "dump to file \"%s\", at %s", state_file, time_str);
+
+    nm_str_buf_append_printf(&sbuf, "time: %s\n", time_str);
+    nm_str_buf_append_printf(&sbuf, "pid: %lld\n", (long long) getpid());
+
+    for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) {
+        NMPObjectType                obj_type = dump_obj_types[i_obj_type].obj_type;
+        const char *const           *ip_argv  = dump_obj_types[i_obj_type].ip_argv;
+        const NMDedupMultiHeadEntry *pl_head_entry;
+        NMDedupMultiIter             pl_iter;
+        const NMPObject             *obj;
+        guint                        i;
+        char                         buf1[1000];
+
+        pl_head_entry = nm_platform_lookup_obj_type(platform, obj_type);
+
+        nm_str_buf_append_printf(&sbuf,
+                                 "\n%s: %u\n",
+                                 nmp_class_from_type(obj_type)->obj_type_name,
+                                 pl_head_entry ? pl_head_entry->len : 0u);
+
+        i = 0;
+        nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &obj) {
+            nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf1, sizeof(buf1));
+            nm_str_buf_append_printf(&sbuf,
+                                     "%s[%u]: %s\n",
+                                     nmp_class_from_type(obj_type)->obj_type_name,
+                                     i,
+                                     buf1);
+            g_assert(strlen(buf1) < sizeof(buf1) - 1u);
+            i++;
+        }
+
+ip_again:
+        if (ip_argv) {
+            gs_free_error GError *error      = NULL;
+            gs_free char         *ip_argv_ss = NULL;
+            gs_free char         *s_stdout   = NULL;
+            gs_free char         *s_stderr   = NULL;
+            int                   exit_code;
+
+            g_spawn_sync(NULL,
+                         (char **) ip_argv,
+                         NULL,
+                         G_SPAWN_SEARCH_PATH,
+                         NULL,
+                         NULL,
+                         &s_stdout,
+                         &s_stderr,
+                         &exit_code,
+                         &error);
+
+            nm_str_buf_append_printf(&sbuf,
+                                     "%s: call %s: ",
+                                     nmp_class_from_type(obj_type)->obj_type_name,
+                                     ip_argv_ss = g_strjoinv(" ", (char **) ip_argv));
+            if (error) {
+                nm_str_buf_append_printf(&sbuf, "FAILED: %s\n", error->message);
+            } else if (WIFEXITED(exit_code) && WEXITSTATUS(exit_code) == 0)
+                nm_str_buf_append_printf(&sbuf, "SUCCESS\n");
+            else {
+                nm_str_buf_append_printf(
+                    &sbuf,
+                    "ERROR: %s\n",
+                    nm_utils_get_process_exit_status_desc_buf(exit_code, buf1, sizeof(buf1)));
+            }
+            if (!nm_str_is_empty(s_stdout))
+                nm_str_buf_append_printf(&sbuf, "STDOUT>\n%s<\n", s_stdout);
+            if (!nm_str_is_empty(s_stderr))
+                nm_str_buf_append_printf(&sbuf, "STDERR>\n%s<\n", s_stderr);
+
+            if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE
+                && ip_argv == dump_obj_types[i_obj_type].ip_argv) {
+                ip_argv = ip_argv_rule6;
+                goto ip_again;
+            }
+        }
+    }
+
+    nm_utils_file_set_contents(state_file,
+                               nm_str_buf_get_str_unsafe(&sbuf),
+                               sbuf.len,
+                               00644,
+                               NULL,
+                               NULL,
+                               NULL);
+
+    nm_log_dbg(LOGD_PLATFORM, "dump to file complete");
+}
+
+static void
+_dump_state_platform_cb(NMPlatform   *platform,
+                        int           obj_type_i,
+                        int           ifindex,
+                        gconstpointer platform_object,
+                        int           change_type_i,
+                        gpointer      unused_user_data)
+{
+    _dump_state(platform, global_opt.state_file);
+}
+
+/*****************************************************************************/
+
 int
 main(int argc, char **argv)
 {
@@ -82,12 +223,32 @@ main(int argc, char **argv)
 
     nm_linux_platform_setup();
 
+    if (global_opt.state_file) {
+        int i_obj_type;
+
+        for (i_obj_type = 0; i_obj_type < (int) G_N_ELEMENTS(dump_obj_types); i_obj_type++) {
+            NMPObjectType obj_type = dump_obj_types[i_obj_type].obj_type;
+
+            g_signal_connect(NM_PLATFORM_GET,
+                             nmp_class_from_type(obj_type)->signal_type,
+                             G_CALLBACK(_dump_state_platform_cb),
+                             NULL);
+        }
+    }
+
     mptcp_addr_dump(NM_PLATFORM_GET);
 
+    _dump_state(NM_PLATFORM_GET, global_opt.state_file);
+
     if (global_opt.persist)
         g_main_loop_run(loop);
 
     g_main_loop_unref(loop);
 
+    g_signal_handlers_disconnect_by_func(NM_PLATFORM_GET,
+                                         G_CALLBACK(_dump_state_platform_cb),
+                                         NULL);
+    g_object_unref(NM_PLATFORM_GET);
+
     return EXIT_SUCCESS;
 }
diff --git a/src/core/platform/tests/test-address.c b/src/core/platform/tests/test-address.c
index 700891e8..0bce00c4 100644
--- a/src/core/platform/tests/test-address.c
+++ b/src/core/platform/tests/test-address.c
@@ -118,7 +118,7 @@ test_ip4_address_general(void)
     addresses = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex);
     g_assert(addresses);
     g_assert_cmpint(addresses->len, ==, 1);
-    address = &g_array_index(addresses, NMPlatformIP4Address, 0);
+    address = &nm_g_array_first(addresses, NMPlatformIP4Address);
     g_assert_cmpint(address->ifindex, ==, ifindex);
     g_assert_cmphex(address->address, ==, addr);
     g_assert_cmphex(address->peer_address, ==, addr);
@@ -193,7 +193,7 @@ test_ip6_address_general(void)
     addresses = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex);
     g_assert(addresses);
     g_assert_cmpint(addresses->len, ==, 1);
-    address = &g_array_index(addresses, NMPlatformIP6Address, 0);
+    address = &nm_g_array_first(addresses, NMPlatformIP6Address);
     g_assert_cmpint(address->ifindex, ==, ifindex);
     g_assert(!memcmp(&address->address, &addr, sizeof(addr)));
     g_assert_cmpint(address->plen, ==, IP6_PLEN);
@@ -453,7 +453,7 @@ _nmtstp_init_tests(int *argc, char ***argv)
 void
 _nmtstp_setup_tests(void)
 {
-#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE)
+#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, FALSE)
     add_test_func("/address/ipv4/general", test_ip4_address_general);
     add_test_func("/address/ipv6/general", test_ip6_address_general);
 
diff --git a/src/core/platform/tests/test-cleanup.c b/src/core/platform/tests/test-cleanup.c
index 2198e921..139a0280 100644
--- a/src/core/platform/tests/test-cleanup.c
+++ b/src/core/platform/tests/test-cleanup.c
@@ -63,7 +63,7 @@ test_cleanup_internal(void)
             nm_platform_process_events(NM_PLATFORM_GET);
         }
         addrs = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex);
-        if (addrs->len == 1 && (a = &g_array_index(addrs, NMPlatformIP6Address, 0))
+        if (addrs->len == 1 && (a = &nm_g_array_first(addrs, NMPlatformIP6Address))
             && IN6_IS_ADDR_LINKLOCAL(&a->address))
             break;
     });
@@ -73,7 +73,7 @@ test_cleanup_internal(void)
                                          addr4,
                                          plen4,
                                          addr4,
-                                         nm_platform_ip4_broadcast_address_create(addr4, plen4),
+                                         nm_ip4_addr_get_broadcast_address(addr4, plen4),
                                          lifetime,
                                          preferred,
                                          0,
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index b5bb1232..a37982fc 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -15,6 +15,7 @@
 #include <linux/rtnetlink.h>
 
 #include "n-acd/src/n-acd.h"
+#include "libnm-platform/nm-platform-utils.h"
 
 #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)"
 #define SIGNAL_DATA_ARG(data)                                                                     \
@@ -22,8 +23,60 @@
         (data)->ifname ? " ifname '" : "", (data)->ifname ?: "", (data)->ifname ? "'" : "",       \
         (data)->received_count
 
-int NMTSTP_ENV1_IFINDEX = -1;
-int NMTSTP_ENV1_EX      = -1;
+int NMTSTP_ENV1_IFINDEXES[];
+
+const char *const NMTSTP_ENV1_DEVICE_NAME[] = {
+    "nm-test-device0",
+    "nm-test-device1",
+};
+
+int NMTSTP_ENV1_EX = -1;
+
+/*****************************************************************************/
+
+typedef struct {
+    const char *module_name;
+
+    NMLinkType iftype;
+
+    /* These modules create additional interfaces, like
+     * "gre0" for "ip_gre" module.
+     *
+     * - actually some modules create multiple interfaces, like "ip_gre"
+     *   creating "gre0", "gretap0", "erspan0".
+     * - if already an interface with such name exist, kernel would create
+     *   names like "gre1" or "gretap1". We don't care for that, because
+     *   we run in our own namespace and we control which interfaces are there.
+     *   We wouldn't create an interface with such a conflicting name.
+     *
+     * Anyway. This is the name of *one* of the interfaces that the module would
+     * create. */
+    const char *ifname;
+
+    /* This only gets set, if iftype is NM_LINK_TYPE_UNKNOWN. It corresponds to
+     * NMPlatformLink.kind. */
+    const char *ifkind;
+
+} IPTunnelModInfo;
+
+#define INF(_module_name, _iftype, _ifname, ...)                                           \
+    {                                                                                      \
+        .module_name = ""_module_name, .iftype = _iftype, .ifname = ""_ifname, __VA_ARGS__ \
+    }
+
+static const IPTunnelModInfo ip_tunnel_mod_infos[] = {
+    INF("ip_gre", NM_LINK_TYPE_GRE, "gre0"),
+    INF("ip_gre", NM_LINK_TYPE_GRETAP, "gretap0"),
+    INF("ip_gre", NM_LINK_TYPE_UNKNOWN, "erspan0", .ifkind = "erspan"),
+    INF("ipip", NM_LINK_TYPE_IPIP, "tunl0"),
+    INF("ip6_tunnel", NM_LINK_TYPE_IP6TNL, "ip6tnl0"),
+    INF("ip6_gre", NM_LINK_TYPE_IP6GRE, "ip6gre0"),
+    INF("sit", NM_LINK_TYPE_SIT, "sit0"),
+    INF("ip_vti", NM_LINK_TYPE_VTI, "ip_vti0"),
+    INF("ip6_vti", NM_LINK_TYPE_VTI6, "ip6_vti0"),
+};
+
+#undef INF
 
 /*****************************************************************************/
 
@@ -218,13 +271,13 @@ _nmtstp_platform_ip_addresses_assert(const char        *filename,
             }
 
             if (!addr_bin->found) {
-                char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+                char sbuf[NM_INET_ADDRSTRLEN];
 
                 g_error("%s:%d: IPv%c address %s was not found on ifindex %d",
                         filename,
                         lineno,
                         nm_utils_addr_family_to_char(addr_bin->addr_family),
-                        nm_utils_inet_ntop(addr_bin->addr_family, &addr_bin->addr, sbuf),
+                        nm_inet_ntop(addr_bin->addr_family, &addr_bin->addr, sbuf),
                         ifindex);
             }
         }
@@ -583,7 +636,7 @@ _nmtstp_assert_ip4_route_exists(const char *file,
     r = _ip4_route_get(platform, ifindex, network, plen, metric, tos, &c);
 
     if (c != c_exists && c_exists != -1) {
-        char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+        char sbuf[NM_INET_ADDRSTRLEN];
 
         NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but "
@@ -591,7 +644,7 @@ _nmtstp_assert_ip4_route_exists(const char *file,
                 file,
                 line,
                 func,
-                _nm_utils_inet4_ntop(network, sbuf),
+                nm_inet4_ntop(network, sbuf),
                 plen,
                 metric,
                 tos,
@@ -683,8 +736,8 @@ _nmtstp_assert_ip6_route_exists(const char            *file,
     r = _ip6_route_get(platform, ifindex, network, plen, metric, src, src_plen, &c);
 
     if (c != c_exists && c_exists != -1) {
-        char s_src[NM_UTILS_INET_ADDRSTRLEN];
-        char s_network[NM_UTILS_INET_ADDRSTRLEN];
+        char s_src[NM_INET_ADDRSTRLEN];
+        char s_network[NM_INET_ADDRSTRLEN];
 
         NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but "
@@ -692,10 +745,10 @@ _nmtstp_assert_ip6_route_exists(const char            *file,
                 file,
                 line,
                 func,
-                _nm_utils_inet6_ntop(network, s_network),
+                nm_inet6_ntop(network, s_network),
                 plen,
                 metric,
-                _nm_utils_inet6_ntop(src, s_src),
+                nm_inet6_ntop(src, s_src),
                 src_plen,
                 c_exists,
                 c);
@@ -739,6 +792,642 @@ nmtstp_run_command(const char *format, ...)
 
 /*****************************************************************************/
 
+static int
+_assert_platform_sort_objs(gconstpointer ptr_a, gconstpointer ptr_b)
+{
+    const NMPObject *a = *((const NMPObject *const *) ptr_a);
+    const NMPObject *b = *((const NMPObject *const *) ptr_b);
+
+    g_assert(NMP_OBJECT_IS_VALID(a));
+    g_assert(NMP_OBJECT_IS_VALID(b));
+    g_assert(NMP_OBJECT_GET_TYPE(a) == NMP_OBJECT_GET_TYPE(b));
+
+    NM_CMP_RETURN(nmp_object_id_cmp(a, b));
+    g_assert_not_reached();
+    return 0;
+}
+
+static void
+_assert_platform_printarr(NMPObjectType obj_type, GPtrArray *arr1, GPtrArray *arr2)
+{
+    char  sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
+    guint i;
+
+    _LOGT("compare arrays of %s. In cache %u entries, fetched %u entries",
+          NMP_OBJECT_TYPE_NAME(obj_type),
+          nm_g_ptr_array_len(arr1),
+          nm_g_ptr_array_len(arr2));
+
+    for (i = 0; i < nm_g_ptr_array_len(arr1); i++) {
+        _LOGT("cache[%u] %s",
+              i,
+              nmp_object_to_string(arr1->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf)));
+    }
+    for (i = 0; i < nm_g_ptr_array_len(arr2); i++) {
+        _LOGT("fetch[%u] %s",
+              i,
+              nmp_object_to_string(arr2->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf)));
+    }
+
+    switch (obj_type) {
+    case NMP_OBJECT_TYPE_LINK:
+        nmtstp_run_command("ip -d link");
+        break;
+    case NMP_OBJECT_TYPE_IP4_ADDRESS:
+        nmtstp_run_command("ip -d -4 address");
+        break;
+    case NMP_OBJECT_TYPE_IP6_ADDRESS:
+        nmtstp_run_command("ip -d -6 address");
+        break;
+    case NMP_OBJECT_TYPE_IP4_ROUTE:
+        nmtstp_run_command("ip -d -4 route show table all");
+        break;
+    case NMP_OBJECT_TYPE_IP6_ROUTE:
+        nmtstp_run_command("ip -d -6 route show table all");
+        break;
+    default:
+        g_assert_not_reached();
+        break;
+    }
+}
+
+static gboolean
+_assert_platform_normalize_all(GPtrArray *arr)
+{
+    guint    i;
+    gboolean normalized = FALSE;
+
+    for (i = 0; i < nm_g_ptr_array_len(arr); i++) {
+        const NMPObject **ptr         = (gpointer) &arr->pdata[i];
+        nm_auto_nmpobj NMPObject *new = NULL;
+        gboolean skip                 = FALSE;
+
+        switch (NMP_OBJECT_GET_TYPE(*ptr)) {
+        case NMP_OBJECT_TYPE_LINK:
+        {
+            const NMPlatformLink *link = NMP_OBJECT_CAST_LINK(*ptr);
+
+            if (nmtstp_link_is_iptunnel_special(link)) {
+                /* These are special interfaces for the ip tunnel modules, like
+                 * "gre0" created by the "ip_gre" module.
+                 *
+                 * These interfaces can appear at any moment, when the module
+                 * gets loaded (by anybody on the host). We might want to avoid
+                 * that by calling nmtstp_ensure_module(), but it's worse.
+                 * Kernel does not send correct RTM_NEWLINK events when those
+                 * interfaces get created. So the cache content based on the
+                 * events will differ from a new load from a dump.
+                 *
+                 * We need to ignore those interfaces. */
+                skip = TRUE;
+            } else if (link->type == NM_LINK_TYPE_UNKNOWN) {
+                /* The link type is not detected. This might be a generated
+                 * interface like nmtstp_link_is_iptunnel_special(), but for
+                 * kernel modules that we don't know about. Ignore them too. */
+                skip = TRUE;
+            }
+
+            if (!skip) {
+                new                  = nmp_object_clone(*ptr, FALSE);
+                new->link.rx_packets = 0;
+                new->link.rx_bytes   = 0;
+                new->link.tx_packets = 0;
+                new->link.tx_bytes   = 0;
+            }
+            if (nmp_object_ref_set(ptr, new))
+                normalized = TRUE;
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    while (g_ptr_array_remove(arr, NULL)) {
+        /* Remove NULL values. */
+        normalized = TRUE;
+    }
+
+    return normalized;
+}
+
+static gboolean
+_assert_platform_compare_arr(NMPObjectType obj_type,
+                             const char   *detail_type,
+                             GPtrArray    *arr1,
+                             GPtrArray    *arr2,
+                             gboolean      normalized,
+                             gboolean      share_multi_idx,
+                             gboolean      do_assert)
+{
+    const NMPClass *obj_class = nmp_class_from_type(obj_type);
+    char            sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
+    char            sbuf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
+    int             idx;
+    int             idx_pointer_comp = -1;
+
+#define _fail_msg(do_assert, ...) \
+    G_STMT_START                  \
+    {                             \
+        if (do_assert) {          \
+            g_error(__VA_ARGS__); \
+        } else {                  \
+            _LOGW(__VA_ARGS__);   \
+            return FALSE;         \
+        }                         \
+    }                             \
+    G_STMT_END
+
+    for (idx = 0; TRUE; idx++) {
+        if (nm_g_ptr_array_len(arr1) == idx && nm_g_ptr_array_len(arr2) == idx)
+            break;
+        if (idx >= nm_g_ptr_array_len(arr1)) {
+            _assert_platform_printarr(obj_type, arr1, arr2);
+            _fail_msg(do_assert,
+                      "Comparing %s (%s) for platform fails. Platform now shows entry #%u which is "
+                      "not in the cache but expected %s",
+                      obj_class->obj_type_name,
+                      detail_type,
+                      idx,
+                      nmp_object_to_string(arr2->pdata[idx],
+                                           NMP_OBJECT_TO_STRING_ALL,
+                                           sbuf1,
+                                           sizeof(sbuf1)));
+        }
+        if (idx >= nm_g_ptr_array_len(arr2)) {
+            _assert_platform_printarr(obj_type, arr1, arr2);
+            _fail_msg(
+                do_assert,
+                "Comparing %s (%s) for platform fails. Platform has no more entry #%u which is "
+                "still in the cache as %s",
+                obj_class->obj_type_name,
+                detail_type,
+                idx,
+                nmp_object_to_string(arr1->pdata[idx],
+                                     NMP_OBJECT_TO_STRING_ALL,
+                                     sbuf1,
+                                     sizeof(sbuf1)));
+        }
+        if (!nmp_object_equal(arr1->pdata[idx], arr2->pdata[idx])) {
+            _assert_platform_printarr(obj_type, arr1, arr2);
+            _fail_msg(do_assert,
+                      "Comparing %s (%s) for platform fails. Platform entry #%u is now %s but in "
+                      "cache is %s",
+                      obj_class->obj_type_name,
+                      detail_type,
+                      idx,
+                      nmp_object_to_string(arr2->pdata[idx],
+                                           NMP_OBJECT_TO_STRING_ALL,
+                                           sbuf1,
+                                           sizeof(sbuf1)),
+                      nmp_object_to_string(arr1->pdata[idx],
+                                           NMP_OBJECT_TO_STRING_ALL,
+                                           sbuf2,
+                                           sizeof(sbuf2)));
+        }
+
+        if (!normalized && (share_multi_idx != (arr1->pdata[idx] == arr2->pdata[idx]))
+            && idx_pointer_comp == -1)
+            idx_pointer_comp = idx;
+    }
+
+    if (idx_pointer_comp != -1) {
+        _assert_platform_printarr(obj_type, arr1, arr2);
+        _fail_msg(do_assert,
+                  "Comparing %s (%s) for platform fails for pointer comparison. Platform entry "
+                  "#%u is now %s but in cache is %s",
+                  obj_class->obj_type_name,
+                  detail_type,
+                  idx_pointer_comp,
+                  nmp_object_to_string(arr2->pdata[idx_pointer_comp],
+                                       NMP_OBJECT_TO_STRING_ALL,
+                                       sbuf1,
+                                       sizeof(sbuf1)),
+                  nmp_object_to_string(arr1->pdata[idx_pointer_comp],
+                                       NMP_OBJECT_TO_STRING_ALL,
+                                       sbuf2,
+                                       sizeof(sbuf2)));
+    }
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+nmtstp_link_is_iptunnel_special(const NMPlatformLink *link)
+{
+    int i;
+
+    g_assert(link);
+
+    /* These interfaces are autogenerated when loading the ip tunnel
+     * modules. For example, loading "ip_gre" results in interfaces
+     * "gre0", "gretap0", "erspan0".
+     *
+     * Actually, if the interface names are already taken ("gre0" already
+     * exists), it will create "gre1" and so on. We don't care about that,
+     * because in our test's netns that is not happening. */
+
+    for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) {
+        const IPTunnelModInfo *module_info = &ip_tunnel_mod_infos[i];
+
+        if (module_info->iftype != link->type)
+            continue;
+        if (!nm_streq(module_info->ifname, link->name))
+            continue;
+        if (module_info->ifkind && !nm_streq(module_info->ifkind, link->kind))
+            continue;
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*****************************************************************************/
+
+gboolean
+nmtstp_ensure_module(const char *module_name)
+{
+    /* using iproute2 seems to fail sometimes? Force use of platform code. */
+    const int              EX          = 0;
+    gs_free char          *test_ifname = NULL;
+    const NMPlatformLink  *link;
+    static int             module_state[G_N_ELEMENTS(ip_tunnel_mod_infos)] = {0};
+    int                    i_module_info;
+    const IPTunnelModInfo *module_info = NULL;
+    int                    i;
+    int                    ifindex;
+    gboolean               result;
+
+    if (!module_name) {
+        result = TRUE;
+        for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) {
+            if (!nmtstp_ensure_module(ip_tunnel_mod_infos[i].module_name))
+                result = FALSE;
+        }
+        return result;
+    }
+
+    for (i_module_info = 0; i_module_info < (int) G_N_ELEMENTS(ip_tunnel_mod_infos);
+         i_module_info++) {
+        if (nm_streq(module_name, ip_tunnel_mod_infos[i_module_info].module_name)) {
+            module_info = &ip_tunnel_mod_infos[i_module_info];
+            break;
+        }
+    }
+    if (!module_info)
+        g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name);
+
+    test_ifname = g_strdup_printf("nm-mod-%s", module_info->ifname);
+    g_assert(nm_utils_ifname_valid_kernel(test_ifname, NULL));
+
+again:
+    i = g_atomic_int_get(&module_state[i_module_info]);
+    if (i != 0)
+        return i > 0;
+
+    /* When tunnel modules get loaded, then interfaces like "gre0", "gretap0"
+     * and "erspan0" (for "ip_gre" module) appear. For other modules, the interfaces
+     * are named differently. Of course, unless those interface name are already taken,
+     * in which case it will create "gre1", etc). So ugly.
+     *
+     * Anyway. as we run unit tests in parallel (`make check -j`), another
+     * test might just load the module just now, which results in the creation of
+     * those interfaces in our current namespace. That can break the test.
+     */
+
+    link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype);
+    g_assert(!link);
+
+    link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype);
+    if (link) {
+        g_assert(nmtstp_link_is_iptunnel_special(link));
+        /* An interface with this name exists. While technically this could not be the interface
+         * generated by the kernel module, in our test netns we can assume that it is. This is
+         * good enough. */
+        result = TRUE;
+        goto out;
+    }
+
+    /* Try to load the module. It probably won't work, because we don't have permissions.
+     * Ignore any failure. */
+    nmp_utils_modprobe(NULL, TRUE, module_info->module_name, NULL);
+
+    if (nm_streq(module_name, "ip_gre")) {
+        link = nmtstp_link_gre_add(NULL,
+                                   EX,
+                                   test_ifname,
+                                   &((const NMPlatformLnkGre){
+                                       .local          = nmtst_inet4_from_string("192.168.233.204"),
+                                       .remote         = nmtst_inet4_from_string("172.168.10.25"),
+                                       .parent_ifindex = 0,
+                                       .ttl            = 174,
+                                       .tos            = 37,
+                                       .path_mtu_discovery = TRUE,
+                                   }));
+    } else if (nm_streq(module_name, "ipip")) {
+        link = nmtstp_link_ipip_add(NULL,
+                                    EX,
+                                    test_ifname,
+                                    &((const NMPlatformLnkIpIp){
+                                        .local              = nmtst_inet4_from_string("1.2.3.4"),
+                                        .remote             = nmtst_inet4_from_string("5.6.7.8"),
+                                        .parent_ifindex     = 0,
+                                        .tos                = 32,
+                                        .path_mtu_discovery = FALSE,
+                                    }));
+    } else if (nm_streq(module_name, "ip6_tunnel")) {
+        link = nmtstp_link_ip6tnl_add(NULL,
+                                      EX,
+                                      test_ifname,
+                                      &((const NMPlatformLnkIp6Tnl){
+                                          .local       = nmtst_inet6_from_string("fd01::15"),
+                                          .remote      = nmtst_inet6_from_string("fd01::16"),
+                                          .tclass      = 20,
+                                          .encap_limit = 6,
+                                          .flow_label  = 1337,
+                                          .proto       = IPPROTO_IPV6,
+                                      }));
+    } else if (nm_streq(module_name, "ip6_gre")) {
+        link = nmtstp_link_ip6gre_add(NULL,
+                                      EX,
+                                      test_ifname,
+                                      &((const NMPlatformLnkIp6Tnl){
+                                          .local      = nmtst_inet6_from_string("fd01::42"),
+                                          .remote     = nmtst_inet6_from_string("fd01::aaaa"),
+                                          .tclass     = 21,
+                                          .flow_label = 1338,
+                                          .is_gre     = TRUE,
+                                      }));
+    } else if (nm_streq(module_name, "sit")) {
+        link = nmtstp_link_sit_add(NULL,
+                                   EX,
+                                   test_ifname,
+                                   &((const NMPlatformLnkSit){
+                                       .local  = nmtst_inet4_from_string("192.168.200.1"),
+                                       .remote = nmtst_inet4_from_string("172.25.100.14"),
+                                       .ttl    = 0,
+                                       .tos    = 31,
+                                       .path_mtu_discovery = FALSE,
+                                   }));
+    } else if (nm_streq(module_name, "ip_vti")) {
+        link = nmtstp_link_vti_add(NULL,
+                                   EX,
+                                   test_ifname,
+                                   &((const NMPlatformLnkVti){
+                                       .local  = nmtst_inet4_from_string("192.168.212.204"),
+                                       .remote = nmtst_inet4_from_string("172.168.11.25"),
+                                       .ikey   = 12,
+                                       .okey   = 13,
+                                   }));
+    } else if (nm_streq(module_name, "ip6_vti")) {
+        link = nmtstp_link_vti6_add(NULL,
+                                    EX,
+                                    test_ifname,
+                                    &((const NMPlatformLnkVti6){
+                                        .local  = nmtst_inet6_from_string("fd01::1"),
+                                        .remote = nmtst_inet6_from_string("fd02::2"),
+                                        .ikey   = 13,
+                                        .okey   = 14,
+                                    }));
+    } else
+        g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name);
+
+    if (!link) {
+        /* We might be unable to add the interface, if the kernel module does not exist.
+         * Be graceful about that. */
+        ifindex = 0;
+        g_assert(!nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype));
+        g_assert(
+            !nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype));
+    } else {
+        ifindex = link->ifindex;
+
+        g_assert(link);
+        g_assert(
+            link
+            == nmtstp_link_get_typed(NM_PLATFORM_GET, ifindex, test_ifname, module_info->iftype));
+
+        nmtstp_link_delete(NULL, -1, link->ifindex, test_ifname, TRUE);
+
+        link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype);
+        g_assert(nmtstp_link_is_iptunnel_special(link));
+    }
+
+    result = ifindex > 0 ? 1 : -1;
+
+out:
+    if (!g_atomic_int_compare_and_exchange(&module_state[i_module_info], 0, result))
+        goto again;
+
+    if (!result) {
+        /* The function aims to be graceful about missing kernel modules. */
+
+        /* g_error("Failure to ensure module \"%s\"", module_name); */
+    }
+
+    return result;
+}
+
+gboolean
+nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert)
+{
+    static const NMPObjectType obj_types[] = {
+        NMP_OBJECT_TYPE_IP4_ADDRESS,
+        NMP_OBJECT_TYPE_IP6_ADDRESS,
+        NMP_OBJECT_TYPE_IP4_ROUTE,
+        NMP_OBJECT_TYPE_IP6_ROUTE,
+        NMP_OBJECT_TYPE_LINK,
+    };
+    gboolean                    obj_type_flags_all = (obj_type_flags == 0u);
+    gs_unref_object NMPlatform *platform2          = NULL;
+    int                         i_obj_types;
+    gboolean                    share_multi_idx = nmtst_get_rand_bool();
+
+    /* This test creates a new NMLinuxPlatform instance. This will fill
+     * the cache with a new dump.
+     *
+     * Then it compares the content with @platform and checks that they
+     * agree. This tests that @platform cache is consistent, as it was
+     * updated based on netlink events. */
+
+    g_assert(NM_IS_LINUX_PLATFORM(platform));
+
+    _LOGD("assert-platform: start");
+
+    nm_platform_process_events(platform);
+
+    platform2 = nm_linux_platform_new(share_multi_idx ? nm_platform_get_multi_idx(platform) : NULL,
+                                      TRUE,
+                                      nmtst_get_rand_bool(),
+                                      nmtst_get_rand_bool());
+    g_assert(NM_IS_LINUX_PLATFORM(platform2));
+
+    for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) {
+        const NMPObjectType          obj_type         = obj_types[i_obj_types];
+        const guint32                i_obj_type_flags = nmp_object_type_to_flags(obj_type);
+        gs_unref_ptrarray GPtrArray *arr1             = NULL;
+        gs_unref_ptrarray GPtrArray *arr2             = NULL;
+        NMPLookup                    lookup;
+        gboolean                     check_unordered = TRUE;
+        guint                        idx;
+        gboolean                     normalized;
+
+        if (!obj_type_flags_all) {
+            if (!NM_FLAGS_ANY(obj_type_flags, i_obj_type_flags))
+                continue;
+            obj_type_flags = NM_FLAGS_UNSET(obj_type_flags, i_obj_type_flags);
+        }
+
+        nmp_lookup_init_obj_type(&lookup, obj_type);
+
+        arr1 = nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new();
+        arr2 = nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new();
+
+        normalized = _assert_platform_normalize_all(arr1);
+        normalized = _assert_platform_normalize_all(arr2);
+
+        if (check_unordered) {
+            /* We need to sort the two lists. */
+            g_ptr_array_sort(arr1, _assert_platform_sort_objs);
+            g_ptr_array_sort(arr2, _assert_platform_sort_objs);
+        }
+
+        if (!_assert_platform_compare_arr(obj_type,
+                                          "main",
+                                          arr1,
+                                          arr2,
+                                          normalized,
+                                          share_multi_idx,
+                                          do_assert))
+            return FALSE;
+
+        if (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) {
+            /* For routes, the WEAK_ID needs to be sorted and match the expected order. Check that. */
+            g_assert(!normalized);
+            for (idx = 0; idx < nm_g_ptr_array_len(arr1); idx++) {
+                const NMPObject             *obj1         = arr1->pdata[idx];
+                const NMPObject             *obj2         = arr2->pdata[idx];
+                gs_unref_ptrarray GPtrArray *arr1b        = NULL;
+                gs_unref_ptrarray GPtrArray *arr2b        = NULL;
+                gs_unref_ptrarray GPtrArray *arr1b_sorted = NULL;
+                gs_unref_ptrarray GPtrArray *arr2b_sorted = NULL;
+                guint                        found_obj1   = 0;
+                guint                        found_obj2   = 0;
+                guint                        i;
+
+                nmp_lookup_init_route_by_weak_id(&lookup, obj1);
+                arr1b =
+                    nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new();
+                g_assert_cmpint(arr1b->len, >, 0u);
+
+                nmp_lookup_init_route_by_weak_id(&lookup, obj2);
+                arr2b =
+                    nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new();
+                g_assert_cmpint(arr2b->len, ==, arr1b->len);
+
+                /* First check that the lists agree, if we sort them. The list of
+                 * weak-ids was supposed to honor the sort order from `ip route show`,
+                 * but as that is not the case (see blow), first check whether at
+                 * least the same routes are in the list (with wrong sort order). */
+                arr1b_sorted = nm_g_ptr_array_new_clone(arr1b, NULL, NULL, NULL);
+                arr2b_sorted = nm_g_ptr_array_new_clone(arr2b, NULL, NULL, NULL);
+                g_ptr_array_sort(arr1b_sorted, _assert_platform_sort_objs);
+                g_ptr_array_sort(arr2b_sorted, _assert_platform_sort_objs);
+                if (!_assert_platform_compare_arr(obj_type,
+                                                  "weak-id-sorted",
+                                                  arr1b_sorted,
+                                                  arr2b_sorted,
+                                                  normalized,
+                                                  share_multi_idx,
+                                                  do_assert))
+                    return FALSE;
+
+                if (obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) {
+                    /* For IPv6, the weak-ids are actually not sorted correctly.
+                     * This is because IPv6 multihop/ECMP routes get split into
+                     * multiple objects, and we don't get this right.
+                     *
+                     * This may be a bug. But we probably don't rely on this
+                     * anymore, because the weak-id were used to find which
+                     * route got replaced with `NLM_F_REPLACE`, but that anyway
+                     * doesn't work. We now always request a new dump. */
+                } else if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) {
+                    /* For IPv4, it also does not reliably always work. This may
+                     * be a bug we want to fix. For now, ignore the check.
+                     *
+                     * a) Kernel can wrongly allow to configure the same route twice.
+                     * That means, the same route is visible in `ip route` output,
+                     * meaning, it would be added twice to the platform cache.
+                     * At least due to that problem, may the weak-id not be properly sorted.
+                     * See https://bugzilla.redhat.com/show_bug.cgi?id=2165720 which is
+                     * a bug of kernel allowing to configure the exact same route twice.
+                     *
+                     * b) See https://bugzilla.redhat.com/show_bug.cgi?id=2162315 which is
+                     * a bug where kernel does allow to configure single-hop routes that differ by
+                     * their next-hop weight, but on the netlink API those routes look the same.
+                     *
+                     * Due to a) and b), the platform cache may contain only one instance
+                     * of a route, which is visible more than once in `ip route` output.
+                     * This merging of different routes causes problems, and it also means
+                     * that the RTM_NEWROUTE events are wrongly interpreted and the weak-id
+                     * is not properly sorted.
+                     */
+                } else {
+                    /* Assert that also the original, not-sorted lists agree. */
+                    if (!_assert_platform_compare_arr(obj_type,
+                                                      "weak-id",
+                                                      arr1b,
+                                                      arr2b,
+                                                      normalized,
+                                                      share_multi_idx,
+                                                      do_assert))
+                        return FALSE;
+                }
+
+                for (i = 0; i < arr1b->len; i++) {
+                    if (arr1b->pdata[i] == obj1)
+                        found_obj1++;
+                    if (arr2b->pdata[i] == obj2)
+                        found_obj2++;
+                }
+
+                g_assert_cmpint(found_obj1, ==, 1u);
+                g_assert_cmpint(found_obj2, ==, 1u);
+            }
+        }
+    }
+
+    g_clear_object(&platform2);
+
+    _LOGD("assert-platform: done");
+
+    g_assert_cmpint(obj_type_flags, ==, 0u);
+
+    return TRUE;
+}
+
+void
+nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags)
+{
+    if (!nmtstp_check_platform_full(platform, obj_type_flags, FALSE)) {
+        /* It's unclear why this failure sometimes happens. It happens
+         * on gitlab-ci on Ubuntu/Debian(??).
+         *
+         * Retrying shortly after seems to avoid it. */
+        g_usleep(20 * 1000);
+        nm_platform_process_events(platform);
+        nmtstp_run_command("ip route");
+        nm_platform_process_events(platform);
+
+        nmtstp_check_platform_full(platform, obj_type_flags, TRUE);
+    }
+}
+
+/*****************************************************************************/
+
 typedef struct {
     GMainLoop *loop;
     guint      signal_counts;
@@ -1036,7 +1725,7 @@ nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr,
 
 static void
 _ip_address_add(NMPlatform     *platform,
-                gboolean        external_command,
+                int             external_command,
                 gboolean        is_v4,
                 int             ifindex,
                 const NMIPAddr *address,
@@ -1058,8 +1747,8 @@ _ip_address_add(NMPlatform     *platform,
         gs_free char *s_valid     = NULL;
         gs_free char *s_preferred = NULL;
         gs_free char *s_label     = NULL;
-        char          b1[NM_UTILS_INET_ADDRSTRLEN];
-        char          b2[NM_UTILS_INET_ADDRSTRLEN];
+        char          b1[NM_INET_ADDRSTRLEN];
+        char          b2[NM_INET_ADDRSTRLEN];
 
         ifname = nm_platform_link_get_name(platform, ifindex);
         g_assert(ifname);
@@ -1072,18 +1761,18 @@ _ip_address_add(NMPlatform     *platform,
             s_label = g_strdup_printf("%s:%s", ifname, label);
 
         if (is_v4) {
-            char s_peer[NM_UTILS_INET_ADDRSTRLEN + 50];
+            char s_peer[NM_INET_ADDRSTRLEN + 50];
 
             g_assert(flags == 0);
 
             if (peer_address->addr4 != address->addr4 || nmtst_get_rand_uint32() % 2) {
                 /* If the peer is the same as the local address, we can omit it. The result should be identical */
-                nm_sprintf_buf(s_peer, " peer %s", _nm_utils_inet4_ntop(peer_address->addr4, b2));
+                nm_sprintf_buf(s_peer, " peer %s", nm_inet4_ntop(peer_address->addr4, b2));
             } else
                 s_peer[0] = '\0';
 
             nmtstp_run_command_check("ip address change %s%s/%d dev %s%s%s%s",
-                                     _nm_utils_inet4_ntop(address->addr4, b1),
+                                     nm_inet4_ntop(address->addr4, b1),
                                      s_peer,
                                      plen,
                                      ifname,
@@ -1096,10 +1785,10 @@ _ip_address_add(NMPlatform     *platform,
             /* flags not implemented (yet) */
             g_assert(flags == 0);
             nmtstp_run_command_check("ip address change %s%s%s/%d dev %s%s%s%s",
-                                     _nm_utils_inet6_ntop(&address->addr6, b1),
+                                     nm_inet6_ntop(&address->addr6, b1),
                                      !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) ? " peer " : "",
                                      !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6)
-                                         ? _nm_utils_inet6_ntop(&peer_address->addr6, b2)
+                                         ? nm_inet6_ntop(&peer_address->addr6, b2)
                                          : "",
                                      plen,
                                      ifname,
@@ -1188,7 +1877,7 @@ _ip_address_add(NMPlatform     *platform,
 
 void
 nmtstp_ip4_address_add(NMPlatform *platform,
-                       gboolean    external_command,
+                       int         external_command,
                        int         ifindex,
                        in_addr_t   address,
                        int         plen,
@@ -1213,7 +1902,7 @@ nmtstp_ip4_address_add(NMPlatform *platform,
 
 void
 nmtstp_ip6_address_add(NMPlatform     *platform,
-                       gboolean        external_command,
+                       int             external_command,
                        int             ifindex,
                        struct in6_addr address,
                        int             plen,
@@ -1257,8 +1946,8 @@ nmtstp_ip4_route_add(NMPlatform      *platform,
     route.metric    = metric;
     route.mss       = mss;
 
-    g_assert(
-        NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route)));
+    g_assert(NMTST_NM_ERR_SUCCESS(
+        nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route, NULL)));
 }
 
 void
@@ -1291,7 +1980,7 @@ nmtstp_ip6_route_add(NMPlatform      *platform,
 
 static void
 _ip_address_del(NMPlatform     *platform,
-                gboolean        external_command,
+                int             external_command,
                 gboolean        is_v4,
                 int             ifindex,
                 const NMIPAddr *address,
@@ -1306,8 +1995,8 @@ _ip_address_del(NMPlatform     *platform,
 
     if (external_command) {
         const char *ifname;
-        char        b1[NM_UTILS_INET_ADDRSTRLEN];
-        char        b2[NM_UTILS_INET_ADDRSTRLEN];
+        char        b1[NM_INET_ADDRSTRLEN];
+        char        b2[NM_INET_ADDRSTRLEN];
         int         success;
         gboolean    had_address;
 
@@ -1325,18 +2014,17 @@ _ip_address_del(NMPlatform     *platform,
             had_address = !!nm_platform_ip6_address_get(platform, ifindex, &address->addr6);
 
         if (is_v4) {
-            success = nmtstp_run_command("ip address delete %s%s%s/%d dev %s",
-                                         _nm_utils_inet4_ntop(address->addr4, b1),
-                                         peer_address->addr4 != address->addr4 ? " peer " : "",
-                                         peer_address->addr4 != address->addr4
-                                             ? _nm_utils_inet4_ntop(peer_address->addr4, b2)
-                                             : "",
-                                         plen,
-                                         ifname);
+            success = nmtstp_run_command(
+                "ip address delete %s%s%s/%d dev %s",
+                nm_inet4_ntop(address->addr4, b1),
+                peer_address->addr4 != address->addr4 ? " peer " : "",
+                peer_address->addr4 != address->addr4 ? nm_inet4_ntop(peer_address->addr4, b2) : "",
+                plen,
+                ifname);
         } else {
             g_assert(!peer_address);
             success = nmtstp_run_command("ip address delete %s/%d dev %s",
-                                         _nm_utils_inet6_ntop(&address->addr6, b1),
+                                         nm_inet6_ntop(&address->addr6, b1),
                                          plen,
                                          ifname);
         }
@@ -1391,7 +2079,7 @@ _ip_address_del(NMPlatform     *platform,
 
 void
 nmtstp_ip4_address_del(NMPlatform *platform,
-                       gboolean    external_command,
+                       int         external_command,
                        int         ifindex,
                        in_addr_t   address,
                        int         plen,
@@ -1408,7 +2096,7 @@ nmtstp_ip4_address_del(NMPlatform *platform,
 
 void
 nmtstp_ip6_address_del(NMPlatform     *platform,
-                       gboolean        external_command,
+                       int             external_command,
                        int             ifindex,
                        struct in6_addr address,
                        int             plen)
@@ -1478,7 +2166,7 @@ nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested,
 
 const NMPlatformLink *
 nmtstp_link_bridge_add(NMPlatform                *platform,
-                       gboolean                   external_command,
+                       int                        external_command,
                        const char                *name,
                        const NMPlatformLnkBridge *lnk)
 {
@@ -1633,10 +2321,7 @@ nmtstp_link_bridge_add(NMPlatform                *platform,
     return pllink;
 }
 const NMPlatformLink *
-nmtstp_link_veth_add(NMPlatform *platform,
-                     gboolean    external_command,
-                     const char *name,
-                     const char *peer)
+nmtstp_link_veth_add(NMPlatform *platform, int external_command, const char *name, const char *peer)
 {
     const NMPlatformLink *pllink  = NULL;
     gboolean              success = FALSE;
@@ -1681,7 +2366,7 @@ again:
 }
 
 const NMPlatformLink *
-nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name)
+nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name)
 {
     const NMPlatformLink *pllink = NULL;
     gboolean              success;
@@ -1706,7 +2391,7 @@ nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const cha
 
 const NMPlatformLink *
 nmtstp_link_gre_add(NMPlatform             *platform,
-                    gboolean                external_command,
+                    int                     external_command,
                     const char             *name,
                     const NMPlatformLnkGre *lnk)
 {
@@ -1734,13 +2419,13 @@ nmtstp_link_gre_add(NMPlatform             *platform,
         obj  = lnk->is_tap ? "link" : "tunnel";
         type = lnk->is_tap ? "type gretap" : "mode gre";
 
-        success = !nmtstp_run_command("ip %s add %s %s %s local %s remote %s ttl %u tos %02x %s",
+        success = !nmtstp_run_command("ip %s add %s %s%s%s local %s remote %s ttl %u tos %02x %s",
                                       obj,
                                       name,
                                       type,
-                                      dev ?: "",
-                                      _nm_utils_inet4_ntop(lnk->local, b1),
-                                      _nm_utils_inet4_ntop(lnk->remote, b2),
+                                      NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""),
+                                      nm_inet4_ntop(lnk->local, b1),
+                                      nm_inet4_ntop(lnk->remote, b2),
                                       lnk->ttl,
                                       lnk->tos,
                                       lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
@@ -1757,14 +2442,14 @@ nmtstp_link_gre_add(NMPlatform             *platform,
 
 const NMPlatformLink *
 nmtstp_link_ip6tnl_add(NMPlatform                *platform,
-                       gboolean                   external_command,
+                       int                        external_command,
                        const char                *name,
                        const NMPlatformLnkIp6Tnl *lnk)
 {
     const NMPlatformLink *pllink = NULL;
     gboolean              success;
-    char                  b1[NM_UTILS_INET_ADDRSTRLEN];
-    char                  b2[NM_UTILS_INET_ADDRSTRLEN];
+    char                  b1[NM_INET_ADDRSTRLEN];
+    char                  b2[NM_INET_ADDRSTRLEN];
     char                  encap[20];
     char                  tclass[20];
     gboolean              encap_ignore;
@@ -1800,13 +2485,13 @@ nmtstp_link_ip6tnl_add(NMPlatform                *platform,
         tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS);
 
         success = !nmtstp_run_command(
-            "ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %s encaplimit %s "
+            "ip -6 tunnel add %s mode %s%s%s local %s remote %s ttl %u tclass %s encaplimit %s "
             "flowlabel %x",
             name,
             mode,
-            dev,
-            _nm_utils_inet6_ntop(&lnk->local, b1),
-            _nm_utils_inet6_ntop(&lnk->remote, b2),
+            NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""),
+            nm_inet6_ntop(&lnk->local, b1),
+            nm_inet6_ntop(&lnk->remote, b2),
             lnk->ttl,
             tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass),
             encap_ignore ? "none" : nm_sprintf_buf(encap, "%u", lnk->encap_limit),
@@ -1823,14 +2508,14 @@ nmtstp_link_ip6tnl_add(NMPlatform                *platform,
 
 const NMPlatformLink *
 nmtstp_link_ip6gre_add(NMPlatform                *platform,
-                       gboolean                   external_command,
+                       int                        external_command,
                        const char                *name,
                        const NMPlatformLnkIp6Tnl *lnk)
 {
     const NMPlatformLink *pllink = NULL;
     gboolean              success;
-    char                  b1[NM_UTILS_INET_ADDRSTRLEN];
-    char                  b2[NM_UTILS_INET_ADDRSTRLEN];
+    char                  b1[NM_INET_ADDRSTRLEN];
+    char                  b2[NM_INET_ADDRSTRLEN];
     char                  tclass[20];
     gboolean              tclass_inherit;
 
@@ -1851,12 +2536,12 @@ nmtstp_link_ip6gre_add(NMPlatform                *platform,
         tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS);
 
         success = !nmtstp_run_command(
-            "ip link add %s type %s %s local %s remote %s ttl %u tclass %s flowlabel %x",
+            "ip link add %s type %s%s%s local %s remote %s ttl %u tclass %s flowlabel %x",
             name,
             lnk->is_tap ? "ip6gretap" : "ip6gre",
-            dev,
-            _nm_utils_inet6_ntop(&lnk->local, b1),
-            _nm_utils_inet6_ntop(&lnk->remote, b2),
+            NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""),
+            nm_inet6_ntop(&lnk->local, b1),
+            nm_inet6_ntop(&lnk->remote, b2),
             lnk->ttl,
             tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass),
             lnk->flow_label);
@@ -1882,7 +2567,7 @@ nmtstp_link_ip6gre_add(NMPlatform                *platform,
 
 const NMPlatformLink *
 nmtstp_link_ipip_add(NMPlatform              *platform,
-                     gboolean                 external_command,
+                     int                      external_command,
                      const char              *name,
                      const NMPlatformLnkIpIp *lnk)
 {
@@ -1905,11 +2590,11 @@ nmtstp_link_ipip_add(NMPlatform              *platform,
                 g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
 
         success = !nmtstp_run_command(
-            "ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s",
+            "ip tunnel add %s mode ipip%s%s local %s remote %s ttl %u tos %02x %s",
             name,
-            dev,
-            _nm_utils_inet4_ntop(lnk->local, b1),
-            _nm_utils_inet4_ntop(lnk->remote, b2),
+            NM_PRINT_FMT_QUOTED2(dev, " ", dev, ""),
+            nm_inet4_ntop(lnk->local, b1),
+            nm_inet4_ntop(lnk->remote, b2),
             lnk->ttl,
             lnk->tos,
             lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
@@ -1925,7 +2610,7 @@ nmtstp_link_ipip_add(NMPlatform              *platform,
 
 const NMPlatformLink *
 nmtstp_link_macvlan_add(NMPlatform                 *platform,
-                        gboolean                    external_command,
+                        int                         external_command,
                         const char                 *name,
                         int                         parent,
                         const NMPlatformLnkMacvlan *lnk)
@@ -1945,10 +2630,10 @@ nmtstp_link_macvlan_add(NMPlatform                 *platform,
     if (external_command) {
         const char *dev;
         char       *modes[] = {
-                  [MACVLAN_MODE_BRIDGE]   = "bridge",
-                  [MACVLAN_MODE_VEPA]     = "vepa",
-                  [MACVLAN_MODE_PRIVATE]  = "private",
-                  [MACVLAN_MODE_PASSTHRU] = "passthru",
+            [MACVLAN_MODE_BRIDGE]   = "bridge",
+            [MACVLAN_MODE_VEPA]     = "vepa",
+            [MACVLAN_MODE_PRIVATE]  = "private",
+            [MACVLAN_MODE_PASSTHRU] = "passthru",
         };
 
         dev = nm_platform_link_get_name(platform, parent);
@@ -1974,7 +2659,7 @@ nmtstp_link_macvlan_add(NMPlatform                 *platform,
 
 const NMPlatformLink *
 nmtstp_link_sit_add(NMPlatform             *platform,
-                    gboolean                external_command,
+                    int                     external_command,
                     const char             *name,
                     const NMPlatformLnkSit *lnk)
 {
@@ -2004,8 +2689,8 @@ nmtstp_link_sit_add(NMPlatform             *platform,
             !nmtstp_run_command("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s",
                                 name,
                                 dev,
-                                _nm_utils_inet4_ntop(lnk->local, b1),
-                                _nm_utils_inet4_ntop(lnk->remote, b2),
+                                nm_inet4_ntop(lnk->local, b1),
+                                nm_inet4_ntop(lnk->remote, b2),
                                 lnk->ttl,
                                 lnk->tos,
                                 lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
@@ -2021,7 +2706,7 @@ nmtstp_link_sit_add(NMPlatform             *platform,
 
 const NMPlatformLink *
 nmtstp_link_tun_add(NMPlatform             *platform,
-                    gboolean                external_command,
+                    int                     external_command,
                     const char             *name,
                     const NMPlatformLnkTun *lnk,
                     int                    *out_fd)
@@ -2085,9 +2770,135 @@ nmtstp_link_tun_add(NMPlatform             *platform,
 }
 
 const NMPlatformLink *
-nmtstp_link_vrf_add(NMPlatform             *platform,
+nmtstp_link_vlan_add(NMPlatform              *platform,
+                     int                      external_command,
+                     const char              *name,
+                     int                      parent,
+                     const NMPlatformLnkVlan *lnk)
+{
+    const NMPlatformLink *pllink = NULL;
+    gboolean              success;
+
+    g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+
+    external_command = nmtstp_run_command_check_external(external_command);
+
+    _init_platform(&platform, external_command);
+
+    if (external_command) {
+        const char *dev;
+
+        dev = nm_platform_link_get_name(platform, parent);
+        g_assert(dev);
+        g_assert(NM_IN_SET(lnk->protocol, ETH_P_8021Q, ETH_P_8021AD));
+
+        success = !nmtstp_run_command(
+            "ip link add name %s link %s type vlan id %hu protocol %s%s%s%s%s",
+            name,
+            dev,
+            lnk->id,
+            lnk->protocol == ETH_P_8021Q ? "802.1Q" : "802.1ad",
+            !(lnk->flags & _NM_VLAN_FLAG_REORDER_HEADERS) ? " reorder_hdr off" : "",
+            (lnk->flags & _NM_VLAN_FLAG_GVRP) ? " gvrp on" : "",
+            (lnk->flags & _NM_VLAN_FLAG_MVRP) ? " mvrp on" : "",
+            (lnk->flags & _NM_VLAN_FLAG_LOOSE_BINDING) ? " loose_binding on" : "");
+        if (success)
+            pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VLAN, 100);
+    } else
+        success =
+            NMTST_NM_ERR_SUCCESS(nm_platform_link_vlan_add(platform, name, parent, lnk, &pllink));
+
+    _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VLAN);
+
+    return pllink;
+}
+
+const NMPlatformLink *
+nmtstp_link_vti_add(NMPlatform             *platform,
                     gboolean                external_command,
                     const char             *name,
+                    const NMPlatformLnkVti *lnk)
+{
+    const NMPlatformLink *pllink = NULL;
+    gboolean              success;
+    char                  b1[INET_ADDRSTRLEN];
+    char                  b2[INET_ADDRSTRLEN];
+
+    g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+    external_command = nmtstp_run_command_check_external(external_command);
+    _init_platform(&platform, external_command);
+    g_assert(lnk->fwmark == 0);
+
+    if (external_command) {
+        gs_free char *dev = NULL;
+
+        if (lnk->parent_ifindex)
+            dev =
+                g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
+
+        success =
+            !nmtstp_run_command("ip link add %s type vti %s local %s remote %s ikey %u okey %u",
+                                name,
+                                dev ?: "",
+                                nm_inet4_ntop(lnk->local, b1),
+                                nm_inet4_ntop(lnk->remote, b2),
+                                lnk->ikey,
+                                lnk->okey);
+        if (success)
+            pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI, 100);
+    } else
+        success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti_add(platform, name, lnk, &pllink));
+
+    _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI);
+
+    return pllink;
+}
+
+const NMPlatformLink *
+nmtstp_link_vti6_add(NMPlatform              *platform,
+                     gboolean                 external_command,
+                     const char              *name,
+                     const NMPlatformLnkVti6 *lnk)
+{
+    const NMPlatformLink *pllink = NULL;
+    gboolean              success;
+    char                  b1[INET6_ADDRSTRLEN];
+    char                  b2[INET6_ADDRSTRLEN];
+
+    g_assert(nm_utils_ifname_valid_kernel(name, NULL));
+    external_command = nmtstp_run_command_check_external(external_command);
+    _init_platform(&platform, external_command);
+    g_assert(lnk->fwmark == 0);
+
+    if (external_command) {
+        gs_free char *dev = NULL;
+
+        if (lnk->parent_ifindex)
+            dev =
+                g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
+
+        success =
+            !nmtstp_run_command("ip link add %s type vti6 %s local %s remote %s ikey %u okey %u",
+                                name,
+                                dev ?: "",
+                                nm_inet6_ntop(&lnk->local, b1),
+                                nm_inet6_ntop(&lnk->remote, b2),
+                                lnk->ikey,
+                                lnk->okey);
+        if (success)
+            pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VTI6, 100);
+    } else
+        success = NMTST_NM_ERR_SUCCESS(nm_platform_link_vti6_add(platform, name, lnk, &pllink));
+
+    _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VTI6);
+
+    return pllink;
+}
+
+const NMPlatformLink *
+nmtstp_link_vrf_add(NMPlatform             *platform,
+                    int                     external_command,
+                    const char             *name,
                     const NMPlatformLnkVrf *lnk,
                     gboolean               *out_not_supported)
 {
@@ -2123,7 +2934,7 @@ nmtstp_link_vrf_add(NMPlatform             *platform,
 
 const NMPlatformLink *
 nmtstp_link_vxlan_add(NMPlatform               *platform,
-                      gboolean                  external_command,
+                      int                       external_command,
                       const char               *name,
                       const NMPlatformLnkVxlan *lnk)
 {
@@ -2139,24 +2950,24 @@ nmtstp_link_vxlan_add(NMPlatform               *platform,
 
     if (external_command) {
         gs_free char *dev = NULL;
-        char          local[NM_UTILS_INET_ADDRSTRLEN];
-        char          group[NM_UTILS_INET_ADDRSTRLEN];
+        char          local[NM_INET_ADDRSTRLEN];
+        char          group[NM_INET_ADDRSTRLEN];
 
         if (lnk->parent_ifindex)
             dev =
                 g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex));
 
         if (lnk->local)
-            _nm_utils_inet4_ntop(lnk->local, local);
+            nm_inet4_ntop(lnk->local, local);
         else if (memcmp(&lnk->local6, &in6addr_any, sizeof(in6addr_any)))
-            _nm_utils_inet6_ntop(&lnk->local6, local);
+            nm_inet6_ntop(&lnk->local6, local);
         else
             local[0] = '\0';
 
         if (lnk->group)
-            _nm_utils_inet4_ntop(lnk->group, group);
+            nm_inet4_ntop(lnk->group, group);
         else if (memcmp(&lnk->group6, &in6addr_any, sizeof(in6addr_any)))
-            _nm_utils_inet6_ntop(&lnk->group6, group);
+            nm_inet6_ntop(&lnk->group6, group);
         else
             group[0] = '\0';
 
@@ -2239,7 +3050,7 @@ nmtstp_link_get(NMPlatform *platform, int ifindex, const char *name)
 
 void
 nmtstp_link_delete(NMPlatform *platform,
-                   gboolean    external_command,
+                   int         external_command,
                    int         ifindex,
                    const char *name,
                    gboolean    require_exist)
@@ -2291,7 +3102,7 @@ nmtstp_link_delete(NMPlatform *platform,
 /*****************************************************************************/
 
 void
-nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up)
+nmtstp_link_set_updown(NMPlatform *platform, int external_command, int ifindex, gboolean up)
 {
     const NMPlatformLink *plink;
     gint64                end_time;
@@ -2739,7 +3550,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a
     NAcdProbe                                         *probe        = NULL;
     int                                                fd;
     int                                                r;
-    char                                               sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                                               sbuf_addr[NM_INET_ADDRSTRLEN];
 
     g_assert_cmpint(ifindex, >, 0);
     g_assert(mac_addr);
@@ -2780,7 +3591,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a
           NM_HASH_OBFUSCATE_PTR(defender),
           ifindex,
           NM_ETHER_ADDR_FORMAT_VAL(mac_addr),
-          _nm_utils_inet4_ntop(ip_addr, sbuf_addr));
+          nm_inet4_ntop(ip_addr, sbuf_addr));
 
     n_acd_probe_set_userdata(defender->probe, defender);
 
diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h
index da30b0ed..2802d8fd 100644
--- a/src/core/platform/tests/test-common.h
+++ b/src/core/platform/tests/test-common.h
@@ -18,8 +18,6 @@
 
 #include "nm-test-utils-core.h"
 
-#define DEVICE_NAME "nm-test-device"
-
 /*****************************************************************************/
 
 #define nmtstp_normalize_jiffies_time(requested_value, kernel_value)                             \
@@ -141,6 +139,13 @@ int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2);
 
 /*****************************************************************************/
 
+gboolean
+nmtstp_check_platform_full(NMPlatform *platform, guint32 obj_type_flags, gboolean do_assert);
+
+void nmtstp_check_platform(NMPlatform *platform, guint32 obj_type_flags);
+
+/*****************************************************************************/
+
 guint                 nmtstp_wait_for_signal(NMPlatform *platform, gint64 timeout_msec);
 guint                 nmtstp_wait_for_signal_until(NMPlatform *platform, gint64 until_ms);
 const NMPlatformLink *nmtstp_wait_for_link(NMPlatform *platform,
@@ -261,7 +266,7 @@ void     nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr,
                                            guint32                    expected_preferred);
 
 void nmtstp_ip4_address_add(NMPlatform *platform,
-                            gboolean    external_command,
+                            int         external_command,
                             int         ifindex,
                             in_addr_t   address,
                             int         plen,
@@ -271,7 +276,7 @@ void nmtstp_ip4_address_add(NMPlatform *platform,
                             guint32     flags,
                             const char *label);
 void nmtstp_ip6_address_add(NMPlatform     *platform,
-                            gboolean        external_command,
+                            int             external_command,
                             int             ifindex,
                             struct in6_addr address,
                             int             plen,
@@ -280,13 +285,13 @@ void nmtstp_ip6_address_add(NMPlatform     *platform,
                             guint32         preferred,
                             guint32         flags);
 void nmtstp_ip4_address_del(NMPlatform *platform,
-                            gboolean    external_command,
+                            int         external_command,
                             int         ifindex,
                             in_addr_t   address,
                             int         plen,
                             in_addr_t   peer_address);
 void nmtstp_ip6_address_del(NMPlatform     *platform,
-                            gboolean        external_command,
+                            int             external_command,
                             int             ifindex,
                             struct in6_addr address,
                             int             plen);
@@ -452,8 +457,7 @@ const NMPlatformLink *nmtstp_link_get(NMPlatform *platform, int ifindex, const c
 
 gboolean nmtstp_kernel_support_get(NMPlatformKernelSupportType type);
 
-void
-nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up);
+void nmtstp_link_set_updown(NMPlatform *platform, int external_command, int ifindex, gboolean up);
 
 const NMPlatformLnkBridge *
 nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested,
@@ -461,61 +465,78 @@ nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested,
                                           NMPlatformLnkBridge       *dst);
 
 const NMPlatformLink *nmtstp_link_bridge_add(NMPlatform                *platform,
-                                             gboolean                   external_command,
+                                             int                        external_command,
                                              const char                *name,
                                              const NMPlatformLnkBridge *lnk);
 const NMPlatformLink *nmtstp_link_veth_add(NMPlatform *platform,
-                                           gboolean    external_command,
+                                           int         external_command,
                                            const char *name,
                                            const char *peer);
 const NMPlatformLink *
-nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name);
+nmtstp_link_dummy_add(NMPlatform *platform, int external_command, const char *name);
 const NMPlatformLink *nmtstp_link_gre_add(NMPlatform             *platform,
-                                          gboolean                external_command,
+                                          int                     external_command,
                                           const char             *name,
                                           const NMPlatformLnkGre *lnk);
 const NMPlatformLink *nmtstp_link_ip6tnl_add(NMPlatform                *platform,
-                                             gboolean                   external_command,
+                                             int                        external_command,
                                              const char                *name,
                                              const NMPlatformLnkIp6Tnl *lnk);
 const NMPlatformLink *nmtstp_link_ip6gre_add(NMPlatform                *platform,
-                                             gboolean                   external_command,
+                                             int                        external_command,
                                              const char                *name,
                                              const NMPlatformLnkIp6Tnl *lnk);
 const NMPlatformLink *nmtstp_link_ipip_add(NMPlatform              *platform,
-                                           gboolean                 external_command,
+                                           int                      external_command,
                                            const char              *name,
                                            const NMPlatformLnkIpIp *lnk);
 const NMPlatformLink *nmtstp_link_macvlan_add(NMPlatform                 *platform,
-                                              gboolean                    external_command,
+                                              int                         external_command,
                                               const char                 *name,
                                               int                         parent,
                                               const NMPlatformLnkMacvlan *lnk);
 const NMPlatformLink *nmtstp_link_sit_add(NMPlatform             *platform,
-                                          gboolean                external_command,
+                                          int                     external_command,
                                           const char             *name,
                                           const NMPlatformLnkSit *lnk);
 const NMPlatformLink *nmtstp_link_tun_add(NMPlatform             *platform,
-                                          gboolean                external_command,
+                                          int                     external_command,
                                           const char             *name,
                                           const NMPlatformLnkTun *lnk,
                                           int                    *out_fd);
-const NMPlatformLink *nmtstp_link_vrf_add(NMPlatform             *platform,
+const NMPlatformLink *nmtstp_link_vlan_add(NMPlatform              *platform,
+                                           int                      external_command,
+                                           const char              *name,
+                                           int                      parent,
+                                           const NMPlatformLnkVlan *lnk);
+const NMPlatformLink *nmtstp_link_vti_add(NMPlatform             *platform,
                                           gboolean                external_command,
                                           const char             *name,
+                                          const NMPlatformLnkVti *lnk);
+const NMPlatformLink *nmtstp_link_vti6_add(NMPlatform              *platform,
+                                           gboolean                 external_command,
+                                           const char              *name,
+                                           const NMPlatformLnkVti6 *lnk);
+const NMPlatformLink *nmtstp_link_vrf_add(NMPlatform             *platform,
+                                          int                     external_command,
+                                          const char             *name,
                                           const NMPlatformLnkVrf *lnk,
                                           gboolean               *out_not_supported);
 const NMPlatformLink *nmtstp_link_vxlan_add(NMPlatform               *platform,
-                                            gboolean                  external_command,
+                                            int                       external_command,
                                             const char               *name,
                                             const NMPlatformLnkVxlan *lnk);
 
 void nmtstp_link_delete(NMPlatform *platform,
-                        gboolean    external_command,
+                        int         external_command,
                         int         ifindex,
                         const char *name,
                         gboolean    require_exist);
 
+gboolean nmtstp_link_is_iptunnel_special(const NMPlatformLink *link);
+
+gboolean nmtstp_ensure_module(const char *module_name);
+
 /*****************************************************************************/
 
 #define nmtst_object_new_mptcp_addr(...) \
@@ -523,36 +544,47 @@ void nmtstp_link_delete(NMPlatform *platform,
 
 /*****************************************************************************/
 
-extern int NMTSTP_ENV1_IFINDEX;
+extern int               NMTSTP_ENV1_IFINDEXES[2];
+extern const char *const NMTSTP_ENV1_DEVICE_NAME[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)];
+
+#define DEVICE_NAME         "nm-test-device0"
+#define NMTSTP_ENV1_IFINDEX (NMTSTP_ENV1_IFINDEXES[0])
+
 extern int NMTSTP_ENV1_EX;
 
 static inline void
 _nmtstp_env1_wrapper_setup(const NmtstTestData *test_data)
 {
-    int     *p_ifindex;
+    int     *p_ifindexes;
+    gpointer p_n_ifaces;
     gpointer p_ifup;
-
-    nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup);
-
-    g_assert(p_ifindex && *p_ifindex == -1);
+    int      n_ifaces;
+    int      i;
 
     _LOGT("TEST[%s]: setup", test_data->testpath);
 
-    nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE);
+    nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, &p_ifup);
 
-    g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL)));
+    n_ifaces = GPOINTER_TO_UINT(p_n_ifaces);
+    g_assert_cmpint(n_ifaces, >=, 1);
+    g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES));
 
-    *p_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME);
-    g_assert_cmpint(*p_ifindex, >, 0);
-    g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, -1);
+    for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) {
+        g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, 0);
+        g_assert_cmpint(p_ifindexes[i], ==, 0);
+    }
 
-    if (GPOINTER_TO_INT(p_ifup))
-        g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, *p_ifindex, IFF_UP, TRUE) >= 0);
+    for (i = 0; i < n_ifaces; i++) {
+        p_ifindexes[i] = nmtstp_link_dummy_add(NULL, -1, NMTSTP_ENV1_DEVICE_NAME[i])->ifindex;
+        if (GPOINTER_TO_INT(p_ifup))
+            nmtstp_link_set_updown(NULL, -1, p_ifindexes[i], TRUE);
+    }
 
     nm_platform_process_events(NM_PLATFORM_GET);
 
-    NMTSTP_ENV1_IFINDEX = *p_ifindex;
-    NMTSTP_ENV1_EX      = nmtstp_run_command_check_external_global();
+    for (i = 0; i < n_ifaces; i++)
+        NMTSTP_ENV1_IFINDEXES[i] = p_ifindexes[i];
+    NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global();
 }
 
 static inline void
@@ -563,7 +595,7 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data)
     GTestFunc            test_func;
     gconstpointer        d;
 
-    nmtst_test_data_unpack(test_data, NULL, &test_func, &test_func_data, &d, NULL);
+    nmtst_test_data_unpack(test_data, NULL, NULL, &test_func, &test_func_data, &d, NULL);
 
     _LOGT("TEST[%s]: run", test_data->testpath);
     if (test_func)
@@ -575,55 +607,73 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data)
 static inline void
 _nmtstp_env1_wrapper_teardown(const NmtstTestData *test_data)
 {
-    int *p_ifindex;
+    int     *p_ifindexes;
+    gpointer p_n_ifaces;
+    int      n_ifaces;
+    int      i;
 
-    nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, NULL);
+    _LOGT("TEST[%s]: teardown", test_data->testpath);
 
-    g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, *p_ifindex);
-    NMTSTP_ENV1_IFINDEX = -1;
+    nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, NULL);
 
-    _LOGT("TEST[%s]: teardown", test_data->testpath);
+    n_ifaces = GPOINTER_TO_UINT(p_n_ifaces);
+    g_assert_cmpint(n_ifaces, >=, 1);
+    g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES));
+
+    for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) {
+        if (i < n_ifaces)
+            g_assert_cmpint(p_ifindexes[i], >, 0);
+        else
+            g_assert_cmpint(p_ifindexes[i], ==, 0);
+        g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, p_ifindexes[i]);
+        NMTSTP_ENV1_IFINDEXES[i] = 0;
+    }
 
-    g_assert_cmpint(*p_ifindex, ==, nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME));
-    g_assert(nm_platform_link_delete(NM_PLATFORM_GET, *p_ifindex));
+    for (i = 0; i < n_ifaces; i++)
+        nmtstp_link_delete(NULL, -1, p_ifindexes[i], NMTSTP_ENV1_DEVICE_NAME[i], TRUE);
 
     nm_platform_process_events(NM_PLATFORM_GET);
 
     _LOGT("TEST[%s]: finished", test_data->testpath);
 
-    *p_ifindex = -1;
+    for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++)
+        p_ifindexes[i] = 0;
 }
 
 /* add test function, that set's up a particular environment, consisting
  * of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */
-#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \
-    nmtst_add_test_func_full(testpath,                                                  \
-                             _nmtstp_env1_wrapper_run,                                  \
-                             _nmtstp_env1_wrapper_setup,                                \
-                             _nmtstp_env1_wrapper_teardown,                             \
-                             ({                                                         \
-                                 static int _ifindex = -1;                              \
-                                 &_ifindex;                                             \
-                             }),                                                        \
-                             ({                                                         \
-                                 GTestFunc _test_func = (test_func);                    \
-                                 _test_func;                                            \
-                             }),                                                        \
-                             ({                                                         \
-                                 GTestDataFunc _test_func = (test_data_func);           \
-                                 _test_func;                                            \
-                             }),                                                        \
-                             (arg),                                                     \
-                             ({                                                         \
-                                 gboolean _ifup = (ifup);                               \
-                                 GINT_TO_POINTER(_ifup);                                \
+#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, n_ifaces, ifup)  \
+    nmtst_add_test_func_full(testpath,                                                             \
+                             _nmtstp_env1_wrapper_run,                                             \
+                             _nmtstp_env1_wrapper_setup,                                           \
+                             _nmtstp_env1_wrapper_teardown,                                        \
+                             ({                                                                    \
+                                 static int _ifindexes[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)] = {0}; \
+                                 _ifindexes;                                                       \
+                             }),                                                                   \
+                             ({                                                                    \
+                                 guint _n_ifaces = (n_ifaces);                                     \
+                                 GUINT_TO_POINTER(_n_ifaces);                                      \
+                             }),                                                                   \
+                             ({                                                                    \
+                                 GTestFunc _test_func = (test_func);                               \
+                                 _test_func;                                                       \
+                             }),                                                                   \
+                             ({                                                                    \
+                                 GTestDataFunc _test_func = (test_data_func);                      \
+                                 _test_func;                                                       \
+                             }),                                                                   \
+                             (arg),                                                                \
+                             ({                                                                    \
+                                 gboolean _ifup = (ifup);                                          \
+                                 GINT_TO_POINTER(!!_ifup);                                         \
                              }))
 
-#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \
-    _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup)
+#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, n_ifaces, ifup) \
+    _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, n_ifaces, ifup)
 
-#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \
-    _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup)
+#define nmtstp_env1_add_test_func(testpath, test_func, n_ifaces, ifup) \
+    _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, n_ifaces, ifup)
 
 /*****************************************************************************/
 
diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c
index b72bcb65..cc1ec6d1 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -161,8 +161,14 @@ software_add(NMLinkType link_type, const char *name)
                 accept_signals(parent_changed, 1, 2);
             free_signal(parent_changed);
 
-            return NMTST_NM_ERR_SUCCESS(
-                nm_platform_link_vlan_add(NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL));
+            return NMTST_NM_ERR_SUCCESS(nm_platform_link_vlan_add(NM_PLATFORM_GET,
+                                                                  name,
+                                                                  parent_ifindex,
+                                                                  &((NMPlatformLnkVlan){
+                                                                      .id       = VLAN_ID,
+                                                                      .protocol = ETH_P_8021Q,
+                                                                  }),
+                                                                  NULL));
         }
     }
     default:
@@ -1183,7 +1189,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode)
 
         for (i = 0; i < num_peers; i++) {
             NMPWireGuardPeer       peer;
-            char                   s_addr[NM_UTILS_INET_ADDRSTRLEN];
+            char                   s_addr[NM_INET_ADDRSTRLEN];
             NMSockAddrUnion        endpoint;
             guint                  i_allowed_ips, n_allowed_ips;
             NMPWireGuardAllowedIP *allowed_ips;
@@ -1203,8 +1209,8 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode)
                     .in6 =
                         {
                             .sin6_family = AF_INET6,
-                            .sin6_addr   = *nmtst_inet6_from_string(
-                                nm_sprintf_buf(s_addr, "a:b:c:e::1:%d", i)),
+                            .sin6_addr =
+                                nmtst_inet6_from_string(nm_sprintf_buf(s_addr, "a:b:c:e::1:%d", i)),
                             .sin6_port = htons(16000 + i),
                         },
                 };
@@ -1225,7 +1231,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode)
                         nm_sprintf_buf(s_addr, "10.%u.%u.0", i, i_allowed_ips));
                     aip->mask = 32 - (i_allowed_ips % 8);
                 } else {
-                    aip->addr.addr6 = *nmtst_inet6_from_string(
+                    aip->addr.addr6 = nmtst_inet6_from_string(
                         nm_sprintf_buf(s_addr, "a:d:f:%02x:%02x::", i, i_allowed_ips));
                     aip->mask = 128 - (i_allowed_ips % 10);
                 }
@@ -1250,7 +1256,7 @@ _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode)
     r = nm_platform_link_wireguard_change(platform,
                                           ifindex,
                                           &lnk_wireguard,
-                                          (const NMPWireGuardPeer *) peers->data,
+                                          nm_g_array_first_p(peers, const NMPWireGuardPeer),
                                           NULL,
                                           peers->len,
                                           NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY
@@ -1280,8 +1286,11 @@ test_software_detect(gconstpointer user_data)
     const gboolean                   ext        = test_data->external_command;
     NMPlatformLnkBridge              lnk_bridge = {};
     NMPlatformLnkTun                 lnk_tun;
-    NMPlatformLnkGre                 lnk_gre = {};
-    nm_auto_close int                tun_fd  = -1;
+    NMPlatformLnkGre                 lnk_gre  = {};
+    NMPlatformLnkVti                 lnk_vti  = {};
+    NMPlatformLnkVti6                lnk_vti6 = {};
+    nm_auto_close int                tun_fd   = -1;
+    gboolean                         module_loaded;
 
     nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME);
     ifindex_parent =
@@ -1296,24 +1305,24 @@ test_software_detect(gconstpointer user_data)
         lnk_bridge.ageing_time   = 2200;
         lnk_bridge.stp_state     = TRUE;
         lnk_bridge.priority      = 22;
-        lnk_bridge.vlan_protocol = 0x8100;
+        lnk_bridge.vlan_protocol = ETH_P_8021Q;
         lnk_bridge.vlan_stats_enabled =
             nmtstp_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED)
                 ? TRUE
                 : FALSE;
-        lnk_bridge.group_fwd_mask             = 8;
-        lnk_bridge.group_addr                 = (NMEtherAddr){{0x01, 0x80, 0xC2, 0x00, 0x00, 0x08}};
-        lnk_bridge.mcast_snooping             = TRUE;
-        lnk_bridge.mcast_router               = 1;
-        lnk_bridge.mcast_query_use_ifaddr     = TRUE;
-        lnk_bridge.mcast_querier              = TRUE;
-        lnk_bridge.mcast_hash_max             = 1024;
-        lnk_bridge.mcast_last_member_count    = 2;
-        lnk_bridge.mcast_startup_query_count  = 3;
-        lnk_bridge.mcast_last_member_interval = 5000;
-        lnk_bridge.mcast_membership_interval  = 25000;
-        lnk_bridge.mcast_querier_interval     = 26000;
-        lnk_bridge.mcast_query_interval       = 12000;
+        lnk_bridge.group_fwd_mask          = 8;
+        lnk_bridge.group_addr              = NM_ETHER_ADDR_INIT(0x01, 0x80, 0xC2, 0x00, 0x00, 0x08);
+        lnk_bridge.mcast_snooping          = TRUE;
+        lnk_bridge.mcast_router            = 1;
+        lnk_bridge.mcast_query_use_ifaddr  = TRUE;
+        lnk_bridge.mcast_querier           = TRUE;
+        lnk_bridge.mcast_hash_max          = 1024;
+        lnk_bridge.mcast_last_member_count = 2;
+        lnk_bridge.mcast_startup_query_count     = 3;
+        lnk_bridge.mcast_last_member_interval    = 5000;
+        lnk_bridge.mcast_membership_interval     = 25000;
+        lnk_bridge.mcast_querier_interval        = 26000;
+        lnk_bridge.mcast_query_interval          = 12000;
         lnk_bridge.mcast_query_response_interval = 5200;
         lnk_bridge.mcast_startup_query_interval  = 3000;
 
@@ -1322,8 +1331,7 @@ test_software_detect(gconstpointer user_data)
         break;
 
     case NM_LINK_TYPE_GRE:
-    {
-        gboolean gracefully_skip = FALSE;
+        module_loaded = nmtstp_ensure_module("ip_gre");
 
         lnk_gre.local              = nmtst_inet4_from_string("192.168.233.204");
         lnk_gre.remote             = nmtst_inet4_from_string("172.168.10.25");
@@ -1332,13 +1340,8 @@ test_software_detect(gconstpointer user_data)
         lnk_gre.tos                = 37;
         lnk_gre.path_mtu_discovery = TRUE;
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gre0")) {
-            /* Seems that the ip_gre module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
-        }
-
         if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip(
                     "Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)");
                 goto out_delete_parent;
@@ -1346,10 +1349,9 @@ test_software_detect(gconstpointer user_data)
             g_error("Failed adding GRE tunnel");
         }
         break;
-    }
+
     case NM_LINK_TYPE_GRETAP:
-    {
-        gboolean gracefully_skip = FALSE;
+        module_loaded = nmtstp_ensure_module("ip_gre");
 
         lnk_gre.local              = nmtst_inet4_from_string("192.168.1.133");
         lnk_gre.remote             = nmtst_inet4_from_string("172.168.101.2");
@@ -1359,13 +1361,8 @@ test_software_detect(gconstpointer user_data)
         lnk_gre.path_mtu_discovery = FALSE;
         lnk_gre.is_tap             = TRUE;
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gretap0")) {
-            /* Seems that the ip_gre module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0;
-        }
-
         if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip("Cannot create gretap tunnel because of missing ip_gre module "
                             "(modprobe ip_gre)");
                 goto out_delete_parent;
@@ -1373,16 +1370,12 @@ test_software_detect(gconstpointer user_data)
             g_error("Failed adding GRETAP tunnel");
         }
         break;
-    }
+
     case NM_LINK_TYPE_IPIP:
     {
-        NMPlatformLnkIpIp lnk_ipip        = {};
-        gboolean          gracefully_skip = FALSE;
+        NMPlatformLnkIpIp lnk_ipip = {};
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "tunl0")) {
-            /* Seems that the ipip module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ipip", NULL) != 0;
-        }
+        module_loaded = nmtstp_ensure_module("ipip");
 
         lnk_ipip.local              = nmtst_inet4_from_string("1.2.3.4");
         lnk_ipip.remote             = nmtst_inet4_from_string("5.6.7.8");
@@ -1391,7 +1384,7 @@ test_software_detect(gconstpointer user_data)
         lnk_ipip.path_mtu_discovery = FALSE;
 
         if (!nmtstp_link_ipip_add(NULL, ext, DEVICE_NAME, &lnk_ipip)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip(
                     "Cannot create ipip tunnel because of missing ipip module (modprobe ipip)");
                 goto out_delete_parent;
@@ -1400,20 +1393,17 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
     case NM_LINK_TYPE_IP6TNL:
     {
-        NMPlatformLnkIp6Tnl lnk_ip6tnl      = {};
-        gboolean            gracefully_skip = FALSE;
+        NMPlatformLnkIp6Tnl lnk_ip6tnl = {};
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6tnl0")) {
-            /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_tunnel", NULL) != 0;
-        }
+        module_loaded = nmtstp_ensure_module("ip6_tunnel");
 
         switch (test_data->test_mode) {
         case 0:
-            lnk_ip6tnl.local          = *nmtst_inet6_from_string("fd01::15");
-            lnk_ip6tnl.remote         = *nmtst_inet6_from_string("fd01::16");
+            lnk_ip6tnl.local          = nmtst_inet6_from_string("fd01::15");
+            lnk_ip6tnl.remote         = nmtst_inet6_from_string("fd01::16");
             lnk_ip6tnl.parent_ifindex = ifindex_parent;
             lnk_ip6tnl.tclass         = 20;
             lnk_ip6tnl.encap_limit    = 6;
@@ -1421,8 +1411,8 @@ test_software_detect(gconstpointer user_data)
             lnk_ip6tnl.proto          = IPPROTO_IPV6;
             break;
         case 1:
-            lnk_ip6tnl.local          = *nmtst_inet6_from_string("fd01::17");
-            lnk_ip6tnl.remote         = *nmtst_inet6_from_string("fd01::18");
+            lnk_ip6tnl.local          = nmtst_inet6_from_string("fd01::17");
+            lnk_ip6tnl.remote         = nmtst_inet6_from_string("fd01::18");
             lnk_ip6tnl.parent_ifindex = ifindex_parent;
             lnk_ip6tnl.tclass         = 0;
             lnk_ip6tnl.encap_limit    = 0;
@@ -1433,7 +1423,7 @@ test_software_detect(gconstpointer user_data)
         }
 
         if (!nmtstp_link_ip6tnl_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip("Cannot create ip6tnl tunnel because of missing ip6_tunnel module "
                             "(modprobe ip6_tunnel)");
                 goto out_delete_parent;
@@ -1442,25 +1432,22 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
     case NM_LINK_TYPE_IP6GRE:
     {
-        NMPlatformLnkIp6Tnl lnk_ip6tnl      = {};
-        gboolean            gracefully_skip = FALSE;
+        NMPlatformLnkIp6Tnl lnk_ip6tnl = {};
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) {
-            /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
-        }
+        module_loaded = nmtstp_ensure_module("ip6_gre");
 
-        lnk_ip6tnl.local          = *nmtst_inet6_from_string("fd01::42");
-        lnk_ip6tnl.remote         = *nmtst_inet6_from_string("fd01::aaaa");
+        lnk_ip6tnl.local          = nmtst_inet6_from_string("fd01::42");
+        lnk_ip6tnl.remote         = nmtst_inet6_from_string("fd01::aaaa");
         lnk_ip6tnl.parent_ifindex = ifindex_parent;
         lnk_ip6tnl.tclass         = 21;
         lnk_ip6tnl.flow_label     = 1338;
         lnk_ip6tnl.is_gre         = TRUE;
 
         if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip("Cannot create ip6gre tunnel because of missing ip6_gre module "
                             "(modprobe ip6_gre)");
                 goto out_delete_parent;
@@ -1469,18 +1456,15 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
     case NM_LINK_TYPE_IP6GRETAP:
     {
-        NMPlatformLnkIp6Tnl lnk_ip6tnl      = {};
-        gboolean            gracefully_skip = FALSE;
+        NMPlatformLnkIp6Tnl lnk_ip6tnl = {};
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) {
-            /* Seems that the ip6_tunnel module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0;
-        }
+        module_loaded = nmtstp_ensure_module("ip6_gre");
 
-        lnk_ip6tnl.local          = *nmtst_inet6_from_string("fe80::abcd");
-        lnk_ip6tnl.remote         = *nmtst_inet6_from_string("fc01::bbbb");
+        lnk_ip6tnl.local          = nmtst_inet6_from_string("fe80::abcd");
+        lnk_ip6tnl.remote         = nmtst_inet6_from_string("fc01::bbbb");
         lnk_ip6tnl.parent_ifindex = ifindex_parent;
         lnk_ip6tnl.ttl            = 10;
         lnk_ip6tnl.tclass         = 22;
@@ -1489,7 +1473,7 @@ test_software_detect(gconstpointer user_data)
         lnk_ip6tnl.is_tap         = TRUE;
 
         if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip("Cannot create ip6gretap tunnel because of missing ip6_gre module "
                             "(modprobe ip6_gre)");
                 goto out_delete_parent;
@@ -1498,6 +1482,7 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
     case NM_LINK_TYPE_MACVLAN:
     {
         NMPlatformLnkMacvlan  lnk_macvlan = {};
@@ -1531,6 +1516,7 @@ test_software_detect(gconstpointer user_data)
             g_error("Failed adding MACVLAN interface");
         break;
     }
+
     case NM_LINK_TYPE_MACVTAP:
     {
         NMPlatformLnkMacvlan lnk_macvtap = {};
@@ -1543,10 +1529,12 @@ test_software_detect(gconstpointer user_data)
             g_error("Failed adding MACVTAP interface");
         break;
     }
+
     case NM_LINK_TYPE_SIT:
     {
-        NMPlatformLnkSit lnk_sit         = {};
-        gboolean         gracefully_skip = FALSE;
+        NMPlatformLnkSit lnk_sit = {};
+
+        module_loaded = nmtstp_ensure_module("sit");
 
         lnk_sit.local              = nmtst_inet4_from_string("192.168.200.1");
         lnk_sit.remote             = nmtst_inet4_from_string("172.25.100.14");
@@ -1555,13 +1543,8 @@ test_software_detect(gconstpointer user_data)
         lnk_sit.tos                = 31;
         lnk_sit.path_mtu_discovery = FALSE;
 
-        if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "sit0")) {
-            /* Seems that the sit module is not loaded... try to load it. */
-            gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "sit", NULL) != 0;
-        }
-
         if (!nmtstp_link_sit_add(NULL, ext, DEVICE_NAME, &lnk_sit)) {
-            if (gracefully_skip) {
+            if (!module_loaded) {
                 g_test_skip(
                     "Cannot create sit tunnel because of missing sit module (modprobe sit)");
                 goto out_delete_parent;
@@ -1570,11 +1553,31 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
     case NM_LINK_TYPE_VLAN:
-        nmtstp_run_command_check("ip link add name %s link %s type vlan id 1242",
-                                 DEVICE_NAME,
-                                 PARENT_NAME);
+    {
+        NMPlatformLnkVlan lnk_vlan = {};
+
+        switch (test_data->test_mode) {
+        case 0:
+            lnk_vlan.id       = 1242;
+            lnk_vlan.protocol = ETH_P_8021Q;
+            lnk_vlan.flags    = _NM_VLAN_FLAG_REORDER_HEADERS;
+            break;
+        case 1:
+            lnk_vlan.id       = 4094;
+            lnk_vlan.protocol = ETH_P_8021AD;
+            lnk_vlan.flags    = _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP;
+            break;
+        default:
+            nm_assert_not_reached();
+        }
+
+        if (!nmtstp_link_vlan_add(NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_vlan))
+            g_error("Failed adding VLAN interface");
         break;
+    }
+
     case NM_LINK_TYPE_VRF:
     {
         NMPlatformLnkVrf lnk_vrf = {};
@@ -1591,6 +1594,45 @@ test_software_detect(gconstpointer user_data)
         }
         break;
     }
+
+    case NM_LINK_TYPE_VTI:
+        module_loaded = nmtstp_ensure_module("ip_vti");
+
+        lnk_vti.local          = nmtst_inet4_from_string("192.168.212.204");
+        lnk_vti.remote         = nmtst_inet4_from_string("172.168.11.25");
+        lnk_vti.parent_ifindex = ifindex_parent;
+        lnk_vti.ikey           = 12;
+        lnk_vti.okey           = 13;
+
+        if (!nmtstp_link_vti_add(NULL, ext, DEVICE_NAME, &lnk_vti)) {
+            if (!module_loaded) {
+                g_test_skip(
+                    "Cannot create vti tunnel because of missing vti module (modprobe ip_vti)");
+                goto out_delete_parent;
+            }
+            g_error("Failed adding VTI tunnel");
+        }
+        break;
+
+    case NM_LINK_TYPE_VTI6:
+        module_loaded = nmtstp_ensure_module("ip6_vti");
+
+        lnk_vti6.local          = nmtst_inet6_from_string("fd01::1");
+        lnk_vti6.remote         = nmtst_inet6_from_string("fd02::2");
+        lnk_vti6.parent_ifindex = ifindex_parent;
+        lnk_vti6.ikey           = 13;
+        lnk_vti6.okey           = 14;
+
+        if (!nmtstp_link_vti6_add(NULL, ext, DEVICE_NAME, &lnk_vti6)) {
+            if (!module_loaded) {
+                g_test_skip(
+                    "Cannot create vti6 tunnel because of missing vti module (modprobe ip_vti)");
+                goto out_delete_parent;
+            }
+            g_error("Failed adding VTI6 tunnel");
+        }
+        break;
+
     case NM_LINK_TYPE_VXLAN:
     {
         NMPlatformLnkVxlan lnk_vxlan = {};
@@ -1608,8 +1650,8 @@ test_software_detect(gconstpointer user_data)
         case 1:
             lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME);
             lnk_vxlan.id             = 11214423;
-            lnk_vxlan.local6         = *nmtst_inet6_from_string("1:2:3:4:334:23::23");
-            lnk_vxlan.group6         = *nmtst_inet6_from_string("ff0e::115");
+            lnk_vxlan.local6         = nmtst_inet6_from_string("1:2:3:4:334:23::23");
+            lnk_vxlan.group6         = nmtst_inet6_from_string("ff0e::115");
             lnk_vxlan.ttl            = 32;
             lnk_vxlan.dst_port       = 57412;
             lnk_vxlan.src_port_min   = 1000;
@@ -1622,6 +1664,7 @@ test_software_detect(gconstpointer user_data)
         g_assert(nmtstp_link_vxlan_add(NULL, ext, DEVICE_NAME, &lnk_vxlan));
         break;
     }
+
     case NM_LINK_TYPE_TUN:
     {
         gboolean owner_valid = nmtst_get_rand_bool();
@@ -1656,6 +1699,7 @@ test_software_detect(gconstpointer user_data)
                                      (!lnk_tun.persist || nmtst_get_rand_bool()) ? &tun_fd : NULL));
         break;
     }
+
     case NM_LINK_TYPE_WIREGUARD:
     {
         const NMPlatformLink *link;
@@ -1727,7 +1771,7 @@ test_software_detect(gconstpointer user_data)
             g_assert_cmpint(plnk->ageing_time, ==, lnk_bridge_norm->ageing_time);
             g_assert_cmpint(plnk->stp_state, ==, TRUE);
             g_assert_cmpint(plnk->priority, ==, 22);
-            g_assert_cmpint(plnk->vlan_protocol, ==, 0x8100);
+            g_assert_cmpint(plnk->vlan_protocol, ==, ETH_P_8021Q);
             g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge_norm->vlan_stats_enabled);
             g_assert_cmpint(plnk->group_fwd_mask, ==, 8);
             g_assert_cmpint(plnk->mcast_snooping, ==, TRUE);
@@ -1910,7 +1954,19 @@ test_software_detect(gconstpointer user_data)
             const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan;
 
             g_assert(plnk == nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, NULL));
-            g_assert_cmpint(plnk->id, ==, 1242);
+
+            switch (test_data->test_mode) {
+            case 0:
+                g_assert_cmpint(plnk->id, ==, 1242);
+                g_assert_cmpint(plnk->protocol, ==, ETH_P_8021Q);
+                g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_REORDER_HEADERS);
+                break;
+            case 1:
+                g_assert_cmpint(plnk->id, ==, 4094);
+                g_assert_cmpint(plnk->protocol, ==, ETH_P_8021AD);
+                g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP);
+                break;
+            }
             break;
         }
         case NM_LINK_TYPE_VRF:
@@ -1921,6 +1977,22 @@ test_software_detect(gconstpointer user_data)
             g_assert_cmpint(plnk->table, ==, 9876);
             break;
         }
+        case NM_LINK_TYPE_VTI:
+        {
+            const NMPlatformLnkVti *plnk = &lnk->lnk_vti;
+
+            g_assert(plnk == nm_platform_link_get_lnk_vti(NM_PLATFORM_GET, ifindex, NULL));
+            g_assert(nm_platform_lnk_vti_cmp(plnk, &lnk_vti) == 0);
+            break;
+        }
+        case NM_LINK_TYPE_VTI6:
+        {
+            const NMPlatformLnkVti6 *plnk = &lnk->lnk_vti6;
+
+            g_assert(plnk == nm_platform_link_get_lnk_vti6(NM_PLATFORM_GET, ifindex, NULL));
+            g_assert(nm_platform_lnk_vti6_cmp(plnk, &lnk_vti6) == 0);
+            break;
+        }
         case NM_LINK_TYPE_VXLAN:
         {
             const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan;
@@ -2511,7 +2583,7 @@ test_create_many_links_do(guint n_devices)
         if (EX == 2)
             nmtstp_run_command_check("ip link delete %s", name);
         else
-            nmtstp_link_delete(NULL, EX, g_array_index(ifindexes, int, i), name, TRUE);
+            nmtstp_link_delete(NULL, EX, nm_g_array_index(ifindexes, int, i), name, TRUE);
     }
 
     _LOGI(">>> process events after deleting devices...");
@@ -2754,7 +2826,7 @@ _test_netns_create_platform(void)
     netns = nmp_netns_new();
     g_assert(NMP_IS_NETNS(netns));
 
-    platform = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    platform = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     g_assert(NM_IS_LINUX_PLATFORM(platform));
 
     nmp_netns_pop(netns);
@@ -2843,7 +2915,7 @@ test_netns_general(gpointer fixture, gconstpointer test_data)
     if (_check_sysctl_skip())
         return;
 
-    platform_1 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    platform_1 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     platform_2 = _test_netns_create_platform();
 
     /* add some dummy devices. The "other-*" devices are there to bump the ifindex */
@@ -2971,7 +3043,7 @@ test_netns_set_netns(gpointer fixture, gconstpointer test_data)
     if (_test_netns_check_skip())
         return;
 
-    platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     platforms[1] = platform_1 = _test_netns_create_platform();
     platforms[2] = platform_2 = _test_netns_create_platform();
 
@@ -3070,7 +3142,7 @@ test_netns_push(gpointer fixture, gconstpointer test_data)
     if (_check_sysctl_skip())
         return;
 
-    pl[0].platform = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    pl[0].platform = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     pl[1].platform = platform_1 = _test_netns_create_platform();
     pl[2].platform = platform_2 = _test_netns_create_platform();
 
@@ -3217,7 +3289,7 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data)
     if (_test_netns_check_skip())
         return;
 
-    platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     platforms[1] = platform_1 = _test_netns_create_platform();
     platforms[2] = platform_2 = _test_netns_create_platform();
 
@@ -3382,7 +3454,7 @@ test_sysctl_netns_switch(void)
     if (_test_netns_check_skip())
         return;
 
-    platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+    platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
     platforms[1] = platform_1 = _test_netns_create_platform();
     platforms[2] = platform_2 = _test_netns_create_platform();
     PL                        = platforms[nmtst_get_rand_uint32() % 3];
@@ -3823,7 +3895,10 @@ _nmtstp_setup_tests(void)
         test_software_detect_add("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
         test_software_detect_add("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0);
         test_software_detect_add("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0);
-        test_software_detect_add("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0);
+        test_software_detect_add("/link/software/detect/vlan/0", NM_LINK_TYPE_VLAN, 0);
+        test_software_detect_add("/link/software/detect/vlan/1", NM_LINK_TYPE_VLAN, 1);
+        test_software_detect_add("/link/software/detect/vti", NM_LINK_TYPE_VTI, 0);
+        test_software_detect_add("/link/software/detect/vti6", NM_LINK_TYPE_VTI6, 0);
         test_software_detect_add("/link/software/detect/vrf", NM_LINK_TYPE_VRF, 0);
         test_software_detect_add("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0);
         test_software_detect_add("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1);
diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c
index c18886c9..cebd44f6 100644
--- a/src/core/platform/tests/test-platform-general.c
+++ b/src/core/platform/tests/test-platform-general.c
@@ -31,7 +31,7 @@ test_init_linux_platform(void)
 {
     gs_unref_object NMPlatform *platform = NULL;
 
-    platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
+    platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
 }
 
 /*****************************************************************************/
@@ -42,7 +42,7 @@ test_link_get_all(void)
     gs_unref_object NMPlatform  *platform = NULL;
     gs_unref_ptrarray GPtrArray *links    = NULL;
 
-    platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
+    platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
 
     links = nm_platform_link_get_all(platform, TRUE);
 }
@@ -101,456 +101,486 @@ static void
 test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
 {
     static const char *const EXPECTED_BUFFER[3] = {
-        [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218074e48000000cb504e03d53e1124c5"
-               "0065aeb2e6fbf952902383028e3b47f2111d891d17a47631c169f11ff9119c40b468b66331bca67a"
-               "034f43340d02683c7545097aea4a849f060ddf57004a5be9b10123e6e4f15ecd1dff045298cfee06"
-               "b7f0318a03000000061db3dd7fde25654a4059d565dbc8a91e014457000000db56189572f32f4af5"
-               "00d785d5c1b5aad487c192f08bd7a09bdd2144439c88b02a0300000004b0595daf37deb499996bfb"
-               "667f072dec035d9c000000f4409bcfb3c167090e00d90a8543961b2ccd47724a3c460ba85183f4c5"
-               "a7afe28303000000d05ff720b16c73761a3fcb675219061ab402c7940000006a883c146972decb8b"
-               "00dac8f5e6d66df46ea139107754dee62c36d2fc6aae660b03000000fc1fb23d24438387def0f4c6"
-               "544e4b275d037146000000d349adc17647176b7c00d856e3b883efebc09dd9d61966b7ae7412041d"
-               "e5fe7d360300000066a3ae3939762df33a2d55060c78d551cb0310fd00000043b9aca07b6e4925dd"
-               "00943a272c171ed15abbbe1cd911db7b56e8f3fe6f0e9e6903000000ec2e2924348352d715cf9d41"
-               "1ea012e5300294b6000000c528ab20075efee38d006f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26"
-               "dac12ebd03000000667224dae775e5ccd141aca2ff0f57676702648100000086b62f22ca33478e1f"
-               "001715df7fc6a902edae579e2e10c7f7a22ba0340436242c03000000ee3fbc164632d4f28aa28c08"
-               "f80dd50c6702dfb400000023a28fa772a19cdf6d00c173f6078a806d329c9b008bbfc9723107f3f5"
-               "bcfe97e1030000007ad742d3d7320a4f880cf47f5dd0cf69cb0d840f0000007749509cb6752b2cee"
-               "00b7e7736a0afc9879ea40e69710fc9f6e8e99cdabf85f0c0300000008d33a2ea67776d7f88d69c8"
-               "c7e3b2d3c900f05400000021abb42316c533d9c90088189c471a1ff2f0ce3ff66e782110125ae2ed"
-               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6002f061d45d864d09f4ecf17625f82d"
-               "00c748288ae21e371051852c972ea7954079884a6c0fc09c03000000809829af4a9df58dbfef186d"
-               "416f3a1ef102d10f000000675203d82a3c84c4eb274ed18687780281d47a32f2235376239daeaacb"
-               "3cae864b0300000091921681c2162b9bfa40546cdb544e44df037c270000009a051bb2ee8192ac0c"
-               "fa3abc1ea0ab3e7d75a2f42b50c6a363401323780422ae3f03000000c43bd794c697895e5bfe47c6"
-               "f53a54e01b021d89000000d3e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38"
-               "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff02f2560000000bd7d5f14870c6d18d"
-               "6242b356955e1a13d1111b067acbce3e3bcdf382bb0ec36f0300000064a2827b7cdd38d0314c178e"
-               "925b568ae512c4e9000000e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
-               "36674cde0300000054cfe8f42bf0e9381ba2c4396de60f032b00f34de2959871c0d4c0d4eb720c4a"
-               "e3c9472e5f2da5d9c5cfd9f2343b21362c19a09269121aec030000000fcc00ac2daa755e5292cd32"
-               "dc096cd5d802c5cf3351ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8"
-               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa70031205000000f39b098b43cd68be6e"
-               "3f81268193fd637e585287dc6ca972a2cfb18b2bf6989d2d030000006ff7add5df2c93e05459507b"
-               "5d8c6a0f460554d2000000a8c67b2e48b47003c81785d3ffdbd9a617857ae8126b028c0e3e295354"
-               "bc51e540030000006c3d048a92d3adc69a84eb47622400207700416a00000087fbd7e2f7dea0077a"
-               "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183607c52a52d030000004e8e8bc17c01ac63eca0c1cf"
-               "bb204132fb0213e4000000c2303dd1d65c7b2cffeca6d1adaccae11f8045a80f855ebf3b772eb2b1"
-               "bae87d60030000005d31e5b2de252167c1c91b3a36ba0c700e118477000000b913a353edc66efc06"
-               "8829c0e66ad0143a0554efb46b382ab0404cdf027a899f2d030000009bf5a5d06776e66a9d80ab13"
-               "2e1ac921eb01adbb0000002f561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
-               "daae7d6303000000c0982b283aa9986700b2a2b3ed257b8b041df48f000000cac72105b327335a25"
-               "a364d1015ffac03089f45539e1b0178462156cd6c4e1966803000000b3121cd5ef51e696c816290d"
-               "baee0e77260a82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4"
-               "d03bc90503000000eb760e3f004a2efc8ffc96b89b1bc8f415034e77be46bae5a1b3cae76665a268"
-               "abfe8a41d149f074a378b881153ece68ade15cec7625ca4a03000000e682f80f94315c45ef817264"
-               "c89a736ed51dd637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5"
-               "2af145480300000004265f6c72923e22594f3f79e208ed2e2e020771db46a54cadb245eafde6b025"
-               "67f814de39dd8f88152a58454d9ca9d20f45dfa700154d12020000009edfa7a74c1c83c18e47935e"
-               "8354846e3403386a000000d94064f6b021281e230094ae20ac26c7d41fd050bf8d85055bcfe3756c"
-               "98c39e7c02000000efc57ff447036b9551adcb22fd50793f79103c7d0000006e759d0c90bc512e13"
-               "00cef5e4a27985bd8d1e69c83dc21056b973972c6855676c02000000017c9148da5892bb4951c3a7"
-               "ed55689d3305ce7d3fd48469845f0c233dbdd2a90035df1f1782dee88487fd0db5971b46b7a350f9"
-               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a620"
-               "00660bbf024b03ff0a8e27c405e6424457393c6f74993ade02000000e62112898dab2dade2ab2fc9"
-               "c56a7c86be0296228ec3f2f439d949b22b9c161c00b745cebdd06352cb2c354434819f4b248eb2b8"
-               "6297dfa702000000fd0d53bb6215de4bb6f6f3d031a790287e06e9c18bb836c0a25ea71f5daaed4d"
-               "00eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a46e91b91502000000be5b534e0a6527f67485ab35"
-               "aca0c7ee4102338500000063e66e3a23d6732578acd14320aefd4503189f7630248e501c9052c22b"
-               "e3ff1a5f02000000b4425486c619147eb0216050ed7afd741000e83600000011f9c797c0c0ec468e"
-               "9de5a9caf493472201e48d163106cc397446a33fa349e1cc02000000b879a45176740744fcb6a035"
-               "339d504c9719d80d000000df765b4d9a130257dc5ff7f03a9518862bc9a89192c1c8be55281a59d1"
-               "bcd7be0702000000e45aaf61814f703b40125e6baf4648cbaa1f7b86000000dafe7d6ec4ac1ea051"
-               "010911915ddb05f2a84e27c18a06e2c9cb29efdfb56e03e0010000002f70249eefc67bce716c856e"
-               "3973dc42a1033be9000000efb1f14d985d5bbe6c00085f3d6176f78a19bb8df1804c122fce5078c1"
-               "57d6249b010000007dc7f17f666969e45c3baf7a119a65ee0b01fa9e000000f3de6dc8dfcf2a3ad3"
-               "00cc0fa3b314f695bd82e9ea8891ed0a3d1b91ca4f1b134801000000449a774929e86716ec852d59"
-               "f3d17232ed026ad600000068843a9aef34983882004f24a17dba1ee7e143fd52599364e13468f80f"
-               "fc1519de010000002545666937c22a3b6e9686dcc405cbee5000c2520000001f7a0165fe20744edb"
-               "00a04f146e4c699341b95f41d9c1e67743efb54e54270e46010000006a894e387625da376feecb80"
-               "ac245409c003cc2700000067179bff0644399ae70064ed0432d599eaa660ac824b7fae389861419c"
-               "9f3c2c8801000000f51f44e644d7cf1d06b2b115d882549e62002fba5b1cdec48d10bbb13b8313d8"
-               "002a24d34fc812bd2f8a59d96d064126666befbb81ae55f0010000000aa20624fbab244735d67c61"
-               "283031667b0274a103e0b1b153222aa590a2312e00f1a35900459582f4ef43c780908872746e39ef"
-               "536c75bb01000000720da9e07ac998b5690807d52617369ee1004ca500000081af5fd7ceacdca10f"
-               "00d7b351a36b178aabc78a4a0d80684ee5dbf45bc7a4c65601000000cd625009a40aeb62a42b6a62"
-               "548e3c38b9002002000000fbfaf1809becd2506300a6da29b2e94d3ab51fe0e9af98b180a36035fb"
-               "f05d7308010000009d82c856ba942455050f63f8282f34644402acf5000000a54b7892f487afa98c"
-               "0021c19c69d9942f460bcea75481f25e307d0de959747cf201000000ab2dadc5a39411fd4ff1116d"
-               "478987316a0e3fc2000000cfc6ebe434a7ae8ff040483e310819e3b10db116431ec6f7691539ce41"
-               "d514573f01000000f6932f6932d1d5f5d0ce2cf6bf7a70ba5100a1620000001b11d9172cf84a0347"
-               "8b82cdd8883b0f3fd6ed84d6c959e553b887edcdf90e54b501000000a779f37c30d2c5053c40ecae"
-               "210aeb0934012a247000b96d06992b6a58acd3995b9663d2df48f333ef467092818aa77d6732b678"
-               "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e785415032b7000000043e0f79ab6902dde53"
-               "36b3ec7127e19f254ac9588319558041fc62ed564153e174010000004d89c0bb5408c1b7d17fb084"
-               "d9e1825fa602e1af0000004b7f9e85ff73e050a2cd89fd276e08cb887464b9344ec3127c750fabdd"
-               "9985d62701000000e491cbbbf9443fd6a77eb3c5bf64b671d6068dbf0000008306c83bc026977911"
-               "4bdade862c224f6f36506ebd455e679cef369bb8fc2b445701000000496ad01bf007eacb0a28aec8"
-               "68282510b61e91ea6480e356def85b75827b26e8a0bef8675b29a197b7b3cceaf5f1bb12c535256a"
-               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e1700b60000006dae02dfa0c26e7c3a"
-               "00603bd35e7fa0f02f2f3313d8469d09a92409c0f257b044000000006a4c619f9037d7a754655354"
-               "c47cf0f5510167700000001bef820fe02931f2d30075049150b8d10ab700cc3a7cf51be0403b654b"
-               "10613fc90000000016a31dc2e46a234558817151b38fc9c909038d71000000d761fdf274fc5dca8c"
-               "00bd1871ae17bb4515856092b4d9e89b676761b2eecc89f600000000d635edea8ffe423d01883918"
-               "039249f39800b37e00000053b2a16e643722d9b700c0d8a41fb7c8307f06b4b29088f20d9ac676d5"
-               "4d4647f5000000000f88e3759a08e7a56663861eea1bf42ccd0572f200000057ca82ac5180101be4"
-               "005cef468ea086ea9aaafdc388811ec787bc45db50da49bf00000000d94beb259f9f0251becc9879"
-               "07879cca681ec7bb000000df248b49953d136cac00054b019402a4c2167721673f2b02b3256ead1f"
-               "02e9b69000000000eacce745ea213cb1a84035b632ef3be6071fafa202187470e9da55709427c226"
-               "0024d9a086ed27184b443181a5edc511c1507a142fea9bc10000000062fb81e733cff620ca7feefe"
-               "1933631e8e03f6d90000002f6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
-               "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d06753001b04000000bb9f2b8a1a8982c42f"
-               "53700a3437c5156e072b2f2fb337c9cfe07c34ddb83579b80000000072437f9b6245de39ec9e71ee"
-               "4b951507be002b340000000f895d5f24037d233302ad3b82d639272e4a46eadfbd2146bf8cfdb205"
-               "ebfdcec30000000067a982be8a934f852cc3d4d82bc0ec7303129f8f00000087965a316af2abb982"
-               "b001ec16901f55f960c55c22f32099a8020bea2a25a5927300000000c4458d70911714f1819aa46f"
-               "be143934c900ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa"
-               "b4d54acb00000000a417f3da01d99dae5f190096cc582cabd51fd15a0000003edc5aa115165eb914"
-               "d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72f4cc43c5100000000c2aac1d67a909b297c703915"
-               "312e9c3c5a03c40b20ea99d5ab90135fa636af4464d210a256be75e0509c4477d19ac64b2e69220d"
-               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec17fb112087b97cfcebbaeb0c565359"
-               "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9b2cd3dbd00000000e958e48d2ab8b0313e7f8933"
-               "e315130fa9034c8288f83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5"
-               "70ccc567df7a49b8a4c73cf5ffa4ffc8a4bab14874afdf54e60fe816a9fe07ca0675e09818e9bbec"
-               "0018c59068baf76ab08940f6fc3bbdc7f6090f7512ea3b9503000000fb95e8332fdfff658483a2d0"
-               "39a7bf148e01481ea9fec460e89f7abdb682380a00eae374835b4a49a2b980b6aba92da6409969aa"
-               "fd338f35030000006e8cc34573654ea6624f138ef9531cd9360002e4a9fe989477a363d55ad47b8a"
-               "11090df8da40d88d40a6d75bc404156225b7eedeb95c593103000000dbe9962069e66142e2275334"
-               "4fda8a3e0b001cf7a9fedd68269b53eccb44a831714acfb58c54dda9ac3bb1b1d43e6505621b9a7f"
-               "1e0f515603000000f57609453df7803357ae79989870ccec3d000ad9a9fee016663fe6ee0228c1da"
-               "5f5b481b9f05a16768cb3bc873b04d937a6ed8f7d7ac5f9903000000822a272bec4501a1e27acfee"
-               "7a8588ffd5016caea9fee59560fad65d67c624f285d7174177a47579dda0b6ead1e1b34901eaedd5"
-               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a011480a9fec42cac4ef3ad378ffec7"
-               "085a62475588456e4ead7cbda620a3abae816e345d68265d020000009b7a559be3acf4fc36fa6b51"
-               "3cf09e8b99009462a9fe10acfce165e332a623840004e5bab49185ad9b7d0070a36dd51323b2c542"
-               "dedea2030200000002a1f8e6f5657f3f9e3eb807cbad76451002edb8a9fe628a70456d79f25d5895"
-               "0057fa60d9279fb2b8fcbac6abea74f7a6df7979b3980deb020000006c1de913df1424ce3ee1d76f"
-               "c1ea76e98c00fc78a9feb53e27c21963f5b4cb2454830c68e44ea74b4f2b83f9b3bee14d861a4c9e"
-               "bc74179402000000438a72e12ae0436a2495a609675344f7e200a5eba9fec85f0a1742f70abe95c5"
-               "0345132a61eda239d9d083c3bf085387046ef8a3b0916e5d020000004a503a712350acbd48411f0d"
-               "c15d2f0f4903d345a9fe06954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
-               "9a6ce5d902000000a6a5ee19d6f63fe9cbdd01f7246f13b2050524a2a9fe5ef7a029c896b4132bd8"
-               "95072cfeffe9d6812231bb02f922259b173c333c1dce2f83010000003a3efc240b9977e166bee737"
-               "fe73670c4301644ca9fe59b4cd20eb7dd3d38785008ffad45e0dfe491d1ba96742c7b5e02b227122"
-               "a9a89f8701000000fcdf6d9be94030b34774d1d7dddedd989901f627a9fe965a87041331b2834bcf"
-               "00b4e3ce848518c4e3f6cbf25e5e1b990fc46767e36f90d401000000fd41fe47684b370b6ec6584d"
-               "64496089570368eaa9feae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11b9ef1b21"
-               "74e7860401000000cd7db68b9ae586dacdb4070c50320427c4033d03a9fef22c636360d696be085a"
-               "007c499dd538d34574c038404923e75d0e09e2fcaefd545b010000007ced4f8b362956cd46646a0e"
-               "222160e5f700bb29a9feed37a2123c5b6a813387e99bb834a19da86c64fefab2548759d313a5b92d"
-               "c896c9a7010000000a5f6abd850b06e3d9662795ce2cc1cee01702aba9feaaa218dc091e9aa1477f"
-               "6fdc9830d7fbc95829a8838314dff34d24c332219a1b163a000000000e2f4f911a0dd8c052ed6b13"
-               "e2815a1a37002bdda9fef1018b587eed72bb58f000201f17bd99fcf72909ac9e7a55299e9bb4fd53"
-               "7666c05300000000ffa8e89871fb9510b900b2061d0c334b81022dd1a9fe4b0f9a60e0e289f08db3"
-               "00bafc4e095a3230fde69eec3af2e6d0bd68ab722cb749e2000000000eb4f73e75039fcf251b32fc"
-               "79685b05dd02aa9ba9fed2e40edb4d758fb554158ae5c7c00d58fbcc58fc4c2fd0d44ff1e51c3515"
-               "40b26f6600000000badfad0fe8fa8f3c1aad2a82530b7447d902f401a9fe2f4c329f997184c9d257"
-               "8e4cf9c48147507e1cd59ea1ae6da48b1eba6d16672d2fa500000000e276c5661a64192db44ce7af"
-               "5eb888e9eb03bb04a9fe943b101e1f553215abf8fbdf17c3641e447514ca565fb9b68c08a5e4351e"
-               "a2f5680800000000faa100fdfdd68d34be184524a6bdc8789c035178a9fe5bcc8da98489c55b8c12"
-               "87e6f7bb020ffdb0e612098610e52bb2a16a40089f3c39f200000000de745cb25851429b3bbfb501"
-               "68dfd04edf033542a9fe5f574c5cf8107ba282f4f983821918f93e742d08f0550b55726f2292362e"),
-        [1] = ("54270e46020000006a894e387625da376feecb80ac245409c058cc271d9c2f67179bff0644399ae7"
-               "9c64ed0432d599eaa660ac824b7fae389861419c7a899f2d010000009bf5a5d06776e66a9d80ab13"
-               "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
-               "c4e1966800000000b3121cd5ef51e696c816290dbaee0e77264c82e1530ff5397c9125f59577d71c"
-               "4b258a005116d11354edff62ceaa458fc75a91c425a5927300000000c4458d70911714f1819aa46f"
-               "be143934c915ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa"
-               "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44"
-               "64d210a256be75e0509c4477d19ac64b2e69220d12ea3b9501000000fb95e8332fdfff658483a2d0"
-               "39a7bf148e4c481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa"
-               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359"
-               "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9bb0ec36f0000000064a2827b7cdd38d0314c178e"
-               "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
-               "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226"
-               "d324d9a086ed27184b443181a5edc511c1507a142fea9bc10300000062fb81e733cff620ca7feefe"
-               "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
-               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911"
-               "4bdade862c224f6f36506ebd455e679cef369bb8d514573f03000000f6932f6932d1d5f5d0ce2cf6"
-               "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
-               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d"
-               "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a41e0f515601000000f57609453df7803357ae7998"
-               "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
-               "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff4cf256000000000000000000000000"
-               "00000001955e1a13d1111b067acbce3e3bcdf38200154d12010000009edfa7a74c1c83c18e47935e"
-               "8354846e346e386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c"
-               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000"
-               "00000000020ffdb0e612098610e52bb2a16a40086f0e9e6903000000ec2e2924348352d715cf9d41"
-               "1ea012e5305a94b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26"
-               "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000"
-               "0000000000459582f4ef43c780908872746e39efaefd545b020000007ced4f8b362956cd46646a0e"
-               "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
-               "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000"
-               "0000000069d9942f460bcea75481f25e307d0de96e91b91501000000be5b534e0a6527f67485ab35"
-               "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
-               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000"
-               "00000000b49185ad9b7d0070a36dd51323b2c542e5fe7d360100000066a3ae3939762df33a2d5506"
-               "0c78d551cb7410fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe"
-               "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2055624a2000000000000000000000000"
-               "00000000ffe9d6812231bb02f922259b173c333cbae87d60000000005d31e5b2de252167c1c91b3a"
-               "36ba0c700e538477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02"
-               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000"
-               "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8"
-               "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
-               "7666c05303000000ffa8e89871fb9510b900b2061d0c334b816a2dd1000000000000000000000000"
-               "00000000095a3230fde69eec3af2e6d0bd68ab7210613fc90200000016a31dc2e46a234558817151"
-               "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
-               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000"
-               "00000000b314f695bd82e9ea8891ed0a3d1b91cab56e03e0010000002f70249eefc67bce716c856e"
-               "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1"
-               "eecc89f601000000d635edea8ffe423d01883918039249f3984ab37e000000000000000000000000"
-               "000000001fb7c8307f06b4b29088f20d9ac676d5f90e54b501000000a779f37c30d2c5053c40ecae"
-               "210aeb0934392a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678"
-               "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaa557b86000000000000000000000000"
-               "000000005ddb05f2a84e27c18a06e2c9cb29efdf50da49bf00000000d94beb259f9f0251becc9879"
-               "07879cca6857c7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f"
-               "a349e1cc00000000b879a45176740744fcb6a035339d504c972bd80d000000000000000000000000"
-               "000000009518862bc9a89192c1c8be55281a59d1ebfdcec30000000067a982be8a934f852cc3d4d8"
-               "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
-               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000"
-               "00000002e6fbf952902383028e3b47f2111d891d536c75bb01000000720da9e07ac998b5690807d5"
-               "2617369ee16d4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b"
-               "17a4763102000000f9119c40b468b66331bca67a034f43340d2c683c00000000000000000000ffff"
-               "b24a5be9b10123e6e4f15ecd1dff045298cfee069c88b02a0300000004b0595daf37deb499996bfb"
-               "667f072dec755d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5"
-               "a7afe28302000000d05ff720b16c73761a3fcb675219061ab466c79489b2cc6a883c146972decb8b"
-               "5adac8f5e6d66df46ea139107754dee62c36d2fcc7a4c65601000000cd625009a40aeb62a42b6a62"
-               "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
-               "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599"
-               "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b21cdb3455c00000000c66af76e80f062ea1a7e0b7b"
-               "e218d067532d1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd"
-               "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee03202aba042aaa218dc091e9aa1477f"
-               "6fdc9830d7fbc95829a8838314dff34d24c332214d4647f5000000000f88e3759a08e7a56663861e"
-               "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
-               "7625ca4a00000000e682f80f94315c45ef817264c89a736ed580d637b077ddad3108be69f9b97d05"
-               "c917ad6b10e693bb6e26f2ba90c8e909e85e20e5b0916e5d030000004a503a712350acbd48411f0d"
-               "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
-               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e"
-               "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c"
-               "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
+        [0] = ("8d1e69c83dc21056b973972c17a47631c169f11ff9119c40b407b6630000007a034f43340d01683c"
+               "0045097aea4a849f060ddf57b24a5be9b10123e6e4f15ecd9c88b02a06d0dc5504b0595daf37deb4"
+               "99996bfb667f072dec025d9cdc8a11f4409bcfb3c167090e00d90a8543961b2ccd47724a3c460ba8"
+               "5183f4c5daae7d63ecc8fb1103000000fcebbaeb0c56535923f14874042a8aff1d018e5e000000cd"
+               "36bbe3c9bb0ec36f00e007bb64a2827b7cdd38d0314c178e925b568ae56bc4e92931f2d303000000"
+               "50b8d10ab700cc3a7cf51be0403b654ba2026808092069affaa100fdfdd68d3400184524a6bdc878"
+               "9cf851782d895bcc8da98489c55b8c1230d2c50503000000210aeb0934cb2a247000b96d06992b6a"
+               "5803d399000000d2df48f333ef467092008aa77d6732b678cf844ef5a943825fcd743f59bd14c89b"
+               "df6f628a03000000f25d5895fb57fa60d9279fb2b8fcbac6ab0274f70000007bf6989d2d6a909e98"
+               "00f7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a89427c2260300000086ed27184b443181"
+               "a5edc511c1507a141e03515600000033f57609453df7803300ae79989870ccec3def0ad92749e016"
+               "663fe6ee0228c1da5f5b481b0300000068cb3bc873b04d937a6ed8f7bc51e5406603d0980000008b"
+               "92d3adc69a84eb47002400207799416af1f0a086fbd7e2f7dea0077ae9c0c0d9f264af6703000000"
+               "c38b6b8ee2614344bea73223e59bf0193402e9fa0000002d8ec7e4b89bf5a5d00076e66a9d80ab13"
+               "2e1ac921eb76adbb229df32e561fa80aabf85f0c0300000008d33a2ea67776d7f88d69c8c7e3b2d3"
+               "c902f05400000020abb42316c533d9c90088189c471a1ff2f0ce3ff66e782110125ae2ed74993ade"
+               "507e308d030000008dab2dade2ab2fc9c56a7c86be5f96228e02f2f4000000b32b9c161c36b745ce"
+               "00d06352cb2c354434819f4b248eb2b8e3ff1a5ffc386da337c27c15030000007e5c53e755c90fa0"
+               "9e9653ddcb1b14d2750d8a130000001734b49e955cbcf46d006a1818053da43ec5085d1df74d1e28"
+               "7271092897e26251de485b1303000000957c22721583c453d15ade4386ffd00b3600858900000013"
+               "f09e601d88d4cb0c004d15c69e89f94ef7b30486591030639eb0e99f7f53702f3c84c4eb03000000"
+               "87780281d47a32f2235376239daeaacb3c02864b000000ae91921681c2162b9bfa40546cdb544e44"
+               "df2d7c2790d3119a051bb2ee8192ac0cfa3abc1e0300000075a2f42b50c6a36340132378b95c5931"
+               "3b03ba640000002069e66142e22753344fda8a3e0b841cf76391dd68269b53eccb44a831714acfb5"
+               "c442139803000000cee64b38b56e03e0f121014a2f70249eef027bce0000006f3973dc42a1003be9"
+               "4f89d8eeb1f14d985d5bbe6c10085f3d6176f78a19bb8df1449a774903000000ec852d59f3d17232"
+               "edd86ad6c3103a6884029aef00000082594f24a17dba1ee7e143fd52599364e13468f80fd514573f"
+               "b572671cf6932f6909f4ecf103000000a1c748288ae21e371051852c972ea7954012884a00000045"
+               "fd13a6826a4c619f9037d7a754655354c47cf0f551e96770a754ac19ef820fe0eafb1ac403000000"
+               "3aa9986700b2a2b3ed257b8b0489f48f0500c8cac72105b327335a25a364d1015ffac03089f45539"
+               "e1b0178462156cd6b83579b8da27345a4d9ca9d20300000074e786047d8683facd7db68b9ae586da"
+               "cd03070c00000027c4dd3d031e33f22c636360d696be085aad7c499dd538d34574c038404923e75d"
+               "36506ebd03000000ef369bb8ebfdcec3f8c8fa6867a982be8a054f85000000d82bc0ec7303f99f8f"
+               "e8d07c84965a316af2abb982b001ec16901f55f960c55c22c896c9a7030000000a5f6abd850b06e3"
+               "d9662795ce2cc1cee00002ab000000a318dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d"
+               "24c332219a1b163a8483a2d0030000008ed3481e0bccc460e89f7abdb682380a6202e3740000004b"
+               "a2b980b6aba92da6409969aabae87d60e00473755d31e5b2de252167c1c91b3a891715df03000000"
+               "edae579e2e10c7f7a22ba0340436242cbe11248c000000174632d4f28aa28c08f80dd50c6712dfb4"
+               "d184e820a28fa772a19cdf6dd1c173f6bc7ef08203000000ef60a8b540b26f66761d1f13badfad0f"
+               "e8038f3c1aad2a82530b7447d9c2f401012f2f4c329f997184c9d2578e4cf9c48147507e1cd59ea1"
+               "6e8e99cd03000000644f0e8ab3121cd5ef51e696c816290dba010e77000000e1530ff5397c9125f5"
+               "9577d71c4b258a005116d11354edff62ceaa458fc75a91c45cfd5ce603000000152f2b70eaef7443"
+               "e0f79ab6902dde53361dec71000000254ac9588319558041fc62ed564153e17478f8c3344d89c0bb"
+               "5408c1b7d17fb084b16c7376030000005219061ab4c5c79489b2cc6a883c1469721dcb8b5adac8f5"
+               "e6d66df46ea139107754dee62c36d2fc70ccc567df7a49b8a4c73cf5ffa4ffc8acdca10f03000000"
+               "a36b178aabc78a4a0d80684ee5dbf45bba0210fb9733cc24904f288262e6ea77a22fa5f863eaeefa"
+               "701d120523bd98f29b098b43cd68be6e37686b5e030000006215de4bb6f6f3d031a790287e45e9c1"
+               "8b0236c0a25ea71f5daaed4d99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4f05d7308b67f91bf"
+               "0408423e03000000886dc91e2e5b0f0b65843fc961f7ad18010a3c67d9714f19f1e41c073164d93b"
+               "1171719c7f81773302d007c983566139c695e0f4b661ee6b3cf09e8b02000000e79c10acfce165e3"
+               "32a62384ec04e5bab40285ad00000071a36dd51323b2c54200154d12f86c260a9edfa7a74c1c83c1"
+               "8e47935e8354846e27c219630200000054830c68e44ea74b4f2b83f9b3bee14d86034c9e000000f6"
+               "abba4a32d635edea00fe423d01883918039249f398f9b37ea0914651b2a16e642c19a09202000000"
+               "92defa453a3efc240b9977e166bee737fe10670c0000004c323b59b4cd20eb7d00d38785c88ffad4"
+               "5e0dfe491d1ba96742c7b5e02b227122fcd32a7f0200000023a5223a2fb2c0764e08db62a3db07d0"
+               "900260b922a7b4a1565e63d3b610c9df00e32703b0ed3a7d0f8b13cd736aaa72021a381fb50e2c42"
+               "54270e46020000006a894e387625da376feecb80ac245409c005cc271d9c2f67179bff0644399ae7"
+               "0064ed0432d599eaa660ac824b7fae389861419c50da49bf8c3b371502000000710c030690f5f18e"
+               "a125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a62000660bbf024b03ff0a8e27c405e64244"
+               "57393c6fd7ac5f99544e4b2702000000a7f810d249adc17647176b7c07d856e3b806efebc09dd9d6"
+               "1966b7ae7412041d00fe7d361f2d5dfd66a3ae3939762df33a2d55060c78d551fcb6a03502000000"
+               "9726d80d9c2d89df765b4d9a130257dc5f19f03a0000002bc9a89192c1c8be55281a59d1fd338f35"
+               "7e75f8ce6e8cc34573654ea6624f138e8c45eb5002000000562dcd24a27c8212127da516b1091d1d"
+               "4f0008170000000d32d823b0617718eb0f3195e2fe917423d314ae73572ff808b373f5b193d661ed"
+               "e45aaf610200000040125e6baf4648cbaaa37b861e6d46dafe1f6ec400000053010911915ddb05f2"
+               "a84e27c18a06e2c9cb29efdf672d2fa57fc85ebbe276c566804c122f0100000059747cf23a1349c8"
+               "ab2dadc5a39411fd4f03116d000000316a553fc28cff0bcd00ebe434a7ae8ff040483e310819e3b1"
+               "0db116431ec6f76949ba9be3010000000c67c3d4afa9c8649da90bd2fff2f2568101870b0000004b"
+               "70c6d18d6242b356005e1a13d1111b067acbce3e3bcdf3824f1b134847ff26ba32d1d5f501000000"
+               "bf7a70ba5193a162bc92ec1b11d9172cf8020347000000d8883b0f3fd6ed84d60059e553b887edcd"
+               "61c2f5d2f381f843b809d259e499db7d2b092583010000009f9f0251becc987907879cca68fec7bb"
+               "bb008edf000000973d136caca5054b010002a4c2167721673f2b02b3256ead1f2fea9bc12f0cfca1"
+               "62fb81e701000000ca7feefe1933631e8e69f6d9d6962d2c6e0304f50000009e47150b9b14010469"
+               "003acb72bb89182f931121966e91b9153b36bc60be5b534e1dff04520100000036674cdefb57b9ae"
+               "54cfe8f42bf0e9381b00c4396de60f032bc7f34de295987100d4c0d4eb720c4ae3c9472e5f2da5d9"
+               "c5cfd9f2343b21363f81268101000000585287dc6ca972a2cfb18b2b2cb749e2e002544d0eb4f73e"
+               "75039fcf251b32fc00685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0fde69eec01000000"
+               "bd68ab722af14548b29572e504265f6c72003e2200000079e208ed2e2ebb07710046a54cadb245ea"
+               "fde6b02567f814de39dd8f88152a5845a4bab14801000000e6aae816430607ca0675e09818e9bbec"
+               "5900c5900000006ab08940f6fc3bbdc700090f756aae660b3b02239efc1fb23d24438387def0f4c6"
+               "cbb110fd01000000b9aca07b6e4925dd27943a272c171ed15a02be1c0000007b56e8f3fededea203"
+               "00a8042602a1f8e6f5657f3f9e3eb807cbad76451055edb81539ce410100000018238487a417f3da"
+               "01d99dae5f190096cc0e2cab0000005b2363d13edc5aa115165eb914d8fbe9a4fdb3d117d76b0de5"
+               "416f95e7bb75d72fc8f1853b010000000e2b00b667b7b16dae02dfa0c26e7c3add003bd3000000f1"
+               "2f2f3313d8469d09a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5d8a4c5cf01000000"
+               "fcc2056cc1ff8e51bbce426cfa4b861cc70192be7b14e7ba9c15acb881ae55f0f512dc1c0aa20624"
+               "fbab244735d67c61283031667b2d74a103e0b1b10100000090a2312e17f1a35900459582f4ef43c7"
+               "80038872000000efa9a89f87189b1d0afcdf6d9be94030b34774d1d7dddedd9899f0f627be16965a"
+               "8704133101000000d9b4e3ce848518c4e3f6cbf25e5e1b990f026767000000bba23eb66e720da9e0"
+               "7ac998b5690807d52617369ee1af4ca5a6a95581af5fd7ce732d803e010000001a0dd8c052ed6b13"
+               "e2815a1a37a52bddd106f101000000ed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53"
+               "bc7417940fcffe3fb60291ea01000000def85b75827b26e8a0bef8675b29a197b71ecceaf5f1bb12"
+               "c535256ab0916e5d58099ffc4a503a712350acbd48411f0dc15d2f0f49dad34565dbc8a900000000"
+               "b550c5db56189572f32f4af595d785d5c117aad4000000f08bd7a09bdd214443008cd9bf66ec305a"
+               "225a0c71b2ce78bb16104c8eaf18c56587e6f7bb00000000e612098610e52bb2a16a4008aefd545b"
+               "7a019db50000008b362956cd46646a0e002160e5f769bb295766ed37a2123c5b6a813387e99bb834"
+               "b7a350f900000000fa7db4532545666937c22a3b6e9686dcc403cbee000000529ed8901d7a0165fe"
+               "00744edb6ba04f146e4c699341b95f41d9c1e67743efb54e4fc812bd000000006d064126666befbb"
+               "98c39e7cc282208fef007ff40000009751adcb22fd50793f007a3c7dadd1c86e759d0c90bc512e13"
+               "7dcef5e4a27985bd348352d7000000001ea012e5307294b6e346dac428ab20075e05e38d000000e6"
+               "ccd2f50c30706cae00dcc2f0ee3d5e2612ea3b95e1d2a5d0fb95e8332fdfff650a6527f600000000"
+               "aca0c7ee4197338581cafd63e66e3a23d61e257800000020aefd4503189f7630008e501c9052c22b"
+               "9f3c39f24b0572b1de745cb25851429bf32099a8000000004d4647f53b1921af0f88e3759a08e7a5"
+               "661f861eea1bf42ccd3c72f2ad0638570082ac5180101be4f85cef468ea086ea9aaafdc388811ec7"
+               "3bbfb50100000000df6b3542f4bb5f574c5cf8107ba282f4f9038219000000772d08f0550b55726f"
+               "2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b513722d9b7000000001fb7c8307f06b4b2"
+               "9088f20d9ac676d55b00ea34000000fec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb"
+               "504e03d53e1124c5d9e1825f00000000284a024b7f9e85ff73e050a2cd89fd276e00cb8800000037"
+               "4ec3127c750fabdd69121aec129cdc690fcc00ac2daa755e5292cd32dc096cd5438a72e100000000"
+               "2495a609675344f7e2e3a5ebaec3c85f0a1242f7000000c70345132a61eda239d9d083c3bf085387"
+               "046ef8a36f0e9e6982d15956ec2e2924ae6da48b00000000bcfe97e1761e12457ad742d3d7320a4f"
+               "8800f47f5dd0cf69cb22840f09b3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f"
+               "57d6249b000000007dc7f17f666969e45c3baf7a119a65ee0b1ffa9e000000f2de6dc8dfcf2a3ad3"
+               "bbcc0fa3b314f695bd82e9ea8891ed0a3d1b91caae8cb60bab90135f0000000064d210a256be75e0"
+               "509c4477d19ac64b2e03220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f"
+               "2c932e655203d82a87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c1755876b6a3f13"
+               "922b6cb8360feaac1d5c302df59dd32a677e5a3af4fe297f608df061d45d864db442548600000000"
+               "b0216050ed7afd741024e8365ea1aa11f90397c0c0ec468e9de5a9caf493472201e48d163106cc39"
+               "7446a33fdc335c8b152c507a0a3c8e7cc67b2e48b47003c81785d3ffdbd9a617857ae8126b028c0e"
+               "3e0f5354a9feb690c628d048eacce74500213cb1a84035b632ef3be6070dafa202187470e9da5570"
+               "40e55b8b03000000c0cfb2b4386bec092fa5757ecde9348bda012ebda9fea6aa667224dae775e5cc"
+               "0041aca2ff0f576767d3648102b61886b62f22ca33478e1ff9531cd903000000d241989477a363d5"
+               "5ad47b8a11090df8da00d88da9fed75bc404156225b7eedefc2b44574b15e2ae496ad01bf007eacb"
+               "0a28aec8682825108c54dda903000000d43e6505621b9a7f0422ae3fc8979ee0c400d794a9fe895e"
+               "5bfe47c6f53a54e01b0c1d89414d94d0e02d2ec587fc330003781b0f294a2b7387bc45db03000000"
+               "36bd39a44e8e8bc17c01ac63eca0c1cfbb004132a9fe13e4862573c2303dd1d65c7b2cffeca6d1ad"
+               "accae11f8045a80f855ebf3b772eb2b1182c028703000000ec4501a1e27acfee7a8588ffd5a06cae"
+               "4201e595a9fed65d67c624f285d7174177a47579dda0b6ead1e1b34901eaedd5bcd7be07d78e6222"
+               "d611d7b103000000f9f23b5f1570a4648a0ff80dd9a37a82e10120aaa9fe258532449deb7afb217d"
+               "58c0bb20de6f36d664873c5be82c5361358730bcde43782d87b8c3b9020000008982c42f53700a34"
+               "37c5156e072b2f2fb300c9cfa9fe34ddb3980deb14ab7b5a001de913df1424ce3ee1d76fc1ea76e9"
+               "8c9dfc78997ab53ec386e9c60200000010ee58bdc75183609985d627b6a4b1cbe402cbbba9fe3fd6"
+               "a77eb3c5bf64b67100f18dbf463f9b8306c83bc0269779114bdade862c224f6fdd65aeb202000000"
+               "902383028e3b47f2111d891d9f3c2c88cd007619a9fe44e644d7cf1d06b2b115d882549e62c12fba"
+               "5b1cdec48d10bbb13b8313d8cd2a24d3be46bae5020000006665a268abfe8a41d149f074a378b881"
+               "1500ce68a9fe5cec25a59273e7519f34c4458d70911714f1819aa46fbe143934c933ae6dec0fb124"
+               "59970695020000000d3c723b91adb0da7c4aa7e7eb5a15bcde035fb9a9fe1fd84cc43c510385b9a4"
+               "c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5078a806d020000008bbfc9723107f3f5"
+               "b2cd3dbddc74e26de905e48da9feb0313e7f8933e315130fa9a84c8288f83f6e70b1646d8026e9f1"
+               "704f1b16286ba2da72437f9b01000000ec9e71ee4b951507beb42b3473cef00e89015f24a9fe2333"
+               "02ad3b82d639272e0046eadfbd2146bf8cfdb205f90e54b58a6ee136a779f37c0d58fbcc01000000"
+               "d0d44ff1e51c35157666c05348e6f507ff01e898a9fe9510b900b2061d0c334b009f2dd1cdf64b0f"
+               "9a60e0e289f08db3e4bafc4e095a3230b4d9e89b010000006855676c277cf1bd017c9148da5892bb"
+               "4903c3a7a9fe689d3391ce7d3fd48469005f0c233dbdd2a97835df1f1782dee88487fd0db5971b46"
+               "9d82c85601000000050f63f8282f3464443cacf59c6379a44b0392f4a9fea98cb121c19c69d9942f"
+               "000bcea75481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e01000000b9ef1b2110613fc9"
+               "f90adac616a31dc2e4002345a9fe7151b38fc9c909cf8d71e64b0bd761fdf274fc5dca8ce1bd1871"
+               "ae17bb451585609236ba0c700100000040318db913a353edc66efc068829c0e66a17143aa9feefb4"
+               "6b382ab0404cdf029a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b2050424a200000000"
+               "a029c896b4132bd895072cfeffe9d6812200bb02a9fe259b173c333cd03bc90500b899afeb760e3f"
+               "004a2efc8ffc96b89b1bc8f415bd4e77c7a4c65600000000cd625009a40aeb62a42b6a62548e3c38"
+               "b9022002a9fec5fbfaf1809becd2506300a6da29b2e94d3ab51fe0e9af98b180a36035fb6297dfa7"
+               "0e09e2fc00000000cc807cdce682f80f94315c45ef817264c802736ea9fed637b077ddad3108be69"
+               "f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e591bf821c00000000c37ff4283a631480"
+               "91acc42cac4ef3ad3702fec7a9fe62475588456e4ead7cbda620a3abae816e34cba1d74f18d1c4ed"
+               "b99c0e49bcb946f71a64192d000000005eb888e9eb37bb046686943b101e1f553203abf8a9fe17c3"
+               "641e447514ca565fb9b68c08a5e4351ea349e1ccf662e058b879a45176740744a19da86c00000000"
+               "548759d313a5b92de36f90d4bc8b496afd03fe47a9fe370b6ec6584d64496089570968ead4d1ae91"
+               "c819bb068196d599efde3246e43f5e7934f0386a000000004064f6b021281e235f94ae20ac26c7d4"
+               "1f0350bfa9fe055bcfe3756ccdb3455c60ca7b11c66af76e80f062ea1a7e0b7be218d067530e1b04"),
+        [1] = ("4d9ca9d20300000074e786047d8683facd7db68b9ae586dacd0b070c50320427c4dd3d031e33f22c"
+               "636360d696be085aad7c499dd538d34574c038404923e75d30d2c50503000000210aeb0934cb2a24"
+               "7000b96d06992b6a5875d3995b9663d2df48f333ef467092818aa77d6732b678cf844ef5a943825f"
+               "cd743f59bd14c89bdf6f628a02000000f25d5895fb57fa60d9279fb2b8fcbac6ab6674f7a6df7979"
+               "f6989d2d6a909e986ff7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a8de485b1301000000"
+               "957c22721583c453d15ade4386ffd00b36518589be395312f09e601d88d4cb0c2f4d15c69e89f94e"
+               "f7b30486591030639eb0e99f7f53702fa4bab14801000000e6aae816430607ca0675e09818e9bbec"
+               "5966c59068baf76ab08940f6fc3bbdc7f6090f756aae660b3b02239efc1fb23d24438387def0f4c6"
+               "36ba0c700000000040318db913a353edc66efc068829c0e66a32143a0554efb46b382ab0404cdf02"
+               "9a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b254270e46000000006a894e387625da37"
+               "6feecb80ac245409c06bcc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae38"
+               "9861419c50da49bf5cfd5ce600000000152f2b70eaef7443e0f79ab6902dde53362fec7127e19f25"
+               "4ac9588319558041fc62ed564153e17478f8c3344d89c0bb5408c1b7d17fb084b16c737600000000"
+               "5219061ab4c5c79489b2cc6a883c14697280cb8b5adac8f5e6d66df46ea139107754dee62c36d2fc"
+               "70ccc567df7a49b8a4c73cf5ffa4ffc83722d9b7000000001fb7c8307f06b4b29088f20d9ac676d5"
+               "5b2dea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5"
+               "87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c5f55876b6a3f13922b6cb8360feaac"
+               "1d5c302df59dd32a677e5a3af4fe297f608df061d45d864d348352d7000000001ea012e5307294b6"
+               "e346dac428ab20075e11e38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e2612ea3b95e1d2a5d0"
+               "fb95e8332fdfff65fa3abc1e0200000075a2f42b50c6a36340132378b95c59313b58ba64dbe99620"
+               "69e66142e22753344fda8a3e0b841cf76391dd68269b53eccb44a831714acfb5f32099a800000000"
+               "4d4647f53b1921af0f88e3759a08e7a56655861efedbf42ccd3c72f2ad063857ca82ac5180101be4"
+               "f85cef468ea086ea9aaafdc388811ec7c442139803000000cee64b38b56e03e0f121014a2f70249e"
+               "ef217bcefeec856e3973dc42a1003be94f89d8eeb1f14d985d5bbe6c10085f3d6176f78a19bb8df1"
+               "09f4ecf100000000a1c748288ae21e371051852c972ea7954066884afed7b044fd13a6826a4c619f"
+               "9037d7a754655354c47cf0f551e96770a754ac19ef820fe0732d803e000000001a0dd8c052ed6b13"
+               "e2815a1a37a52bddd109f101fed87eed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53"
+               "bc7417940fcffe3feafb1ac4020000003aa9986700b2a2b3ed257b8b0489f48f055ac8cafea105b3"
+               "27335a25a364d1015ffac03089f45539e1b0178462156cd6b83579b8da27345a544e4b2700000000"
+               "a7f810d249adc17647176b7c07d856e3b87befebfe9dd9d61966b7ae7412041de5fe7d361f2d5dfd"
+               "66a3ae3939762df33a2d55060c78d551dd65aeb202000000902383028e3b47f2111d891d9f3c2c88"
+               "cd1676190000000000000000000000000000000162c12fba5b1cdec48d10bbb13b8313d8cd2a24d3"
+               "27c219630100000054830c68e44ea74b4f2b83f9b3bee14d866e4c9e000000000000000000000000"
+               "0000000101883918039249f398f9b37ea0914651b2a16e64a19da86c64fefab2548759d313a5b92d"
+               "e36f90d4bc8b496afd57fe4700000000000000000000000000000000d4d1ae91c819bb068196d599"
+               "efde3246e43f5e79507e308d030000008dab2dade2ab2fc9c56a7c86be5f96228e05f2f400000000"
+               "000000000000000000000000cb2c354434819f4b248eb2b8e3ff1a5ffc386da38483a2d003000000"
+               "8ed3481e0bccc460e89f7abdb682380a6208e37400000000000000000000000000000000bae87d60"
+               "e00473755d31e5b2de252167c1c91b3a87e6f7bb03000000e612098610e52bb2a16a4008aefd545b"
+               "7a219db500000000000000000000000000000000f769bb295766ed37a2123c5b6a813387e99bb834"
+               "bc7ef08203000000ef60a8b540b26f66761d1f13badfad0fe8178f3c000000000000000000000000"
+               "00000000329f997184c9d2578e4cf9c48147507e1cd59ea13f81268103000000585287dc6ca972a2"
+               "cfb18b2b2cb749e2e022544d00000000000000000000000000000000ddd3aa9ba511d2e40edb4d75"
+               "8fb554158ae5c7c09d82c85602000000050f63f8282f3464443cacf59c6379a44b8092f400000000"
+               "0000000000000000000000005481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e02000000"
+               "b9ef1b2110613fc9f90adac616a31dc2e44923450000000000000000000000000000000061fdf274"
+               "fc5dca8ce1bd1871ae17bb4515856092cbb110fd02000000b9aca07b6e4925dd27943a272c171ed1"
+               "5a66be1c0000000000000000000000000000000002a1f8e6f5657f3f9e3eb807cbad76451055edb8"
+               "5f5b481b0100000068cb3bc873b04d937a6ed8f7bc51e5406674d098000000000000000000000000"
+               "000000007799416af1f0a086fbd7e2f7dea0077ae9c0c0d9ecc8fb1101000000fcebbaeb0c565359"
+               "23f14874042a8aff1d008e5e0000000000000000000000000000000064a2827b7cdd38d0314c178e"
+               "925b568ae56bc4e9438a72e1000000002495a609675344f7e2e3a5ebaec3c85f0a5742f700000000"
+               "000000000000000000000000bf085387046ef8a36f0e9e6982d15956ec2e2924891715df00000000"
+               "edae579e2e10c7f7a22ba0340436242cbe53248c000000000000000000000000000000006712dfb4"
+               "d184e820a28fa772a19cdf6dd1c173f6e45aaf610000000040125e6baf4648cbaaa37b861e6d46da"
+               "fe556ec4000000000000000000000000000000008a06e2c9cb29efdf672d2fa57fc85ebbe276c566"
+               "8c45eb5003000000562dcd24a27c8212127da516b1091d1d4f5f0817000000000000000000000000"
+               "00000000fe917423d314ae73572ff808b373f5b193d661edbe46bae5030000006665a268abfe8a41"
+               "d149f074a378b8811526ce6800000000000000000000000000000000911714f1819aa46fbe143934"
+               "c933ae6dec0fb124fcd32a7f0300000023a5223a2fb2c0764e08db62a3db07d0907360b900000000"
+               "000000000000000000000000b0ed3a7d0f8b13cd736aaa72021a381fb50e2c421dff045203000000"
+               "36674cdefb57b9ae54cfe8f42bf0e9381b04c43900000000000000000000000000000000eb720c4a"
+               "e3c9472e5f2da5d9c5cfd9f2343b21363c84c4eb0200000087780281d47a32f2235376239daeaacb"
+               "3c54864b00000000000000000000000000000000db544e44df2d7c2790d3119a051bb2ee8192ac0c"
+               "804c122f0100000059747cf23a1349c8ab2dadc5a39411fd4f60116d000000000000000000000000"
+               "00000000a7ae8ff040483e310819e3b10db116431ec6f76987b8c3b9010000008982c42f53700a34"
+               "37c5156e072b2f2fb372c9cf00000000000000000000000000000000df1424ce3ee1d76fc1ea76e9"
+               "8c9dfc78997ab53ef9531cd901000000d241989477a363d55ad47b8a11090df8da18d88d00000000"
+               "0000000000000000000000004b15e2ae496ad01bf007eacb0a28aec86828251065dbc8a900000000"
+               "b550c5db56189572f32f4af595d785d5c10eaad40000000000000000000000000000000066ec305a"
+               "225a0c71b2ce78bb16104c8eaf18c56557d6249b000000007dc7f17f666969e45c3baf7a119a65ee"
+               "0b34fa9e00000000000000000000000000000000b314f695bd82e9ea8891ed0a3d1b91caae8cb60b"
+               "fcb6a035000000009726d80d9c2d89df765b4d9a130257dc5f2bf03a000000000000000000000000"
+               "00000000fd338f357e75f8ce6e8cc34573654ea6624f138e91bf821c03000000c37ff4283a631480"
+               "91acc42cac4ef3ad373bfec700000000000000000000000000000002ae816e34cba1d74f18d1c4ed"
+               "b99c0e49bcb946f734f0386a010000004064f6b021281e235f94ae20ac26c7d41f2a50bf00000000"
+               "000000000000000000000002c66af76e80f062ea1a7e0b7be218d067530e1b04c386e9c601000000"
+               "10ee58bdc75183609985d627b6a4b1cbe413cbbb00000000000000000000000000000002463f9b83"
+               "06c83bc0269779114bdade862c224f6f1a64192d010000005eb888e9eb37bb046686943b101e1f55"
+               "3273abf800000000000000000000000000000002a5e4351ea349e1ccf662e058b879a45176740744"
+               "32d1d5f503000000bf7a70ba5193a162bc92ec1b11d9172cf83a0347000000000000000000000000"
+               "00000002b887edcd61c2f5d2f381f843b809d259e499db7dfde69eec01000000bd68ab722af14548"
+               "b29572e504265f6c726d3e2200000000000000000000ffffdb46a54cadb245eafde6b02567f814de"
+               "39dd8f88152a584562fb81e702000000ca7feefe1933631e8e69f6d9d6962d2c6e5804f57239c09c"
+               "47150b9b14010469823acb72bb89182f931121966e91b9153b36bc60be5b534e6e8e99cd01000000"
+               "644f0e8ab3121cd5ef51e696c816290dba040e7726d082e1530ff5397c9125f59577d71c4b258a00"
+               "5116d11354edff62ceaa458fc75a91c4b4d9e89b010000006855676c277cf1bd017c9148da5892bb"
+               "4945c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46"
+               "2c19a0920000000092defa453a3efc240b9977e166bee737fe1c670c439a644c323b59b4cd20eb7d"
+               "d3d38785c88ffad45e0dfe491d1ba96742c7b5e02b227122ab90135f0300000064d210a256be75e0"
+               "509c4477d19ac64b2e64220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f"
+               "2c932e655203d82a59970695030000000d3c723b91adb0da7c4aa7e7eb5a15bcde525fb98b841fd8"
+               "4cc43c510385b9a4c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d58c3b371502000000"
+               "710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a6205f660bbf024b03ff"
+               "0a8e27c405e6424457393c6fd7ac5f9940e55b8b01000000c0cfb2b4386bec092fa5757ecde9348b"
+               "da4c2ebd4f78a6aa667224dae775e5ccd141aca2ff0f576767d3648102b61886b62f22ca33478e1f"
+               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c954f054e93f8120abb42316c533d9c9"
+               "4d88189c471a1ff2f0ce3ff66e782110125ae2ed74993adeae6da48b00000000bcfe97e1761e1245"
+               "7ad742d3d7320a4f8815f47f5dd0cf69cb22840f09b3fe7749509cb6752b2cee30b7e7736a0afc98"
+               "79ea40e69710fc9f0408423e00000000886dc91e2e5b0f0b65843fc961f7ad18014c3c67d9714f19"
+               "f1e41c073164d93b1171719c7f81773302d007c983566139c695e0f4b661ee6bc896c9a703000000"
+               "0a5f6abd850b06e3d9662795ce2cc1cee00802abfec2aaa218dc091e9aa1477f6fdc9830d7fbc958"
+               "29a8838314dff34d24c332219a1b163a3bbfb50103000000df6b3542f4bb5f574c5cf8107ba282f4"
+               "f9188219fef93e742d08f0550b55726f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51"
+               "c8f1853b030000000e2b00b667b7b16dae02dfa0c26e7c3add113bd3febfa0f02f2f3313d8469d09"
+               "a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5b442548601000000b0216050ed7afd74"
+               "1024e8365ea1aa11f92797c0feac468e9de5a9caf493472201e48d163106cc397446a33fdc335c8b"
+               "152c507a0a3c8e7cacdca10f01000000a36b178aabc78a4a0d80684ee5dbf45bba3010fbfeb3cc24"
+               "904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e37c27c1500000000"
+               "7e5c53e755c90fa09e9653ddcb1b14d2757f8a13feac3c1734b49e955cbcf46d256a1818053da43e"
+               "c5085d1df74d1e287271092897e2625103e0b1b10100000090a2312e17f1a35900459582f4ef43c7"
+               "807b887200000000000000000000000000000001e94030b34774d1d7dddedd9899f0f627be16965a"
+               "87bc45db0100000036bd39a44e8e8bc17c01ac63eca0c1cfbb4f4132000000000000000000000000"
+               "00000001eca6d1adaccae11f8045a80f855ebf3b772eb2b1449a774903000000ec852d59f3d17232"
+               "edd86ad6c3103a68844c9aef00000000000000000000000000000001599364e13468f80fd514573f"
+               "b572671cf6932f69d9e1825f03000000284a024b7f9e85ff73e050a2cd89fd276e0acb8800000000"
+               "000000000000000000000001129cdc690fcc00ac2daa755e5292cd32dc096cd572437f9b02000000"
+               "ec9e71ee4b951507beb42b3473cef00e896b5f2400000000000000000000000000000001bd2146bf"
+               "8cfdb205f90e54b58a6ee136a779f37c2931f2d30300000050b8d10ab700cc3a7cf51be0403b654b"
+               "a242680800000000000000000000000000000000a6bdc8789cf851782d895bcc8da98489c55b8c12"
+               "37686b5e030000006215de4bb6f6f3d031a790287e45e9c18b7136c0000000000000000000000000"
+               "00000000ec7d3abc4ccf30d3aaaf02a4f05d7308b67f91bfc7a4c65603000000cd625009a40aeb62"
+               "a42b6a62548e3c38b96a200200000000000000000000000000000000b2e94d3ab51fe0e9af98b180"
+               "a36035fb6297dfa78704133102000000d9b4e3ce848518c4e3f6cbf25e5e1b990f13676700000000"
+               "000000000000000000000000690807d52617369ee1af4ca5a6a95581af5fd7ceb7a350f902000000"
+               "fa7db4532545666937c22a3b6e9686dcc43ccbee000000000000000000000000000000006ba04f14"
+               "6e4c699341b95f41d9c1e67743efb54e3cf09e8b01000000e79c10acfce165e332a62384ec04e5ba"
+               "b46785ad00000000000000000000000000000000f86c260a9edfa7a74c1c83c18e47935e8354846e"
+               "4fc812bd010000006d064126666befbb98c39e7cc282208fef4a7ff4000000000000000000000000"
+               "00000000add1c86e759d0c90bc512e137dcef5e4a27985bdd8a4c5cf01000000fcc2056cc1ff8e51"
+               "bbce426cfa4b861cc73992be000000000000000000000000000000000aa20624fbab244735d67c61"
+               "283031667b2d74a10d58fbcc01000000d0d44ff1e51c35157666c05348e6f507ff72e89800000000"
+               "000000000000000000000000cdf64b0f9a60e0e289f08db3e4bafc4e095a32302b09258301000000"
+               "9f9f0251becc987907879cca68fec7bbbb4d8edf0000000000000000000000000000000016772167"
+               "3f2b02b3256ead1f2fea9bc12f0cfca1078a806d000000008bbfc9723107f3f5b2cd3dbddc74e26d"
+               "e956e48d0000000000000000000000000000000088f83f6e70b1646d8026e9f1704f1b16286ba2da"
+               "0a6527f600000000aca0c7ee4197338581cafd63e66e3a23d6572578000000000000000000000000"
+               "000000009052c22b9f3c39f24b0572b1de745cb25851429b8c54dda900000000d43e6505621b9a7f"
+               "0422ae3fc8979ee0c400d79400000000000000000000000000000000414d94d0e02d2ec587fc3300"
+               "03781b0f294a2b73050424a203000000a029c896b4132bd895072cfeffe9d6812232bb0200000000"
+               "000000000000000000000000eb760e3f004a2efc8ffc96b89b1bc8f415bd4e77d611d7b103000000"
+               "f9f23b5f1570a4648a0ff80dd9a37a82e10c20aa00000000000000000000000000000000de6f36d6"
+               "64873c5be82c5361358730bcde43782df264af6703000000c38b6b8ee2614344bea73223e59bf019"
+               "345ae9fa000000000000000000000000000000009d80ab132e1ac921eb76adbb229df32e561fa80a"
+               "49ba9be3020000000c67c3d4afa9c8649da90bd2fff2f2568108870b000000000000000000000000"
+               "00000000d1111b067acbce3e3bcdf3824f1b134847ff26ba0e09e2fc01000000cc807cdce682f80f"
+               "94315c45ef817264c83c736e00000000000000000000000000000000c917ad6b10e693bb6e26f2ba"
+               "90c8e909e85e20e5182c028701000000ec4501a1e27acfee7a8588ffd5a06cae4228e59500000000"
+               "000000000000000000000000dda0b6ead1e1b34901eaedd5bcd7be07d78e62229427c22601000000"
+               "86ed27184b443181a5edc511c1507a141e4a5156000000000000000000000000000000009870ccec"
+               "3def0ad92749e016663fe6ee0228c1da1539ce410000000018238487a417f3da01d99dae5f190096"
+               "cc292cab00000000000000000000000000000000d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72f"
+               "b60291ea00000000def85b75827b26e8a0bef8675b29a197b71bccea000000000000000000000000"
+               "000000004a503a712350acbd48411f0dc15d2f0f49dad3458d1e69c801000000b973972c17a47631"
+               "c169f11ff9119c40b47cb66300000000000000000000000000000002ea4a849f060ddf57b24a5be9"
+               "b10123e6e4f15ecdc67b2e48000000001785d3ffdbd9a617857ae8126b028c0e3e4b535400000000"
+               "000000000000000000000002a84035b632ef3be6070dafa202187470e9da55709c88b02a02000000"
+               "04b0595daf37deb499996bfb667f072dec2c5d9c00000000000000000000ffff99d90a8543961b2c"
+               "cd47724a3c460ba85183f4c5daae7d6336506ebd00000000ef369bb8ebfdcec3f8c8fa6867a982be"
+               "8a534f8500000000000000000000ffffe8d07c84965a316af2abb982b001ec16901f55f960c55c22"),
+        [2] = ("df6f628a02000000f25d5895fb57fa60d9279fb2b8fcbac6ab6674f7a6df7979f6989d2d6a909e98"
+               "6ff7add5df2c93e05459507b5d8c6a0f466554d2ae4d52a8fa3abc1e0200000075a2f42b50c6a363"
+               "40132378b95c59313b58ba64dbe9962069e66142e22753344fda8a3e0b841cf76391dd68269b53ec"
+               "cb44a831714acfb554270e46000000006a894e387625da376feecb80ac245409c06bcc271d9c2f67"
+               "179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c50da49bfb16c737600000000"
+               "5219061ab4c5c79489b2cc6a883c14697280cb8b5adac8f5e6d66df46ea139107754dee62c36d2fc"
+               "70ccc567df7a49b8a4c73cf5ffa4ffc83722d9b7000000001fb7c8307f06b4b29088f20d9ac676d5"
+               "5b2dea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5"
+               "87bb24e500000000e9cf9f6df9615189f78ee815b2781ea55c5f55876b6a3f13922b6cb8360feaac"
+               "1d5c302df59dd32a677e5a3af4fe297f608df061d45d864d4d9ca9d20300000074e786047d8683fa"
+               "cd7db68b9ae586dacd0b070c50320427c4dd3d031e33f22c636360d696be085aad7c499dd538d345"
+               "74c038404923e75d30d2c50503000000210aeb0934cb2a247000b96d06992b6a5875d3995b9663d2"
+               "df48f333ef467092818aa77d6732b678cf844ef5a943825fcd743f59bd14c89bde485b1301000000"
+               "957c22721583c453d15ade4386ffd00b36518589be395312f09e601d88d4cb0c2f4d15c69e89f94e"
+               "f7b30486591030639eb0e99f7f53702fa4bab14801000000e6aae816430607ca0675e09818e9bbec"
+               "5966c59068baf76ab08940f6fc3bbdc7f6090f756aae660b3b02239efc1fb23d24438387def0f4c6"
+               "36ba0c700000000040318db913a353edc66efc068829c0e66a32143a0554efb46b382ab0404cdf02"
+               "9a6ce5d906837dbca6a5ee19d6f63fe9cbdd01f7246f13b25cfd5ce600000000152f2b70eaef7443"
+               "e0f79ab6902dde53362fec7127e19f254ac9588319558041fc62ed564153e17478f8c3344d89c0bb"
+               "5408c1b7d17fb084348352d7000000001ea012e5307294b6e346dac428ab20075e11e38dd16f8ee4"
+               "ccd2f50c30706cae3fdcc2f0ee3d5e2612ea3b95e1d2a5d0fb95e8332fdfff65c442139803000000"
+               "cee64b38b56e03e0f121014a2f70249eef217bcefeec856e3973dc42a1003be94f89d8eeb1f14d98"
+               "5d5bbe6c10085f3d6176f78a19bb8df109f4ecf100000000a1c748288ae21e371051852c972ea795"
+               "4066884afed7b044fd13a6826a4c619f9037d7a754655354c47cf0f551e96770a754ac19ef820fe0"
+               "f32099a8000000004d4647f53b1921af0f88e3759a08e7a56655861efedbf42ccd3c72f2ad063857"
+               "ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec7732d803e000000001a0dd8c052ed6b13"
+               "e2815a1a37a52bddd109f101fed87eed72bb58f0e9201f17bd99fcf72909ac9e7a55299e9bb4fd53"
+               "bc7417940fcffe3f544e4b2700000000a7f810d249adc17647176b7c07d856e3b87befebfe9dd9d6"
+               "1966b7ae7412041de5fe7d361f2d5dfd66a3ae3939762df33a2d55060c78d551eafb1ac402000000"
+               "3aa9986700b2a2b3ed257b8b0489f48f055ac8cafea105b327335a25a364d1015ffac03089f45539"
+               "e1b0178462156cd6b83579b8da27345add65aeb202000000902383028e3b47f2111d891d9f3c2c88"
+               "cd1676190000000000000000000000000000000162c12fba5b1cdec48d10bbb13b8313d8cd2a24d3"
+               "27c219630100000054830c68e44ea74b4f2b83f9b3bee14d866e4c9e000000000000000000000000"
+               "0000000101883918039249f398f9b37ea0914651b2a16e648483a2d0030000008ed3481e0bccc460"
+               "e89f7abdb682380a6208e37400000000000000000000000000000000bae87d60e00473755d31e5b2"
+               "de252167c1c91b3a8c45eb5003000000562dcd24a27c8212127da516b1091d1d4f5f081700000000"
+               "000000000000000000000000fe917423d314ae73572ff808b373f5b193d661edbe46bae503000000"
+               "6665a268abfe8a41d149f074a378b8811526ce6800000000000000000000000000000000911714f1"
+               "819aa46fbe143934c933ae6dec0fb124fcd32a7f0300000023a5223a2fb2c0764e08db62a3db07d0"
+               "907360b900000000000000000000000000000000b0ed3a7d0f8b13cd736aaa72021a381fb50e2c42"
+               "1dff04520300000036674cdefb57b9ae54cfe8f42bf0e9381b04c439000000000000000000000000"
+               "00000000eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21363c84c4eb0200000087780281d47a32f2"
+               "235376239daeaacb3c54864b00000000000000000000000000000000db544e44df2d7c2790d3119a"
+               "051bb2ee8192ac0c804c122f0100000059747cf23a1349c8ab2dadc5a39411fd4f60116d00000000"
+               "000000000000000000000000a7ae8ff040483e310819e3b10db116431ec6f76987b8c3b901000000"
+               "8982c42f53700a3437c5156e072b2f2fb372c9cf00000000000000000000000000000000df1424ce"
+               "3ee1d76fc1ea76e98c9dfc78997ab53eecc8fb1101000000fcebbaeb0c56535923f14874042a8aff"
+               "1d008e5e0000000000000000000000000000000064a2827b7cdd38d0314c178e925b568ae56bc4e9"
+               "f9531cd901000000d241989477a363d55ad47b8a11090df8da18d88d000000000000000000000000"
+               "000000004b15e2ae496ad01bf007eacb0a28aec86828251065dbc8a900000000b550c5db56189572"
+               "f32f4af595d785d5c10eaad40000000000000000000000000000000066ec305a225a0c71b2ce78bb"
+               "16104c8eaf18c56557d6249b000000007dc7f17f666969e45c3baf7a119a65ee0b34fa9e00000000"
+               "000000000000000000000000b314f695bd82e9ea8891ed0a3d1b91caae8cb60b891715df00000000"
+               "edae579e2e10c7f7a22ba0340436242cbe53248c000000000000000000000000000000006712dfb4"
+               "d184e820a28fa772a19cdf6dd1c173f6fcb6a035000000009726d80d9c2d89df765b4d9a130257dc"
+               "5f2bf03a00000000000000000000000000000000fd338f357e75f8ce6e8cc34573654ea6624f138e"
+               "a19da86c64fefab2548759d313a5b92de36f90d4bc8b496afd57fe47000000000000000000000000"
+               "00000000d4d1ae91c819bb068196d599efde3246e43f5e79507e308d030000008dab2dade2ab2fc9"
+               "c56a7c86be5f96228e05f2f400000000000000000000000000000000cb2c354434819f4b248eb2b8"
+               "e3ff1a5ffc386da387e6f7bb03000000e612098610e52bb2a16a4008aefd545b7a219db500000000"
+               "000000000000000000000000f769bb295766ed37a2123c5b6a813387e99bb834bc7ef08203000000"
+               "ef60a8b540b26f66761d1f13badfad0fe8178f3c00000000000000000000000000000000329f9971"
+               "84c9d2578e4cf9c48147507e1cd59ea13f81268103000000585287dc6ca972a2cfb18b2b2cb749e2"
+               "e022544d00000000000000000000000000000000ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0"
+               "9d82c85602000000050f63f8282f3464443cacf59c6379a44b8092f4000000000000000000000000"
+               "000000005481f25e307d0de9a7afe283bd2f22ead05ff72045aaf95e02000000b9ef1b2110613fc9"
+               "f90adac616a31dc2e44923450000000000000000000000000000000061fdf274fc5dca8ce1bd1871"
+               "ae17bb4515856092cbb110fd02000000b9aca07b6e4925dd27943a272c171ed15a66be1c00000000"
+               "00000000000000000000000002a1f8e6f5657f3f9e3eb807cbad76451055edb85f5b481b01000000"
+               "68cb3bc873b04d937a6ed8f7bc51e5406674d098000000000000000000000000000000007799416a"
+               "f1f0a086fbd7e2f7dea0077ae9c0c0d9438a72e1000000002495a609675344f7e2e3a5ebaec3c85f"
+               "0a5742f700000000000000000000000000000000bf085387046ef8a36f0e9e6982d15956ec2e2924"
+               "e45aaf610000000040125e6baf4648cbaaa37b861e6d46dafe556ec4000000000000000000000000"
+               "000000008a06e2c9cb29efdf672d2fa57fc85ebbe276c56691bf821c03000000c37ff4283a631480"
+               "91acc42cac4ef3ad373bfec700000000000000000000000000000002ae816e34cba1d74f18d1c4ed"
+               "b99c0e49bcb946f732d1d5f503000000bf7a70ba5193a162bc92ec1b11d9172cf83a034700000000"
+               "000000000000000000000002b887edcd61c2f5d2f381f843b809d259e499db7d34f0386a01000000"
+               "4064f6b021281e235f94ae20ac26c7d41f2a50bf00000000000000000000000000000002c66af76e"
+               "80f062ea1a7e0b7be218d067530e1b04c386e9c60100000010ee58bdc75183609985d627b6a4b1cb"
+               "e413cbbb00000000000000000000000000000002463f9b8306c83bc0269779114bdade862c224f6f"
+               "1a64192d010000005eb888e9eb37bb046686943b101e1f553273abf8000000000000000000000000"
+               "00000002a5e4351ea349e1ccf662e058b879a45176740744fde69eec01000000bd68ab722af14548"
+               "b29572e504265f6c726d3e2200000000000000000000ffffdb46a54cadb245eafde6b02567f814de"
+               "39dd8f88152a5845ab90135f0300000064d210a256be75e0509c4477d19ac64b2e64220d6c0fc09c"
+               "25d6534c809829af4a9df58dbfef186d416f3a1ef170d10f2c932e655203d82a5997069503000000"
+               "0d3c723b91adb0da7c4aa7e7eb5a15bcde525fb98b841fd84cc43c510385b9a4c2aac1d67a909b29"
+               "7c703915312e9c3c5a06c40b20ea99d562fb81e702000000ca7feefe1933631e8e69f6d9d6962d2c"
+               "6e5804f57239c09c47150b9b14010469823acb72bb89182f931121966e91b9153b36bc60be5b534e"
                "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620"
-               "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2"
-               "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
-               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9"
-               "4d88189c471a1ff2f0ce3ff66e782110125ae2eddaae7d6300000000c0982b283aa9986700b2a2b3"
-               "ed257b8b042ff48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6"
-               "6855676c00000000017c9148da5892bb4951c3a7ed55689d336bce7d3fd48469845f0c233dbdd2a9"
-               "7835df1f1782dee88487fd0db5971b46b7a350f998c39e7c00000000efc57ff447036b9551adcb22"
-               "fd50793f791c3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c"
-               "bc51e540030000006c3d048a92d3adc69a84eb47622400207708416afef0a086fbd7e2f7dea0077a"
-               "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183600422ae3f03000000c43bd794c697895e5bfe47c6"
-               "f53a54e01b211d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38"
-               "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51"
-               "bbce426cfa4b861cc78592be7b14e7ba9c15acb836674cde0200000054cfe8f42bf0e9381ba2c439"
-               "6de60f032b5af34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092"
-               "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9274c82feb83f6e70b1646d8026e9f1"
-               "704f1b16286ba2dabc7ef0820c91ed33ef60a8b5bcfe97e1000000007ad742d3d7320a4f880cf47f"
-               "5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd"
-               "1dce2f83020000003a3efc240b9977e166bee737fe73670c436b644c000000000000000000000000"
-               "000000015e0dfe491d1ba96742c7b5e02b227122cf844ef501000000cd743f59bd14c89b5cfd5ce6"
-               "e82e7854157b2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56"
-               "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000"
-               "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f"
-               "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
-               "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6705dfb4000000000000000000000000"
-               "00000000078a806d329c9b008bbfc9723107f3f52af145480300000004265f6c72923e22594f3f79"
-               "e208ed2e2e7107710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7"
-               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000"
-               "000000008ae21e371051852c972ea7954079884a9a1b163a030000000e2f4f911a0dd8c052ed6b13"
-               "e2815a1a37322bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53"
-               "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62042fba000000000000000000000000"
-               "000000004fc812bd2f8a59d96d064126666befbbf257b044030000006a4c619f9037d7a754655354"
-               "c47cf0f5512167700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b"
-               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000"
-               "00000000d538d34574c038404923e75d0e09e2fcfc1519de010000002545666937c22a3b6e9686dc"
-               "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
-               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000"
-               "00000000b883efebc09dd9d61966b7ae7412041dd7ac5f9901000000822a272bec4501a1e27acfee"
-               "7a8588ffd5286cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5"
-               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000"
-               "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096"
-               "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
-               "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000"
-               "000000005b29a197b7b3cceaf5f1bb12c535256a74993ade03000000e62112898dab2dade2ab2fc9"
-               "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
-               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000"
-               "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050"
-               "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
-               "bc74179403000000438a72e12ae0436a2495a609675344f7e226a5eb000000000000000000000000"
-               "0000000061eda239d9d083c3bf085387046ef8a34153e174020000004d89c0bb5408c1b7d17fb084"
-               "d9e1825fa613e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd"
-               "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef154d10f000000000000000000000000"
-               "0000000087780281d47a32f2235376239daeaacba9a89f8701000000fcdf6d9be94030b34774d1d7"
-               "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
-               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000"
-               "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc"
-               "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
-               "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e004457000000000000000000000000"
-               "00000000c1b5aad487c192f08bd7a09bdd21444359747cf200000000ab2dadc5a39411fd4ff1116d"
-               "478987316a293fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41"
-               "b95c593100000000dbe9962069e66142e22753344fda8a3e0b001cf7000000000000000000000000"
-               "000000008c54dda9ac3bb1b1d43e6505621b9a7f672d2fa501000000e276c5661a64192db44ce7af"
-               "5eb888e9eb73bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e"
-               "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf2a3542000000000000000000000000"
-               "0000000218f93e742d08f0550b55726f2292362e70ccc56700000000a4c73cf5ffa4ffc8a4bab148"
-               "74afdf54e64be8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75"
-               "4f1b134803000000449a774929e86716ec852d59f3d17232ed3a6ad6000000000000000000000000"
-               "000000027dba1ee7e143fd52599364e13468f80f40b26f6603000000badfad0fe8fa8f3c1aad2a82"
-               "530b7447d93bf401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16"
-               "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451013edb8000000000000000000000000"
-               "00000002d9279fb2b8fcbac6abea74f7a6df7979f6989d2d000000006ff7add5df2c93e05459507b"
-               "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
-        [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44"
-               "64d210a256be75e0509c4477d19ac64b2e69220d54270e46020000006a894e387625da376feecb80"
-               "ac245409c058cc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c"
-               "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148e4c481e0bccc460e89f7abdb682380a"
-               "62eae374835b4a49a2b980b6aba92da6409969aa7a899f2d010000009bf5a5d06776e66a9d80ab13"
-               "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
-               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359"
-               "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9c4e1966800000000b3121cd5ef51e696c816290d"
-               "baee0e77264c82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4"
-               "25a5927300000000c4458d70911714f1819aa46fbe143934c915ae6dec0fb124f264af67eed7c9a8"
-               "c38b6b8ee2614344bea73223e59bf0193432e9fa2fea9bc10300000062fb81e733cff620ca7feefe"
-               "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
-               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911"
-               "4bdade862c224f6f36506ebd455e679cef369bb8bb0ec36f0000000064a2827b7cdd38d0314c178e"
-               "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
-               "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226"
-               "d324d9a086ed27184b443181a5edc511c1507a14d514573f03000000f6932f6932d1d5f5d0ce2cf6"
-               "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
-               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d"
-               "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4ae8cb60b0300000038d856a40c67c3d4afa9c864"
-               "9da90bd2ff4cf25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382"
-               "00154d12010000009edfa7a74c1c83c18e47935e8354846e346e386a000000000000000000000000"
-               "00000001ac26c7d41fd050bf8d85055bcfe3756c1e0f515601000000f57609453df7803357ae7998"
-               "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
-               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000"
-               "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8"
-               "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
-               "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5305a94b6000000000000000000000000"
-               "00000000ccd2f50c30706cae3fdcc2f0ee3d5e267666c05303000000ffa8e89871fb9510b900b206"
-               "1d0c334b816a2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72"
-               "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000"
-               "0000000000459582f4ef43c780908872746e39ef10613fc90200000016a31dc2e46a234558817151"
-               "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
-               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000"
-               "00000000b314f695bd82e9ea8891ed0a3d1b91ca6e91b91501000000be5b534e0a6527f67485ab35"
-               "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
-               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000"
-               "00000000b49185ad9b7d0070a36dd51323b2c542b56e03e0010000002f70249eefc67bce716c856e"
-               "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1"
-               "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cb7410fd000000000000000000000000"
-               "000000002c171ed15abbbe1cd911db7b56e8f3feeecc89f601000000d635edea8ffe423d01883918"
-               "039249f3984ab37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5"
-               "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934392a24000000000000000000000000"
-               "00000000df48f333ef467092818aa77d6732b678bcd7be0700000000e45aaf61814f703b40125e6b"
-               "af4648cbaa557b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf"
-               "50da49bf00000000d94beb259f9f0251becc987907879cca6857c7bb000000000000000000000000"
-               "000000009402a4c2167721673f2b02b3256ead1fa349e1cc00000000b879a45176740744fcb6a035"
-               "339d504c972bd80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1"
-               "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700e538477000000000000000000000000"
-               "000000006ad0143a0554efb46b382ab0404cdf02ebfdcec30000000067a982be8a934f852cc3d4d8"
-               "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
-               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000"
-               "00000000020ffdb0e612098610e52bb2a16a4008aefd545b020000007ced4f8b362956cd46646a0e"
-               "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
-               "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000"
-               "0000000069d9942f460bcea75481f25e307d0de99a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7"
-               "246f13b2055624a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c"
-               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000"
-               "00000002e6fbf952902383028e3b47f2111d891d17a4763102000000f9119c40b468b66331bca67a"
-               "034f43340d2c683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06"
-               "536c75bb01000000720da9e07ac998b5690807d52617369ee16d4ca500000000000000000000ffff"
-               "47d7b351a36b178aabc78a4a0d80684ee5dbf45bb0916e5d030000004a503a712350acbd48411f0d"
-               "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
-               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e"
-               "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c"
-               "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
-               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620"
-               "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2"
-               "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
-               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9"
-               "4d88189c471a1ff2f0ce3ff66e782110125ae2edc7a4c65601000000cd625009a40aeb62a42b6a62"
-               "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
-               "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b042ff48f053ec8cac72105b327335a25"
-               "a364d1015ffac03089f45539e1b0178462156cd66855676c00000000017c9148da5892bb4951c3a7"
-               "ed55689d336bce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9"
-               "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067532d1b0487b8c3b99f2b8a1a8982c42f"
-               "53700a3437c5156e072b2f2fb337c9cfe07c34ddc896c9a7000000000a5f6abd850b06e3d9662795"
-               "ce2cc1cee03202aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221"
-               "98c39e7c00000000efc57ff447036b9551adcb22fd50793f791c3c7dadd1c86e759d0c90bc512e13"
-               "7dcef5e4a27985bd8d1e69c83dc21056b973972c7625ca4a00000000e682f80f94315c45ef817264"
-               "c89a736ed580d637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5"
-               "9c88b02a0300000004b0595daf37deb499996bfb667f072dec755d9cdc8a11f4409bcfb3c167090e"
-               "99d90a8543961b2ccd47724a3c460ba85183f4c5a7afe28302000000d05ff720b16c73761a3fcb67"
-               "5219061ab466c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc"
-               "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599"
-               "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b214d4647f5000000000f88e3759a08e7a56663861e"
-               "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
-               "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b211d89fecd94d0e02d2ec587fc3300"
-               "03781b0f294a2b73c4421398f4de67e9cee64b38bc51e540030000006c3d048a92d3adc69a84eb47"
-               "622400207708416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360"
-               "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032b5af34dfe959871c0d4c0d4eb720c4a"
-               "e3c9472e5f2da5d9c5cfd9f2343b21362c19a092b2cd3dbd01000000e958e48d2ab8b0313e7f8933"
-               "e315130fa9274c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5"
-               "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee"
-               "30b7e7736a0afc9879ea40e69710fc9f6e8e99cd69121aec010000000fcc00ac2daa755e5292cd32"
-               "dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8"
-               "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000"
-               "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f"
-               "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
-               "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854157b2b70000000000000000000000000"
-               "0000000127e19f254ac9588319558041fc62ed561dce2f83020000003a3efc240b9977e166bee737"
-               "fe73670c436b644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122"
-               "2af145480300000004265f6c72923e22594f3f79e208ed2e2e710771000000000000000000000000"
-               "0000000039dd8f88152a58454d9ca9d20f45dfa774993ade03000000e62112898dab2dade2ab2fc9"
-               "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
-               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000"
-               "000000008ae21e371051852c972ea7954079884a9f3c2c8803000000f51f44e644d7cf1d06b2b115"
-               "d882549e62042fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb"
-               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000"
-               "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050"
-               "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
-               "f257b044030000006a4c619f9037d7a754655354c47cf0f551216770000000000000000000000000"
-               "0000000050b8d10ab700cc3a7cf51be0403b654bbc74179403000000438a72e12ae0436a2495a609"
-               "675344f7e226a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3"
-               "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa613e1af000000000000000000000000"
-               "000000006e08cb887464b9344ec3127c750fabdd6c0fc09c02000000809829af4a9df58dbfef186d"
-               "416f3a1ef154d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb"
-               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000"
-               "00000000d538d34574c038404923e75d0e09e2fca9a89f8701000000fcdf6d9be94030b34774d1d7"
-               "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
-               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000"
-               "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc"
-               "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
-               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000"
-               "00000000b883efebc09dd9d61966b7ae7412041db7f0318a01000000061db3dd7fde25654a4059d5"
-               "65dbc8a91e00445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443"
-               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000"
-               "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096"
-               "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
-               "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a293fc2000000000000000000000000"
-               "000000000819e3b10db116431ec6f7691539ce41b95c593100000000dbe9962069e66142e2275334"
-               "4fda8a3e0b001cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f"
-               "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000"
-               "000000005b29a197b7b3cceaf5f1bb12c535256a0436242c03000000ee3fbc164632d4f28aa28c08"
-               "f80dd50c6705dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5"
-               "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37322bdd000000000000000000000000"
-               "00000000bd99fcf72909ac9e7a55299e9bb4fd53fc1519de010000002545666937c22a3b6e9686dc"
-               "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
-               "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5286cae000000000000000000000000"
-               "0000000077a47579dda0b6ead1e1b34901eaedd54f1b134803000000449a774929e86716ec852d59"
-               "f3d17232ed3a6ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f"
-               "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d93bf401000000000000000000000000"
-               "000000028147507e1cd59ea1ae6da48b1eba6d16dedea2030100000002a1f8e6f5657f3f9e3eb807"
-               "cbad76451013edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979"
-               "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb73bb04000000000000000000000000"
-               "00000002641e447514ca565fb9b68c08a5e4351e9f3c39f201000000de745cb25851429b3bbfb501"
-               "68dfd04edf2a35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e"
-               "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e64be816000000000000000000000000"
-               "0000000268baf76ab08940f6fc3bbdc7f6090f75f6989d2d000000006ff7add5df2c93e05459507b"
-               "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
+               "5f660bbf024b03ff0a8e27c405e6424457393c6fd7ac5f996e8e99cd01000000644f0e8ab3121cd5"
+               "ef51e696c816290dba040e7726d082e1530ff5397c9125f59577d71c4b258a005116d11354edff62"
+               "ceaa458fc75a91c440e55b8b01000000c0cfb2b4386bec092fa5757ecde9348bda4c2ebd4f78a6aa"
+               "667224dae775e5ccd141aca2ff0f576767d3648102b61886b62f22ca33478e1fabf85f0c01000000"
+               "08d33a2ea67776d7f88d69c8c7e3b2d3c954f054e93f8120abb42316c533d9c94d88189c471a1ff2"
+               "f0ce3ff66e782110125ae2ed74993adeb4d9e89b010000006855676c277cf1bd017c9148da5892bb"
+               "4945c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46"
+               "ae6da48b00000000bcfe97e1761e12457ad742d3d7320a4f8815f47f5dd0cf69cb22840f09b3fe77"
+               "49509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f2c19a0920000000092defa453a3efc24"
+               "0b9977e166bee737fe1c670c439a644c323b59b4cd20eb7dd3d38785c88ffad45e0dfe491d1ba967"
+               "42c7b5e02b2271220408423e00000000886dc91e2e5b0f0b65843fc961f7ad18014c3c67d9714f19"
+               "f1e41c073164d93b1171719c7f81773302d007c983566139c695e0f4b661ee6bc896c9a703000000"
+               "0a5f6abd850b06e3d9662795ce2cc1cee00802abfec2aaa218dc091e9aa1477f6fdc9830d7fbc958"
+               "29a8838314dff34d24c332219a1b163a3bbfb50103000000df6b3542f4bb5f574c5cf8107ba282f4"
+               "f9188219fef93e742d08f0550b55726f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51"
+               "c8f1853b030000000e2b00b667b7b16dae02dfa0c26e7c3add113bd3febfa0f02f2f3313d8469d09"
+               "a92409c0b7f0318a575a4f8e061db3dd7fde25654a4059d5acdca10f01000000a36b178aabc78a4a"
+               "0d80684ee5dbf45bba3010fbfeb3cc24904f288262e6ea77a22fa5f863eaeefa701d120523bd98f2"
+               "9b098b43cd68be6e37c27c15000000007e5c53e755c90fa09e9653ddcb1b14d2757f8a13feac3c17"
+               "34b49e955cbcf46d256a1818053da43ec5085d1df74d1e287271092897e26251b442548601000000"
+               "b0216050ed7afd741024e8365ea1aa11f92797c0feac468e9de5a9caf493472201e48d163106cc39"
+               "7446a33fdc335c8b152c507a0a3c8e7c449a774903000000ec852d59f3d17232edd86ad6c3103a68"
+               "844c9aef00000000000000000000000000000001599364e13468f80fd514573fb572671cf6932f69"
+               "d9e1825f03000000284a024b7f9e85ff73e050a2cd89fd276e0acb88000000000000000000000000"
+               "00000001129cdc690fcc00ac2daa755e5292cd32dc096cd572437f9b02000000ec9e71ee4b951507"
+               "beb42b3473cef00e896b5f2400000000000000000000000000000001bd2146bf8cfdb205f90e54b5"
+               "8a6ee136a779f37c87bc45db0100000036bd39a44e8e8bc17c01ac63eca0c1cfbb4f413200000000"
+               "000000000000000000000001eca6d1adaccae11f8045a80f855ebf3b772eb2b103e0b1b101000000"
+               "90a2312e17f1a35900459582f4ef43c7807b887200000000000000000000000000000001e94030b3"
+               "4774d1d7dddedd9899f0f627be16965a050424a203000000a029c896b4132bd895072cfeffe9d681"
+               "2232bb0200000000000000000000000000000000eb760e3f004a2efc8ffc96b89b1bc8f415bd4e77"
+               "d611d7b103000000f9f23b5f1570a4648a0ff80dd9a37a82e10c20aa000000000000000000000000"
+               "00000000de6f36d664873c5be82c5361358730bcde43782df264af6703000000c38b6b8ee2614344"
+               "bea73223e59bf019345ae9fa000000000000000000000000000000009d80ab132e1ac921eb76adbb"
+               "229df32e561fa80ac7a4c65603000000cd625009a40aeb62a42b6a62548e3c38b96a200200000000"
+               "000000000000000000000000b2e94d3ab51fe0e9af98b180a36035fb6297dfa749ba9be302000000"
+               "0c67c3d4afa9c8649da90bd2fff2f2568108870b00000000000000000000000000000000d1111b06"
+               "7acbce3e3bcdf3824f1b134847ff26ba8704133102000000d9b4e3ce848518c4e3f6cbf25e5e1b99"
+               "0f13676700000000000000000000000000000000690807d52617369ee1af4ca5a6a95581af5fd7ce"
+               "b7a350f902000000fa7db4532545666937c22a3b6e9686dcc43ccbee000000000000000000000000"
+               "000000006ba04f146e4c699341b95f41d9c1e67743efb54e0e09e2fc01000000cc807cdce682f80f"
+               "94315c45ef817264c83c736e00000000000000000000000000000000c917ad6b10e693bb6e26f2ba"
+               "90c8e909e85e20e5182c028701000000ec4501a1e27acfee7a8588ffd5a06cae4228e59500000000"
+               "000000000000000000000000dda0b6ead1e1b34901eaedd5bcd7be07d78e62223cf09e8b01000000"
+               "e79c10acfce165e332a62384ec04e5bab46785ad00000000000000000000000000000000f86c260a"
+               "9edfa7a74c1c83c18e47935e8354846e4fc812bd010000006d064126666befbb98c39e7cc282208f"
+               "ef4a7ff400000000000000000000000000000000add1c86e759d0c90bc512e137dcef5e4a27985bd"
+               "9427c2260100000086ed27184b443181a5edc511c1507a141e4a5156000000000000000000000000"
+               "000000009870ccec3def0ad92749e016663fe6ee0228c1da0d58fbcc01000000d0d44ff1e51c3515"
+               "7666c05348e6f507ff72e89800000000000000000000000000000000cdf64b0f9a60e0e289f08db3"
+               "e4bafc4e095a32302b092583010000009f9f0251becc987907879cca68fec7bbbb4d8edf00000000"
+               "000000000000000000000000167721673f2b02b3256ead1f2fea9bc12f0cfca1078a806d00000000"
+               "8bbfc9723107f3f5b2cd3dbddc74e26de956e48d0000000000000000000000000000000088f83f6e"
+               "70b1646d8026e9f1704f1b16286ba2da0a6527f600000000aca0c7ee4197338581cafd63e66e3a23"
+               "d6572578000000000000000000000000000000009052c22b9f3c39f24b0572b1de745cb25851429b"
+               "1539ce410000000018238487a417f3da01d99dae5f190096cc292cab000000000000000000000000"
+               "00000000d8fbe9a4fdb3d117d76b0de5416f95e7bb75d72fb60291ea00000000def85b75827b26e8"
+               "a0bef8675b29a197b71bccea000000000000000000000000000000004a503a712350acbd48411f0d"
+               "c15d2f0f49dad3452931f2d30300000050b8d10ab700cc3a7cf51be0403b654ba242680800000000"
+               "000000000000000000000000a6bdc8789cf851782d895bcc8da98489c55b8c1237686b5e03000000"
+               "6215de4bb6f6f3d031a790287e45e9c18b7136c000000000000000000000000000000000ec7d3abc"
+               "4ccf30d3aaaf02a4f05d7308b67f91bfd8a4c5cf01000000fcc2056cc1ff8e51bbce426cfa4b861c"
+               "c73992be000000000000000000000000000000000aa20624fbab244735d67c61283031667b2d74a1"
+               "8c54dda900000000d43e6505621b9a7f0422ae3fc8979ee0c400d794000000000000000000000000"
+               "00000000414d94d0e02d2ec587fc330003781b0f294a2b738d1e69c801000000b973972c17a47631"
+               "c169f11ff9119c40b47cb66300000000000000000000000000000002ea4a849f060ddf57b24a5be9"
+               "b10123e6e4f15ecdc67b2e48000000001785d3ffdbd9a617857ae8126b028c0e3e4b535400000000"
+               "000000000000000000000002a84035b632ef3be6070dafa202187470e9da557036506ebd00000000"
+               "ef369bb8ebfdcec3f8c8fa6867a982be8a534f8500000000000000000000ffffe8d07c84965a316a"
+               "f2abb982b001ec16901f55f960c55c229c88b02a0200000004b0595daf37deb499996bfb667f072d"
+               "ec2c5d9c00000000000000000000ffff99d90a8543961b2ccd47724a3c460ba85183f4c5daae7d63"),
     };
     const int   TEST_DATA_I      = GPOINTER_TO_INT(test_data);
     const int   addr_family      = (TEST_DATA_I == 0 ? AF_INET : AF_INET6);
diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c
index 148ed792..bd8fdc27 100644
--- a/src/core/platform/tests/test-route.c
+++ b/src/core/platform/tests/test-route.c
@@ -27,7 +27,7 @@ _wait_for_ipv4_addr_device_route(NMPlatform *platform,
     /* Wait that the addresses gets a device-route. After adding a address,
      * the device route is not added immediately. It takes a moment... */
 
-    addr = nm_utils_ip4_address_clear_host_address(addr, plen);
+    addr = nm_ip4_addr_clear_host_address(addr, plen);
     NMTST_WAIT_ASSERT(400, {
         NMDedupMultiIter iter;
         NMPLookup        lookup;
@@ -41,7 +41,7 @@ _wait_for_ipv4_addr_device_route(NMPlatform *platform,
             &o) {
             const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(o);
 
-            if (r->plen == plen && addr == nm_utils_ip4_address_clear_host_address(r->network, plen)
+            if (r->plen == plen && addr == nm_ip4_addr_clear_host_address(r->network, plen)
                 && r->metric == 0 && r->scope_inv == nm_platform_route_scope_inv(RT_SCOPE_LINK)
                 && r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
                 return;
@@ -333,30 +333,33 @@ test_ip4_route(void)
     /* Test route listing */
     routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex);
     memset(rts, 0, sizeof(rts));
-    rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
-    rts[0].network   = gateway;
-    rts[0].plen      = 32;
-    rts[0].ifindex   = ifindex;
-    rts[0].gateway   = INADDR_ANY;
-    rts[0].metric    = metric;
-    rts[0].mss       = mss;
-    rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK);
-    rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
-    rts[1].network   = network;
-    rts[1].plen      = plen;
-    rts[1].ifindex   = ifindex;
-    rts[1].gateway   = gateway;
-    rts[1].metric    = metric;
-    rts[1].mss       = mss;
-    rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE);
-    rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
-    rts[2].network   = 0;
-    rts[2].plen      = 0;
-    rts[2].ifindex   = ifindex;
-    rts[2].gateway   = gateway;
-    rts[2].metric    = metric;
-    rts[2].mss       = mss;
-    rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE);
+    rts[0].rt_source  = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
+    rts[0].network    = gateway;
+    rts[0].plen       = 32;
+    rts[0].ifindex    = ifindex;
+    rts[0].gateway    = INADDR_ANY;
+    rts[0].metric     = metric;
+    rts[0].mss        = mss;
+    rts[0].scope_inv  = nm_platform_route_scope_inv(RT_SCOPE_LINK);
+    rts[0].n_nexthops = 1;
+    rts[1].rt_source  = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
+    rts[1].network    = network;
+    rts[1].plen       = plen;
+    rts[1].ifindex    = ifindex;
+    rts[1].gateway    = gateway;
+    rts[1].metric     = metric;
+    rts[1].mss        = mss;
+    rts[1].scope_inv  = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE);
+    rts[1].n_nexthops = 1;
+    rts[2].rt_source  = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
+    rts[2].network    = 0;
+    rts[2].plen       = 0;
+    rts[2].ifindex    = ifindex;
+    rts[2].gateway    = gateway;
+    rts[2].metric     = metric;
+    rts[2].mss        = mss;
+    rts[2].scope_inv  = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE);
+    rts[2].n_nexthops = 1;
     g_assert_cmpint(routes->len, ==, 3);
     nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata,
                                          rts,
@@ -635,21 +638,22 @@ test_ip4_route_options(gconstpointer test_data)
     switch (TEST_IDX) {
     case 1:
         rts_add[rts_n++] = ((NMPlatformIP4Route){
-            .ifindex   = IFINDEX,
-            .rt_source = NM_IP_CONFIG_SOURCE_USER,
-            .network   = nmtst_inet4_from_string("172.16.1.0"),
-            .plen      = 24,
-            .metric    = 20,
-            .tos       = 0x28,
-            .window    = 10000,
-            .cwnd      = 16,
-            .initcwnd  = 30,
-            .initrwnd  = 50,
-            .mtu       = 1350,
-            .lock_cwnd = TRUE,
-            .mss       = 1300,
-            .quickack  = TRUE,
-            .rto_min   = 1000,
+            .ifindex    = IFINDEX,
+            .rt_source  = NM_IP_CONFIG_SOURCE_USER,
+            .network    = nmtst_inet4_from_string("172.16.1.0"),
+            .plen       = 24,
+            .metric     = 20,
+            .tos        = 0x28,
+            .window     = 10000,
+            .cwnd       = 16,
+            .initcwnd   = 30,
+            .initrwnd   = 50,
+            .mtu        = 1350,
+            .lock_cwnd  = TRUE,
+            .mss        = 1300,
+            .quickack   = TRUE,
+            .rto_min    = 1000,
+            .n_nexthops = 1,
         });
         break;
     case 2:
@@ -663,12 +667,13 @@ test_ip4_route_options(gconstpointer test_data)
               .n_ifa_flags  = 0,
         });
         rts_add[rts_n++] = ((NMPlatformIP4Route){
-            .ifindex   = IFINDEX,
-            .rt_source = NM_IP_CONFIG_SOURCE_USER,
-            .network   = nmtst_inet4_from_string("172.17.1.0"),
-            .gateway   = nmtst_inet4_from_string("172.16.1.1"),
-            .plen      = 24,
-            .metric    = 20,
+            .ifindex    = IFINDEX,
+            .rt_source  = NM_IP_CONFIG_SOURCE_USER,
+            .network    = nmtst_inet4_from_string("172.17.1.0"),
+            .gateway    = nmtst_inet4_from_string("172.16.1.1"),
+            .plen       = 24,
+            .metric     = 20,
+            .n_nexthops = 1,
         });
         rts_add[rts_n++] = ((NMPlatformIP4Route){
             .ifindex     = IFINDEX,
@@ -678,6 +683,7 @@ test_ip4_route_options(gconstpointer test_data)
             .r_rtm_flags = RTNH_F_ONLINK,
             .plen        = 24,
             .metric      = 20,
+            .n_nexthops  = 1,
         });
         break;
     default:
@@ -691,24 +697,23 @@ test_ip4_route_options(gconstpointer test_data)
         const NMPlatformIP4Address *a = &addr[i];
 
         g_assert(a->ifindex == IFINDEX);
-        g_assert(nm_platform_ip4_address_add(
-            NM_PLATFORM_GET,
-            a->ifindex,
-            a->address,
-            a->plen,
-            a->peer_address,
-            nm_platform_ip4_broadcast_address_create(a->address, a->plen),
-            a->lifetime,
-            a->preferred,
-            a->n_ifa_flags,
-            a->label));
+        g_assert(nm_platform_ip4_address_add(NM_PLATFORM_GET,
+                                             a->ifindex,
+                                             a->address,
+                                             a->plen,
+                                             a->peer_address,
+                                             nm_ip4_addr_get_broadcast_address(a->address, a->plen),
+                                             a->lifetime,
+                                             a->preferred,
+                                             a->n_ifa_flags,
+                                             a->label));
         if (a->peer_address == a->address)
             _wait_for_ipv4_addr_device_route(NM_PLATFORM_GET, 200, a->ifindex, a->address, a->plen);
     }
 
     for (i = 0; i < rts_n; i++)
         g_assert(NMTST_NM_ERR_SUCCESS(
-            nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i])));
+            nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i], NULL)));
 
     for (i = 0; i < rts_n; i++) {
         rts_cmp[i] = rts_add[i];
@@ -747,7 +752,7 @@ test_ip6_route_get(void)
         nmtstp_wait_for_signal(NM_PLATFORM_GET, 10);
         if (nmtstp_ip6_route_get(NM_PLATFORM_GET,
                                  ifindex,
-                                 nmtst_inet6_from_string("fd01:abcd::"),
+                                 nmtst_inet6_from_string_p("fd01:abcd::"),
                                  64,
                                  NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6,
                                  NULL,
@@ -755,7 +760,7 @@ test_ip6_route_get(void)
             break;
     });
 
-    a      = nmtst_inet6_from_string("fd01:abcd::42");
+    a      = nmtst_inet6_from_string_p("fd01:abcd::42");
     result = nm_platform_ip_route_get(NM_PLATFORM_GET,
                                       AF_INET6,
                                       a,
@@ -797,7 +802,7 @@ test_ip6_route_options(gconstpointer test_data)
         rts_add[rts_n++] = ((NMPlatformIP6Route){
             .ifindex   = IFINDEX,
             .rt_source = NM_IP_CONFIG_SOURCE_USER,
-            .network   = *nmtst_inet6_from_string("2001:db8:a:b:0:0:0:0"),
+            .network   = nmtst_inet6_from_string("2001:db8:a:b:0:0:0:0"),
             .plen      = 64,
             .gateway   = in6addr_any,
             .metric    = 1024,
@@ -812,7 +817,7 @@ test_ip6_route_options(gconstpointer test_data)
     case 2:
         addr[addr_n++]   = ((NMPlatformIP6Address){
               .ifindex      = IFINDEX,
-              .address      = *nmtst_inet6_from_string("2000::2"),
+              .address      = nmtst_inet6_from_string("2000::2"),
               .plen         = 128,
               .peer_address = in6addr_any,
               .lifetime     = NM_PLATFORM_LIFETIME_PERMANENT,
@@ -822,17 +827,17 @@ test_ip6_route_options(gconstpointer test_data)
         rts_add[rts_n++] = ((NMPlatformIP6Route){
             .ifindex   = IFINDEX,
             .rt_source = NM_IP_CONFIG_SOURCE_USER,
-            .network   = *nmtst_inet6_from_string("1010::1"),
+            .network   = nmtst_inet6_from_string("1010::1"),
             .plen      = 128,
             .gateway   = in6addr_any,
             .metric    = 256,
-            .pref_src  = *nmtst_inet6_from_string("2000::2"),
+            .pref_src  = nmtst_inet6_from_string("2000::2"),
         });
         break;
     case 3:
         addr[addr_n++]   = ((NMPlatformIP6Address){
               .ifindex      = IFINDEX,
-              .address      = *nmtst_inet6_from_string("2001:db8:8086::5"),
+              .address      = nmtst_inet6_from_string("2001:db8:8086::5"),
               .plen         = 128,
               .peer_address = in6addr_any,
               .lifetime     = NM_PLATFORM_LIFETIME_PERMANENT,
@@ -842,7 +847,7 @@ test_ip6_route_options(gconstpointer test_data)
         rts_add[rts_n++] = ((NMPlatformIP6Route){
             .ifindex   = IFINDEX,
             .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER),
-            .network   = *nmtst_inet6_from_string("2001:db8:8086::"),
+            .network   = nmtst_inet6_from_string("2001:db8:8086::"),
             .plen      = 110,
             .metric    = 10021,
             .mss       = 0,
@@ -850,9 +855,9 @@ test_ip6_route_options(gconstpointer test_data)
         rts_add[rts_n++] = ((NMPlatformIP6Route){
             .ifindex   = IFINDEX,
             .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER),
-            .network   = *nmtst_inet6_from_string("2001:db8:abad:c0de::"),
+            .network   = nmtst_inet6_from_string("2001:db8:abad:c0de::"),
             .plen      = 64,
-            .gateway   = *nmtst_inet6_from_string("2001:db8:8086::1"),
+            .gateway   = nmtst_inet6_from_string("2001:db8:8086::1"),
             .metric    = 21,
             .mss       = 0,
         });
@@ -918,7 +923,7 @@ test_ip(gconstpointer test_data)
     const NMPlatformLink *l;
     char                  ifname[IFNAMSIZ];
     char                  ifname2[IFNAMSIZ];
-    char                  s1[NM_UTILS_INET_ADDRSTRLEN];
+    char                  s1[NM_INET_ADDRSTRLEN];
     NMPlatform           *platform = NM_PLATFORM_GET;
     const int             EX_      = -1;
     struct {
@@ -983,8 +988,8 @@ again_find_idx:
             order_idx[order_len++] = idx;
 
             r->ifindex = iface_data[idx].ifindex;
-            g_assert(
-                NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r)));
+            g_assert(NMTST_NM_ERR_SUCCESS(
+                nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r, NULL)));
         } else {
             i   = nmtst_get_rand_uint32() % order_len;
             idx = order_idx[i];
@@ -1171,7 +1176,7 @@ _rule_check_kernel_support(NMPlatform *platform, int attribute)
                  .addr_family = AF_INET,
                  .priority    = PROBE_PRORITY,
                  .uid_range =
-                     {
+                {
                          .start = 0,
                          .end   = 0,
                 },
@@ -1278,7 +1283,7 @@ _rule_create_random(NMPlatform *platform)
     guint32                p;
     int                    addr_size;
     guint                  i;
-    char                   saddr[NM_UTILS_INET_ADDRSTRLEN];
+    char                   saddr[NM_INET_ADDRSTRLEN];
     static struct {
         guint32 uid;
         guint32 euid;
@@ -1331,7 +1336,7 @@ _rule_create_random(NMPlatform *platform)
                     p_addr->addr4 =
                         nmtst_inet4_from_string(nm_sprintf_buf(saddr, "192.192.5.%u", (~p) % 256u));
                 else
-                    p_addr->addr6 = *nmtst_inet6_from_string(
+                    p_addr->addr6 = nmtst_inet6_from_string(
                         nm_sprintf_buf(saddr, "1:2:3:4::f:%02x", (~p) % 256u));
             } else if ((p % 3u) == 1)
                 nmtst_rand_buf(NULL, p_addr, addr_size);
@@ -1931,7 +1936,7 @@ test_blackhole(gconstpointer test_data)
     nm_platform_ip_route_normalize(addr_family, &rr.rx);
 
     if (IS_IPv4)
-        r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4);
+        r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4, NULL);
     else
         r = nm_platform_ip6_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r6);
 
@@ -2155,6 +2160,229 @@ test_mptcp(gconstpointer test_data)
 
 /*****************************************************************************/
 
+static void
+_ensure_onlink_routes(void)
+{
+    int i;
+
+    for (i = 0; i < G_N_ELEMENTS(NMTSTP_ENV1_DEVICE_NAME) && NMTSTP_ENV1_DEVICE_NAME[i]; i++) {
+        nmtstp_run_command("ip route append 7.7.7.0/24 dev %s%s",
+                           NMTSTP_ENV1_DEVICE_NAME[i],
+                           nmtst_is_debug() ? "" : " &>/dev/null");
+        nmtstp_run_command("ip route append 7:7:7::/64 dev %s%s",
+                           NMTSTP_ENV1_DEVICE_NAME[i],
+                           nmtst_is_debug() ? "" : " &>/dev/null");
+    }
+}
+
+static void
+test_cache_consistency_routes(gconstpointer test_data)
+{
+    const int                    TEST_IDX      = GPOINTER_TO_INT(test_data);
+    NMPlatform                  *platform      = NM_PLATFORM_GET;
+    gboolean                     is_test_quick = nmtst_test_quick();
+    const int                    N_RUN         = is_test_quick ? 50 : 500;
+    int                          i_run;
+    gs_unref_ptrarray GPtrArray *keeper = g_ptr_array_new_with_free_func(g_free);
+
+    _ensure_onlink_routes();
+
+    for (i_run = 0; i_run < N_RUN; i_run++) {
+        const char   *extra_options[100];
+        gsize         n_extra_options   = 0;
+        gs_free char *extra_options_str = NULL;
+        int           i_if;
+        int           ifindex;
+        const char   *ifname;
+        int           IS_IPv4;
+        const char   *op;
+        const char   *prefix;
+        const char   *s;
+        const char   *route_type;
+        int           i;
+        int           n;
+        char          addr_family_char[2] = {'6', '4'};
+
+        g_ptr_array_set_size(keeper, 0);
+
+        switch (TEST_IDX) {
+        case 1:
+            IS_IPv4 = TRUE;
+            break;
+        case 2:
+            IS_IPv4 = FALSE;
+            break;
+        default:
+            IS_IPv4 = nmtst_get_rand_bool();
+            break;
+        }
+
+        i_if = nmtst_get_rand_uint32() % 2;
+        op   = nmtst_rand_select_str("flush", "add", "change", "append", "prepend", "replace");
+
+        ifindex = NMTSTP_ENV1_IFINDEXES[i_if];
+        ifname  = NMTSTP_ENV1_DEVICE_NAME[i_if];
+
+        g_assert_cmpint(ifindex, ==, nm_platform_link_get_ifindex(platform, ifname));
+
+        if (nm_streq(op, "flush")) {
+            if (!nmtst_get_rand_one_case_in(10)) {
+                /* flush more seldom. */
+                continue;
+            }
+            nmtstp_run_command("ip -%c route flush dev %s"
+                               " table %s" /* table */
+                               "%s"        /* redirect */
+                               "",
+                               addr_family_char[IS_IPv4],
+                               ifname,
+                               nmtst_rand_select_str("main", "10222", "10223", "all"),
+                               nmtst_is_debug() ? "" : " &>/dev/null");
+            _ensure_onlink_routes();
+            goto done;
+        }
+
+        route_type = nmtst_get_rand_one_case_in(4)
+                         ? nmtst_rand_select_str("unicast", "blackhole", "local", "broadcast")
+                         : NULL;
+
+        if (NM_IN_STRSET(route_type, "blackhole")) {
+            ifindex = 0;
+            ifname  = NULL;
+        }
+
+        if (IS_IPv4) {
+            prefix = nmtst_rand_select_str("192.168.4.0/24",
+                                           "192.168.5.0/24",
+                                           "192.168.5.5/32",
+                                           "default");
+        } else {
+            prefix =
+                nmtst_rand_select_str("a:b:c:d::/64", "a:b:c:e::/64", "a:b:c:f::/64", "default");
+        }
+
+        s = nmtst_rand_select_str(NULL, "kernel", "bird");
+        if (s) {
+            if (nmtst_get_rand_bool()) {
+                s = nm_streq(s, "kernel") ? nmtst_rand_select_str("boot", "static", "ra")
+                                          : nmtst_rand_select_str("zebra", "bird");
+            }
+            extra_options[n_extra_options++] = "proto";
+            extra_options[n_extra_options++] = s;
+        }
+
+        s = nmtst_rand_select_str(NULL, "10", "20");
+        if (s) {
+            extra_options[n_extra_options++] = "metric";
+            extra_options[n_extra_options++] = s;
+        }
+
+        s = nmtst_rand_select_str(NULL, "10222", "10223");
+        if (s) {
+            extra_options[n_extra_options++] = "table";
+            extra_options[n_extra_options++] = s;
+        }
+
+        if (!IS_IPv4 && NM_IN_STRSET(op, "add", "change", "append", "prepend", "replace")) {
+            /* kernel has a bug with append/prepend of IPv6 routes with next-hops.
+             * This leads to wrong notification messages, wrong merging of multi-hop
+             * routes and cache inconsistency in NMPlatform.
+             *
+             * https://bugzilla.redhat.com/show_bug.cgi?id=2161994
+             *
+             * For now, disable the test case to make the unit test not fail.
+             *
+             * While being a kernel bug, it leads to cache inconsistency in NMPlatform,
+             * which is a problem for NetworkManager. I don't see how we can detect
+             * this problem to trigger a refresh. */
+        } else if (ifname && nmtst_get_rand_one_case_in(3)) {
+            n = (nmtst_get_rand_uint32() % 4) + 1;
+            for (i = 0; i < n; i++) {
+                extra_options[n_extra_options++] = "nexthop";
+                extra_options[n_extra_options++] = "via";
+                if (IS_IPv4) {
+                    extra_options[n_extra_options++] =
+                        nmtst_keeper_printf(&keeper, "7.7.7.%d", i + 1);
+                } else {
+                    extra_options[n_extra_options++] =
+                        nmtst_keeper_printf(&keeper, "7:7:7:7::%d", i + 1);
+                }
+                extra_options[n_extra_options++] = "dev";
+                extra_options[n_extra_options++] = NMTSTP_ENV1_DEVICE_NAME[nmtst_get_rand_bool()];
+                if (IS_IPv4 && i == 0) {
+                    /* For IPv4, there is a problem if we configure a route with
+                     * only one next-hop and a weight. In that case, kernel allows
+                     * to add duplicates (that only differ by weight), but on netlink
+                     * the weight is not exposed, so the routes look identical and
+                     * are deduplicated by the hash.
+                     * See https://bugzilla.redhat.com/show_bug.cgi?id=2162315
+                     *
+                     * This needs a kernel fix. Workaround that issue here, otherwise the test
+                     * will randomly fail. */
+                } else if (nmtst_get_rand_one_case_in(3)) {
+                    extra_options[n_extra_options++] = "weight";
+                    extra_options[n_extra_options++] = "5";
+                }
+            }
+
+            ifname  = NULL;
+            ifindex = 0;
+        }
+
+        g_assert_cmpint(n_extra_options, <, G_N_ELEMENTS(extra_options));
+        extra_options[n_extra_options] = NULL;
+
+        if (nmtst_is_debug())
+            nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]);
+
+        /* We ignore errors. The reason is that operations like "change" might fail if
+         * the route doesn't exist. That's fine for our test. We just do randomly things
+         * and some of them will stick. */
+        nmtstp_run_command(
+            "ip -%c route "
+            "%s"   /* op */
+            "%s%s" /* route_type */
+            " %s"  /* prefix */
+            "%s%s" /* ifname */
+            "%s%s" /* extra_options */
+            "%s"   /* redirect */
+            "",
+            addr_family_char[IS_IPv4],
+            op,
+            NM_PRINT_FMT_QUOTED2(route_type, " ", route_type, ""),
+            prefix,
+            NM_PRINT_FMT_QUOTED2(ifname, " dev ", ifname, ""),
+            NM_PRINT_FMT_QUOTED2(extra_options[0],
+                                 " ",
+                                 (extra_options_str = g_strjoinv(" ", (char **) extra_options)),
+                                 ""),
+            nmtst_is_debug() ? "" : " &>/dev/null");
+
+        if (nmtst_is_debug())
+            nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]);
+done:
+        nm_platform_process_events(platform);
+
+        if (!is_test_quick || (i_run + 1 == N_RUN) || nmtst_get_rand_one_case_in(5)) {
+            nmtstp_check_platform(
+                platform,
+                nmtst_get_rand_one_case_in(5)
+                    ? 0u
+                    : nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)));
+        }
+    }
+
+    if (is_test_quick) {
+        gs_free char *msg = NULL;
+
+        msg = g_strdup_printf("Ran a quick version of test %s (try NMTST_DEBUG=slow)",
+                              nmtst_test_get_path());
+        g_test_skip(msg);
+    }
+}
+
+/*****************************************************************************/
+
 NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
 
 void
@@ -2166,9 +2394,11 @@ _nmtstp_init_tests(int *argc, char ***argv)
 void
 _nmtstp_setup_tests(void)
 {
-#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE)
+#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, TRUE)
 #define add_test_func_data(testpath, test_func, arg) \
-    nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE)
+    nmtstp_env1_add_test_func_data(testpath, test_func, arg, 1, TRUE)
+#define add_test_func_data_with_if2(testpath, test_func, arg) \
+    nmtstp_env1_add_test_func_data(testpath, test_func, arg, 2, TRUE)
 
     add_test_func("/route/ip4", test_ip4_route);
     add_test_func("/route/ip6", test_ip6_route);
@@ -2201,4 +2431,15 @@ _nmtstp_setup_tests(void)
         add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1));
         add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2));
     }
+    if (nmtstp_is_root_test()) {
+        add_test_func_data_with_if2("/route/test_cache_consistency_routes/1",
+                                    test_cache_consistency_routes,
+                                    GINT_TO_POINTER(1));
+        add_test_func_data_with_if2("/route/test_cache_consistency_routes/2",
+                                    test_cache_consistency_routes,
+                                    GINT_TO_POINTER(2));
+        add_test_func_data_with_if2("/route/test_cache_consistency_routes/3",
+                                    test_cache_consistency_routes,
+                                    GINT_TO_POINTER(3));
+    }
 }
diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c
index 6a2019c8..832fbea6 100644
--- a/src/core/platform/tests/test-tc.c
+++ b/src/core/platform/tests/test-tc.c
@@ -214,8 +214,8 @@ _nmtstp_init_tests(int *argc, char ***argv)
 void
 _nmtstp_setup_tests(void)
 {
-    nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, TRUE);
-    nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, TRUE);
-    nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, TRUE);
-    nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, TRUE);
+    nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, 1, TRUE);
+    nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, 1, TRUE);
+    nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, 1, TRUE);
+    nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, 1, TRUE);
 }