1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017 Thomas Danckaert <post@thomasdanckaert.be>
3 ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
4 ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
6 ;;; This file is part of GNU Guix.
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21 (define-module (gnu tests networking)
22 #:use-module (gnu tests)
23 #:use-module (gnu system)
24 #:use-module (gnu system vm)
25 #:use-module (gnu services)
26 #:use-module (gnu services base)
27 #:use-module (gnu services networking)
28 #:use-module (guix gexp)
29 #:use-module (guix store)
30 #:use-module (guix monads)
31 #:use-module (gnu packages bash)
32 #:use-module (gnu packages networking)
33 #:use-module (gnu services shepherd)
34 #:export (%test-inetd %test-openvswitch %test-dhcpd %test-tor))
37 ;; Operating system with 2 inetd services.
38 (simple-operating-system
40 (service inetd-service-type
55 (program (file-append bash
58 (list "bash" (plain-file "my-dict.sh" "\
61 if [[ $line =~ ^DEFINE\\ (.*)$ ]]
63 case ${BASH_REMATCH[1]} in
65 echo GNU Guix is a package management tool for the GNU system.
68 echo Like an S-expression but with a G.
71 echo NO DEFINITION FOUND
79 (define* (run-inetd-test)
80 "Run tests in %INETD-OS, where the inetd service provides an echo service on
81 port 7, and a dict service on port 2628."
83 (marionette-operating-system %inetd-os))
88 (port-forwardings `((8007 . 7)
92 (with-imported-modules '((gnu build marionette))
94 (use-modules (ice-9 rdelim)
96 (gnu build marionette))
98 (make-marionette (list #$vm)))
105 ;; Make sure the PID file is created.
106 (test-assert "PID file"
108 '(file-exists? "/var/run/inetd.pid")
111 ;; Test the echo service.
112 (test-equal "echo response"
114 (let ((echo (socket PF_INET SOCK_STREAM 0))
115 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8007)))
117 (display "Hello, Guix!\n" echo)
118 (let ((response (read-line echo)))
122 ;; Test the dict service
123 (test-equal "dict response"
124 "GNU Guix is a package management tool for the GNU system."
125 (let ((dict (socket PF_INET SOCK_STREAM 0))
126 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8628)))
128 (display "DEFINE Guix\n" dict)
129 (let ((response (read-line dict)))
134 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
136 (gexp->derivation "inetd-test" test))
141 (description "Connect to a host with an INETD server.")
142 (value (run-inetd-test))))
149 (define setup-openvswitch
150 #~(let ((ovs-vsctl (lambda (str)
151 (zero? (apply system*
152 #$(file-append openvswitch "/bin/ovs-vsctl")
153 (string-tokenize str)))))
154 (add-native-port (lambda (if)
155 (string-append "--may-exist add-port br0 " if
156 " vlan_mode=native-untagged"
157 " -- set Interface " if
159 (and (ovs-vsctl "--may-exist add-br br0")
160 ;; Connect eth0 as an "untagged" port (no VLANs).
161 (ovs-vsctl "--may-exist add-port br0 eth0 vlan_mode=native-untagged")
162 (ovs-vsctl (add-native-port "ovs0")))))
164 (define openvswitch-configuration-service
165 (simple-service 'openvswitch-configuration shepherd-root-service-type
166 (list (shepherd-service
167 (provision '(openvswitch-configuration))
168 (requirement '(vswitchd))
170 #$setup-openvswitch))
173 (define %openvswitch-os
174 (simple-operating-system
175 (static-networking-service "ovs0" "10.1.1.1"
176 #:netmask "255.255.255.252"
177 #:requirement '(openvswitch-configuration))
178 (service openvswitch-service-type
179 (openvswitch-configuration
180 (package openvswitch)))
181 openvswitch-configuration-service))
183 (define (run-openvswitch-test)
185 (marionette-operating-system %openvswitch-os
186 #:imported-modules '((gnu services herd))))
189 (with-imported-modules '((gnu build marionette))
191 (use-modules (gnu build marionette)
197 (make-marionette (list #$(virtual-machine os))))
202 (test-begin "openvswitch")
204 ;; Make sure the bridge is created.
205 (test-assert "br0 exists"
207 '(zero? (system* "ovs-vsctl" "br-exists" "br0"))
210 ;; Make sure eth0 is connected to the bridge.
211 (test-equal "eth0 is connected to br0"
215 (use-modules (ice-9 popen) (ice-9 rdelim))
216 (let* ((port (open-pipe*
218 (string-append #$openvswitch "/bin/ovs-vsctl")
219 "port-to-br" "eth0"))
220 (output (read-line port)))
225 ;; Make sure the virtual interface got a static IP.
226 (test-assert "networking has started on ovs0"
229 (use-modules (gnu services herd)
231 (live-service-running
233 (memq 'networking-ovs0
234 (live-service-provision live)))
235 (current-services))))
239 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
241 (gexp->derivation "openvswitch-test" test))
243 (define %test-openvswitch
246 (description "Test a running OpenvSwitch configuration.")
247 (value (run-openvswitch-test))))
254 (define minimal-dhcpd-v4-config-file
255 (plain-file "dhcpd.conf"
257 default-lease-time 600;
260 subnet 192.168.1.0 netmask 255.255.255.0 {
261 range 192.168.1.100 192.168.1.200;
262 option routers 192.168.1.1;
263 option domain-name-servers 192.168.1.2, 192.168.1.3;
264 option domain-name \"dummy.domain.name.abc123xyz\";
268 (define dhcpd-v4-configuration
270 (config-file minimal-dhcpd-v4-config-file)
272 (interfaces '("eth0"))))
275 (simple-operating-system
276 (static-networking-service "eth0" "192.168.1.4"
277 #:netmask "255.255.255.0"
278 #:gateway "192.168.1.1"
279 #:name-servers '("192.168.1.2" "192.168.1.3"))
280 (service dhcpd-service-type dhcpd-v4-configuration)))
282 (define (run-dhcpd-test)
284 (marionette-operating-system %dhcpd-os
285 #:imported-modules '((gnu services herd))))
288 (with-imported-modules '((gnu build marionette))
290 (use-modules (gnu build marionette)
296 (make-marionette (list #$(virtual-machine os))))
303 (test-assert "pid file exists"
306 #$(dhcpd-configuration-pid-file dhcpd-v4-configuration))
309 (test-assert "lease file exists"
312 #$(dhcpd-configuration-lease-file dhcpd-v4-configuration))
315 (test-assert "run directory exists"
318 #$(dhcpd-configuration-run-directory dhcpd-v4-configuration))
321 (test-assert "dhcpd is alive"
324 (use-modules (gnu services herd)
326 (live-service-running
329 (live-service-provision live)))
330 (current-services))))
334 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
336 (gexp->derivation "dhcpd-test" test))
341 (description "Test a running DHCP daemon configuration.")
342 (value (run-dhcpd-test))))
346 ;;; Services related to Tor
350 (simple-operating-system
353 (define %tor-os/unix-socks-socket
354 (simple-operating-system
355 (service tor-service-type
358 (plain-file "test-torrc"
360 SocksPort unix:/var/run/tor/socks-sock
361 UnixSocksGroupWritable 1
365 (define (run-tor-test)
367 (marionette-operating-system %tor-os
368 #:imported-modules '((gnu services herd))
369 #:requirements '(tor)))
371 (define os/unix-socks-socket
372 (marionette-operating-system %tor-os/unix-socks-socket
373 #:imported-modules '((gnu services herd))
374 #:requirements '(tor)))
377 (with-imported-modules '((gnu build marionette))
379 (use-modules (gnu build marionette)
385 (make-marionette (list #$(virtual-machine os))))
387 (define (tor-is-alive? marionette)
390 (use-modules (gnu services herd)
392 (live-service-running
395 (live-service-provision live)))
396 (current-services))))
404 ;; Test the usual Tor service.
406 (test-assert "tor is alive"
407 (tor-is-alive? marionette))
409 (test-assert "tor is listening"
410 (let ((default-port 9050))
411 (wait-for-tcp-port default-port marionette)))
413 ;; Don't run two VMs at once.
414 (marionette-control "quit" marionette)
416 ;; Test the Tor service using a SOCKS socket.
418 (let* ((socket-directory "/tmp/more-sockets")
419 (_ (mkdir socket-directory))
420 (marionette/unix-socks-socket
422 (list #$(virtual-machine os/unix-socks-socket))
423 ;; We can't use the same socket directory as the first
425 #:socket-directory socket-directory)))
426 (test-assert "tor is alive, even when using a SOCKS socket"
427 (tor-is-alive? marionette/unix-socks-socket))
429 (test-assert "tor is listening, even when using a SOCKS socket"
430 (wait-for-unix-socket "/var/run/tor/socks-sock"
431 marionette/unix-socks-socket)))
434 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
436 (gexp->derivation "tor-test" test))
441 (description "Test a running Tor daemon configuration.")
442 (value (run-tor-test))))