network-generator: support to set NamePolicy= through kernel command line argument

Closes #16296.
This commit is contained in:
Yu Watanabe 2021-12-14 11:29:08 +09:00
parent ff516b4341
commit a4c9bf2e51
5 changed files with 164 additions and 20 deletions

View file

@ -59,6 +59,7 @@
<varlistentry>
<term><varname>ifname=</varname></term>
<term><varname>net.ifname-policy=</varname></term>
<listitem>
<para>— translated into
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
@ -88,6 +89,8 @@
<para>See
<citerefentry project='man-pages'><refentrytitle>dracut.kernel</refentrytitle><manvolnum>7</manvolnum></citerefentry>
and
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for option syntax and details.</para>
</refsect1>

View file

@ -224,6 +224,45 @@
appear, which affects older name derivation algorithms, too.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>net.ifname-policy=<replaceable>policy1</replaceable>[,<replaceable>policy2</replaceable>,…][,<replaceable>MAC</replaceable>]</varname></term>
<listitem>
<para>Specifies naming policies applied when renaming network interfaces. Takes a list of
policies and an optional MAC address separated with comma. Each policy value must be one of
the policies understood by the <varname>NamePolicy=</varname> setting in .link files, e.g.
<literal>onboard</literal> or <literal>path</literal>. See
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more details. When the MAC address is specified, the policies are applied to the
interface which has the address. When no MAC address is specified, the policies are applied
to all interfaces. This kernel command line argument can be specified multiple times. This
argument is not directly read from <command>systemd-udevd</command>, but read and converted
to a .link file by <command>systemd-network-generator</command>. So, to make this argument
take effect, <filename>systemd-network-generator.service</filename> must be enabled and
started before starting <filename>systemd-udevd.service</filename>. See
<citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for more details about the service.</para>
<para>Example:
<programlisting>net.ifname-policy=keep,kernel,path,slot,onboard,01:23:45:67:89:ab
net.ifname-policy=keep,kernel,path,slot,onboard,mac</programlisting>
This is mostly equivalent to creating the following .link files:
<programlisting># 91-name-policy-with-mac.link
[Match]
MACAddress=01:23:45:67:89:ab
[Link]
NamePolicy=keep kernel path slot onboard
AlternativeNamePolicy=path slot onboard</programlisting>
and
<programlisting># 92-name-policy-for-all.link
[Match]
OriginalName=*
[Link]
NamePolicy=keep kernel path slot onboard mac
AlternativeNamePolicy=path slot onboard mac</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
<!-- when adding entries here, consider also adding them in kernel-command-line.xml -->
</refsect1>

View file

