[PATCH] services: networking: Add dhcpcd service.

  • Open
  • quality assurance status badge
Details
One participant
  • soeren
Owner
unassigned
Submitted by
soeren
Severity
normal
S
S
soeren wrote on 29 Jan 21:45 +0100
(address . guix-patches@gnu.org)
20250129204935.2331-1-soeren@soeren-tempel.net
From: Sören Tempel <soeren@soeren-tempel.net>

This is intended as an alternative to dhcp-client-service-type as
isc-dhcp has reached its end-of-life in 2022 (three years ago!),
see #68619 for more details. Long-term, this services is therefore
intended to replace dhcp-client-service-type.

* gnu/services/networking.scm (dhcpcd-service-type): New service.
(dhcpcd-shepherd-service): New procedure.
(dhcpcd-account-service): New variable.
(dhcpcd-config-file): New procedure.
(dhcpcd-configuration): New record type.
(dhcpcd-serialize-list-of-strings, dhcpcd-serialize-boolean)
(dhcpcd-serialize-string): New procedures.
* gnu/tests/networking.scm (run-dhcpcd-test): New procedure.
(%dhcpcd-os, %test-dhcpcd): New variables.
* doc/guix.texi (Networking Services): Document it.
---
Previously, an integration into the dhcp-client-service-type was
attempted. However, the discussion there established that a new
entirely separate service would be a better fit.

See https://issues.guix.gnu.org/68675for the prior discussion.

doc/guix.texi | 57 ++++++++++++++
gnu/services/networking.scm | 147 ++++++++++++++++++++++++++++++++++++
gnu/tests/networking.scm | 106 ++++++++++++++++++++++++++
3 files changed, 310 insertions(+)

