Toggle diff (448 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index f7de378bdd..b529a8db6c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -16762,32 +16762,166 @@ This section describes the various network setup services available,
starting with static network configuration.
@defvr {Scheme Variable} static-networking-service-type
-This is the type for statically-configured network interfaces.
-@c TODO Document <static-networking> data structures.
+This is the type for statically-configured network interfaces. Its
+value must be a list of @code{static-networking} records. Each of them
+declares a set of @dfn{addresses}, @dfn{routes}, and @dfn{links}, as
+show below.
+
+@cindex network interface controller (NIC)
+@cindex NIC, networking interface controller
+Here is the simplest configuration, with only one network interface
+controller (NIC) and only IPv4 connectivity:
+
+@example
+;; Static networking for one NIC, IPv4-only.
+(service static-networking-service-type
+ (list (static-networking
+ (addresses
+ (list (network-address
+ (device "eno1")
+ (value "10.0.2.15/24"))))
+ (routes
+ (list (network-route
+ (destination "default")
+ (gateway "10.0.2.2"))))
+ (name-servers '("10.0.2.3")))))
+@end example
+
+The snippet above can be added to the @code{services} field of your
+operating system configuration (@pxref{Using the Configuration System}).
+It will configure your machine to have 10.0.2.15 as its IP address, with
+a 24-bit netmask for the local network---meaning that any 10.0.2.@var{x}
+address is on the local area network (LAN). Traffic to addresses
+outside the local network is routed @i{via} 10.0.2.2. Host names are
+resolved by sending domain name system (DNS) queries to 10.0.2.3.
@end defvr
-@deffn {Scheme Procedure} static-networking-service @var{interface} @var{ip} @
- [#:netmask #f] [#:gateway #f] [#:name-servers @code{'()}] @
- [#:requirement @code{'(udev)}]
-Return a service that starts @var{interface} with address @var{ip}. If
-@var{netmask} is true, use it as the network mask. If @var{gateway} is true,
-it must be a string specifying the default network gateway. @var{requirement}
-can be used to declare a dependency on another service before configuring the
-interface.
+@deftp {Data Type} static-networking
+This is the data type representing a static network configuration.
-This procedure can be called several times, one for each network
-interface of interest. Behind the scenes what it does is extend
-@code{static-networking-service-type} with additional network interfaces
-to handle.
-
-For example:
+As an example, here is how you would declare the configuration of a
+machine with a single network interface controller (NIC) available as
+@code{eno1}, and with one IPv4 and one IPv6 address:
@lisp
-(static-networking-service "eno1" "192.168.1.82"
- #:gateway "192.168.1.2"
- #:name-servers '("192.168.1.2"))
+;; Network configuration for one NIC, IPv4 + IPv6.
+(static-networking
+ (addresses (list (network-address
+ (device "eno1")
+ (value "10.0.2.15/24"))
+ (network-address
+ (device "eno1")
+ (value "2001:123:4567:101::1"))))
+ (routes (list (network-route
+ (destination "default")
+ (gateway "10.0.2.2"))
+ (network-route
+ (destination "default")
+ (gateway "2020:321:4567:42::1"))))
+ (name-servers '("10.0.2.3")))
@end lisp
-@end deffn
+
+If you are familiar with the @command{ip} command of the
+@uref{https://wiki.linuxfoundation.org/networking/iproute2,
+@code{iproute2} package} found on Linux-based systems, the declaration
+above is equivalent to typing:
+
+@example
+ip address add 10.0.2.15/24 dev eno1
+ip address add 2001:123:4567:101::1 dev eno1
+ip route add default via inet 10.0.2.2
+ip route add default via inet6 2020:321:4567:42::1
+@end example
+
+Run @command{man 8 ip} for more info. Venerable GNU/Linux users will
+certainly know how to do it with @command{ifconfig} and @command{route},
+but we'll spare you that.
+
+The available fields of this data type are as follows:
+
+@table @asis
+@item @code{addresses}
+@itemx @code{links} (default: @code{'()})
+@itemx @code{links} (default: @code{'()})
+The list of @code{network-address}, @code{network-link}, and
+@code{network-route} records for this network (see below).
+
+@item @code{name-servers} (default: @code{'()})
+The list of IP addresses (strings) of domain name servers. These IP
+addresses go to @file{/etc/resolv.conf}.
+
+@item @code{provision} (default: @code{#f})
+If true, this should be a list of symbols for the Shepherd service
+corresponding to this network configuration. When @code{#f},
+@code{'(networking)} or @code{'(loopback)} is used.
+
+@item @code{requirement} (default @code{'()})
+The list of Shepherd services depended on.
+@end table
+@end deftp
+
+@deftp {Data Type} network-address
+This is the data type representing the IP address of a network
+interface.
+
+@table @code
+@item device
+The name of the network interface for this address---e.g.,
+@code{"eno1"}.
+
+@item value
+The actual IP address and network mask, in
+@uref{https://en.wikipedia.org/wiki/CIDR#CIDR_notation, @acronym{CIDR,
+Classless Inter-Domain Routing} notation}, as a string.
+
+For example, @code{"10.0.2.15/24"} denotes IPv4 address 10.0.2.15 on a
+24-bit sub-network---all 10.0.2.@var{x} addresses are on the same local
+network.
+
+@item ipv6?
+Whether @code{value} denotes an IPv6 address. By default this is
+automatically determined.
+@end table
+@end deftp
+
+@deftp {Data Type} network-route
+This is the data type representing a network route.
+
+@table @asis
+@item @code{destination}
+The route destination (a string), either an IP address or
+@code{"default"} to denote the default route.
+
+@item @code{source} (default: @code{#f})
+The route source.
+
+@item @code{device} (default: @code{#f})
+The device used for this route---e.g., @code{"eno2"}.
+
+@item @code{ipv6?} (default: auto)
+Whether this is an IPv6 route. By default this is automatically
+determined based on @code{destination} or @code{gateway}.
+
+@item @code{gateway} (default: @code{#f})
+IP address (a string) through which traffic is routed.
+@end table
+@end deftp
+
+@deftp {Data Type} network-link
+Data type for a network link (@pxref{Link,,, guile-netlink,
+Guile-Netlink Manual}).
+
+@table @code
+@item name
+The name of the link---e.g., @code{"v0p0"}.
+
+@item type
+A symbol denoting the type of the link---e.g., @code{'veth}.
+
+@item arguments
+List of arguments for this type of link.
+@end table
+@end deftp
@cindex DHCP, networking service
@defvr {Scheme Variable} dhcp-client-service-type
@@ -30300,11 +30434,18 @@ cluster node that supports multiple storage backends, and installs the
"ganeti-instance-guix" "ganeti-instance-debootstrap"))
%base-packages))
(services
- (append (list (static-networking-service "eth0" "192.168.1.201"
- #:netmask "255.255.255.0"
- #:gateway "192.168.1.254"
- #:name-servers '("192.168.1.252"
- "192.168.1.253"))
+ (append (list (service static-networking-service-type
+ (list (static-networking
+ (addresses
+ (list (network-address
+ (device "eth0")
+ (value "192.168.1.201/24"))))
+ (routes
+ (list (network-route
+ (destination "default")
+ (gateway "192.168.1.254"))))
+ (name-servers '("192.168.1.252"
+ "192.168.1.253")))))
;; Ganeti uses SSH to communicate between nodes.
(service openssh-service-type
diff --git a/gnu/build/hurd-boot.scm b/gnu/build/hurd-boot.scm
index 8b27995438..ac36bd17d4 100644
--- a/gnu/build/hurd-boot.scm
+++ b/gnu/build/hurd-boot.scm
@@ -185,13 +185,9 @@ (define servers
("servers/crash-suspend" ("/hurd/crash" "--suspend"))
("servers/password" ("/hurd/password"))
("servers/socket/1" ("/hurd/pflocal"))
- ("servers/socket/2" ("/hurd/pfinet"
- "--interface" "eth0"
- "--address"
- "10.0.2.15" ;the default QEMU guest IP
- "--netmask" "255.255.255.0"
- "--gateway" "10.0.2.2"
- "--ipv6" "/servers/socket/26"))
+ ;; /servers/socket/2 and /26 are created by 'static-networking-service'.
+ ;; XXX: Spawn pfinet without arguments on these nodes so that a DHCP
+ ;; client has someone to talk to?
("proc" ("/hurd/procfs" "--stat-mode=444"))))
(define devices
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index d5ee03bbbd..03f6b388a8 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -35,6 +35,8 @@
(define-module (gnu services base)
#:use-module (guix store)
#:use-module (guix deprecation)
+ #:autoload (guix diagnostics) (warning)
+ #:autoload (guix i18n) (G_)
#:use-module (gnu services)
#:use-module (gnu services admin)
#:use-module (gnu services shepherd)
@@ -54,6 +56,7 @@ (define-module (gnu services base)
#:use-module ((gnu packages base)
#:select (coreutils glibc glibc-utf8-locales))
#:autoload (gnu packages guile-xyz) (guile-netlink)
+ #:autoload (gnu packages hurd) (hurd)
#:use-module (gnu packages package-management)
#:use-module ((gnu packages gnupg) #:select (guile-gcrypt))
#:use-module (gnu packages linux)
@@ -81,14 +84,32 @@ (define-module (gnu services base)
virtual-terminal-service-type
static-networking
-
static-networking?
- static-networking-interface
- static-networking-ip
- static-networking-netmask
- static-networking-gateway
+ static-networking-addresses
+ static-networking-links
+ static-networking-routes
static-networking-requirement
+ network-address
+ network-address?
+ network-address-device
+ network-address-value
+ network-address-ipv6?
+
+ network-link
+ network-link?
+ network-link-name
+ network-link-type
+ network-link-arguments
+
+ network-route
+ network-route?
+ network-route-destination
+ network-route-source
+ network-route-device
+ network-route-ipv6?
+ network-route-gateway
+
static-networking-service
static-networking-service-type
@@ -2316,113 +2337,254 @@ (define kmscon-command
(description "Start the @command{kmscon} virtual terminal emulator for the
Linux @dfn{kernel mode setting} (KMS).")))
+
+;;;
+;;; Static networking.
+;;;
+
+(define (ipv6-address? str)
+ "Return true if STR denotes an IPv6 address."
+ (false-if-exception (->bool (inet-pton AF_INET6 str))))
+
(define-record-type* <static-networking>
static-networking make-static-networking
static-networking?
- (interface static-networking-interface)
- (ip static-networking-ip)
- (netmask static-networking-netmask
- (default #f))
- (gateway static-networking-gateway ;FIXME: doesn't belong here
- (default #f))
+ (addresses static-networking-addresses) ;list of <network-address>
+ (links static-networking-links (default '())) ;list of <network-link>
+ (routes static-networking-routes (default '())) ;list of <network-routes>
(provision static-networking-provision
(default #f))
(requirement static-networking-requirement
- (default '()))
+ (default '(udev)))
(name-servers static-networking-name-servers ;FIXME: doesn't belong here
(default '())))
-(define static-networking-shepherd-service
+(define-record-type* <network-address>
+ network-address make-network-address
+ network-address?
+ (device network-address-device) ;string--e.g., "en01"
+ (value network-address-value) ;string--CIDR notation
+ (ipv6? network-address-ipv6? ;Boolean
+ (thunked)
+ (default
+ (ipv6-address? (cidr->ip (network-address-value this-record))))))
+
+(define-record-type* <network-link>
+ network-link make-network-link
+ network-link?
+ (name network-link-name) ;string--e.g, "v0p0"
+ (type network-link-type) ;symbol--e.g.,'veth
+ (arguments network-link-arguments)) ;list
+
+(define-record-type* <network-route>
+ network-route make-network-route
+ network-route?
+ (destination network-route-destination)
+ (source network-route-source (default #f))
+ (device network-route-device (default #f))
+ (ipv6? network-route-ipv6? (thunked)
+ (default
+ (or (ipv6-address? (network-route-destination this-record))
+ (and=> (network-route-gateway this-record)
+ ipv6-address?))))
+ (gateway network-route-gateway (default #f)))
+
+(define* (cidr->netmask str #:optional (family AF_INET))
+ "Given @var{str}, a string in CIDR notation (e.g., \"1.2.3.4/24\"), return
+the netmask as a string like \"255.255.255.0\"."
+ (match (string-split str #\/)
+ ((ip (= string->number bits))
+ (let ((mask (ash (- (expt 2 bits) 1)
+ (- (if (= family AF_INET6) 128 32)
+ bits))))
+ (inet-ntop family mask)))
+ (_ #f)))
+
+(define (cidr->ip str)
+ "Strip the netmask bit of @var{str}, a CIDR-notation IP/netmask address."
+ (match (string-split str #\/)
+ ((or (ip _) (ip))
+ ip)))
+
+(define* (ip+netmask->cidr ip netmask #:optional (family AF_INET))
+ "Return the CIDR notation (a string) for @var{ip} and @var{netmask}, two
+@var{family} address strings, where @var{family} is @code{AF_INET} or
+@code{AF_INET6}."
+ (let* ((netmask (inet-pton family netmask))
+ (bits (logcount netmask)))
+ (string-append ip "/" (number->string bits))))
+
+(define (static-networking->hurd-pfinet-options config)
+ "Return command-line options for the Hurd's pfinet translator corresponding
+to CONFIG."
+ (unless (null? (static-networking-links config))
+ ;; XXX: Presumably this is not supported, or perhaps could be approximated
+ ;; by running separate pfinet instances in some cases?
+ (warning (G_ "network links are currently ignored on GNU/Hurd~%")))
+
+ (match (static-networking-addresses config)
+ ((and addresses (first _ ...))
+ `("--ipv6" "/servers/socket/26"
+ "--interface" ,(network-address-device first)
+ ,@(append-map (lambda (address)
+ `(,(if (network-address-ipv6? address)
+ "--address6"
+ "--address")
+ ,(cidr->ip (network-address-value address))
+ ,@(match (cidr->netmask (network-address-value address)
+ (if (network-address-ipv6? address)
+ AF_INET6
+ AF_INET))
+ (#f '())
+ (mask (list "--netmask" mask)))))
+ addresses)
+ ,@(append-map (lambda (route)
+ (match route
+ (($ <network-route> "default" #f device _ gateway)
+ (if (network-route-ipv6? route)
+ `("--gateway6" ,gateway)
+ `("--gateway" ,gateway)))
+ (($ <network-route> destination)
+ (warning (G_ "ignoring network route for '~a'~%")
+ destination)
+ '())))
+ (static-networking-routes config))))))
+
+(define (network-set-up/hurd config)
+ "Set up networking for the Hurd."
+ ;; The Hurd implements SIOCGIFADDR and other old-style ioctls, but the only
+ ;; way to set up IPv6 is by starting pfinet with the right options.
+ (if (equal? (static-networking-provision config) '(loopback))
+ (scheme-file "set-up-pflocal" #~(begin 'nothing-to-do! #t))
+ (scheme-file "set-up-pfinet"
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 format))
+
+ ;; TODO: Do that without forking.
+ (let ((options '#$(static-networking->hurd-pfinet-options
+ config)))
+ (format #t "starting '~a~{ ~s~}'~%"
+ #$(file-append hurd "/hurd/pfinet")
+ options)
+ (apply invoke #$(file-append hurd "/bin/settrans") "-fac"
+ "/servers/socket/2"
+ #$(file-append hurd "/hurd/pfinet")
+ options)))))))
+
+(define (network-tear-down/hurd config)
+ (scheme-file "tear-down-pfinet"
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build util