]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: save IPv4/IPv6 address states into state file
authorLetzteInstanz <faust6@inbox.ru>
Sun, 11 Apr 2021 20:29:11 +0000 (23:29 +0300)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 13 Apr 2021 23:51:02 +0000 (08:51 +0900)
This also introduces RequiredFamilyForOnline= setting to .network file,
and IPv4AddressState/IPv6AddressState DBus properties.

14 files changed:
man/systemd.network.xml
src/libsystemd/sd-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/network/networkd-link-bus.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-bus.c
src/network/networkd-manager.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-state-file.c
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-unit-file/directives-all.service

index a49e6011e349b991ed844f58a2e3fb831653fbfe..5ab1e9f0a8ebaffeb0c39d8a899d42e182b7cca4 100644 (file)
           if <literal>RequiredForOnline=no</literal>.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>RequiredFamilyForOnline=</varname></term>
+        <listitem>
+          <para>Specifies an address family. When specified,
+          <command>systemd-networkd-wait-online</command> waits for at least one routable or link-local
+          IP address in the family should be configured on the link. Takes one of
+          <literal>ipv4</literal>, <literal>ipv6</literal>, <literal>both</literal>, or
+          <literal>any</literal>. Defaults to <literal>any</literal>. Note that this will be used only
+          when <varname>RequiredForOnline=</varname> is true, or its minimum operational state is
+          <literal>degraded</literal> or above. Otherwise, it will be ignored.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>ActivationPolicy=</varname></term>
         <listitem>
index acf7500970e382e619a9f4772ee19231920581c8..c1d3a9f6a2dd634edc58955319b35e92bc6bfe06 100644 (file)
@@ -56,6 +56,15 @@ static const char* const link_carrier_state_table[_LINK_CARRIER_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(link_carrier_state, LinkCarrierState);
 
+static const char* const link_required_address_family_table[_ADDRESS_FAMILY_MAX] = {
+        [ADDRESS_FAMILY_NO]   = "any",
+        [ADDRESS_FAMILY_IPV4] = "ipv4",
+        [ADDRESS_FAMILY_IPV6] = "ipv6",
+        [ADDRESS_FAMILY_YES]  = "both",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_required_address_family, AddressFamily);
+
 static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
         [LINK_ADDRESS_STATE_OFF]      = "off",
         [LINK_ADDRESS_STATE_DEGRADED] = "degraded",
index ad8632d0120b0051c4fbbf67da6312956c9799ed..3bd10c42de8e7dcb0a947e2a89b349dc7bed22c9 100644 (file)
@@ -60,6 +60,9 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
 const char* link_carrier_state_to_string(LinkCarrierState s) _const_;
 LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
 
+const char* link_required_address_family_to_string(AddressFamily s) _const_;
+AddressFamily link_required_address_family_from_string(const char *s) _pure_;
+
 const char* link_address_state_to_string(LinkAddressState s) _const_;
 LinkAddressState link_address_state_from_string(const char *s) _pure_;
 
index 16058c6fcf2649ccc2d5cfd13d489bcfcf6c01b1..a999b05845c1422221d3bcdff0120a34b0077337 100644 (file)
@@ -684,6 +684,8 @@ const sd_bus_vtable link_vtable[] = {
         SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Link, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Link, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Link, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Link, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),
 
index 6c08203c54ade0395d1eb74822e31c65704b7e35..65a6410a5a86a84f1cf54516d7f4440a6e62dcbc 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/if.h>
 #include <linux/if_arp.h>
 #include <linux/if_link.h>
+#include <sys/socket.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -163,12 +164,25 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) {
         link_update_operstate(master, true);
 }
 
