From 68f500f5cee9c784fda4399e0839fc9b705457a4 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 24 Jan 2022 22:42:07 +0100 Subject: [PATCH] initrd: add support for rd.znet_ifnames This uses interface names specified rd.znet_ifnames on kernel command line instead of automatically generated names if possible. Accompanied by a test. https://bugzilla.redhat.com/show_bug.cgi?id=1980387 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1070 --- src/nm-initrd-generator/nmi-cmdline-reader.c | 39 ++++++++++- .../tests/test-cmdline-reader.c | 70 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 8a56039b7f..412c751067 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -34,6 +34,7 @@ typedef struct { NMConnection *bootdev_connection; /* connection for bootdev=$ifname */ NMConnection *default_connection; /* connection not bound to any ifname */ char *hostname; + GHashTable *znet_ifnames; /* Parameters to be set for all connections */ gboolean ignore_auto_dns; @@ -55,6 +56,7 @@ reader_new(void) 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(), + .znet_ifnames = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free), }; return reader; @@ -70,6 +72,7 @@ reader_destroy(Reader *reader, gboolean free_hash) g_hash_table_unref(reader->explicit_ip_connections); hash = g_steal_pointer(&reader->hash); nm_clear_g_free(&reader->hostname); + g_hash_table_unref(reader->znet_ifnames); nm_clear_g_free(&reader->dhcp4_vci); nm_g_slice_free(reader); if (!free_hash) @@ -1091,19 +1094,36 @@ reader_parse_ib_pkey(Reader *reader, char *argument) _LOGW(LOGD_CORE, "Ignoring extra: '%s' for ib.pkey=", argument); } +static void +reader_parse_znet_ifname(Reader *reader, char *argument) +{ + char *ifname; + + ifname = get_word(&argument, ':'); + if (!ifname) { + _LOGW(LOGD_CORE, "rd.znet_ifname= without argument"); + return; + } + + if (!g_hash_table_replace(reader->znet_ifnames, g_strdup(argument), g_strdup(ifname))) { + _LOGW(LOGD_CORE, "duplicate rd.znet_ifname for ifname=%s", ifname); + } +} + static void reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) { const char *nettype; const char *subchannels[4] = {0, 0, 0, 0}; const char *tmp; - gs_free char *ifname = NULL; + gs_free char *ifname = NULL; + gs_free char *str_subchannels = NULL; const char *prefix; NMConnection *connection; NMSettingWired *s_wired; static int count_ctc = 0; static int count_eth = 0; - int index; + int index = -1; nettype = get_word(&argument, ','); subchannels[0] = get_word(&argument, ','); @@ -1131,7 +1151,13 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) } } - if (net_ifnames == TRUE) { + str_subchannels = g_strjoinv(",", (char **) subchannels); + ifname = g_hash_table_lookup(reader->znet_ifnames, str_subchannels); + + if (ifname) { + ifname = g_strdup(ifname); + g_hash_table_remove(reader->znet_ifnames, str_subchannels); + } else if (net_ifnames == TRUE) { const char *bus_id; size_t bus_id_len; size_t bus_id_start; @@ -1144,6 +1170,7 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) ifname = g_strdup_printf("%sc%s", prefix, bus_id); } else { + nm_assert(index > -1); ifname = g_strdup_printf("%s%d", prefix, index); } @@ -1423,6 +1450,8 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir, if (!znets) znets = g_ptr_array_new_with_free_func(g_free); g_ptr_array_add(znets, g_strdup(argument)); + } else if (nm_streq(tag, "rd.znet_ifname")) { + reader_parse_znet_ifname(reader, argument); } else if (g_ascii_strcasecmp(tag, "BOOTIF") == 0) { nm_clear_g_free(&bootif_val); bootif_val = g_strdup(argument); @@ -1533,6 +1562,10 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir, reader_parse_rd_znet(reader, znets->pdata[i], net_ifnames); } + if (g_hash_table_size(reader->znet_ifnames)) { + _LOGW(LOGD_CORE, "Mismatch between rd.znet_ifname and rd.znet"); + } + g_hash_table_foreach(reader->hash, _normalize_conn, NULL); NM_SET_OUT(hostname, g_steal_pointer(&reader->hostname)); diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c index 009af54006..d7b7b3cba2 100644 --- a/src/nm-initrd-generator/tests/test-cmdline-reader.c +++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c @@ -1897,6 +1897,75 @@ test_rd_znet_malformed(void) } } +static void +test_rd_znet_ifnames(void) +{ + gs_unref_hashtable GHashTable *connections = NULL; + const char *const *const ARGV = + NM_MAKE_STRV("rd.znet_ifname=zeth1:0.0.0600,0.0.0601", + "rd.znet=qeth,0.0.0800,0.0.0801,0.0.0802,layer2=0,portno=1,foo", + "rd.znet=ctc,0.0.0600,0.0.0601", + "rd.znet_ifname=zeth0:0.0.0800,0.0.0801,0.0.0802", + "ip=zeth0:dhcp", + "ip=zeth1:dhcp"); + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + gs_free char *hostname = NULL; + gint64 carrier_timeout_sec = 0; + const char *const *v_subchannels; + + connections = _parse(ARGV, &hostname, &carrier_timeout_sec); + g_assert_cmpint(g_hash_table_size(connections), ==, 2); + + connection = g_hash_table_lookup(connections, "zeth0"); + g_assert(NM_IS_CONNECTION(connection)); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(NM_IS_SETTING_CONNECTION(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), ==, "zeth0"); + g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "zeth0"); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert_cmpstr(nm_setting_wired_get_s390_nettype(s_wired), ==, "qeth"); + g_assert(s_wired); + + v_subchannels = nm_setting_wired_get_s390_subchannels(s_wired); + g_assert(v_subchannels); + g_assert_cmpstr(v_subchannels[0], ==, "0.0.0800"); + g_assert_cmpstr(v_subchannels[1], ==, "0.0.0801"); + g_assert_cmpstr(v_subchannels[2], ==, "0.0.0802"); + g_assert_cmpstr(v_subchannels[3], ==, NULL); + + nmtst_assert_connection_verifies_without_normalization(connection); + + connection = g_hash_table_lookup(connections, "zeth1"); + g_assert(NM_IS_CONNECTION(connection)); + + s_con = nm_connection_get_setting_connection(connection); + g_assert(NM_IS_SETTING_CONNECTION(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), ==, "zeth1"); + g_assert_cmpstr(nm_setting_connection_get_interface_name(s_con), ==, "zeth1"); + + s_wired = nm_connection_get_setting_wired(connection); + g_assert_cmpstr(nm_setting_wired_get_s390_nettype(s_wired), ==, "ctc"); + g_assert(s_wired); + + v_subchannels = nm_setting_wired_get_s390_subchannels(s_wired); + g_assert(v_subchannels); + g_assert_cmpstr(v_subchannels[0], ==, "0.0.0600"); + g_assert_cmpstr(v_subchannels[1], ==, "0.0.0601"); + g_assert_cmpstr(v_subchannels[2], ==, NULL); + + nmtst_assert_connection_verifies_without_normalization(connection); +} + static void test_bootif_ip(void) { @@ -2483,6 +2552,7 @@ main(int argc, char **argv) g_test_add_func("/initrd/cmdline/rd_znet/legacy", test_rd_znet_legacy); g_test_add_func("/initrd/cmdline/rd_znet/no_ip", test_rd_znet_no_ip); g_test_add_func("/initrd/cmdline/rd_znet/empty", test_rd_znet_malformed); + g_test_add_func("/initrd/cmdline/rd_znet/ifnames", test_rd_znet_ifnames); g_test_add_func("/initrd/cmdline/bootif/ip", test_bootif_ip); g_test_add_func("/initrd/cmdline/bootif/no_ip", test_bootif_no_ip); g_test_add_func("/initrd/cmdline/bootif/hwtype", test_bootif_hwtype);