summary refs log tree commit diff
path: root/src/initrd
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-12-06 21:57:59 +0100
committerMichael Biebl <biebl@debian.org>2020-12-06 21:57:59 +0100
commit65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (patch)
tree180827692f002e5f1dad6a0fa8ca489e6bb6438f /src/initrd
parentf2ddac4cbc895837ddcc55015fae112f9859cd0a (diff)
New upstream version 1.28.0 upstream/1.28.0
Diffstat (limited to 'src/initrd')
-rw-r--r--src/initrd/nm-initrd-generator.h24
-rw-r--r--src/initrd/nmi-cmdline-reader.c121
-rw-r--r--src/initrd/nmi-ibft-reader.c4
-rw-r--r--src/initrd/tests/test-cmdline-reader.c413
4 files changed, 510 insertions, 52 deletions
diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h
index cac01cb8..69c24b1b 100644
--- a/src/initrd/nm-initrd-generator.h
+++ b/src/initrd/nm-initrd-generator.h
@@ -11,17 +11,23 @@
 
 #define NMI_WAIT_DEVICE_TIMEOUT_MS 60000
 
-static inline gboolean
-guess_ip_address_family(const char *str)
+static inline int
+get_ip_address_family(const char *str, gboolean with_prefix)
 {
-    if (str == NULL)
-        return AF_UNSPEC;
-    else if (strchr(str, '.'))
-        return AF_INET;
-    else if (strchr(str, ':'))
-        return AF_INET6;
-    else
+    int addr_family;
+
+    if (!str)
         return AF_UNSPEC;
+
+    if (with_prefix) {
+        if (nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, str, &addr_family, NULL, NULL))
+            return addr_family;
+    } else {
+        if (nm_utils_parse_inaddr_bin(AF_UNSPEC, str, &addr_family, NULL))
+            return addr_family;
+    }
+
+    return AF_UNSPEC;
 }
 
 GHashTable *nmi_ibft_read(const char *sysfs_dir);
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index 5e610e15..1c1b43eb 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -24,6 +24,8 @@
 typedef struct {
     GHashTable *  hash;
     GPtrArray *   array;
+    GPtrArray *   vlan_parents;
+    GHashTable *  explicit_ip_connections;
     NMConnection *bootdev_connection; /* connection for bootdev=$ifname */
     NMConnection *default_connection; /* connection not bound to any ifname */
     char *        hostname;
@@ -41,8 +43,11 @@ reader_new(void)
 
     reader  = g_slice_new(Reader);
     *reader = (Reader){
-        .hash  = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref),
-        .array = g_ptr_array_new(),
+        .hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_object_unref),
+        .explicit_ip_connections =
+            g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL),
+        .vlan_parents = g_ptr_array_new_with_free_func(g_free),
+        .array        = g_ptr_array_new(),
     };
 
     return reader;
@@ -54,6 +59,8 @@ reader_destroy(Reader *reader, gboolean free_hash)
     gs_unref_hashtable GHashTable *hash = NULL;
 
     g_ptr_array_unref(reader->array);
+    g_ptr_array_unref(reader->vlan_parents);
+    g_hash_table_unref(reader->explicit_ip_connections);
     hash = g_steal_pointer(&reader->hash);
     nm_clear_g_free(&reader->hostname);
     nm_clear_g_free(&reader->dhcp4_vci);
@@ -383,6 +390,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
     NMSettingIPConfig *s_ip4 = NULL, *s_ip6 = NULL;
     gs_unref_hashtable GHashTable *ibft = NULL;
     const char *                   tmp;
+    const char *                   tmp2;
     const char *                   kind             = NULL;
     const char *                   client_ip        = NULL;
     const char *                   peer             = NULL;
