initrd-generator: add support for the "rd.net.dns" command line option

Introduce a new kernel command line option named "rd.net.dns" that can
be used to specify a global name server. It accepts name server in a
URI-like form, as for example:

  rd.net.dns=dns+tls://[fd01::1]:5353#mydomain.com
This commit is contained in:
Beniamino Galvani
2024-12-05 11:11:43 +01:00
parent 4422b14704
commit 4c7f1857df
5 changed files with 141 additions and 32 deletions

View File

@@ -157,6 +157,7 @@
<member><option>rd.bootif</option></member> <member><option>rd.bootif</option></member>
<member><option>rd.neednet</option></member> <member><option>rd.neednet</option></member>
<member><option>rd.ethtool</option></member> <member><option>rd.ethtool</option></member>
<member><option>rd.net.dns</option></member>
<member><option>rd.net.timeout.dhcp</option></member> <member><option>rd.net.timeout.dhcp</option></member>
<member><option>rd.net.dhcp.retry</option></member> <member><option>rd.net.dhcp.retry</option></member>
<member><option>rd.net.dhcp.vendor-class</option></member> <member><option>rd.net.dhcp.vendor-class</option></member>
@@ -233,6 +234,22 @@
</para> </para>
</listitem> </listitem>
<listitem>
<para>NetworkManager supports the
<option>rd.net.dns</option>=<replaceable>SERVER</replaceable>
kernel command line option to configure a global (non
interface-specific) DNS server. The option can be specified
multiple time to add more than one server. Each server can be
specified as a plain IP or as an URI according to the
description in the "global-dns-domains sections" paragraph of
<link
linkend='NetworkManager.conf'><citerefentry><refentrytitle>NetworkManager.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></link>. For
example: <literal>rd.net.dns=2001:db8::1</literal>,
<literal>rd.net.dns=dns+tls://192.0.2.0</literal>,
<literal>rd.net.dns=dns+tls://[2001:db8::2]:5353#example.org</literal>.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</refsect1> </refsect1>

View File