Toggle diff (364 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..6f51d1e1f6 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -21468,6 +21468,63 @@ which provides the @code{networking} Shepherd service.
@end table
@end deftp
+@cindex DHCPCD, networking service
+
+@defvar dhcpcd-service-type
+This is a service which runs @var{dhcpcd}, an alternative Dynamic
+Host Configuration Protocol (DHCP) client.
+@end defvar
+
+@deftp {Data Type} dhcpcd-configuration
+Available @code{dhcpcd-configuration} fields are:
+
+@table @asis
+@item @code{interfaces} (default: @code{()}) (type: list)
+List of interfaces to start a DHCP client for.
+
+@item @code{command-args} (default: @code{("-q" "-q")}) (type: list)
+List of additional command-line options.
+
+@item @code{hostname} (default: @code{""}) (type: maybe-string)
+Hostname to send via DHCP, defaults to the current system hostname.
+
+@item @code{duid} (default: @code{""}) (type: maybe-string)
+Use and generate a DHCP Unique Identifier.
+
+@item @code{persistent} (default: @code{#t}) (type: boolean)
+Do not de-configure on shutdown.
+
+@item @code{option} (default: @code{("rapid_commit" "domain_name_servers" "domain_name" "domain_search" "host_name" "classless_static_routes" "interface_mtu")}) (type: list-of-strings)
+List of options to request from the server.
+
+@item @code{require} (default: @code{("dhcp_server_identifier")}) (type: list-of-strings)
+List of options to require in responses.
+
+@item @code{slaac} (default: @code{"private"}) (type: maybe-string)
+Interface identifier used for SLAAC generated IPv6 addresses.
+
+@item @code{nooption} (default: @code{()}) (type: list-of-strings)
+List of options to remove from the message before it's processed.
+
+@item @code{nohook} (default: @code{()}) (type: list-of-strings)
+List of hook script which should not be invoked.
+
+@item @code{static} (default: @code{()}) (type: list-of-strings)
+Configure a static value (e.g. ip_address).
+
+@item @code{vendorclassid} (type: maybe-string)
+Set the DHCP Vendor Class.
+
+@item @code{clientid} (type: maybe-string)
+Use the interface hardware address or the given string as a Client ID.
+
+@item @code{extra-content} (type: maybe-string)
+Extra content to append to the configuration as-is.
+
+@end table
+@end deftp
+
+
@cindex NetworkManager
@defvar network-manager-service-type
diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm
index af28bd0626..c97d50eccf 100644
--- a/gnu/services/networking.scm
+++ b/gnu/services/networking.scm
@@ -108,6 +108,24 @@ (define-module (gnu services networking)
dhcpd-configuration-pid-file
dhcpd-configuration-interfaces
+ dhcpcd-service-type
+ dhcpcd-configuration
+ dhcpcd-configuration?
+ dhcpcd-configuration-interfaces
+ dhcpcd-configuration-command-args
+ dhcpcd-configuration-hostname
+ dhcpcd-configuration-duid
+ dhcpcd-configuration-persistent
+ dhcpcd-configuration-option
+ dhcpcd-configuration-require
+ dhcpcd-configuration-slaac
+ dhcpcd-configuration-nooption
+ dhcpcd-configuration-nohook
+ dhcpcd-configuration-static
+ dhcpcd-configuration-vendorclassid
+ dhcpcd-configuration-clientid
+ dhcpcd-configuration-extra-content
+
ntp-configuration
ntp-configuration?
ntp-configuration-ntp
@@ -491,6 +509,135 @@ (define dhcpd-service-type
(description "Run a DHCP (Dynamic Host Configuration Protocol) daemon. The
daemon is responsible for allocating IP addresses to its client.")))
+
+;;
+;; DHCPCD.
+;;
+
+(define (dhcpcd-serialize-string field-name value)
+ (let ((field (object->string field-name)))
+ (if (string=? field "extra-content")
+ #~(string-append #$value "\n")
+ #~(format #f "~a ~a~%" #$field #$value))))
+
+(define (dhcpcd-serialize-boolean field-name value)
+ (if value
+ #~(format #f "~a~%" #$(object->string field-name))
+ ""))
+
+(define (dhcpcd-serialize-list-of-strings field-name value)
+ #~(string-append #$@(map (cut dhcpcd-serialize-string field-name <>) value)))
+
+;; Some fields (e.g. hostname) can be specified with an empty string argument.
+;; Therefore, we need a maybe type to differentiate disabled/empty-string.
+(define-maybe string (prefix dhcpcd-))
+
+(define-configuration dhcpcd-configuration
+ (interfaces
+ (list '())
+ "List of interfaces to start a DHCP client for."
+ empty-serializer)
+ (command-args
+ (list '("-q" "-q"))
+ "List of additional command-line options."
+ empty-serializer)
+
+ ;; The following defaults replicate the default dhcpcd configuration file.
+ ;;
+ ;; See https://github.com/NetworkConfiguration/dhcpcd/tree/v10.0.10#configuration
+ (hostname
+ (maybe-string "")
+ "Hostname to send via DHCP, defaults to the current system hostname.")
+ (duid
+ (maybe-string "")
+ "Use and generate a DHCP Unique Identifier.")
+ (persistent
+ (boolean #t)
+ "Do not de-configure on shutdown.")
+ (option
+ (list-of-strings
+ '("rapid_commit"
+ "domain_name_servers"
+ "domain_name"
+ "domain_search"
+ "host_name"
+ "classless_static_routes"
+ "interface_mtu"))
+ "List of options to request from the server.")
+ (require
+ (list-of-strings '("dhcp_server_identifier"))
+ "List of options to require in responses.")
+ (slaac
+ (maybe-string "private")
+ "Interface identifier used for SLAAC generated IPv6 addresses.")
+
+ ;; Common options not set in the default configuration file.
+ (nooption
+ (list-of-strings '())
+ "List of options to remove from the message before it's processed.")
+ (nohook
+ (list-of-strings '())
+ "List of hook script which should not be invoked.")
+ (static
+ (list-of-strings '())
+ "Configure a static value (e.g. ip_address).")
+ (vendorclassid
+ maybe-string
+ "Set the DHCP Vendor Class.")
+ (clientid
+ maybe-string
+ "Use the interface hardware address or the given string as a Client ID.")
+
+ ;; Escape hatch for the generated configuration file.
+ (extra-content
+ maybe-string
+ "Extra content to append to the configuration as-is.")
+
+ (prefix dhcpcd-))
+
+(define (dhcpcd-config-file config)
+ (mixed-text-file "dhcpcd.conf"
+ (serialize-configuration
+ config
+ dhcpcd-configuration-fields)))
+
+(define dhcpcd-account-service
+ (list (user-group (name "dhcpcd") (system? #t))
+ (user-account
+ (name "dhcpcd")
+ (group "dhcpcd")
+ (system? #t)
+ (comment "dhcpcd daemon user")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))))
+
+(define (dhcpcd-shepherd-service config)
+ (let* ((config-file (dhcpcd-config-file config))
+ (command-args (dhcpcd-configuration-command-args config))
+ (ifaces (dhcpcd-configuration-interfaces config)))
+ (list (shepherd-service
+ (documentation "dhcpcd daemon.")
+ (provision '(networking))
+ (requirement '(user-processes udev))
+ (actions (list (shepherd-configuration-action config-file)))
+ (start
+ #~(lambda _
+ (fork+exec-command
+ (list (string-append #$dhcpcd "/sbin/dhcpcd")
+ #$@command-args "-B" "-f" #$config-file #$@ifaces))))
+ (stop #~(make-kill-destructor))))))
+
+(define dhcpcd-service-type
+ (service-type (name 'dhcpcd)
+ (description "Run the dhcpcd daemon.")
+ (extensions
+ (list (service-extension account-service-type
+ (const dhcpcd-account-service))
+ (service-extension shepherd-root-service-type
+ dhcpcd-shepherd-service)))
+ (compose concatenate)
+ (default-value (dhcpcd-configuration))))
+
;;;
;;; NTP.
diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm
index e7c02b9e00..720f123953 100644
--- a/gnu/tests/networking.scm
+++ b/gnu/tests/networking.scm
@@ -32,6 +32,7 @@ (define-module (gnu tests networking)
#:use-module (guix store)
#:use-module (guix monads)
#:use-module (guix modules)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages linux)
#:use-module (gnu packages networking)
@@ -44,6 +45,7 @@ (define-module (gnu tests networking)
%test-inetd
%test-openvswitch
%test-dhcpd
+ %test-dhcpcd
%test-tor
%test-iptables
%test-ipfs))
@@ -673,6 +675,110 @@ (define %test-dhcpd
(description "Test a running DHCP daemon configuration.")
(value (run-dhcpd-test))))
+
+;;;
+;;; DHCPCD Daemon
+;;;
+
+(define %dhcpcd-os
+ (let ((base-os
+ (simple-operating-system
+ (service dhcpcd-service-type
+ (dhcpcd-configuration
+ (command-args '("--debug" "--logfile" "/dev/console"))
+ (interfaces '("ens3")))))))
+ (operating-system
+ (inherit base-os)
+ (packages
+ (append (list dhcpcd iproute)
+ (operating-system-packages base-os))))))
+
+(define (run-dhcpcd-test)
+ "Run tests in %dhcpcd-os with a running dhcpcd daemon on localhost."
+ (define os
+ (marionette-operating-system
+ %dhcpcd-os
+ #:imported-modules '((gnu services herd))))
+
+ (define vm
+ (virtual-machine os))
+
+ (define test
+ (with-imported-modules '((gnu build marionette))
+ #~(begin
+ (use-modules (srfi srfi-64)
+ (gnu build marionette))
+ (define marionette
+ (make-marionette (list #$vm)))
+
+ (define (wait-for-lease)
+ (marionette-eval
+ '(begin
+ (use-modules (ice-9 popen) (ice-9 rdelim))
+
+ (let loop ((i 15))
+ (if (> i 0)
+ (let* ((port (open-input-pipe "dhcpcd --dumplease ens3"))
+ (output (read-string port)))
+ (close-port port)
+ (unless (string-contains output "reason=BOUND")
+ (sleep 1)
+ (loop (- i 1))))
+ (error "failed to obtain a DHCP lease"))))
+ marionette))
+
+ (test-runner-current (system-test-runner #$output))
+ (test-begin "dhcpcd")
+
+ (test-assert "service is running"
+ (marionette-eval
+ '(begin
+ (use-modules (gnu services herd))
+
+ ;; Make sure the 'dhcpcd' command is found.
+ (setenv "PATH" "/run/current-system/profile/sbin")
+
+ (wait-for-service 'networking))
+ marionette))
+
+ (test-assert "IPC socket exists"
+ (marionette-eval
+ '(file-exists? "/var/run/dhcpcd/ens3.sock")
+ marionette))
+
+ (test-equal "IPC is functional"
+ 0
+ (marionette-eval
+ '(status:exit-val
+ (system* "dhcpcd" "--dumplease" "ens3"))
+ marionette))
+
+ (test-equal "aquires IPv4 address via DHCP"
+ 1
+ (and
+ (wait-for-lease)
+ (marionette-eval
+ '(begin
+ (use-modules (ice-9 popen) (ice-9 rdelim))
+
+ (let* ((port (open-input-pipe "ip -4 address show dev ens3"))
+ (lines (string-split (read-string port) #\newline)))
+ (close-port port)
+ (length
+ (filter (lambda (line)
+ (string-contains line "scope global dynamic"))
+ lines))))
+ marionette)))
+
+ (test-end))))
+ (gexp->derivation "dhcpcd-test" test))
+
+(define %test-dhcpcd
+ (system-test
+ (name "dhcpcd")
+ (description "Test that the dhcpcd obtains IP DHCP leases.")
+ (value (run-dhcpcd-test))))
+
;;;
;;; Services related to Tor
?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send an email to 75934@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 75934
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch