From f766b3cbae0db7e3b7421bfdffc1d6182717e05c 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) --- 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 cac01cb861..69c24b1b0b 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 b9432d4a62..9c5699afb8 100644 --- a/src/initrd/nmi-cmdline-reader.c +++ b/src/initrd/nmi-cmdline-reader.c @@ -411,7 +411,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; @@ -437,11 +437,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 { @@ -506,9 +506,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 +530,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); @@ -618,22 +617,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; } } @@ -941,7 +934,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 80b2e5f9e9..b7ce467197 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 c42d7f11a9..281d690dcd 100644 --- a/src/initrd/tests/test-cmdline-reader.c +++ b/src/initrd/tests/test-cmdline-reader.c @@ -1219,6 +1219,54 @@ test_team(void) 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) { @@ -1852,6 +1900,7 @@ 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);