]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
wait-online: wait for address family
authorLetzteInstanz <faust6@inbox.ru>
Tue, 13 Apr 2021 11:57:19 +0000 (14:57 +0300)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 14 Apr 2021 00:00:08 +0000 (09:00 +0900)
This introduce -4 and -6 commandline options.

man/systemd-networkd-wait-online.service.xml
src/network/wait-online/link.c
src/network/wait-online/link.h
src/network/wait-online/manager.c
src/network/wait-online/manager.h
src/network/wait-online/wait-online.c

index 6d2c71d8c7bf3f001e92478806d501b3a82bb8d5..31aa02218af8334e318fa9165445993cc669b7f1 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-4</option></term>
+        <term><option>--ipv4</option></term>
+
+        <listitem><para>Waiting for an IPv4 address of each network interface to be configured. If this
+        option is specified with <option>--any</option>, then
+        <command>systemd-networkd-wait-online</command> exits with success when at least one interface
+        becomes online and has an IPv4 address. The option is applied only for the operational state
+        <literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
+        <option>--ipv6</option> is specified, then the value from
+        <varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
+        file is used if present.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-6</option></term>
+        <term><option>--ipv6</option></term>
+
+        <listitem><para>Waiting for an IPv6 address of each network interface to be configured. If this
+        option is specified with <option>--any</option>, then
+        <command>systemd-networkd-wait-online</command> exits with success when at least one interface
+        becomes online and has an IPv6 address. The option is applied only for the operational state
+        <literal>degraded</literal> or above. If neither <option>--ipv4</option> nor
+        <option>--ipv6</option> is specified, then the value from
+        <varname>RequiredFamilyForOnline=</varname> in the corresponding <filename>.network</filename>
+        file is used if present.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--any</option></term>
 
index f2d556f099e622a5cec1d61392846cf555d71cb1..5a33d563c240537eac7acf7ee760320fe7077a77 100644 (file)
@@ -97,7 +97,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
 }
 
 int link_update_monitor(Link *l) {
-        _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
+        _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL,
+                *ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL;
         int r, ret = 0;
 
         assert(l);
@@ -135,6 +136,47 @@ int link_update_monitor(Link *l) {
                         l->operational_state = s;
         }
 
+        r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family);
+        if (r < 0)
+                ret = log_link_debug_errno(l, r, "Failed to get required address family, ignoring: %m");
+        else if (isempty(required_family))
+                l->required_family = ADDRESS_FAMILY_NO;
+        else {
+                AddressFamily f;
+
+                f = link_required_address_family_from_string(required_family);
+                if (f < 0)
+                        ret = log_link_debug_errno(l, f, "Failed to parse required address family, ignoring: %m");
+                else
+                        l->required_family = f;
+        }
+
+        r = sd_network_link_get_ipv4_address_state(l->ifindex, &ipv4_address_state);
+        if (r < 0)
+                ret = log_link_debug_errno(l, r, "Failed to get IPv4 address state, ignoring: %m");
+        else {
+                LinkAddressState s;
+
+                s = link_address_state_from_string(ipv4_address_state);
+                if (s < 0)
+                        ret = log_link_debug_errno(l, s, "Failed to parse IPv4 address state, ignoring: %m");
+                else
+                        l->ipv4_address_state = s;
+        }
+
+        r = sd_network_link_get_ipv6_address_state(l->ifindex, &ipv6_address_state);
+        if (r < 0)
+                ret = log_link_debug_errno(l, r, "Failed to get IPv6 address state, ignoring: %m");
+        else {
+                LinkAddressState s;
+
+                s = link_address_state_from_string(ipv6_address_state);
+                if (s < 0)
+                        ret = log_link_debug_errno(l, s, "Failed to parse IPv6 address state, ignoring: %m");
+                else
+                        l->ipv6_address_state = s;
+        }
+
         r = sd_network_link_get_setup_state(l->ifindex, &state);
         if (r < 0)
                 ret = log_link_debug_errno(l, r, "Failed to get setup state, ignoring: %m");
index 3aa835729383cd27b5b54c6d23d13d9fd23b0412..3072a91e565b21beb9f0ff5a6fb2f944830660db 100644 (file)
@@ -19,6 +19,9 @@ struct Link {
         bool required_for_online;
         LinkOperationalStateRange required_operstate;
         LinkOperationalState operational_state;
+        AddressFamily required_family;
+        LinkAddressState ipv4_address_state;
+        LinkAddressState ipv6_address_state;
         char *state;
 };
 
