Subject: bug#63516: Static networking should wait for interfaces to be up

From: Ludovic Courtès <[email-masked]>
Date: Sun, 21 May 2023 01:03:26 +0200

Ludovic Courtès <[email-masked]> skribis:

> Before doing ‘addr-add’ in ‘network-set-up/linux’, should we wait for
> the interface to show up, by calling ‘get-links’ from Guile-Netlink or
> something like that?

Below is a simple workaround.  How does that sound?

A better fix would be to poll(2) on the underlying AF_NETLINK socket.
In fact, we could also implement something like systemd’s
‘network-online.target’ by doing that.  For that we’d need Guile-Netlink
to let us create SOCK_NONBLOCK sockets and to use real ports instead of
raw file descriptors; Fibers would then take care of the rest.

Thoughts?

Ludo’.

diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index fd79c9e232..5d43d998c3 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -2794,10 +2794,39 @@ (define (network-set-up/linux config)
     (scheme-file "set-up-network"
                  (with-extensions (list guile-netlink)
                    #~(begin
-                       (use-modules (ip addr) (ip link) (ip route))
+                       (use-modules (ip addr) (ip link) (ip route)
+                                    (srfi srfi-1))
+
+                       (define (wait-for-device device)
+                         ;; Wait for DEVICE to show up.
+                         ;; XXX: Polling is ridiculous.  We should open a
+                         ;; SOCK_NONBLOCK netlink socket and wait on it.
+                         (let loop ((attempts 0))
+                           (unless (find (lambda (link)
+                                           (string=? (link-name link)
+                                                     device))
+                                         (get-links))
+                             (if (< attempts 30)
+                                 (begin
+                                   (format #t
+                                           "waiting for \
+networking device '~a'...~%"
+                                           device)
+                                   ((@ (fibers) sleep) 1)
+                                   (loop (+ 1 attempts)))
+                                 (begin
+                                   (format #t "networking device '~a' \
+did not show up; bailing out~%"
+                                           device)
+                                   #f)))))
 
                        #$@(map (lambda (address)
                                  #~(begin
+                                     ;; Before going any further, wait for the
+                                     ;; device to show up.
+                                     (wait-for-device
+                                      #$(network-address-device address))
+
                                      (addr-add #$(network-address-device address)
                                                #$(network-address-value address)
                                                #:ipv6?