@ -65,9 +65,10 @@ static int link_save(Link *link, const char *dest_dir) {
assert(link);
r = asprintf(&filename, "90-%s.link",
link->ifname);
if (r < 0)
filename = strjoin(!isempty(link->ifname) ? "90" :
!hw_addr_is_null(&link->mac) ? "91" : "92",
"-", link->filename, ".link");
if (!filename)
return log_oom();
r = generator_open_unit_file(dest_dir, "kernel command line", filename, &f);
@ -104,7 +105,7 @@ static int context_save(Context *context) {
r = k;
}
HASHMAP_FOREACH(link, context->links_by_name) {
HASHMAP_FOREACH(link, context->links_by_filename) {
k = link_save(link, p);
if (k < 0 && r >= 0)
r = k;

View file

@ -5,6 +5,7 @@
#include "hostname-util.h"
#include "log.h"
#include "macro.h"
#include "netif-naming-scheme.h"
#include "network-generator.h"
#include "parse-util.h"
#include "proc-cmdline.h"
@ -25,6 +26,7 @@
# .link
ifname=<interface>:<MAC>
net.ifname-policy=policy1[,policy2,...][,<MAC>] # This is an original rule, not supported by other tools.
# .netdev
vlan=<vlanname>:<phydevice>
@ -263,7 +265,10 @@ static Link *link_free(Link *link) {
if (!link)
return NULL;
free(link->filename);
free(link->ifname);
strv_free(link->policies);
strv_free(link->alt_policies);
return mfree(link);
}
@ -276,28 +281,43 @@ static int link_new(
Link **ret) {
_cleanup_(link_freep) Link *link = NULL;
_cleanup_free_ char *ifname = NULL;
_cleanup_free_ char *ifname = NULL, *filename = NULL;
int r;
assert(context);
assert(mac);
if (!ifname_valid(name))
return -EINVAL;
if (name) {
if (!ifname_valid(name))
return -EINVAL;
ifname = strdup(name);
if (!ifname)
return -ENOMEM;
ifname = strdup(name);
if (!ifname)
return -ENOMEM;
filename = strdup(name);
if (!filename)
return -ENOMEM;
}
if (!filename) {
filename = strdup(hw_addr_is_null(mac) ? "default" :
HW_ADDR_TO_STR_FULL(mac, HW_ADDR_TO_STRING_NO_COLON));
if (!filename)
return -ENOMEM;
}
link = new(Link, 1);
if (!link)
return -ENOMEM;
*link = (Link) {
.filename = TAKE_PTR(filename),
.ifname = TAKE_PTR(ifname),
.mac = *mac,
};
r = hashmap_ensure_put(&context->links_by_name, &string_hash_ops, link->ifname, link);
r = hashmap_ensure_put(&context->links_by_filename, &string_hash_ops, link->filename, link);
if (r < 0)
return r;
@ -308,8 +328,10 @@ static int link_new(
return 0;
}
Link *link_get(Context *context, const char *ifname) {
return hashmap_get(context->links_by_name, ifname);
Link *link_get(Context *context, const char *filename) {
assert(context);
assert(filename);
return hashmap_get(context->links_by_filename, filename);
}
static int network_set_dhcp_type(Context *context, const char *ifname, const char *dhcp_type) {
@ -922,6 +944,65 @@ static int parse_cmdline_ifname(Context *context, const char *key, const char *v
return link_new(context, name, &mac, NULL);
}
static int parse_cmdline_ifname_policy(Context *context, const char *key, const char *value) {
_cleanup_strv_free_ char **policies = NULL, **alt_policies = NULL;
struct hw_addr_data mac = HW_ADDR_NULL;
Link *link;
int r;
/* net.ifname-policy=policy1[,policy2,...][,<MAC>] */
if (proc_cmdline_value_missing(key, value))
return -EINVAL;
for (const char *q = value; ; ) {
_cleanup_free_ char *word = NULL;
NamePolicy p;
r = extract_first_word(&q, &word, ",", 0);
if (r == 0)
break;
if (r < 0)
return r;
p = name_policy_from_string(word);
if (p < 0) {
r = parse_hw_addr(word, &mac);
if (r < 0)
return r;
if (hw_addr_is_null(&mac))
return -EINVAL;
if (!isempty(q))
return -EINVAL;
break;
}
if (alternative_names_policy_from_string(word) >= 0) {
r = strv_extend(&alt_policies, word);
if (r < 0)
return r;
}
r = strv_consume(&policies, TAKE_PTR(word));
if (r < 0)
return r;
}
if (strv_isempty(policies))
return -EINVAL;
r = link_new(context, NULL, &mac, &link);
if (r < 0)
return r;
link->policies = TAKE_PTR(policies);
link->alt_policies = TAKE_PTR(alt_policies);
return 0;
}
int parse_cmdline_item(const char *key, const char *value, void *data) {
Context *context = data;
@ -944,6 +1025,8 @@ int parse_cmdline_item(const char *key, const char *value, void *data) {
return parse_cmdline_bond(context, key, value);
if (streq(key, "ifname"))
return parse_cmdline_ifname(context, key, value);
if (streq(key, "net.ifname-policy"))
return parse_cmdline_ifname_policy(context, key, value);
return 0;
}
@ -995,7 +1078,7 @@ void context_clear(Context *context) {
hashmap_free_with_destructor(context->networks_by_name, network_free);
hashmap_free_with_destructor(context->netdevs_by_name, netdev_free);
hashmap_free_with_destructor(context->links_by_name, link_free);
hashmap_free_with_destructor(context->links_by_filename, link_free);
}
static int address_dump(Address *address, FILE *f) {
@ -1123,11 +1206,25 @@ void link_dump(Link *link, FILE *f) {
if (!hw_addr_is_null(&link->mac))
fprintf(f, "MACAddress=%s\n", HW_ADDR_TO_STR(&link->mac));
else
fputs("OriginalName=*\n", f);
fprintf(f,
"\n[Link]\n"
"Name=%s\n",
link->ifname);
fputs("\n[Link]\n", f);
if (!isempty(link->ifname))
fprintf(f, "Name=%s\n", link->ifname);
if (!strv_isempty(link->policies)) {
fputs("NamePolicy=", f);
fputstrv(f, link->policies, " ", NULL);
fputc('\n', f);
}
if (!strv_isempty(link->alt_policies)) {
fputs("AlternativeNamesPolicy=", f);
fputstrv(f, link->alt_policies, " ", NULL);
fputc('\n', f);
}
}
int network_format(Network *network, char **ret) {

View file

@ -81,17 +81,21 @@ struct NetDev {
};
struct Link {
char *filename;
/* [Match] */
struct hw_addr_data mac;
/* [Link] */
char *ifname;
char **policies;
char **alt_policies;
};
typedef struct Context {
Hashmap *networks_by_name;
Hashmap *netdevs_by_name;
Hashmap *links_by_name;
Hashmap *links_by_filename;
} Context;
int parse_cmdline_item(const char *key, const char *value, void *data);
@ -106,6 +110,6 @@ NetDev *netdev_get(Context *context, const char *ifname);
void netdev_dump(NetDev *netdev, FILE *f);
int netdev_format(NetDev *netdev, char **ret);
Link *link_get(Context *context, const char *ifname);
Link *link_get(Context *context, const char *filename);
void link_dump(Link *link, FILE *f);
int link_format(Link *link, char **ret);