+static LinkAddressState address_state_from_scope(uint8_t scope) {
+        if (scope < RT_SCOPE_SITE)
+                /* universally accessible addresses found */
+                return LINK_ADDRESS_STATE_ROUTABLE;
+
+        if (scope < RT_SCOPE_HOST)
+                /* only link or site local addresses found */
+                return LINK_ADDRESS_STATE_DEGRADED;
+
+        /* no useful addresses found */
+        return LINK_ADDRESS_STATE_OFF;
+}
+
 void link_update_operstate(Link *link, bool also_update_master) {
         LinkOperationalState operstate;
         LinkCarrierState carrier_state;
-        LinkAddressState address_state;
+        LinkAddressState ipv4_address_state, ipv6_address_state, address_state;
         _cleanup_strv_free_ char **p = NULL;
-        uint8_t scope = RT_SCOPE_NOWHERE;
+        uint8_t ipv4_scope = RT_SCOPE_NOWHERE, ipv6_scope = RT_SCOPE_NOWHERE, scope;
         bool changed = false;
         Address *address;
 
@@ -201,8 +215,11 @@ void link_update_operstate(Link *link, bool also_update_master) {
                 if (!address_is_ready(address))
                         continue;
 
-                if (address->scope < scope)
-                        scope = address->scope;
+                if (address->family == AF_INET && address->scope < ipv4_scope)
+                        ipv4_scope = address->scope;
+
+                if (address->family == AF_INET6 && address->scope < ipv6_scope)
+                        ipv6_scope = address->scope;
         }
 
         /* for operstate we also take foreign addresses into account */
@@ -210,19 +227,18 @@ void link_update_operstate(Link *link, bool also_update_master) {
                 if (!address_is_ready(address))
                         continue;
 
-                if (address->scope < scope)
-                        scope = address->scope;
+                if (address->family == AF_INET && address->scope < ipv4_scope)
+                        ipv4_scope = address->scope;
+
+                if (address->family == AF_INET6 && address->scope < ipv6_scope)
+                        ipv6_scope = address->scope;
         }
 
-        if (scope < RT_SCOPE_SITE)
-                /* universally accessible addresses found */
-                address_state = LINK_ADDRESS_STATE_ROUTABLE;
-        else if (scope < RT_SCOPE_HOST)
-                /* only link or site local addresses found */
-                address_state = LINK_ADDRESS_STATE_DEGRADED;
-        else
-                /* no useful addresses found */
-                address_state = LINK_ADDRESS_STATE_OFF;
+        ipv4_address_state = address_state_from_scope(ipv4_scope);
+        ipv6_address_state = address_state_from_scope(ipv6_scope);
+
+        scope = MIN(ipv4_scope, ipv6_scope);
+        address_state = address_state_from_scope(scope);
 
         /* Mapping of address and carrier state vs operational state
          *                                                     carrier state
@@ -256,6 +272,20 @@ void link_update_operstate(Link *link, bool also_update_master) {
                         log_oom();
         }
 
+        if (link->ipv4_address_state != ipv4_address_state) {
+                link->ipv4_address_state = ipv4_address_state;
+                changed = true;
+                if (strv_extend(&p, "IPv4AddressState") < 0)
+                        log_oom();
+        }
+
+        if (link->ipv6_address_state != ipv6_address_state) {
+                link->ipv6_address_state = ipv6_address_state;
+                changed = true;
+                if (strv_extend(&p, "IPv6AddressState") < 0)
+                        log_oom();
+        }
+
         if (link->operstate != operstate) {
                 link->operstate = operstate;
                 changed = true;
index d3353a1c4f20ccd928c11566de7d96d0eb55dd64..a8bdd971f5fd5bd6fcdcfeac5d4b87abc3408579 100644 (file)
@@ -75,6 +75,8 @@ typedef struct Link {
         LinkOperationalState operstate;
         LinkCarrierState carrier_state;
         LinkAddressState address_state;
+        LinkAddressState ipv4_address_state;
+        LinkAddressState ipv6_address_state;
 
         unsigned address_messages;
         unsigned address_remove_messages;
index 138d76541415a2d0fa8b73df34d455342421eb83..0acaeb2bed0cb2af251e92901b6312014d9b847e 100644 (file)
@@ -235,6 +235,8 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Manager, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
 
         SD_BUS_METHOD_WITH_ARGS("ListLinks",
                                 SD_BUS_NO_ARGS,
index 7f630fccc7fbdad51544dd1d248f099dd345234f..e78b57b9362c4e226cb5c60f1ae4cee473e5ad1b 100644 (file)
@@ -39,6 +39,8 @@ struct Manager {
         LinkOperationalState operational_state;
         LinkCarrierState carrier_state;
         LinkAddressState address_state;
+        LinkAddressState ipv4_address_state;
+        LinkAddressState ipv6_address_state;
 
         Hashmap *links;
         Hashmap *netdevs;
index 8ebeec8640d5b5dcb0e9bf40b40d5b016d4867b0..6c37e32453117c7b5b61e89b7a9e82a3cab75927 100644 (file)
@@ -67,6 +67,7 @@ Link.Promiscuous,                            config_parse_tristate,
 Link.Unmanaged,                              config_parse_bool,                                        0,                             offsetof(Network, unmanaged)
 Link.ActivationPolicy,                       config_parse_activation_policy,                           0,                             offsetof(Network, activation_policy)
 Link.RequiredForOnline,                      config_parse_required_for_online,                         0,                             0
+Link.RequiredFamilyForOnline,                config_parse_required_family_for_online,                  0,                             offsetof(Network, required_family_for_online)
 SR-IOV.VirtualFunction,                      config_parse_sr_iov_uint32,                               0,                             0
 SR-IOV.VLANId,                               config_parse_sr_iov_uint32,                               0,                             0
 SR-IOV.QualityOfService,                     config_parse_sr_iov_uint32,                               0,                             0
index 8ae2315f1b8bb2c4e06f815b1bcd323a4206cb77..7f086ceae28520ed7b17f513537841693c1d9edb 100644 (file)
@@ -1196,6 +1196,9 @@ int config_parse_required_for_online(
         return 0;
 }
 
+DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
+                         "Failed to parse RequiredFamilyForOnline= setting");
+
 DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
                          "Failed to parse KeepConfiguration= setting");
 
index 44b1d0205faf3f1300f523a9bb3729528ab8e0cc..df77b4261926d073b2a03dcc5a874099e47ebd67 100644 (file)
@@ -104,6 +104,7 @@ struct Network {
         bool unmanaged;
         bool required_for_online; /* Is this network required to be considered online? */
         LinkOperationalStateRange required_operstate_for_online;
+        AddressFamily required_family_for_online;
         ActivationPolicy activation_policy;
 
         /* misc settings */
@@ -348,6 +349,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
 CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
 CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
+CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online);
 CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