index 1438b27445c898ea3d8550dcfefc326322b2912b..8238b70464825db5e7cb02f28e0282b8915db230 100644 (file)
@@ -32,6 +32,13 @@ static bool manager_ignore_link(Manager *m, Link *link) {
 }
 
 static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
+        AddressFamily required_family;
+        bool needs_ipv4;
+        bool needs_ipv6;
+
+        assert(m);
+        assert(l);
+
         /* This returns the following:
          * -EAGAIN: not processed by udev or networkd
          *       0: operstate is not enough
@@ -60,7 +67,35 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
                 return 0;
         }
 
+        required_family = m->required_family > 0 ? m->required_family : l->required_family;
+        needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
+        needs_ipv6 = required_family & ADDRESS_FAMILY_IPV6;
+
+        if (s.min >= LINK_OPERSTATE_DEGRADED) {
+                if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
+                        goto ipv4_not_ready;
+
+                if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED)
+                        goto ipv6_not_ready;
+        }
+
+        if (s.min >= LINK_OPERSTATE_ROUTABLE) {
+                if (needs_ipv4 && l->ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE)
+                        goto ipv4_not_ready;
+
+                if (needs_ipv6 && l->ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE)
+                        goto ipv6_not_ready;
+        }
+
         return 1;
+
+ipv4_not_ready:
+        log_link_debug(l, "No routable or link-local IPv4 address is configured.");
+        return 0;
+
+ipv6_not_ready:
+        log_link_debug(l, "No routable or link-local IPv6 address is configured.");
+        return 0;
 }
 
 bool manager_configured(Manager *m) {
@@ -298,6 +333,7 @@ static int manager_network_monitor_listen(Manager *m) {
 
 int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
                 LinkOperationalStateRange required_operstate,
+                AddressFamily required_family,
                 bool any, usec_t timeout) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -312,6 +348,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
                 .interfaces = interfaces,
                 .ignore = ignore,
                 .required_operstate = required_operstate,
+                .required_family = required_family,
                 .any = any,
         };
 
index 9892a43dc989cb999268ed7682a841f291391084..f2e091638c42f85b42d9b725c01cacc8a606a475 100644 (file)
@@ -21,6 +21,7 @@ struct Manager {
         char **ignore;
 
         LinkOperationalStateRange required_operstate;
+        AddressFamily required_family;
         bool any;
 
         sd_netlink *rtnl;
@@ -35,6 +36,7 @@ struct Manager {
 Manager* manager_free(Manager *m);
 int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
                 LinkOperationalStateRange required_operstate,
+                AddressFamily required_family,
                 bool any, usec_t timeout);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
index ca0116e7f307278908d062f944b8bfb7ae106e9f..bbf51b6d3b6cfe1d42f9f8efc9d96c303b08b7ee 100644 (file)
@@ -19,6 +19,7 @@ static usec_t arg_timeout = 120 * USEC_PER_SEC;
 static Hashmap *arg_interfaces = NULL;
 static char **arg_ignore = NULL;
 static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
 static bool arg_any = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
@@ -42,6 +43,8 @@ static int help(void) {
                "     --ignore=INTERFACE     Don't take these interfaces into account\n"
                "  -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
                "                            Required operational state\n"
+               "  -4 --ipv4                 Requires at least one IPv4 address\n"
+               "  -6 --ipv6                 Requires at least one IPv6 address\n"
                "     --any                  Wait until at least one of the interfaces is online\n"
                "     --timeout=SECS         Maximum time to wait for network connectivity\n"
                "\nSee the %s for details.\n",
@@ -111,6 +114,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "interface",         required_argument, NULL, 'i'         },
                 { "ignore",            required_argument, NULL, ARG_IGNORE  },
                 { "operational-state", required_argument, NULL, 'o'         },
+                { "ipv4",              no_argument,       NULL, '4'         },
+                { "ipv6",              no_argument,       NULL, '6'         },
                 { "any",               no_argument,       NULL, ARG_ANY     },
                 { "timeout",           required_argument, NULL, ARG_TIMEOUT },
                 {}
@@ -121,7 +126,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hi:qo:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "hi:qo:46", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -159,6 +164,15 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
                 }
+
+                case '4':
+                        arg_required_family |= ADDRESS_FAMILY_IPV4;
+                        break;
+
+                case '6':
+                        arg_required_family |= ADDRESS_FAMILY_IPV6;
+                        break;
+
                 case ARG_ANY:
                         arg_any = true;
                         break;
@@ -197,7 +211,7 @@ static int run(int argc, char *argv[]) {
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
 
-        r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_any, arg_timeout);
+        r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
         if (r < 0)
                 return log_error_errno(r, "Could not create manager: %m");