a8c905
From b99b055ba975c1663beaf94dbfe8f5c5c7398996 Mon Sep 17 00:00:00 2001
a8c905
From: Yu Watanabe <watanabe.yu+github@gmail.com>
a8c905
Date: Sun, 15 Dec 2019 23:21:18 +0900
a8c905
Subject: [PATCH] udev: support AlternativeName= setting in .link file
a8c905
a8c905
(cherry picked from commit a5053a158b43c5ddee90f4915b9fc603e0191d6d)
a8c905
a8c905
Related: #1850986
a8c905
---
a8c905
 man/systemd.link.xml                     |  8 ++++
a8c905
 src/libsystemd/sd-netlink/netlink-util.c | 40 ++++++++++++++++
a8c905
 src/libsystemd/sd-netlink/netlink-util.h |  1 +
a8c905
 src/shared/conf-parser.c                 | 60 ++++++++++++++++++++++++
a8c905
 src/shared/conf-parser.h                 |  1 +
a8c905
 src/udev/net/link-config-gperf.gperf     |  1 +
a8c905
 src/udev/net/link-config.c               |  5 ++
a8c905
 src/udev/net/link-config.h               |  1 +
a8c905
 8 files changed, 117 insertions(+)
a8c905
a8c905
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
a8c905
index 32657308d0..0b0d83349d 100644
a8c905
--- a/man/systemd.link.xml
a8c905
+++ b/man/systemd.link.xml
a8c905
@@ -343,6 +343,14 @@
a8c905
           </para>
a8c905
         </listitem>
a8c905
       </varlistentry>
a8c905
+      <varlistentry>
a8c905
+        <term><varname>AlternativeName=</varname></term>
a8c905
+        <listitem>
a8c905
+          <para>The alternative interface name to use. This option can be specified multiple times.
a8c905
+          If the empty string is assigned to this option, the list is reset, and all prior assignments
a8c905
+          have no effect.</para>
a8c905
+        </listitem>
a8c905
+      </varlistentry>
a8c905
       <varlistentry>
a8c905
         <term><varname>MTUBytes=</varname></term>
a8c905
         <listitem>
a8c905
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
a8c905
index 3928dfbabf..c1c306f121 100644
a8c905
--- a/src/libsystemd/sd-netlink/netlink-util.c
a8c905
+++ b/src/libsystemd/sd-netlink/netlink-util.c
a8c905
@@ -4,6 +4,7 @@
a8c905
 
a8c905
 #include "netlink-internal.h"
a8c905
 #include "netlink-util.h"
a8c905
+#include "strv.h"
a8c905
 
a8c905
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
a8c905
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
a8c905
@@ -80,6 +81,45 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
a8c905
         return 0;
a8c905
 }
a8c905
 
a8c905
+int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
a8c905
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
a8c905
+        int r;
a8c905
+
a8c905
+        assert(rtnl);
a8c905
+        assert(ifindex > 0);
a8c905
+
a8c905
+        if (strv_isempty(alternative_names))
a8c905
+                return 0;
a8c905
+
a8c905
+        if (!*rtnl) {
a8c905
+                r = sd_netlink_open(rtnl);
a8c905
+                if (r < 0)
a8c905
+                        return r;
a8c905
+        }
a8c905
+
a8c905
+        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, ifindex);
a8c905
+        if (r < 0)
a8c905
+                return r;
a8c905
+
a8c905
+        r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
a8c905
+        if (r < 0)
a8c905
+                return r;
a8c905
+
a8c905
+        r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names);
a8c905
+        if (r < 0)
a8c905
+                return r;
a8c905
+
a8c905
+        r = sd_netlink_message_close_container(message);
a8c905
+        if (r < 0)
a8c905
+                return r;
a8c905
+
a8c905
+        r = sd_netlink_call(*rtnl, message, 0, NULL);
a8c905
+        if (r < 0)
a8c905
+                return r;
a8c905
+
a8c905
+        return 0;
a8c905
+}
a8c905
+
a8c905
 int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
a8c905
         struct nlmsgerr *err;
a8c905
         int r;
a8c905
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
a8c905
index 882a616310..92de19c092 100644
a8c905
--- a/src/libsystemd/sd-netlink/netlink-util.h
a8c905
+++ b/src/libsystemd/sd-netlink/netlink-util.h
a8c905
@@ -38,6 +38,7 @@ static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
a8c905
 
a8c905
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
a8c905
 int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
a8c905
+int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
a8c905
 
a8c905
 int rtnl_log_parse_error(int r);
a8c905
 int rtnl_log_create_error(int r);
a8c905
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
a8c905
index 246b7431e4..1f40f00c72 100644
a8c905
--- a/src/shared/conf-parser.c
a8c905
+++ b/src/shared/conf-parser.c
a8c905
@@ -970,6 +970,66 @@ int config_parse_ifname(
a8c905
         return 0;
a8c905
 }
a8c905
 