@@ -411,17 +419,37 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
         /* ip={dhcp|on|any|dhcp6|auto6|ibft} */
         kind = tmp;
     } else {
-        client_ip_family = guess_ip_address_family(tmp);
-        if (client_ip_family != AF_UNSPEC) {
-            /* <client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>: */
-            client_ip       = tmp;
-            peer            = get_word(&argument, ':');
+        tmp2 = get_word(&argument, ':');
+        if (NM_IN_STRSET(tmp2,
+                         "none",
+                         "off",
+                         "dhcp",
+                         "on"
+                         "any",
+                         "dhcp6",
+                         "auto",
+                         "auto6",
+                         "ibft")) {
+            /* <ifname>:{none|off|dhcp|on|any|dhcp6|auto|auto6|ibft} */
+            iface_spec = tmp;
+            kind       = tmp2;
+        } else {
+            /* <client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<kind> */
+            client_ip = tmp;
+            if (client_ip) {
+                client_ip_family = get_ip_address_family(client_ip, TRUE);
+                if (client_ip_family == AF_UNSPEC) {
+                    _LOGW(LOGD_CORE, "Invalid IP address '%s'.", client_ip);
+                    return;
+                }
+            }
+
+            peer            = tmp2;
             gateway_ip      = get_word(&argument, ':');
             netmask         = get_word(&argument, ':');
             client_hostname = get_word(&argument, ':');
             iface_spec      = get_word(&argument, ':');
-        } else {
-            iface_spec = tmp;
+            kind            = get_word(&argument, ':');
         }
 
         if (client_hostname && !nm_sd_hostname_is_valid(client_hostname, FALSE))
@@ -432,16 +460,12 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
             reader->hostname = g_strdup(client_hostname);
         }
 
-        /* <ifname>:{none|off|dhcp|on|any|dhcp6|auto6|ibft} */
-
-        kind = get_word(&argument, ':');
-
         tmp                = get_word(&argument, ':');
-        dns_addr_family[0] = guess_ip_address_family(tmp);
+        dns_addr_family[0] = get_ip_address_family(tmp, FALSE);
         if (dns_addr_family[0] != AF_UNSPEC) {
             dns[0]             = tmp;
             dns[1]             = get_word(&argument, ':');
-            dns_addr_family[1] = guess_ip_address_family(dns[1]);
+            dns_addr_family[1] = get_ip_address_family(dns[1], FALSE);
             if (*argument)
                 _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
         } else {
@@ -461,6 +485,8 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
     else
         connection = reader_get_default_connection(reader);
 
+    g_hash_table_add(reader->explicit_ip_connections, g_object_ref(connection));
+
     s_ip4 = nm_connection_get_setting_ip4_config(connection);
     s_ip6 = nm_connection_get_setting_ip6_config(connection);
 
@@ -506,9 +532,8 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
                 _LOGW(LOGD_CORE, "Invalid address '%s': %s", client_ip, error->message);
                 g_clear_error(&error);
             }
-        } else {
-            _LOGW(LOGD_CORE, "Unrecognized address: %s", client_ip);
-        }
+        } else
+            nm_assert_not_reached();
 
         if (address) {
             switch (client_ip_family) {
@@ -531,7 +556,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
                 nm_setting_ip_config_add_address(s_ip6, address);
                 break;
             default:
-                _LOGW(LOGD_CORE, "Unknown address family: %s", client_ip);
+                nm_assert_not_reached();
                 break;
             }
             nm_ip_address_unref(address);
@@ -543,7 +568,7 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
         if (nm_setting_ip_config_get_num_addresses(s_ip6) == 0) {
             g_object_set(s_ip6,
                          NM_SETTING_IP_CONFIG_METHOD,
-                         NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+                         NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
                          NULL);
         }
         if (nm_setting_ip_config_get_num_addresses(s_ip4) == 0) {
@@ -618,22 +643,16 @@ reader_parse_ip(Reader *reader, const char *sysfs_dir, char *argument)
         _LOGW(LOGD_CORE, "Ignoring peer: %s (not implemented)\n", peer);
 
     if (gateway_ip && *gateway_ip) {
-        int addr_family = guess_ip_address_family(gateway_ip);
-
-        if (nm_utils_ipaddr_is_valid(addr_family, gateway_ip)) {
-            switch (addr_family) {
-            case AF_INET:
-                g_object_set(s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
-                break;
-            case AF_INET6:
-                g_object_set(s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
-                break;
-            default:
-                _LOGW(LOGD_CORE, "Unknown address family: %s", gateway_ip);
-                break;
-            }
-        } else {
+        switch (get_ip_address_family(gateway_ip, FALSE)) {
+        case AF_INET:
+            g_object_set(s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
+            break;
+        case AF_INET6:
+            g_object_set(s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL);
+            break;
+        default:
             _LOGW(LOGD_CORE, "Invalid gateway: %s", gateway_ip);
+            break;
         }
     }
 
@@ -842,6 +861,9 @@ reader_parse_vlan(Reader *reader, char *argument)
 
     if (argument && *argument)
         _LOGW(LOGD_CORE, "Ignoring extra: '%s'.", argument);
+
+    if (!nm_strv_ptrarray_contains(reader->vlan_parents, phy))
+        g_ptr_array_add(reader->vlan_parents, g_strdup(phy));
 }
 
 static void
@@ -941,7 +963,7 @@ reader_add_nameservers(Reader *reader, GPtrArray *nameservers)
 
     for (i = 0; i < nameservers->len; i++) {
         ns          = nameservers->pdata[i];
-        addr_family = guess_ip_address_family(ns);
+        addr_family = get_ip_address_family(ns, FALSE);
         if (addr_family == AF_UNSPEC) {
             _LOGW(LOGD_CORE, "Unknown address family: %s", ns);
             continue;
@@ -1085,6 +1107,33 @@ nmi_cmdline_reader_parse(const char *sysfs_dir, const char *const *argv, char **
         }
     }
 
+    for (i = 0; i < reader->vlan_parents->len; i++) {
+        NMConnection *     connection;
+        NMSettingIPConfig *s_ip;
+
+        /* Disable IP configuration for parent connections of VLANs,
+         * unless those interfaces were explicitly configured otherwise. */
+
+        connection = reader_get_connection(reader, reader->vlan_parents->pdata[i], NULL, TRUE);
+        if (!g_hash_table_contains(reader->explicit_ip_connections, connection)) {
+            s_ip = nm_connection_get_setting_ip4_config(connection);
+            if (s_ip) {
+                g_object_set(s_ip,
+                             NM_SETTING_IP_CONFIG_METHOD,
+                             NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+                             NULL);
+            }
+
+            s_ip = nm_connection_get_setting_ip6_config(connection);
+            if (s_ip) {
+                g_object_set(s_ip,
+                             NM_SETTING_IP_CONFIG_METHOD,
+                             NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
+                             NULL);
+            }
+        }
+    }
+
     if (ignore_bootif)
         nm_clear_g_free(&bootif_val);
     if (bootif_val) {
diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c
index 80b2e5f9..b7ce4671 100644
--- a/src/initrd/nmi-ibft-reader.c
+++ b/src/initrd/nmi-ibft-reader.c
@@ -165,9 +165,9 @@ ip_setting_add_from_block(GHashTable *nic, NMConnection *connection, GError **er
                      NULL);
     }
 
-    family = guess_ip_address_family(s_ipaddr);
+    family = get_ip_address_family(s_ipaddr, FALSE);
     if (family == AF_UNSPEC)
-        family = guess_ip_address_family(s_gateway);
+        family = get_ip_address_family(s_gateway, FALSE);
 
     switch (family) {
     case AF_INET:
diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
index c3511332..14a83c08 100644
--- a/src/initrd/tests/test-cmdline-reader.c
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -80,6 +80,116 @@ test_auto(void)
 }
 
 static void
+test_dhcp_with_hostname(void)
+{
+    gs_unref_hashtable GHashTable *connections = NULL;
+    const char *const *            ARGV        = NM_MAKE_STRV("ip=::::host1::dhcp");
+    NMConnection *                 connection;
+    NMSettingConnection *          s_con;
+    NMSettingWired *               s_wired;
+    NMSettingIPConfig *            s_ip4;
+    NMSettingIPConfig *            s_ip6;
+    gs_free char *                 hostname = NULL;
+
+    connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV, &hostname);
+    g_assert(connections);
+    g_assert_cmpint(g_hash_table_size(connections), ==, 1);
+    g_assert_cmpstr(hostname, ==, "host1");
+
+    connection = g_hash_table_lookup(connections, "default_connection");
+    g_assert(connection);
+    nmtst_assert_connection_verifies_without_normalization(connection);
+
+    g_assert(!nm_connection_get_setting_vlan(connection));
+
+    s_con = nm_connection_get_setting_connection(connection);
+    g_assert(s_con);
+    g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
+                    ==,
+                    NM_SETTING_WIRED_SETTING_NAME);
+    g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "Wired Connection");
+    g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0);
+    g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
+                    ==,
+                    NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
+    g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
+
+    g_assert(nm_setting_connection_get_autoconnect(s_con));
+
+    s_wired = nm_connection_get_setting_wired(connection);
+    g_assert(s_wired);
+    g_assert(!nm_setting_wired_get_mac_address(s_wired));
+    g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 0);
+
+    s_ip4 = nm_connection_get_setting_ip4_config(connection);
+    g_assert(s_ip4);
+    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+    s_ip6 = nm_connection_get_setting_ip6_config(connection);
+    g_assert(s_ip6);
+    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+}
+
+static void
+test_dhcp_with_mtu(void)
+{
+    const char *const *ARGV0  = NM_MAKE_STRV("ip=:dhcp:1499");
+    const char *const *ARGV1  = NM_MAKE_STRV("ip=::::::dhcp:1499");
+    const char *const *ARGV[] = {ARGV0, ARGV1};
+    guint              i;
+
+    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
+        gs_unref_hashtable GHashTable *connections = NULL;
+        NMConnection *                 connection;
+        NMSettingConnection *          s_con;
+        NMSettingWired *               s_wired;
+        NMSettingIPConfig *            s_ip4;
+        NMSettingIPConfig *            s_ip6;
+        gs_free char *                 hostname = NULL;
+
+        connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV[i], &hostname);
+        g_assert(connections);
+        g_assert_cmpint(g_hash_table_size(connections), ==, 1);
+        g_assert_cmpstr(hostname, ==, NULL);
+
+        connection = g_hash_table_lookup(connections, "default_connection");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+
+        s_con = nm_connection_get_setting_connection(connection);
+        g_assert(s_con);
+        g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
+                        ==,
+                        NM_SETTING_WIRED_SETTING_NAME);
+        g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "Wired Connection");
+        g_assert_cmpint(nm_setting_connection_get_timestamp(s_con), ==, 0);
+        g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
+                        ==,
+                        NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
+        g_assert_cmpint(nm_setting_connection_get_wait_device_timeout(s_con), ==, -1);
+
+        g_assert(nm_setting_connection_get_autoconnect(s_con));
+
+        s_wired = nm_connection_get_setting_wired(connection);
+        g_assert(s_wired);
+        g_assert(!nm_setting_wired_get_mac_address(s_wired));
+        g_assert_cmpint(nm_setting_wired_get_mtu(s_wired), ==, 1499);
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+    }
+}
+
+static void
 test_if_auto_with_mtu(void)
 {
     gs_unref_hashtable GHashTable *connections = NULL;
@@ -235,7 +345,9 @@ test_if_ip4_manual(void)
 
     s_ip6 = nm_connection_get_setting_ip6_config(connection);
     g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                    ==,
+                    NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
     g_assert(nm_setting_ip_config_get_may_fail(s_ip6));
 
     connection = g_hash_table_lookup(connections, "eth4");
@@ -305,6 +417,52 @@ test_if_ip6_manual(void)
 }
 
 static void
