summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSören Tempel <soeren@soeren-tempel.net>2025-03-07 15:29:05 +0100
committerLudovic Courtès <ludo@gnu.org>2025-03-08 16:09:41 +0100
commit5ead9fa56c9ca97456796b09079fcfe0f24d8aa3 (patch)
treef3745c362b94621a309450acdb35e5c610698c2a
parenta8db2cb547f93f915726eae8ebae7646a4361094 (diff)
services: networking: Add dhcpcd service.
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. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
-rw-r--r--doc/guix.texi89
-rw-r--r--gnu/services/networking.scm161
-rw-r--r--gnu/tests/networking.scm106
3 files changed, 356 insertions, 0 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 6844470ce2f..6529865c090 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -21594,6 +21594,95 @@ which provides the @code{networking} Shepherd service.
21594@end table 21594@end table
21595@end deftp 21595@end deftp
21596 21596
21597@cindex DHCPCD, networking service
21598
21599@defvar dhcpcd-service-type
21600This the type for a service running @command{dhcpcd}, a @acronym{DHCP,
21601Dynamic Host Configuration Protocol} client that can be used as a
21602replacement for the historical ISC client supported by
21603@code{dhcp-client-service-type}.
21604
21605Its value must be a @code{dhcpcd-configuration} record, as described
21606below. As an example, consider the following setup which runs
21607@command{dhcpcd} with a local @acronym{DNS, Domain Name System}
21608resolver:
21609
21610@lisp
21611(service dhcpcd-service-type
21612 (dhcpcd-configuration
21613 (option '("rapid_commit" "interface_mtu"))
21614 (no-option '("nd_rdnss"
21615 "dhcp6_name_servers"
21616 "domain_name_servers"
21617 "domain_name"
21618 "domain_search"))
21619 (static '("domain_name_servers=127.0.0.1"))
21620 (no-hook '("hostname")))))
21621@end lisp
21622@end defvar
21623
21624@deftp {Data Type} dhcpcd-configuration
21625Available @code{dhcpcd-configuration} fields are:
21626
21627@table @asis
21628@item @code{interfaces} (default: @code{()}) (type: list)
21629List of networking interfaces---e.g., @code{"eth0"}---to start a DHCP
21630client for. If no interface is specified (i.e., the list is empty) then
21631@command{dhcpcd} discovers available Ethernet interfaces, that can be
21632configured, automatically.
21633
21634@item @code{command-arguments} (default: @code{("-q" "-q")}) (type: list)
21635List of additional command-line options.
21636
21637@item @code{host-name} (default: @code{""}) (type: maybe-string)
21638Host name to send via DHCP, defaults to the current system host name.
21639
21640@item @code{duid} (default: @code{""}) (type: maybe-string)
21641DHCPv4 clients require a unique client identifier, this option uses the
21642DHCPv6 Unique Identifier as a DHCPv4 client identifier as well. For
21643more information, refer to @uref{https://www.rfc-editor.org/rfc/rfc4361, RFC 4361}
21644and @code{dhcpcd.conf(5)}.
21645
21646@item @code{persistent?} (default: @code{#t}) (type: boolean)
21647When true, automatically de-configure the interface when @command{dhcpcd}
21648exits.
21649
21650@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)
21651List of options to request from the server.
21652
21653@item @code{require} (default: @code{("dhcp_server_identifier")}) (type: list-of-strings)
21654List of options to require in responses.
21655
21656@item @code{slaac} (default: @code{"private"}) (type: maybe-string)
21657Interface identifier used for SLAAC generated IPv6 addresses.
21658
21659@item @code{no-option} (default: @code{()}) (type: list-of-strings)
21660List of options to remove from the message before it's processed.
21661
21662@item @code{no-hook} (default: @code{()}) (type: list-of-strings)
21663List of hook script which should not be invoked.
21664
21665@item @code{static} (default: @code{()}) (type: list-of-strings)
21666DHCP client can request different options from a DHCP server, through
21667@code{static} it is possible to configure static values for selected
21668options. For example, @code{"domain_name_servers=127.0.0.1"}.
21669
21670@item @code{vendor-class-id} (type: maybe-string)
21671Set the DHCP Vendor Class (e.g., @code{MSFT}). For more information,
21672refer to @uref{https://www.rfc-editor.org/rfc/rfc2132#section-9.13,RFC
216732132}.
21674
21675@item @code{client-id} (type: maybe-string)
21676Use the interface hardware address or the given string as a client
21677identifier, this is matually exclusive with the @code{duid} option.
21678
21679@item @code{extra-content} (type: maybe-string)
21680Extra content to append to the configuration as-is.
21681
21682@end table
21683@end deftp
21684
21685
21597@cindex NetworkManager 21686@cindex NetworkManager
21598 21687
21599@defvar network-manager-service-type 21688@defvar network-manager-service-type
diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm
index 53840c27642..85ad5287f26 100644
--- a/gnu/services/networking.scm
+++ b/gnu/services/networking.scm
@@ -109,6 +109,24 @@
109 dhcpd-configuration-pid-file 109 dhcpd-configuration-pid-file
110 dhcpd-configuration-interfaces 110 dhcpd-configuration-interfaces
111 111
112 dhcpcd-service-type
113 dhcpcd-configuration
114 dhcpcd-configuration?
115 dhcpcd-configuration-interfaces
116 dhcpcd-configuration-command-arguments
117 dhcpcd-configuration-host-name
118 dhcpcd-configuration-duid
119 dhcpcd-configuration-persistent?
120 dhcpcd-configuration-option
121 dhcpcd-configuration-require
122 dhcpcd-configuration-slaac
123 dhcpcd-configuration-no-option
124 dhcpcd-configuration-no-hook
125 dhcpcd-configuration-static
126 dhcpcd-configuration-vendor-class-id
127 dhcpcd-configuration-client-id
128 dhcpcd-configuration-extra-content
129
112 ntp-configuration 130 ntp-configuration
113 ntp-configuration? 131 ntp-configuration?
114 ntp-configuration-ntp 132 ntp-configuration-ntp
@@ -493,6 +511,149 @@ Protocol (DHCP) client, on all the non-loopback network interfaces.")))
493daemon is responsible for allocating IP addresses to its client."))) 511daemon is responsible for allocating IP addresses to its client.")))
494 512
495 513
514;;
515;; DHCPCD.
516;;
517
518(define (serialize-field-name field-name)
519 (let ((str (symbol->string field-name)))
520 (string-replace-substring
521 (if (string-suffix? "?" str)
522 (string-drop-right str 1)
523 str)
524 "-" "")))
525
526(define (dhcpcd-serialize-string field-name value)
527 (if (equal? field-name 'extra-content)
528 #~(string-append #$value "\n")
529 #~(format #f "~a ~a~%" #$(serialize-field-name field-name) #$value)))
530
531(define (dhcpcd-serialize-boolean field-name value)
532 (if value
533 #~(format #f "~a~%" #$(serialize-field-name field-name))
534 ""))
535
536(define (dhcpcd-serialize-list-of-strings field-name value)
537 #~(string-append #$@(map (cut dhcpcd-serialize-string field-name <>) value)))
538
539;; Some fields (e.g. host-name) can be specified with an empty string argument.
540;; Therefore, we need a maybe type to differentiate disabled/empty-string.
541(define-maybe string (prefix dhcpcd-))
542
543(define-configuration dhcpcd-configuration
544 (interfaces
545 (list '())
546 "List of networking interfaces---e.g., @code{\"eth0\"}---to start a DHCP client
547for. If no interface is specified (i.e., the list is empty) then @command{dhcpcd}
548discovers available Ethernet interfaces, that can be configured, automatically."
549 empty-serializer)
550 (command-arguments
551 (list '("-q" "-q"))
552 "List of additional command-line options."
553 empty-serializer)
554
555 ;; The following defaults replicate the default dhcpcd configuration file.
556 ;;
557 ;; See https://github.com/NetworkConfiguration/dhcpcd/tree/v10.0.10#configuration
558 (host-name
559 (maybe-string "")
560 "Host name to send via DHCP, defaults to the current system host name.")
561 (duid
562 (maybe-string "")
563 "DHCPv4 clients require a unique client identifier, this option uses the DHCPv6
564Unique Identifier as a DHCPv4 client identifier as well. For more information, refer
565to @uref{https://www.rfc-editor.org/rfc/rfc4361, RFC 4361} and @code{dhcpcd.conf(5)}.")
566 (persistent?
567 (boolean #t)
568 "When true, automatically de-configure the interface when @command{dhcpcd} exits.")
569 (option
570 (list-of-strings
571 '("rapid_commit"
572 "domain_name_servers"
573 "domain_name"
574 "domain_search"
575 "host_name"
576 "classless_static_routes"
577 "interface_mtu"))
578 "List of options to request from the server.")
579 (require
580 (list-of-strings '("dhcp_server_identifier"))
581 "List of options to require in responses.")
582 (slaac
583 (maybe-string "private")
584 "Interface identifier used for SLAAC generated IPv6 addresses.")
585
586 ;; Common options not set in the default configuration file.
587 (no-option
588 (list-of-strings '())
589 "List of options to remove from the message before it's processed.")
590 (no-hook
591 (list-of-strings '())
592 "List of hook script which should not be invoked.")
593 (static
594 (list-of-strings '())
595 "DHCP client can request different options from a DHCP server, through
596@code{static} it is possible to configure static values for selected options. For
597example, @code{\"domain_name_servers=127.0.0.1\"}.")
598 (vendor-class-id
599 maybe-string
600 "Set the DHCP Vendor Class (e.g., @code{MSFT}). For more information, refer
601to @uref{https://www.rfc-editor.org/rfc/rfc2132#section-9.13,RFC 2132}.")
602 (client-id
603 maybe-string
604 "Use the interface hardware address or the given string as a client identifier,
605this is matually exclusive with the @code{duid} option.")
606
607 ;; Escape hatch for the generated configuration file.
608 (extra-content
609 maybe-string
610 "Extra content to append to the configuration as-is.")
611
612 (prefix dhcpcd-))
613
614(define (dhcpcd-config-file config)
615 (mixed-text-file "dhcpcd.conf"
616 (serialize-configuration
617 config
618 dhcpcd-configuration-fields)))
619
620(define dhcpcd-account-service
621 (list (user-group (name "dhcpcd") (system? #t))
622 (user-account
623 (name "dhcpcd")
624 (group "dhcpcd")
625 (system? #t)
626 (comment "dhcpcd daemon user")
627 (home-directory "/var/empty")
628 (shell (file-append shadow "/sbin/nologin")))))
629
630(define (dhcpcd-shepherd-service config)
631 (let* ((config-file (dhcpcd-config-file config))
632 (command-args (dhcpcd-configuration-command-arguments config))
633 (ifaces (dhcpcd-configuration-interfaces config)))
634 (list (shepherd-service
635 (documentation "dhcpcd daemon.")
636 (provision '(networking))
637 (requirement '(user-processes udev))
638 (actions (list (shepherd-configuration-action config-file)))
639 (start
640 #~(make-forkexec-constructor
641 (list (string-append #$dhcpcd "/sbin/dhcpcd")
642 #$@command-args "-B" "-f" #$config-file #$@ifaces)))
643 (stop #~(make-kill-destructor))))))
644
645(define dhcpcd-service-type
646 (service-type (name 'dhcpcd)
647 (description "Run the dhcpcd daemon.")
648 (extensions
649 (list (service-extension account-service-type
650 (const dhcpcd-account-service))
651 (service-extension shepherd-root-service-type
652 dhcpcd-shepherd-service)))
653 (compose concatenate)
654 (default-value (dhcpcd-configuration))))
655
656
496;;; 657;;;
497;;; NTP. 658;;; NTP.
498;;; 659;;;
diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm
index e7c02b9e009..7d54ebba50e 100644
--- a/gnu/tests/networking.scm
+++ b/gnu/tests/networking.scm
@@ -32,6 +32,7 @@
32 #:use-module (guix store) 32 #:use-module (guix store)
33 #:use-module (guix monads) 33 #:use-module (guix monads)
34 #:use-module (guix modules) 34 #:use-module (guix modules)
35 #:use-module (gnu packages admin)
35 #:use-module (gnu packages bash) 36 #:use-module (gnu packages bash)
36 #:use-module (gnu packages linux) 37 #:use-module (gnu packages linux)
37 #:use-module (gnu packages networking) 38 #:use-module (gnu packages networking)
@@ -44,6 +45,7 @@
44 %test-inetd 45 %test-inetd
45 %test-openvswitch 46 %test-openvswitch
46 %test-dhcpd 47 %test-dhcpd
48 %test-dhcpcd
47 %test-tor 49 %test-tor
48 %test-iptables 50 %test-iptables
49 %test-ipfs)) 51 %test-ipfs))
@@ -675,6 +677,110 @@ subnet 192.168.1.0 netmask 255.255.255.0 {
675 677
676 678
677;;; 679;;;
680;;; DHCPCD Daemon
681;;;
682
683(define %dhcpcd-os
684 (let ((base-os
685 (simple-operating-system
686 (service dhcpcd-service-type
687 (dhcpcd-configuration
688 (command-arguments '("--debug" "--logfile" "/dev/console"))
689 (interfaces '("ens3")))))))
690 (operating-system
691 (inherit base-os)
692 (packages
693 (append (list dhcpcd iproute)
694 (operating-system-packages base-os))))))
695
696(define (run-dhcpcd-test)
697 "Run tests in %dhcpcd-os with a running dhcpcd daemon on localhost."
698 (define os
699 (marionette-operating-system
700 %dhcpcd-os
701 #:imported-modules '((gnu services herd))))
702
703 (define vm
704 (virtual-machine os))
705
706 (define test
707 (with-imported-modules '((gnu build marionette))
708 #~(begin
709 (use-modules (srfi srfi-64)
710 (gnu build marionette))
711 (define marionette
712 (make-marionette (list #$vm)))
713
714 (define (wait-for-lease)
715 (marionette-eval
716 '(begin
717 (use-modules (ice-9 popen) (ice-9 rdelim))
718
719 (let loop ((i 15))
720 (if (> i 0)
721 (let* ((port (open-input-pipe "dhcpcd --dumplease ens3"))
722 (output (read-string port)))
723 (close-port port)
724 (unless (string-contains output "reason=BOUND")
725 (sleep 1)
726 (loop (- i 1))))
727 (error "failed to obtain a DHCP lease"))))
728 marionette))
729
730 (test-runner-current (system-test-runner #$output))
731 (test-begin "dhcpcd")
732
733 (test-assert "service is running"
734 (marionette-eval
735 '(begin
736 (use-modules (gnu services herd))
737
738 ;; Make sure the 'dhcpcd' command is found.
739 (setenv "PATH" "/run/current-system/profile/sbin")
740
741 (wait-for-service 'networking))
742 marionette))
743
744 (test-assert "IPC socket exists"
745 (marionette-eval
746 '(file-exists? "/var/run/dhcpcd/ens3.sock")
747 marionette))
748
749 (test-equal "IPC is functional"
750 0
751 (marionette-eval
752 '(status:exit-val
753 (system* "dhcpcd" "--dumplease" "ens3"))
754 marionette))
755
756 (test-equal "aquires IPv4 address via DHCP"
757 1
758 (and
759 (wait-for-lease)
760 (marionette-eval
761 '(begin
762 (use-modules (ice-9 popen) (ice-9 rdelim))
763
764 (let* ((port (open-input-pipe "ip -4 address show dev ens3"))
765 (lines (string-split (read-string port) #\newline)))
766 (close-port port)
767 (length
768 (filter (lambda (line)
769 (string-contains line "scope global dynamic"))
770 lines))))
771 marionette)))
772
773 (test-end))))
774 (gexp->derivation "dhcpcd-test" test))
775
776(define %test-dhcpcd
777 (system-test
778 (name "dhcpcd")
779 (description "Test that the dhcpcd obtains IP DHCP leases.")
780 (value (run-dhcpcd-test))))
781
782
783;;;
678;;; Services related to Tor 784;;; Services related to Tor
679;;; 785;;;
680 786