From a2ce810b87981f586e4acb993f7658c1da06b67e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 17 Nov 2020 11:10:54 +0100 Subject: [PATCH] initrd: fix parsing of ip= argument with dotted interface name The command line parser looks for a dot or a colon to determine whether the first token in a ip= argument is a IPv4 address (dot), an IPv6 address (colon) or an interface name (none). This strategy doesn't work for interface names containing a dot (typically VLANs). Instead, try to parse the IPv4/IPv6 address in the token; if this fails then consider the token as an interface name. https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/581 (cherry picked from commit 4aa902ecf537fbf14fd483a336b83b2139025681) (cherry picked from commit f766b3cbae0db7e3b7421bfdffc1d6182717e05c) --- src/initrd/nm-initrd-generator.h | 24 ++++++++----- src/initrd/nmi-cmdline-reader.c | 39 +++++++++----------- src/initrd/nmi-ibft-reader.c | 4 +-- src/initrd/tests/test-cmdline-reader.c | 49 ++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h index 8e17f04555..e7647edd7f 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 316d7930fd..73002b7e21 100644 --- a/src/initrd/nmi-cmdline-reader.c +++ b/src/initrd/nmi-cmdline-reader.c @@ -385,7 +385,7 @@ 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); + client_ip_family = get_ip_address_family (tmp, TRUE); if (client_ip_family != AF_UNSPEC) { /* :[]:::: */ client_ip = tmp; @@ -411,11 +411,11 @@ reader_parse_ip (Reader *reader, const char *sysfs_dir, char *argument) 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 { @@ -475,9 +475,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) { @@ -496,7 +495,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); @@ -579,22 +578,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; } } @@ -893,7 +886,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; diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c index fe6f6432a4..bdb99e67ef 100644 --- a/src/initrd/nmi-ibft-reader.c +++ b/src/initrd/nmi-ibft-reader.c @@ -162,9 +162,9 @@ ip_setting_add_from_block (GHashTable *nic, 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 68b1ed441c..81a7fe2410 100644 --- a/src/initrd/tests/test-cmdline-reader.c +++ b/src/initrd/tests/test-cmdline-reader.c @@ -1107,6 +1107,54 @@ test_team (void) g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } +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), ==, 1); + g_assert_cmpstr (hostname, ==, NULL); + + 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); + } +} + static void test_ibft_ip_dev (void) { @@ -1719,6 +1767,7 @@ int 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/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);