a8c905
+int config_parse_ifnames(
a8c905
+                const char *unit,
a8c905
+                const char *filename,
a8c905
+                unsigned line,
a8c905
+                const char *section,
a8c905
+                unsigned section_line,
a8c905
+                const char *lvalue,
a8c905
+                int ltype,
a8c905
+                const char *rvalue,
a8c905
+                void *data,
a8c905
+                void *userdata) {
a8c905
+
a8c905
+        _cleanup_strv_free_ char **names = NULL;
a8c905
+        char ***s = data;
a8c905
+        const char *p;
a8c905
+        int r;
a8c905
+
a8c905
+        assert(filename);
a8c905
+        assert(lvalue);
a8c905
+        assert(rvalue);
a8c905
+        assert(data);
a8c905
+
a8c905
+        if (isempty(rvalue)) {
a8c905
+                *s = strv_free(*s);
a8c905
+                return 0;
a8c905
+        }
a8c905
+
a8c905
+        p = rvalue;
a8c905
+        for (;;) {
a8c905
+                _cleanup_free_ char *word = NULL;
a8c905
+
a8c905
+                r = extract_first_word(&p, &word, NULL, 0);
a8c905
+                if (r < 0) {
a8c905
+                        log_syntax(unit, LOG_ERR, filename, line, r,
a8c905
+                                   "Failed to extract interface name, ignoring assignment: %s",
a8c905
+                                   rvalue);
a8c905
+                        return 0;
a8c905
+                }
a8c905
+                if (r == 0)
a8c905
+                        break;
a8c905
+
a8c905
+                if (!ifname_valid_full(word, ltype)) {
a8c905
+                        log_syntax(unit, LOG_ERR, filename, line, 0,
a8c905
+                                   "Interface name is not valid or too long, ignoring assignment: %s",
a8c905
+                                   word);
a8c905
+                        continue;
a8c905
+                }
a8c905
+
a8c905
+                r = strv_consume(&names, TAKE_PTR(word));
a8c905
+                if (r < 0)
a8c905
+                        return log_oom();
a8c905
+        }
a8c905
+
a8c905
+        r = strv_extend_strv(s, names, true);
a8c905
+        if (r < 0)
a8c905
+                return log_oom();
a8c905
+
a8c905
+        return 0;
a8c905
+}
a8c905
+
a8c905
 int config_parse_ip_port(
a8c905
                 const char *unit,
a8c905
                 const char *filename,
a8c905
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
a8c905
index a0a5c89c27..375b2e5a74 100644
a8c905
--- a/src/shared/conf-parser.h
a8c905
+++ b/src/shared/conf-parser.h
a8c905
@@ -137,6 +137,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_signal);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_personality);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_permille);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_ifname);
a8c905
+CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_join_controllers);
a8c905
 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
a8c905
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
a8c905
index b37836d852..913c754145 100644
a8c905
--- a/src/udev/net/link-config-gperf.gperf
a8c905
+++ b/src/udev/net/link-config-gperf.gperf
a8c905
@@ -34,6 +34,7 @@ Link.MACAddressPolicy,           config_parse_mac_policy,    0,
a8c905
 Link.MACAddress,                 config_parse_hwaddr,        0,                             offsetof(link_config, mac)
a8c905
 Link.NamePolicy,                 config_parse_name_policy,   0,                             offsetof(link_config, name_policy)
a8c905
 Link.Name,                       config_parse_ifname,        0,                             offsetof(link_config, name)
a8c905
+Link.AlternativeName,            config_parse_ifnames,       1,                             offsetof(link_config, alternative_names)
a8c905
 Link.Alias,                      config_parse_ifalias,       0,                             offsetof(link_config, alias)
a8c905
 Link.MTUBytes,                   config_parse_mtu,           AF_UNSPEC,                     offsetof(link_config, mtu)
a8c905
 Link.BitsPerSecond,              config_parse_si_size,       0,                             offsetof(link_config, speed)
a8c905
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
a8c905
index 5113586457..d07a1a1874 100644
a8c905
--- a/src/udev/net/link-config.c
a8c905
+++ b/src/udev/net/link-config.c
a8c905
@@ -67,6 +67,7 @@ static void link_config_free(link_config *link) {
a8c905
         free(link->mac);
a8c905
         free(link->name_policy);
a8c905
         free(link->name);
a8c905
+        strv_free(link->alternative_names);
a8c905
         free(link->alias);
a8c905
 
a8c905
         free(link);
a8c905
@@ -468,6 +469,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
a8c905
         if (r < 0)
a8c905
                 return log_warning_errno(r, "Could not set Alias=, MACAddress= or MTU= on %s: %m", old_name);
a8c905
 
a8c905
+        r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, config->alternative_names);
a8c905
+        if (r < 0)
a8c905
+                return log_warning_errno(r, "Could not set AlternativeName= on %s: %m", old_name);
a8c905
+
a8c905
         *name = new_name;
a8c905
 
a8c905
         return 0;
a8c905
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
a8c905
index 4798bb101c..93d5fdce59 100644
a8c905
--- a/src/udev/net/link-config.h
a8c905
+++ b/src/udev/net/link-config.h
a8c905
@@ -50,6 +50,7 @@ struct link_config {
a8c905
         MACPolicy mac_policy;
a8c905
         NamePolicy *name_policy;
a8c905
         char *name;
a8c905
+        char **alternative_names;
a8c905
         char *alias;
a8c905
         uint32_t mtu;
a8c905
         size_t speed;