index 96232018ddfc559f831a561150be8073f0292bfa..34613697d6862c834a831d80ff7276de47692da6 100644 (file)
@@ -105,10 +105,11 @@ static int ordered_set_put_in4_addrv(
 
 int manager_save(Manager *m) {
         _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
-        const char *operstate_str, *carrier_state_str, *address_state_str;
+        const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str;
         LinkOperationalState operstate = LINK_OPERSTATE_OFF;
         LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
-        LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
+        LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF,
+                address_state = LINK_ADDRESS_STATE_OFF;
         _cleanup_(unlink_and_freep) char *temp_path = NULL;
         _cleanup_strv_free_ char **p = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -133,6 +134,12 @@ int manager_save(Manager *m) {
                 if (link->address_state > address_state)
                         address_state = link->address_state;
 
+                if (link->ipv4_address_state > ipv4_address_state)
+                        ipv4_address_state = link->ipv4_address_state;
+
+                if (link->ipv6_address_state > ipv6_address_state)
+                        ipv6_address_state = link->ipv6_address_state;
+
                 if (!link->network)
                         continue;
 
@@ -226,6 +233,12 @@ int manager_save(Manager *m) {
         address_state_str = link_address_state_to_string(address_state);
         assert(address_state_str);
 
+        ipv4_address_state_str = link_address_state_to_string(ipv4_address_state);
+        assert(ipv4_address_state_str);
+
+        ipv6_address_state_str = link_address_state_to_string(ipv6_address_state);
+        assert(ipv6_address_state_str);
+
         r = fopen_temporary(m->state_file, &f, &temp_path);
         if (r < 0)
                 return r;
@@ -236,8 +249,10 @@ int manager_save(Manager *m) {
                 "# This is private data. Do not parse.\n"
                 "OPER_STATE=%s\n"
                 "CARRIER_STATE=%s\n"
-                "ADDRESS_STATE=%s\n",
-                operstate_str, carrier_state_str, address_state_str);
+                "ADDRESS_STATE=%s\n"
+                "IPV4_ADDRESS_STATE=%s\n"
+                "IPV6_ADDRESS_STATE=%s\n",
+                operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str);
 
         ordered_set_print(f, "DNS=", dns);
         ordered_set_print(f, "NTP=", ntp);
@@ -273,6 +288,18 @@ int manager_save(Manager *m) {
                         log_oom();
         }
 
+        if (m->ipv4_address_state != ipv4_address_state) {
+                m->ipv4_address_state = ipv4_address_state;
+                if (strv_extend(&p, "IPv4AddressState") < 0)
+                        log_oom();
+        }
+
+        if (m->ipv6_address_state != ipv6_address_state) {
+                m->ipv6_address_state = ipv6_address_state;
+                if (strv_extend(&p, "IPv6AddressState") < 0)
+                        log_oom();
+        }
+
         if (p) {
                 r = manager_send_changed_strv(m, p);
                 if (r < 0)
@@ -376,7 +403,7 @@ static void serialize_addresses(
 }
 
 int link_save(Link *link) {
-        const char *admin_state, *oper_state, *carrier_state, *address_state;
+        const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state;
         _cleanup_(unlink_and_freep) char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
@@ -403,6 +430,12 @@ int link_save(Link *link) {
         address_state = link_address_state_to_string(link->address_state);
         assert(address_state);
 
+        ipv4_address_state = link_address_state_to_string(link->ipv4_address_state);
+        assert(ipv4_address_state);
+
+        ipv6_address_state = link_address_state_to_string(link->ipv6_address_state);
+        assert(ipv6_address_state);
+
         r = fopen_temporary(link->state_file, &f, &temp_path);
         if (r < 0)
                 return r;
@@ -414,8 +447,10 @@ int link_save(Link *link) {
                 "ADMIN_STATE=%s\n"
                 "OPER_STATE=%s\n"
                 "CARRIER_STATE=%s\n"
-                "ADDRESS_STATE=%s\n",
-                admin_state, oper_state, carrier_state, address_state);
+                "ADDRESS_STATE=%s\n"
+                "IPV4_ADDRESS_STATE=%s\n"
+                "IPV6_ADDRESS_STATE=%s\n",
+                admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
 
         if (link->network) {
                 char **dhcp6_domains = NULL, **dhcp_domains = NULL;
@@ -431,6 +466,9 @@ int link_save(Link *link) {
                         st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
                         st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
 
+                fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
+                        link_required_address_family_to_string(link->network->required_family_for_online));
+
                 fprintf(f, "ACTIVATION_POLICY=%s\n",
                         activation_policy_to_string(link->network->activation_policy));
 
index 399eeafa98c541bdc0f6c7badc5cc2d54f3932da..016be501ed8b0936589a560664fbd1edc7d1524d 100644 (file)
@@ -32,6 +32,7 @@ PermanentMACAddress=
 [Link]
 ActivationPolicy=
 RequiredForOnline=
+RequiredFamilyForOnline=
 ARP=
 AllMulticast=
 Unmanaged=
index 310dbd6add2be3b09e1e344f122a1b677511bd29..a152bebd734f6ba02b786046006225da410bbcdf 100644 (file)
@@ -545,6 +545,7 @@ RemoteChecksumRx=
 RemoteChecksumTx=
 ReorderHeader=
 RequestBroadcast=
+RequiredFamilyForOnline=
 RequiredForOnline=
 ResendIGMP=
 RootDistanceMaxSec=