]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/address-label: allow to configure IPv6 address label in networkd.conf 34018/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 17 Aug 2024 04:33:35 +0000 (13:33 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 20 Aug 2024 11:50:56 +0000 (20:50 +0900)
Closes #23159.

man/networkd.conf.xml
man/systemd.network.xml
src/network/networkd-address-label.c
src/network/networkd-address-label.h
src/network/networkd-conf.c
src/network/networkd-gperf.gperf
src/network/networkd-manager.c
src/network/networkd-manager.h
test/test-network/conf/networkd-address-label.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index 9daa1254b6e5f12075f6f844b85173098d3be8fa..4772e827997105a4d9112dae6f585575c999f203 100644 (file)
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>[IPv6AddressLabel] Section Options</title>
+
+    <para>An [IPv6AddressLabel] section accepts the following keys. Specify multiple [IPv6AddressLabel]
+    sections to configure multiple address labels. IPv6 address labels are used for address selection.
+    See <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>. Precedence is managed by
+    userspace, and only the label itself is stored in the kernel.</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>Label=</varname></term>
+        <listitem>
+          <para>The label for the prefix, an unsigned integer in the range 0…4294967294. 0xffffffff is
+          reserved. This setting is mandatory.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Prefix=</varname></term>
+        <listitem>
+          <para>IPv6 prefix is an address with a prefix length, separated by a slash
+          <literal>/</literal> character. This setting is mandatory.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[DHCPv4] Section Options</title>
 
index 0374dc7cdc0f771a747e59cad3a7fa99e6c47b9a..6f6746b13bbf674114e9b4d180532e87e20b94ad 100644 (file)
@@ -1628,8 +1628,8 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
       <varlistentry>
         <term><varname>Label=</varname></term>
         <listitem>
-          <para>The label for the prefix, an unsigned integer in the range 0…4294967294. 0xffffffff is
-          reserved. This setting is mandatory.</para>
+          <para>The label for the prefix. Takes an unsigned integer in the range 0…4294967294 (0xfffffffe).
+          4294967295 (0xffffffff) is reserved. This setting is mandatory.</para>
 
           <xi:include href="version-info.xml" xpointer="v234"/>
         </listitem>
@@ -1638,8 +1638,8 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
       <varlistentry>
         <term><varname>Prefix=</varname></term>
         <listitem>
-          <para>IPv6 prefix is an address with a prefix length, separated by a slash
-          <literal>/</literal> character. This setting is mandatory. </para>
+          <para>Takes an IPv6 address with a prefix length, separated by a slash
+          <literal>/</literal> character. This setting is mandatory.</para>
 
           <xi:include href="version-info.xml" xpointer="v234"/>
         </listitem>
index eea37acce652cf3b7af6382d180ce368c8fddca6..3ac0cca72ebae034afeaf5fab4ad17210d90544f 100644 (file)
@@ -22,6 +22,11 @@ AddressLabel *address_label_free(AddressLabel *label) {
                 hashmap_remove(label->network->address_labels_by_section, label->section);
         }
 
+        if (label->manager) {
+                assert(label->section);
+                hashmap_remove(label->manager->address_labels_by_section, label->section);
+        }
+
         config_section_free(label->section);
         return mfree(label);
 }
@@ -36,21 +41,30 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
         AddressLabel,
         address_label_free);
 
