diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 7a5b39e68..51c12c00e 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -1170,6 +1170,97 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) } } +static void +reader_parse_ethtool(Reader *reader, char *argument) +{ + NMConnection * connection = NULL; + NMSettingWired *s_wired = NULL; + const char * read = NULL; + const char * interface = NULL; + gboolean autoneg = FALSE; + guint speed = 0; + + interface = get_word(&argument, ':'); + if (!interface) { + _LOGW(LOGD_CORE, "Impossible to set rd.ethtool options: invalid format"); + return; + } + if (!*argument) + return; + + read = get_word(&argument, ':'); + if (read) { + autoneg = _nm_utils_ascii_str_to_bool(read, -1); + if (autoneg == -1) + _LOGW(LOGD_CORE, "rd.ethtool autoneg was not set, invalid value"); + else { + connection = + reader_get_connection(reader, interface, NM_SETTING_WIRED_SETTING_NAME, TRUE); + s_wired = nm_connection_get_setting_wired(connection); + g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg, NULL); + } + } + if (!*argument) + return; + + read = get_word(&argument, ':'); + if (read) { + speed = _nm_utils_ascii_str_to_int64(read, 10, 0, G_MAXUINT32, -1); + if (speed == -1) { + _LOGW(LOGD_CORE, + "rd.ethtool speed was not set, invalid value. Then, duplex was disregarded."); + /* Duplex does not need to be evaluated after, because it can't be set without speed value */ + return; + } else { + connection = + reader_get_connection(reader, interface, NM_SETTING_WIRED_SETTING_NAME, TRUE); + s_wired = nm_connection_get_setting_wired(connection); + /* duplex option is available for legacy purposes */ + /* speed must be always set having duplex set, otherwise it will fail in verifications */ + + if (*argument) { + /* duplex value was informed, and has a valid value */ + if (NM_IN_STRSET(argument, "half", "full")) + g_object_set(s_wired, + NM_SETTING_WIRED_SPEED, + speed, + NM_SETTING_WIRED_DUPLEX, + argument, + NULL); + + /* duplex value was informed, and does not have a valid value */ + else { + _LOGW(LOGD_CORE, + "rd.ethtool.duplex has a invalid format, duplex was set as default:full"); + g_object_set(s_wired, + NM_SETTING_WIRED_SPEED, + speed, + NM_SETTING_WIRED_DUPLEX, + "full", + NULL); + } + } else { + /* duplex value was not informed, then it will have default 'full' value */ + g_object_set(s_wired, + NM_SETTING_WIRED_SPEED, + speed, + NM_SETTING_WIRED_DUPLEX, + "full", + NULL); + } + + /* Duplex does not need to be evaluated alone - it can't be set without speed value */ + return; + } + } + if (!*argument) + return; + else { + /* Duplex does not need to be evaluated, because it can't be set without speed value */ + _LOGW(LOGD_CORE, "rd.ethtool.duplex needs rd.ethtool.speed value to be set"); + } +} + static void _normalize_conn(gpointer key, gpointer value, gpointer user_data) { @@ -1351,7 +1442,8 @@ nmi_cmdline_reader_parse(const char * sysfs_dir, } else if (g_ascii_strcasecmp(tag, "BOOTIF") == 0) { nm_clear_g_free(&bootif_val); bootif_val = g_strdup(argument); - } + } else if (nm_streq(tag, "rd.ethtool")) + reader_parse_ethtool(reader, argument); } for (i = 0; i < reader->vlan_parents->len; i++) { diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c index 57ecfbe06..aa1237876 100644 --- a/src/nm-initrd-generator/tests/test-cmdline-reader.c +++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c @@ -2262,6 +2262,193 @@ test_carrier_timeout(void) g_assert_cmpint(carrier_timeout_sec, ==, 20); } +/* Obs1.: this function is implemented as macro, and not as a function, to show g_assert() line on debug */ +#define _ethtool_connection_check_and_get(connection) \ + ({ \ + NMSettingWired *_s_wired = NULL; \ + NMConnection * _connection = connection; \ + \ + g_assert(nm_connection_get_setting_connection(_connection)); \ + g_assert(nm_connection_is_type(_connection, NM_SETTING_WIRED_SETTING_NAME)); \ + g_assert(nm_connection_get_setting_ip4_config(_connection)); \ + g_assert(nm_connection_get_setting_ip6_config(_connection)); \ + _s_wired = nm_connection_get_setting_wired(_connection); \ + g_assert(_s_wired); \ + \ + _s_wired; \ + }) + +static void +test_rd_ethtool(void) +{ + const char *const *ARGV = NULL; + NMConnection * connection = NULL; + GHashTable * connections = NULL; + NMSettingWired * s_wired = NULL; + + ARGV = NM_MAKE_STRV("rd.ethtool="); + NMTST_EXPECT_NM_WARN("cmdline-reader: Impossible to set rd.ethtool options: invalid format"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_hash_table_unref(connections); + g_test_assert_expected_messages(); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=::"); + NMTST_EXPECT_NM_WARN("cmdline-reader: Impossible to set rd.ethtool options: invalid format"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:on"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:off"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:true"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:false"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:1"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:0"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:randomstring"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.ethtool autoneg was not set, invalid value"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::astring"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.ethtool speed was not set, invalid value. Then, " + "duplex was disregarded."); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::1000000000000000000000000000000000000"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.ethtool speed was not set, invalid value. Then, " + "duplex was disregarded."); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::0.67"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.ethtool speed was not set, invalid value. Then, " + "duplex was disregarded."); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::-23"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.ethtool speed was not set, invalid value. Then, " + "duplex was disregarded."); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:1:10"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 10); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "full"); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::100"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 100); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "full"); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:::half"); + NMTST_EXPECT_NM_WARN( + "cmdline-reader: rd.ethtool.duplex needs rd.ethtool.speed value to be set"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0::10:half"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(!nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 10); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "half"); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:on:100:full"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 100); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "full"); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=eth0:on:100:anyvalue"); + NMTST_EXPECT_NM_WARN( + "cmdline-reader: rd.ethtool.duplex has a invalid format, duplex was set as default:full"); + connection = _parse_con(ARGV, "eth0"); + s_wired = _ethtool_connection_check_and_get(connection); + g_assert(nm_setting_wired_get_auto_negotiate(s_wired)); + g_assert_cmpint(nm_setting_wired_get_speed(s_wired), ==, 100); + g_assert_cmpstr(nm_setting_wired_get_duplex(s_wired), ==, "full"); + g_test_assert_expected_messages(); + g_object_unref(connection); + + ARGV = NM_MAKE_STRV("rd.ethtool=:::"); + NMTST_EXPECT_NM_WARN("cmdline-reader: Impossible to set rd.ethtool options: invalid format"); + connections = _parse_cons(ARGV); + g_assert_cmpint(g_hash_table_size(connections), ==, 0); + g_test_assert_expected_messages(); + g_hash_table_unref(connections); +} + NMTST_DEFINE(); int @@ -2316,6 +2503,7 @@ main(int argc, char **argv) g_test_add_func("/initrd/cmdline/infiniband/mac", test_infiniband_mac); g_test_add_func("/initrd/cmdline/infiniband/pkey", test_infiniband_pkey); g_test_add_func("/initrd/cmdline/carrier_timeout", test_carrier_timeout); + g_test_add_func("/initrd/cmdline/rd_ethtool", test_rd_ethtool); return g_test_run(); }