+test_if_off(void)
+{
+    gs_unref_hashtable GHashTable *connections = NULL;
+    const char *const *            ARGV        = NM_MAKE_STRV("ip=off",
+                                           "ip=ens3:off",
+                                           "ip=10.0.0.8:::::ens4:off",
+                                           "ip=[2001:DB8::8]:::::ens5:off");
+    NMConnection *                 connection;
+    NMSettingIPConfig *            s_ip4;
+    NMSettingIPConfig *            s_ip6;
+    gs_free char *                 hostname = NULL;
+    struct {
+        const char name[32];
+        const char ipv4_method[32];
+        const char ipv6_method[32];
+
+    } conn_expected[] = {
+        {"default_connection",
+         NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+         NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
+        {"ens3", NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
+        {"ens4", NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NM_SETTING_IP6_CONFIG_METHOD_DISABLED},
+        {"ens5", NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_MANUAL},
+    };
+
+    connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV, &hostname);
+    g_assert(connections);
+    g_assert_cmpint(g_hash_table_size(connections), ==, G_N_ELEMENTS(conn_expected));
+    g_assert_cmpstr(hostname, ==, NULL);
+
+    for (int i = 0; i < G_N_ELEMENTS(conn_expected); ++i) {
+        connection = g_hash_table_lookup(connections, conn_expected[i].name);
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4), ==, conn_expected[i].ipv4_method);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, conn_expected[i].ipv6_method);
+    }
+}
+
+static void
 test_if_mac_ifname(void)
 {
     gs_unref_hashtable GHashTable *connections = NULL;
@@ -353,8 +511,9 @@ static void
 test_multiple_merge(void)
 {
     gs_unref_hashtable GHashTable *connections = NULL;
-    const char *const *ARGV = NM_MAKE_STRV("ip=192.0.2.2:::::eth0", "ip=[2001:db8::2]:::56::eth0");
-    NMConnection *     connection;
+    const char *const *            ARGV =
+        NM_MAKE_STRV("ip=192.0.2.2/16:::::eth0", "ip=[2001:db8::2]:::56::eth0");
+    NMConnection *       connection;
     NMSettingConnection *s_con;
     NMSettingWired *     s_wired;
     NMSettingIPConfig *  s_ip4;
@@ -389,6 +548,7 @@ test_multiple_merge(void)
     ip_addr = nm_setting_ip_config_get_address(s_ip4, 0);
     g_assert(ip_addr);
     g_assert_cmpstr(nm_ip_address_get_address(ip_addr), ==, "192.0.2.2");
+    g_assert_cmpint(nm_ip_address_get_prefix(ip_addr), ==, 16);
 
     s_ip6 = nm_connection_get_setting_ip6_config(connection);
     g_assert(s_ip6);
@@ -454,7 +614,7 @@ test_bootdev(void)
 
     connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV, &hostname);
     g_assert(connections);
-    g_assert_cmpint(g_hash_table_size(connections), ==, 2);
+    g_assert_cmpint(g_hash_table_size(connections), ==, 3);
     g_assert_cmpstr(hostname, ==, NULL);
 
     connection = g_hash_table_lookup(connections, "ens3");
@@ -483,6 +643,18 @@ test_bootdev(void)
                     NM_SETTING_VLAN_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "vlan2");
     g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "vlan2");