-static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
+static int address_label_new_static(
+                Manager *manager,
+                Network *network,
+                const char *filename,
+                unsigned section_line,
+                AddressLabel **ret) {
+
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(address_label_freep) AddressLabel *label = NULL;
+        Hashmap **address_labels_by_section;
         int r;
 
-        assert(network);
+        assert(!manager != !network);
         assert(ret);
         assert(filename);
         assert(section_line > 0);
 
+        address_labels_by_section = manager ? &manager->address_labels_by_section : &network->address_labels_by_section;
+
         r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
-        label = hashmap_get(network->address_labels_by_section, n);
+        label = hashmap_get(*address_labels_by_section, n);
         if (label) {
                 *ret = TAKE_PTR(label);
                 return 0;
@@ -61,12 +75,13 @@ static int address_label_new_static(Network *network, const char *filename, unsi
                 return -ENOMEM;
 
         *label = (AddressLabel) {
+                .manager = manager,
                 .network = network,
                 .section = TAKE_PTR(n),
                 .label = UINT32_MAX,
         };
 
-        r = hashmap_ensure_put(&network->address_labels_by_section, &address_label_section_hash_ops, label->section, label);
+        r = hashmap_ensure_put(address_labels_by_section, &address_label_section_hash_ops, label->section, label);
         if (r < 0)
                 return r;
 
@@ -74,7 +89,7 @@ static int address_label_new_static(Network *network, const char *filename, unsi
         return 0;
 }
 
-static int address_label_configure_handler(
+static int link_address_label_configure_handler(
                 sd_netlink *rtnl,
                 sd_netlink_message *m,
                 Request *req,
@@ -94,7 +109,7 @@ static int address_label_configure_handler(
         }
 
         if (link->static_address_label_messages == 0) {
-                log_link_debug(link, "Addresses label set");
+                log_link_debug(link, "Addresses label set.");
                 link->static_address_labels_configured = true;
                 link_check_ready(link);
         }
@@ -102,6 +117,33 @@ static int address_label_configure_handler(
         return 1;
 }
 
+static int manager_address_label_configure_handler(
+                sd_netlink *rtnl,
+                sd_netlink_message *m,
+                Request *req,
+                Link *link,
+                void *userdata) {
+
+        Manager *manager = ASSERT_PTR(ASSERT_PTR(req)->manager);
+        int r;
+
+        assert(m);
+        assert(!link);
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_message_warning_errno(m, r, "Could not set address label");
+                return 1;
+        }
+
+        if (manager->static_address_label_messages == 0) {
+                log_debug("Addresses label set.");
+                manager->static_address_labels_configured = true;
+        }
+
+        return 1;
+}
+
 static int address_label_fill_message(AddressLabel *label, sd_netlink_message *m) {
         int r;
 
@@ -119,7 +161,7 @@ static int address_label_fill_message(AddressLabel *label, sd_netlink_message *m
         return sd_netlink_message_append_in6_addr(m, IFA_ADDRESS, &label->prefix);
 }
 
-static int address_label_configure(AddressLabel *label, Link *link, Request *req) {
+static int link_address_label_configure(AddressLabel *label, Link *link, Request *req) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
@@ -142,23 +184,62 @@ static int address_label_configure(AddressLabel *label, Link *link, Request *req
         return request_call_netlink_async(link->manager->rtnl, m, req);
 }
 
-static int address_label_process_request(Request *req, Link *link, void *userdata) {
+static int manager_address_label_configure(AddressLabel *label, Manager *manager, Request *req) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        int r;
+
+        assert(label);
+        assert(manager);
+        assert(manager->rtnl);
+        assert(req);
+
+        r = sd_rtnl_message_new_addrlabel(manager->rtnl, &m, RTM_NEWADDRLABEL, 0, AF_INET6);
+        if (r < 0)
+                return r;
+
+        r = address_label_fill_message(label, m);
+        if (r < 0)
+                return r;
+
+        return request_call_netlink_async(manager->rtnl, m, req);
+}
+
+static int link_address_label_process_request(Request *req, Link *link, void *userdata) {
         AddressLabel *label = ASSERT_PTR(userdata);
         int r;
 
         assert(req);
         assert(link);
+        assert(link->manager);
 
-        if (!link_is_ready_to_configure(link, false))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return 0;
 
-        r = address_label_configure(label, link, req);
+        if (!link->manager->static_address_labels_configured)
+                return 0;
+
+        r = link_address_label_configure(label, link, req);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to configure address label: %m");
 
         return 1;
 }
 
+static int manager_address_label_process_request(Request *req, Link *link, void *userdata) {
+        AddressLabel *label = ASSERT_PTR(userdata);
+        int r;
+
+        assert(req);
+        assert(req->manager);
+        assert(!link);
+
+        r = manager_address_label_configure(label, req->manager, req);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to configure address label: %m");
+
+        return 1;
+}
+
 int link_request_static_address_labels(Link *link) {
         AddressLabel *label;
         int r;
@@ -171,9 +252,9 @@ int link_request_static_address_labels(Link *link) {
         HASHMAP_FOREACH(label, link->network->address_labels_by_section) {
                 r = link_queue_request_full(link, REQUEST_TYPE_ADDRESS_LABEL,
                                             label, NULL, trivial_hash_func, trivial_compare_func,
-                                            address_label_process_request,
+                                            link_address_label_process_request,
                                             &link->static_address_label_messages,
-                                            address_label_configure_handler, NULL);
+                                            link_address_label_configure_handler, NULL);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Failed to request address label: %m");
         }
@@ -189,6 +270,32 @@ int link_request_static_address_labels(Link *link) {
         return 0;
 }
 
+int manager_request_static_address_labels(Manager *manager) {
+        AddressLabel *label;
+        int r;
+
+        assert(manager);
+
+        manager->static_address_labels_configured = false;
+
+        HASHMAP_FOREACH(label, manager->address_labels_by_section) {
+                r = manager_queue_request_full(manager, REQUEST_TYPE_ADDRESS_LABEL,
+                                               label, NULL, trivial_hash_func, trivial_compare_func,
+                                               manager_address_label_process_request,
+                                               &manager->static_address_label_messages,
+                                               manager_address_label_configure_handler, NULL);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to request address label: %m");
+        }
+
+        if (manager->static_address_label_messages == 0)
+                manager->static_address_labels_configured = true;
+        else
+                log_debug("Setting address labels.");
+
+        return 0;
+}
+
 static int address_label_section_verify(AddressLabel *label) {
         assert(label);
         assert(label->section);
@@ -211,16 +318,24 @@ static int address_label_section_verify(AddressLabel *label) {
         return 0;
 }
 
-void network_drop_invalid_address_labels(Network *network) {
+static void drop_invalid_address_labels(Hashmap *address_labels_by_section) {
         AddressLabel *label;
 
-        assert(network);
-
-        HASHMAP_FOREACH(label, network->address_labels_by_section)
+        HASHMAP_FOREACH(label, address_labels_by_section)
                 if (address_label_section_verify(label) < 0)
                         address_label_free(label);
 }
 
+void network_drop_invalid_address_labels(Network *network) {
+        assert(network);
+        drop_invalid_address_labels(network->address_labels_by_section);
+}
+
+void manager_drop_invalid_address_labels(Manager *manager) {
+        assert(manager);
+        drop_invalid_address_labels(manager->address_labels_by_section);
+}
+
 int config_parse_address_label_prefix(
                 const char *unit,
                 const char *filename,
@@ -234,7 +349,8 @@ int config_parse_address_label_prefix(
                 void *userdata) {
 
         _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
-        Network *network = userdata;
+        Manager *manager = ltype ? userdata : NULL;
+        Network *network = ltype ? NULL : userdata;
         unsigned char prefixlen;
         union in_addr_union a;
         int r;
@@ -245,7 +361,7 @@ int config_parse_address_label_prefix(
         assert(rvalue);
         assert(userdata);
 
-        r = address_label_new_static(network, filename, section_line, &n);
+        r = address_label_new_static(manager, network, filename, section_line, &n);
         if (r < 0)
                 return log_oom();
 
@@ -289,7 +405,8 @@ int config_parse_address_label(
                 void *userdata) {
 
         _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
-        Network *network = userdata;
+        Manager *manager = ltype ? userdata : NULL;
+        Network *network = ltype ? NULL : userdata;
         uint32_t k;
         int r;
 
@@ -299,7 +416,7 @@ int config_parse_address_label(
         assert(rvalue);
         assert(userdata);
 
-        r = address_label_new_static(network, filename, section_line, &n);
+        r = address_label_new_static(manager, network, filename, section_line, &n);
         if (r < 0)
                 return log_oom();
 
index 1e2ee70deeb23c377b32b5828b28e43b90da27b5..d2165719c37346cfaf2dc9e0e524e925bf891475 100644 (file)
@@ -8,9 +8,11 @@
 #include "networkd-util.h"
 
 typedef struct Link Link;
+typedef struct Manager Manager;
 typedef struct Network Network;
 
 typedef struct AddressLabel {
+        Manager *manager;
         Network *network;
         ConfigSection *section;
 
@@ -23,8 +25,10 @@ typedef struct AddressLabel {
 AddressLabel *address_label_free(AddressLabel *label);
 
 void network_drop_invalid_address_labels(Network *network);
+void manager_drop_invalid_address_labels(Manager *manager);
 
 int link_request_static_address_labels(Link *link);
+int manager_request_static_address_labels(Manager *manager);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_address_label);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_label_prefix);
index 6a6814d59c9c8ac132426e84661d0794696f2b06..0ec45d3dd91b9e8837afd1a7a187fe4e537fb79f 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "conf-parser.h"
 #include "constants.h"
+#include "networkd-address-label.h"
 #include "networkd-conf.h"
 #include "networkd-manager.h"
 #include "networkd-speed-meter.h"
@@ -18,6 +19,7 @@ int manager_parse_config_file(Manager *m) {
                         "systemd/networkd.conf",
                         "Network\0"
                         "IPv6AcceptRA\0"
+                        "IPv6AddressLabel\0"
                         "DHCPv4\0"
                         "DHCPv6\0"
                         "DHCPServer\0"
@@ -34,5 +36,7 @@ int manager_parse_config_file(Manager *m) {
                 m->speed_meter_interval_usec = SPEED_METER_MINIMUM_TIME_INTERVAL;
         }
 
+        manager_drop_invalid_address_labels(m);
+
         return 0;
 }
index f02dfd7a05f9a87deb876cd2d720a89c88587824..8b64b3af305ab4ab3a2e151fe37a958dd36b2fd0 100644 (file)
@@ -5,6 +5,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #endif
 #include <stddef.h>
 #include "conf-parser.h"
+#include "networkd-address-label.h"
 #include "networkd-conf.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dns.h"
@@ -33,6 +34,8 @@ Network.IPv6Forwarding,                  config_parse_tristate,
 Network.IPv6PrivacyExtensions,           config_parse_ipv6_privacy_extensions,   0,          offsetof(Manager, ipv6_privacy_extensions)
 Network.UseDomains,                      config_parse_use_domains,               0,          offsetof(Manager, use_domains)
 IPv6AcceptRA.UseDomains,                 config_parse_use_domains,               0,          offsetof(Manager, ndisc_use_domains)
+IPv6AddressLabel.Prefix,                 config_parse_address_label_prefix,      1,          0
+IPv6AddressLabel.Label,                  config_parse_address_label,             1,          0
 DHCPv4.UseDomains,                       config_parse_use_domains,               0,          offsetof(Manager, dhcp_use_domains)
 DHCPv4.DUIDType,                         config_parse_duid_type,                 0,          offsetof(Manager, dhcp_duid)
 DHCPv4.DUIDRawData,                      config_parse_duid_rawdata,              0,          offsetof(Manager, dhcp_duid)
index 4ec4550caf3cb1f035f101f15dcbdeb96cfb96f8..6893ade66adbd610b91edb833f8fee7765b35002 100644 (file)
@@ -32,6 +32,7 @@
 #include "local-addresses.h"
 #include "netlink-util.h"
 #include "network-internal.h"
+#include "networkd-address-label.h"
 #include "networkd-address-pool.h"
 #include "networkd-address.h"
 #include "networkd-dhcp-server-bus.h"
@@ -664,6 +665,8 @@ Manager* manager_free(Manager *m) {
         m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
         m->nexthop_ids = set_free(m->nexthop_ids);
 
+        m->address_labels_by_section = hashmap_free(m->address_labels_by_section);
+
         sd_event_source_unref(m->speed_meter_event_source);
         sd_event_unref(m->event);
 
@@ -692,6 +695,10 @@ int manager_start(Manager *m) {
 
         manager_set_sysctl(m);
 
+        r = manager_request_static_address_labels(m);
+        if (r < 0)
+                return r;
+
         r = manager_start_speed_meter(m);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize speed meter: %m");
index 354cc6273ec8fd2c39e0d0ddb3684828f6d010eb..a70b3e708f4812338cc58ce1896eeabde418c183 100644 (file)
@@ -88,6 +88,11 @@ struct Manager {
         unsigned route_remove_messages;
         Set *routes;
 
+        /* IPv6 Address Label */
+        Hashmap *address_labels_by_section;
+        unsigned static_address_label_messages;
+        bool static_address_labels_configured;
+
         /* Route table name */
         Hashmap *route_table_numbers_by_name;
         Hashmap *route_table_names_by_number;
diff --git a/test/test-network/conf/networkd-address-label.conf b/test/test-network/conf/networkd-address-label.conf
new file mode 100644 (file)
index 0000000..253b9f3
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[IPv6AddressLabel]
+Label=5555
+Prefix=2004:da8:2:0::/64
index 3a30c8bfd19639a0fbbbada5a49d4f47a6235122..3cf743dec2e321f28f49f133c8cab004a1dd82f8 100755 (executable)
@@ -3729,12 +3729,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
 
     def test_ipv6_address_label(self):
         copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
+        copy_networkd_conf_dropin('networkd-address-label.conf')
         start_networkd()
         self.wait_online('dummy98:degraded')
 
         output = check_output('ip addrlabel list')
         print(output)
-        self.assertRegex(output, '2004:da8:1::/64')
+        self.assertRegex(output, '2004:da8:1::/64 dev dummy98 label 4444')
+        self.assertRegex(output, '2004:da8:2::/64 label 5555')
 
     def test_ipv6_proxy_ndp(self):
         copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')