From patchwork Fri Mar 7 14:29:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?S=C3=B6ren_Tempel?= X-Patchwork-Id: 39757 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id BFC7327BBEA; Fri, 7 Mar 2025 14:33:28 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-8.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_CERTIFIED,RCVD_IN_VALIDITY_RPBL, RCVD_IN_VALIDITY_SAFE,SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 927AA27BBE2 for ; Fri, 7 Mar 2025 14:33:27 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tqYlF-0002cd-Ky; Fri, 07 Mar 2025 09:33:14 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tqYl5-0002Yj-7d for guix-patches@gnu.org; Fri, 07 Mar 2025 09:33:08 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tqYl4-0006cG-Tt; Fri, 07 Mar 2025 09:33:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:In-Reply-To:References:Subject; bh=iUpbWAmNEPZ0Ft9LYBmfxbR7IAPR1q0wvzNLBJtEgn8=; b=OmLgRVtnGfpu5lqyiItwPBqCRTinmdncs+gWJPvFd4XGsyI+exveJ7DEE9tjWL59Dkxr2vkQ1/4YUdbeb6h9ePlhFBIHHXBVUv03sSvnd9M1j95AeT2y3ggGjoe64e9yNFkgDYwm7sWX826qIEJUYipNynGB7o/tWEsqQTrmzXx3/OAKpSZGXqG7Z+39SQbpH4u3aDVYHHKU1KokZZpww+61J1gAlm6Fb/dGVDu0ne62kx4g8bivWqLLkbzu9koy9veR/2YzrAz9atiVzIJws83uYxpb0dYDFYZIQqZc4mfaJfMAKyU9ExjkMXC5c6j+DBzysVL+FMbivUiecOvUwA==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tqYl3-0004z3-Pv; Fri, 07 Mar 2025 09:33:01 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#75934] [PATCH v2] services: networking: Add dhcpcd service. References: <20250129204935.2331-1-soeren@soeren-tempel.net> In-Reply-To: <20250129204935.2331-1-soeren@soeren-tempel.net> Resent-From: soeren@soeren-tempel.net Original-Sender: "Debbugs-submit" Resent-CC: ludo@gnu.org, maxim.cournoyer@gmail.com, guix-patches@gnu.org Resent-Date: Fri, 07 Mar 2025 14:33:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 75934 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 75934@debbugs.gnu.org Cc: ludo@gnu.org, Ludovic =?utf-8?q?Court=C3=A8s?= , Maxim Cournoyer X-Debbugs-Original-Xcc: Ludovic =?utf-8?q?Court=C3=A8s?= , Maxim Cournoyer Received: via spool by 75934-submit@debbugs.gnu.org id=B75934.174135793019073 (code B ref 75934); Fri, 07 Mar 2025 14:33:01 +0000 Received: (at 75934) by debbugs.gnu.org; 7 Mar 2025 14:32:10 +0000 Received: from localhost ([127.0.0.1]:47585 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tqYkC-0004xY-Sh for submit@debbugs.gnu.org; Fri, 07 Mar 2025 09:32:09 -0500 Received: from magnesium.8pit.net ([2001:19f0:6c01:4ae:5400:ff:fe66:af9d]:12580) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tqYjj-0004wH-5g; Fri, 07 Mar 2025 09:31:40 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=iUpbWAmN EPZ0Ft9LYBmfxbR7IAPR1q0wvzNLBJtEgn8=; h=date:subject:cc:to:from; d=soeren-tempel.net; b=VYrS65tUUMjTk48PYefnu67zskUyFlaepECQkdb69hZ5fio zg/xtyCBLi91TnG/96czm+f2JQDllZt9HyMnaCujy1BVtQj2A3kgce6FyXpflQO59LflxA xd8GvKxHcwwOIcQS2yNRfUo2b6zjo2yBLGypJv0XnxO+5GKMN5H474= Received: from localhost ( [2a02:560:4d83:4d00:581f:35a1:9dc3:d0ba]) by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id 120462f3 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); Fri, 7 Mar 2025 15:31:34 +0100 (CET) From: soeren@soeren-tempel.net Date: Fri, 7 Mar 2025 15:29:05 +0100 Message-ID: X-Mailer: git-send-email 2.48.1 MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches From: Sören Tempel 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. (serialize-field-name): New procedure. * gnu/tests/networking.scm (run-dhcpcd-test): New procedure. (%dhcpcd-os, %test-dhcpcd): New variables. * doc/guix.texi (Networking Services): Document it. --- Change since v1: * Expand documentation and include a larger configuration example * Improve record type by hyphening record field names * Use make-forkexec-constructor in shepherd service * Fix indention of if expression doc/guix.texi | 89 ++++++++++++++++++++ gnu/services/networking.scm | 161 ++++++++++++++++++++++++++++++++++++ gnu/tests/networking.scm | 106 ++++++++++++++++++++++++ 3 files changed, 356 insertions(+) base-commit: 9bc4c9f521caab8aa8d88aa948a650945bb55838 diff --git a/doc/guix.texi b/doc/guix.texi index 6844470ce2..6529865c09 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -21594,6 +21594,95 @@ Networking Setup @end table @end deftp +@cindex DHCPCD, networking service + +@defvar dhcpcd-service-type +This the type for a service running @command{dhcpcd}, a @acronym{DHCP, +Dynamic Host Configuration Protocol} client that can be used as a +replacement for the historical ISC client supported by +@code{dhcp-client-service-type}. + +Its value must be a @code{dhcpcd-configuration} record, as described +below. As an example, consider the following setup which runs +@command{dhcpcd} with a local @acronym{DNS, Domain Name System} +resolver: + +@lisp +(service dhcpcd-service-type + (dhcpcd-configuration + (option '("rapid_commit" "interface_mtu")) + (no-option '("nd_rdnss" + "dhcp6_name_servers" + "domain_name_servers" + "domain_name" + "domain_search")) + (static '("domain_name_servers=127.0.0.1")) + (no-hook '("hostname"))))) +@end lisp +@end defvar + +@deftp {Data Type} dhcpcd-configuration +Available @code{dhcpcd-configuration} fields are: + +@table @asis +@item @code{interfaces} (default: @code{()}) (type: list) +List of networking interfaces---e.g., @code{"eth0"}---to start a DHCP +client for. If no interface is specified (i.e., the list is empty) then +@command{dhcpcd} discovers available Ethernet interfaces, that can be +configured, automatically. + +@item @code{command-arguments} (default: @code{("-q" "-q")}) (type: list) +List of additional command-line options. + +@item @code{host-name} (default: @code{""}) (type: maybe-string) +Host name to send via DHCP, defaults to the current system host name. + +@item @code{duid} (default: @code{""}) (type: maybe-string) +DHCPv4 clients require a unique client identifier, this option uses the +DHCPv6 Unique Identifier as a DHCPv4 client identifier as well. For +more information, refer to @uref{https://www.rfc-editor.org/rfc/rfc4361, RFC 4361} +and @code{dhcpcd.conf(5)}. + +@item @code{persistent?} (default: @code{#t}) (type: boolean) +When true, automatically de-configure the interface when @command{dhcpcd} +exits. + +@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{no-option} (default: @code{()}) (type: list-of-strings) +List of options to remove from the message before it's processed. + +@item @code{no-hook} (default: @code{()}) (type: list-of-strings) +List of hook script which should not be invoked. + +@item @code{static} (default: @code{()}) (type: list-of-strings) +DHCP client can request different options from a DHCP server, through +@code{static} it is possible to configure static values for selected +options. For example, @code{"domain_name_servers=127.0.0.1"}. + +@item @code{vendor-class-id} (type: maybe-string) +Set the DHCP Vendor Class (e.g., @code{MSFT}). For more information, +refer to @uref{https://www.rfc-editor.org/rfc/rfc2132#section-9.13,RFC +2132}. + +@item @code{client-id} (type: maybe-string) +Use the interface hardware address or the given string as a client +identifier, this is matually exclusive with the @code{duid} option. + +@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 53840c2764..85ad5287f2 100644 --- a/gnu/services/networking.scm +++ b/gnu/services/networking.scm @@ -109,6 +109,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-arguments + dhcpcd-configuration-host-name + dhcpcd-configuration-duid + dhcpcd-configuration-persistent? + dhcpcd-configuration-option + dhcpcd-configuration-require + dhcpcd-configuration-slaac + dhcpcd-configuration-no-option + dhcpcd-configuration-no-hook + dhcpcd-configuration-static + dhcpcd-configuration-vendor-class-id + dhcpcd-configuration-client-id + dhcpcd-configuration-extra-content + ntp-configuration ntp-configuration? ntp-configuration-ntp @@ -492,6 +510,149 @@ (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 (serialize-field-name field-name) + (let ((str (symbol->string field-name))) + (string-replace-substring + (if (string-suffix? "?" str) + (string-drop-right str 1) + str) + "-" ""))) + +(define (dhcpcd-serialize-string field-name value) + (if (equal? field-name 'extra-content) + #~(string-append #$value "\n") + #~(format #f "~a ~a~%" #$(serialize-field-name field-name) #$value))) + +(define (dhcpcd-serialize-boolean field-name value) + (if value + #~(format #f "~a~%" #$(serialize-field-name 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. host-name) 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 networking interfaces---e.g., @code{\"eth0\"}---to start a DHCP client +for. If no interface is specified (i.e., the list is empty) then @command{dhcpcd} +discovers available Ethernet interfaces, that can be configured, automatically." + empty-serializer) + (command-arguments + (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 + (host-name + (maybe-string "") + "Host name to send via DHCP, defaults to the current system host name.") + (duid + (maybe-string "") + "DHCPv4 clients require a unique client identifier, this option uses the DHCPv6 +Unique Identifier as a DHCPv4 client identifier as well. For more information, refer +to @uref{https://www.rfc-editor.org/rfc/rfc4361, RFC 4361} and @code{dhcpcd.conf(5)}.") + (persistent? + (boolean #t) + "When true, automatically de-configure the interface when @command{dhcpcd} exits.") + (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. + (no-option + (list-of-strings '()) + "List of options to remove from the message before it's processed.") + (no-hook + (list-of-strings '()) + "List of hook script which should not be invoked.") + (static + (list-of-strings '()) + "DHCP client can request different options from a DHCP server, through +@code{static} it is possible to configure static values for selected options. For +example, @code{\"domain_name_servers=127.0.0.1\"}.") + (vendor-class-id + maybe-string + "Set the DHCP Vendor Class (e.g., @code{MSFT}). For more information, refer +to @uref{https://www.rfc-editor.org/rfc/rfc2132#section-9.13,RFC 2132}.") + (client-id + maybe-string + "Use the interface hardware address or the given string as a client identifier, +this is matually exclusive with the @code{duid} option.") + + ;; 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-arguments 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 + #~(make-forkexec-constructor + (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..7d54ebba50 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-arguments '("--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