@@ -154,6 +154,7 @@ main(int argc, char *argv[])
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
gs_unref_array GArray *confs = NULL; gs_unref_array GArray *confs = NULL;
guint i; guint i;
gs_strfreev char **global_dns_servers = NULL;
option_context = g_option_context_new( option_context = g_option_context_new(
"-- [ip=...] [rd.route=...] [bridge=...] [bond=...] [team=...] [vlan=...] " "-- [ip=...] [rd.route=...] [bridge=...] [bond=...] [team=...] [vlan=...] "
@@ -193,7 +194,8 @@ main(int argc, char *argv[])
sysfs_dir, sysfs_dir,
(const char *const *) remaining, (const char *const *) remaining,
&hostname, &hostname,
&carrier_timeout_sec); &carrier_timeout_sec,
&global_dns_servers);
confs = g_array_new(FALSE, FALSE, sizeof(NMUtilsNamedValue)); confs = g_array_new(FALSE, FALSE, sizeof(NMUtilsNamedValue));
g_array_set_clear_func(confs, (GDestroyNotify) nm_utils_named_value_clear_with_g_free); g_array_set_clear_func(confs, (GDestroyNotify) nm_utils_named_value_clear_with_g_free);
@@ -233,6 +235,36 @@ main(int argc, char *argv[])
g_array_append_val(confs, v); g_array_append_val(confs, v);
} }
if (global_dns_servers) {
nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
NMUtilsNamedValue v;
gs_free char *value = NULL;
value = g_strjoinv(",", global_dns_servers);
keyfile = g_key_file_new();
g_key_file_set_list_separator(keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR);
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS,
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS,
"");
g_key_file_set_value(keyfile,
NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN "*",
NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS,
value);
if (!dump_to_stdout) {
add_keyfile_comment(keyfile, "from \"rd.net.dns\"");
}
v = (NMUtilsNamedValue) {
.name = g_strdup_printf("%s/16-global-dns.conf", run_config_dir),
.value_str = g_key_file_to_data(keyfile, NULL, NULL),
};
g_array_append_val(confs, v);
}
if (dump_to_stdout) { if (dump_to_stdout) {
nm_clear_g_free(&connections_dir); nm_clear_g_free(&connections_dir);
nm_clear_g_free(&initrd_dir); nm_clear_g_free(&initrd_dir);

View File

@@ -45,6 +45,7 @@ GHashTable *nmi_cmdline_reader_parse(const char *etc_connections_dir,
const char *sysfs_dir, const char *sysfs_dir,
const char *const *argv, const char *const *argv,
char **hostname, char **hostname,
gint64 *carrier_timeout_sec); gint64 *carrier_timeout_sec,
char ***global_dns_servers);
#endif /* __NM_INITRD_GENERATOR_H__ */ #endif /* __NM_INITRD_GENERATOR_H__ */

View File

@@ -36,10 +36,11 @@ typedef struct {
GHashTable *znet_ifnames; GHashTable *znet_ifnames;
/* Parameters to be set for all connections */ /* Parameters to be set for all connections */
gboolean ignore_auto_dns; gboolean ignore_auto_dns;
int dhcp_timeout; int dhcp_timeout;
char *dhcp4_vci; char *dhcp4_vci;
char *dhcp_dscp; char *dhcp_dscp;
GPtrArray *global_dns;
gint64 carrier_timeout_sec; gint64 carrier_timeout_sec;
} Reader; } Reader;
@@ -69,6 +70,7 @@ reader_destroy(Reader *reader, gboolean free_hash)
g_ptr_array_unref(reader->array); g_ptr_array_unref(reader->array);
g_ptr_array_unref(reader->vlan_parents); g_ptr_array_unref(reader->vlan_parents);
nm_clear_pointer(&reader->global_dns, g_ptr_array_unref);
g_hash_table_unref(reader->explicit_ip_connections); g_hash_table_unref(reader->explicit_ip_connections);
hash = g_steal_pointer(&reader->hash); hash = g_steal_pointer(&reader->hash);
nm_clear_g_free(&reader->hostname); nm_clear_g_free(&reader->hostname);
@@ -1219,6 +1221,21 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames)
} }
} }
static void
reader_parse_global_dns(Reader *reader, char *argument)
{
if (!nm_dns_uri_parse(AF_UNSPEC, argument, NULL)) {
_LOGW(LOGD_CORE, "rd.net.dns: invalid server '%s'", argument);
return;
}
if (!reader->global_dns) {
reader->global_dns = g_ptr_array_new_with_free_func(g_free);
}
g_ptr_array_add(reader->global_dns, g_strdup(argument));
}
static void static void
reader_parse_ethtool(Reader *reader, char *argument) reader_parse_ethtool(Reader *reader, char *argument)
{ {
@@ -1392,7 +1409,8 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
const char *sysfs_dir, const char *sysfs_dir,
const char *const *argv, const char *const *argv,
char **hostname, char **hostname,
gint64 *carrier_timeout_sec) gint64 *carrier_timeout_sec,
char ***global_dns_servers)
{ {
Reader *reader; Reader *reader;
const char *tag; const char *tag;
@@ -1509,6 +1527,8 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
bootif_val = g_strdup(argument); bootif_val = g_strdup(argument);
} else if (nm_streq(tag, "rd.ethtool")) { } else if (nm_streq(tag, "rd.ethtool")) {
reader_parse_ethtool(reader, argument); reader_parse_ethtool(reader, argument);
} else if (nm_streq(tag, "rd.net.dns")) {
reader_parse_global_dns(reader, argument);
} }
} }
@@ -1626,5 +1646,15 @@ nmi_cmdline_reader_parse(const char *etc_connections_dir,
NM_SET_OUT(carrier_timeout_sec, reader->carrier_timeout_sec); NM_SET_OUT(carrier_timeout_sec, reader->carrier_timeout_sec);
if (reader->global_dns) {
if (global_dns_servers) {
g_ptr_array_add(reader->global_dns, NULL);
*global_dns_servers = (char **) g_ptr_array_free(reader->global_dns, FALSE);
reader->global_dns = NULL;
}
} else {
NM_SET_OUT(global_dns_servers, NULL);
}
return reader_destroy(reader, FALSE); return reader_destroy(reader, FALSE);
} }

View File

@@ -23,7 +23,7 @@
/*****************************************************************************/ /*****************************************************************************/
#define _parse(ARGV, out_hostname, out_carrier_timeout_sec) \ #define _parse(ARGV, out_hostname, out_carrier_timeout_sec, _out_global_dns_servers) \
({ \ ({ \
const char *const *const _ARGV = (ARGV); \ const char *const *const _ARGV = (ARGV); \
char **const _out_hostname = (out_hostname); \ char **const _out_hostname = (out_hostname); \
@@ -34,26 +34,28 @@
TEST_INITRD_DIR "/sysfs", \ TEST_INITRD_DIR "/sysfs", \
_ARGV, \ _ARGV, \
_out_hostname, \ _out_hostname, \
_out_carrier_timeout_sec); \ _out_carrier_timeout_sec, \
_out_global_dns_servers); \
\ \
g_assert(_connections); \ g_assert(_connections); \
\ \
_connections; \ _connections; \
}) })
#define _parse_cons(ARGV) \ #define _parse_cons(ARGV) \
({ \ ({ \
GHashTable *_con_connections; \ GHashTable *_con_connections; \
gs_free char *_con_hostname = NULL; \ gs_free char *_con_hostname = NULL; \
gint64 _con_carrier_timeout_sec = 0; \ gint64 _con_carrier_timeout_sec = 0; \
\ \
_con_connections = _parse((ARGV), \ _con_connections = _parse((ARGV), \
nmtst_get_rand_bool() ? &_con_hostname : NULL, \ nmtst_get_rand_bool() ? &_con_hostname : NULL, \
nmtst_get_rand_bool() ? &_con_carrier_timeout_sec : NULL); \ nmtst_get_rand_bool() ? &_con_carrier_timeout_sec : NULL, \
g_assert_cmpstr(_con_hostname, ==, NULL); \ NULL); \
g_assert_cmpint(_con_carrier_timeout_sec, ==, 0); \ g_assert_cmpstr(_con_hostname, ==, NULL); \
\ g_assert_cmpint(_con_carrier_timeout_sec, ==, 0); \
_con_connections; \ \
_con_connections; \
}) })
#define _parse_con(ARGV, connection_name) \ #define _parse_con(ARGV, connection_name) \
@@ -154,7 +156,7 @@ test_dhcp_with_hostname(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1); g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "host1"); g_assert_cmpstr(hostname, ==, "host1");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -424,7 +426,7 @@ test_if_ip4_manual(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2); g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "hostname1.example.com"); g_assert_cmpstr(hostname, ==, "hostname1.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -505,7 +507,7 @@ test_if_ip4_auto(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1); g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "myhostname"); g_assert_cmpstr(hostname, ==, "myhostname");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -596,7 +598,7 @@ test_if_ip6_manual(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1); g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "hostname0.example.com"); g_assert_cmpstr(hostname, ==, "hostname0.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -684,7 +686,7 @@ test_if_mac_ifname(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 1); g_assert_cmpint(g_hash_table_size(connections), ==, 1);
g_assert_cmpstr(hostname, ==, "hostname0"); g_assert_cmpstr(hostname, ==, "hostname0");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -1840,7 +1842,7 @@ test_rd_znet(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2); g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "foo.example.com"); g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -1927,7 +1929,7 @@ test_rd_znet_legacy(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2); g_assert_cmpint(g_hash_table_size(connections), ==, 2);
g_assert_cmpstr(hostname, ==, "foo.example.com"); g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -2006,7 +2008,7 @@ test_rd_znet_ifnames(void)
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
const char *const *v_subchannels; const char *const *v_subchannels;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 2); g_assert_cmpint(g_hash_table_size(connections), ==, 2);
connection = g_hash_table_lookup(connections, "zeth0"); connection = g_hash_table_lookup(connections, "zeth0");
@@ -2281,7 +2283,7 @@ test_nameserver(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 3); g_assert_cmpint(g_hash_table_size(connections), ==, 3);
g_assert_cmpstr(hostname, ==, "foo.example.com"); g_assert_cmpstr(hostname, ==, "foo.example.com");
g_assert_cmpint(carrier_timeout_sec, ==, 0); g_assert_cmpint(carrier_timeout_sec, ==, 0);
@@ -2460,12 +2462,38 @@ test_carrier_timeout(void)
gs_free char *hostname = NULL; gs_free char *hostname = NULL;
gint64 carrier_timeout_sec = 0; gint64 carrier_timeout_sec = 0;
connections = _parse(ARGV, &hostname, &carrier_timeout_sec); connections = _parse(ARGV, &hostname, &carrier_timeout_sec, NULL);
g_assert_cmpint(g_hash_table_size(connections), ==, 0); g_assert_cmpint(g_hash_table_size(connections), ==, 0);
g_assert_cmpstr(hostname, ==, NULL); g_assert_cmpstr(hostname, ==, NULL);
g_assert_cmpint(carrier_timeout_sec, ==, 20); g_assert_cmpint(carrier_timeout_sec, ==, 20);
} }
static void
test_global_dns(void)
{
gs_unref_hashtable GHashTable *connections = NULL;
const char *const *ARGV = NM_MAKE_STRV("rd.net.dns=dns+tls://8.8.8.8",
"rd.net.dns=1.1.1.1",
"rd.net.dns=foobar",
"rd.net.dns=dns+tls://[fd01::1]:35#name");
gs_free char *hostname = NULL;
gs_strfreev char **global_dns_servers = NULL;
gint64 carrier_timeout_sec = 0;
NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dns: invalid server 'foobar'");
connections = _parse(ARGV, &hostname, &carrier_timeout_sec, &global_dns_servers);
g_test_assert_expected_messages();
g_assert_cmpint(g_hash_table_size(connections), ==, 0);
g_assert_cmpstr(hostname, ==, NULL);
g_assert_cmpint(carrier_timeout_sec, ==, 0);
g_assert(global_dns_servers != NULL);
g_assert_cmpstr(global_dns_servers[0], ==, "dns+tls://8.8.8.8");
g_assert_cmpstr(global_dns_servers[1], ==, "1.1.1.1");
g_assert_cmpstr(global_dns_servers[2], ==, "dns+tls://[fd01::1]:35#name");
g_assert_cmpstr(global_dns_servers[3], ==, NULL);
}
#define _ethtool_check_inval(arg) \ #define _ethtool_check_inval(arg) \
G_STMT_START \ G_STMT_START \
{ \ { \
@@ -2686,6 +2714,7 @@ main(int argc, char **argv)
g_test_add_func("/initrd/cmdline/carrier_timeout", test_carrier_timeout); g_test_add_func("/initrd/cmdline/carrier_timeout", test_carrier_timeout);
g_test_add_func("/initrd/cmdline/rd_ethtool", test_rd_ethtool); g_test_add_func("/initrd/cmdline/rd_ethtool", test_rd_ethtool);
g_test_add_func("/initrd/cmdline/plain_equal_char", test_plain_equal_char); g_test_add_func("/initrd/cmdline/plain_equal_char", test_plain_equal_char);
g_test_add_func("/initrd/cmdline/global_dns", test_global_dns);
return g_test_run(); return g_test_run();
} }