From a4c9bf2e51c92b411c88804070373f1e2123331a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 14 Dec 2021 11:29:08 +0900 Subject: [PATCH] network-generator: support to set NamePolicy= through kernel command line argument Closes #16296. --- man/systemd-network-generator.service.xml | 3 + man/systemd-udevd.service.xml | 39 +++++++ src/network/generator/main.c | 9 +- src/network/generator/network-generator.c | 125 +++++++++++++++++++--- src/network/generator/network-generator.h | 8 +- 5 files changed, 164 insertions(+), 20 deletions(-) diff --git a/man/systemd-network-generator.service.xml b/man/systemd-network-generator.service.xml index 99a032179e6..6b7e2564d46 100644 --- a/man/systemd-network-generator.service.xml +++ b/man/systemd-network-generator.service.xml @@ -59,6 +59,7 @@ ifname= + net.ifname-policy= — translated into systemd.link5 files. @@ -88,6 +89,8 @@ See dracut.kernel7 + and + systemd-udevd.service8 for option syntax and details. diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index 32cb82bac30..1bc86469be9 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -224,6 +224,45 @@ appear, which affects older name derivation algorithms, too. + + net.ifname-policy=policy1[,policy2,…][,MAC] + + 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 NamePolicy= setting in .link files, e.g. + onboard or path. See + systemd.link5 + 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 systemd-udevd, but read and converted + to a .link file by systemd-network-generator. So, to make this argument + take effect, systemd-network-generator.service must be enabled and + started before starting systemd-udevd.service. See + systemd-network-generator.service8 + for more details about the service. + Example: + net.ifname-policy=keep,kernel,path,slot,onboard,01:23:45:67:89:ab +net.ifname-policy=keep,kernel,path,slot,onboard,mac + This is mostly equivalent to creating the following .link files: + # 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 + and + # 92-name-policy-for-all.link +[Match] +OriginalName=* + +[Link] +NamePolicy=keep kernel path slot onboard mac +AlternativeNamePolicy=path slot onboard mac + + + diff --git a/src/network/generator/main.c b/src/network/generator/main.c index 624b0a0b5df..a36fe98c864 100644 --- a/src/network/generator/main.c +++ b/src/network/generator/main.c @@ -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; diff --git a/src/network/generator/network-generator.c b/src/network/generator/network-generator.c index 67480974e83..c081ec673c8 100644 --- a/src/network/generator/network-generator.c +++ b/src/network/generator/network-generator.c @@ -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=: + net.ifname-policy=policy1[,policy2,...][,] # This is an original rule, not supported by other tools. # .netdev vlan=: @@ -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,...][,] */ + + 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) { diff --git a/src/network/generator/network-generator.h b/src/network/generator/network-generator.h index deaa40f8f35..dd0a58738b6 100644 --- a/src/network/generator/network-generator.h +++ b/src/network/generator/network-generator.h @@ -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);