+
+    connection = g_hash_table_lookup(connections, "ens5");
+    g_assert(connection);
+    nmtst_assert_connection_verifies_without_normalization(connection);
+
+    s_con = nm_connection_get_setting_connection(connection);
+    g_assert(s_con);
+    g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
+                    ==,
+                    NM_SETTING_WIRED_SETTING_NAME);
+    g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "ens5");
+    g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "ens5");
 }
 
 static void
@@ -729,7 +901,9 @@ test_bond_ip(void)
 
     s_ip6 = nm_connection_get_setting_ip6_config(connection);
     g_assert(s_ip6);
-    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+    g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                    ==,
+                    NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
     g_assert(!nm_setting_ip_config_get_ignore_auto_dns(s_ip6));
     g_assert_cmpint(nm_setting_ip_config_get_num_dns(s_ip6), ==, 0);
     g_assert(!nm_setting_ip_config_get_gateway(s_ip6));
@@ -1168,6 +1342,229 @@ test_team(void)
 }
 
 static void
+test_vlan(void)
+{
+    const char *const *ARGV0  = NM_MAKE_STRV("ip=eth0.100:dhcp", "vlan=eth0.100:eth0");
+    const char *const *ARGV1  = NM_MAKE_STRV("vlan=eth0.100:eth0", "ip=eth0.100:dhcp");
+    const char *const *ARGV[] = {ARGV0, ARGV1};
+    guint              i;
+
+    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
+        gs_unref_hashtable GHashTable *connections = NULL;
+        NMConnection *                 connection;
+        NMSettingIPConfig *            s_ip4;
+        NMSettingIPConfig *            s_ip6;
+        NMSettingVlan *                s_vlan;
+        gs_free char *                 hostname = NULL;
+
+        connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV[i], &hostname);
+        g_assert(connections);
+        g_assert_cmpint(g_hash_table_size(connections), ==, 2);
+        g_assert_cmpstr(hostname, ==, NULL);
+
+        /* VLAN eth0.100 */
+        connection = g_hash_table_lookup(connections, "eth0.100");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_VLAN_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0.100");
+
+        s_vlan = nm_connection_get_setting_vlan(connection);
+        g_assert(s_vlan);
+        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
+        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 100);
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+
+        /* Ethernet eth0 */
+        connection = g_hash_table_lookup(connections, "eth0");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_WIRED_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0");
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+    }
+}
+
+static void
+test_vlan_with_dhcp_on_parent(void)
+{
+    const char *const *ARGV0  = NM_MAKE_STRV("vlan=eth0.100:eth0", "ip=eth0:dhcp");
+    const char *const *ARGV1  = NM_MAKE_STRV("ip=eth0:dhcp", "vlan=eth0.100:eth0");
+    const char *const *ARGV[] = {ARGV0, ARGV1};
+    guint              i;
+
+    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
+        gs_unref_hashtable GHashTable *connections = NULL;
+        NMConnection *                 connection;
+        NMSettingIPConfig *            s_ip4;
+        NMSettingIPConfig *            s_ip6;
+        NMSettingVlan *                s_vlan;
+        gs_free char *                 hostname = NULL;
+
+        connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV[i], &hostname);
+        g_assert(connections);
+        g_assert_cmpint(g_hash_table_size(connections), ==, 2);
+        g_assert_cmpstr(hostname, ==, NULL);
+
+        /* VLAN eth0.100 */
+        connection = g_hash_table_lookup(connections, "eth0.100");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_VLAN_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0.100");
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+
+        s_vlan = nm_connection_get_setting_vlan(connection);
+        g_assert(s_vlan);
+        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
+        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 100);
+
+        /* Ethernet eth0 */
+        connection = g_hash_table_lookup(connections, "eth0");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_WIRED_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "eth0");
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+    }
+}
+
+static void
+test_vlan_over_bond(void)
+{
+    const char *const *ARGV0  = NM_MAKE_STRV("ip=1.2.3.4:::24::vlan1:none",
+                                            "bond=bond2:ens3,ens4:mode=active-backup",
+                                            "vlan=vlan1:bond2");
+    const char *const *ARGV1  = NM_MAKE_STRV("vlan=vlan1:bond2",
+                                            "ip=1.2.3.4:::24::vlan1:none",
+                                            "bond=bond2:ens3,ens4:mode=active-backup");
+    const char *const *ARGV2  = NM_MAKE_STRV("bond=bond2:ens3,ens4:mode=active-backup",
+                                            "ip=1.2.3.4:::24::vlan1:none",
+                                            "vlan=vlan1:bond2");
+    const char *const *ARGV[] = {ARGV0, ARGV1, ARGV2};
+    guint              i;
+
+    for (i = 0; i < G_N_ELEMENTS(ARGV); i++) {
+        gs_unref_hashtable GHashTable *connections = NULL;
+        NMConnection *                 connection;
+        NMSettingIPConfig *            s_ip4;
+        NMSettingIPConfig *            s_ip6;
+        NMSettingVlan *                s_vlan;
+        gs_free char *                 hostname = NULL;
+
+        connections = nmi_cmdline_reader_parse(TEST_INITRD_DIR "/sysfs", ARGV[i], &hostname);
+        g_assert(connections);
+        g_assert_cmpint(g_hash_table_size(connections), ==, 4);
+        g_assert_cmpstr(hostname, ==, NULL);
+
+        /* VLAN vlan1 */
+        connection = g_hash_table_lookup(connections, "vlan1");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_VLAN_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "vlan1");
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+
+        s_vlan = nm_connection_get_setting_vlan(connection);
+        g_assert(s_vlan);
+        g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "bond2");
+        g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 1);
+
+        /* Bond bond2 */
+        connection = g_hash_table_lookup(connections, "bond2");
+        g_assert(connection);
+        nmtst_assert_connection_verifies_without_normalization(connection);
+        g_assert_cmpstr(nm_connection_get_connection_type(connection),
+                        ==,
+                        NM_SETTING_BOND_SETTING_NAME);
+        g_assert_cmpstr(nm_connection_get_id(connection), ==, "bond2");
+
+        s_ip4 = nm_connection_get_setting_ip4_config(connection);
+        g_assert(s_ip4);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip4),
+                        ==,
+                        NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
+
+        s_ip6 = nm_connection_get_setting_ip6_config(connection);
+        g_assert(s_ip6);
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip6),
+                        ==,
+                        NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+
+        /* Ethernet ens3 and ens4 */
+        connection = g_hash_table_lookup(connections, "ens3");
+        g_assert(connection);
+        connection = g_hash_table_lookup(connections, "ens4");
+        g_assert(connection);
+    }
+}
+
+static void
 test_ibft_ip_dev(void)
 {
     const char *const *ARGV                    = NM_MAKE_STRV("ip=eth0:ibft");
@@ -1784,12 +2181,15 @@ main(int argc, char **argv)
     nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT");
 
     g_test_add_func("/initrd/cmdline/auto", test_auto);
+    g_test_add_func("/initrd/cmdline/dhcp_with_hostname", test_dhcp_with_hostname);
+    g_test_add_func("/initrd/cmdline/dhcp_with_mtu", test_dhcp_with_mtu);
     g_test_add_func("/initrd/cmdline/if_auto_with_mtu", test_if_auto_with_mtu);
     g_test_add_func("/initrd/cmdline/if_dhcp6", test_if_dhcp6);
     g_test_add_func("/initrd/cmdline/if_auto_with_mtu_and_mac", test_if_auto_with_mtu_and_mac);
     g_test_add_func("/initrd/cmdline/if_ip4_manual", test_if_ip4_manual);
     g_test_add_func("/initrd/cmdline/if_ip6_manual", test_if_ip6_manual);
     g_test_add_func("/initrd/cmdline/if_mac_ifname", test_if_mac_ifname);
+    g_test_add_func("/initrd/cmdline/if_off", test_if_off);
     g_test_add_func("/initrd/cmdline/multiple/merge", test_multiple_merge);
     g_test_add_func("/initrd/cmdline/multiple/bootdev", test_multiple_bootdev);
     g_test_add_func("/initrd/cmdline/nameserver", test_nameserver);
@@ -1799,6 +2199,9 @@ main(int argc, char **argv)
     g_test_add_func("/initrd/cmdline/bond/ip", test_bond_ip);
     g_test_add_func("/initrd/cmdline/bond/default", test_bond_default);
     g_test_add_func("/initrd/cmdline/team", test_team);
+    g_test_add_func("/initrd/cmdline/vlan", test_vlan);
+    g_test_add_func("/initrd/cmdline/vlan/dhcp-on-parent", test_vlan_with_dhcp_on_parent);
+    g_test_add_func("/initrd/cmdline/vlan/over-bond", test_vlan_over_bond);
     g_test_add_func("/initrd/cmdline/bridge", test_bridge);
     g_test_add_func("/initrd/cmdline/bridge/default", test_bridge_default);
     g_test_add_func("/initrd/cmdline/bridge/ip", test_bridge_ip);