services: hurd-vm: Resurrect system-test by using raw disk-image.
[jackhill/guix/guix.git] / gnu / tests / networking.scm
CommitLineData
9260b9d1
TD
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 Thomas Danckaert <post@thomasdanckaert.be>
54461153 3;;; Copyright © 2017, 2020 Marius Bakke <marius@gnu.org>
b0f951e4 4;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
9926b8f8 5;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net>
9260b9d1
TD
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
22(define-module (gnu tests networking)
23 #:use-module (gnu tests)
24 #:use-module (gnu system)
9260b9d1
TD
25 #:use-module (gnu system vm)
26 #:use-module (gnu services)
d94e81db 27 #:use-module (gnu services base)
9260b9d1
TD
28 #:use-module (gnu services networking)
29 #:use-module (guix gexp)
30 #:use-module (guix store)
31 #:use-module (guix monads)
32 #:use-module (gnu packages bash)
9926b8f8 33 #:use-module (gnu packages linux)
671dbdb9
MB
34 #:use-module (gnu packages networking)
35 #:use-module (gnu services shepherd)
9926b8f8
AI
36 #:use-module (ice-9 match)
37 #:export (%test-inetd %test-openvswitch %test-dhcpd %test-tor %test-iptables))
9260b9d1
TD
38
39(define %inetd-os
40 ;; Operating system with 2 inetd services.
892d9089 41 (simple-operating-system
39d7fdce 42 (service dhcp-client-service-type)
892d9089
LC
43 (service inetd-service-type
44 (inetd-configuration
45 (entries (list
46 (inetd-entry
47 (name "echo")
48 (socket-type 'stream)
49 (protocol "tcp")
50 (wait? #f)
51 (user "root"))
52 (inetd-entry
53 (name "dict")
54 (socket-type 'stream)
55 (protocol "tcp")
56 (wait? #f)
57 (user "root")
58 (program (file-append bash
59 "/bin/bash"))
60 (arguments
61 (list "bash" (plain-file "my-dict.sh" "\
9260b9d1
TD
62while read line
63do
64 if [[ $line =~ ^DEFINE\\ (.*)$ ]]
65 then
66 case ${BASH_REMATCH[1]} in
67 Guix)
68 echo GNU Guix is a package management tool for the GNU system.
69 ;;
70 G-expression)
71 echo Like an S-expression but with a G.
72 ;;
73 *)
74 echo NO DEFINITION FOUND
75 ;;
76 esac
77 else
78 echo ERROR
79 fi
892d9089 80done" ))))))))))
9260b9d1
TD
81
82(define* (run-inetd-test)
83 "Run tests in %INETD-OS, where the inetd service provides an echo service on
84port 7, and a dict service on port 2628."
8b113790
LC
85 (define os
86 (marionette-operating-system %inetd-os))
9260b9d1 87
8b113790
LC
88 (define vm
89 (virtual-machine
90 (operating-system os)
91 (port-forwardings `((8007 . 7)
92 (8628 . 2628)))))
9260b9d1 93
8b113790
LC
94 (define test
95 (with-imported-modules '((gnu build marionette))
96 #~(begin
97 (use-modules (ice-9 rdelim)
98 (srfi srfi-64)
99 (gnu build marionette))
100 (define marionette
101 (make-marionette (list #$vm)))
9260b9d1 102
8b113790
LC
103 (mkdir #$output)
104 (chdir #$output)
9260b9d1 105
8b113790 106 (test-begin "inetd")
9260b9d1 107
8b113790
LC
108 ;; Make sure the PID file is created.
109 (test-assert "PID file"
110 (marionette-eval
111 '(file-exists? "/var/run/inetd.pid")
112 marionette))
9260b9d1 113
8b113790
LC
114 ;; Test the echo service.
115 (test-equal "echo response"
116 "Hello, Guix!"
117 (let ((echo (socket PF_INET SOCK_STREAM 0))
118 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8007)))
119 (connect echo addr)
120 (display "Hello, Guix!\n" echo)
121 (let ((response (read-line echo)))
122 (close echo)
123 response)))
9260b9d1 124
8b113790
LC
125 ;; Test the dict service
126 (test-equal "dict response"
127 "GNU Guix is a package management tool for the GNU system."
128 (let ((dict (socket PF_INET SOCK_STREAM 0))
129 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8628)))
130 (connect dict addr)
131 (display "DEFINE Guix\n" dict)
132 (let ((response (read-line dict)))
133 (close dict)
134 response)))
135
136 (test-end)
137 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
138
139 (gexp->derivation "inetd-test" test))
9260b9d1
TD
140
141(define %test-inetd
142 (system-test
143 (name "inetd")
144 (description "Connect to a host with an INETD server.")
145 (value (run-inetd-test))))
671dbdb9
MB
146
147\f
148;;;
149;;; Open vSwitch
150;;;
151
152(define setup-openvswitch
153 #~(let ((ovs-vsctl (lambda (str)
154 (zero? (apply system*
155 #$(file-append openvswitch "/bin/ovs-vsctl")
156 (string-tokenize str)))))
157 (add-native-port (lambda (if)
158 (string-append "--may-exist add-port br0 " if
159 " vlan_mode=native-untagged"
160 " -- set Interface " if
161 " type=internal"))))
162 (and (ovs-vsctl "--may-exist add-br br0")
163 ;; Connect eth0 as an "untagged" port (no VLANs).
164 (ovs-vsctl "--may-exist add-port br0 eth0 vlan_mode=native-untagged")
165 (ovs-vsctl (add-native-port "ovs0")))))
166
167(define openvswitch-configuration-service
168 (simple-service 'openvswitch-configuration shepherd-root-service-type
169 (list (shepherd-service
170 (provision '(openvswitch-configuration))
171 (requirement '(vswitchd))
172 (start #~(lambda ()
173 #$setup-openvswitch))
174 (respawn? #f)))))
175
176(define %openvswitch-os
54461153
MB
177 (operating-system
178 (inherit (simple-operating-system
179 (static-networking-service "ovs0" "10.1.1.1"
180 #:netmask "255.255.255.252"
181 #:requirement '(openvswitch-configuration))
182 (service openvswitch-service-type)
183 openvswitch-configuration-service))
184 ;; Ensure the interface name does not change depending on the driver.
185 (kernel-arguments (cons "net.ifnames=0" %default-kernel-arguments))))
671dbdb9
MB
186
187(define (run-openvswitch-test)
188 (define os
189 (marionette-operating-system %openvswitch-os
190 #:imported-modules '((gnu services herd))))
191
192 (define test
193 (with-imported-modules '((gnu build marionette))
194 #~(begin
195 (use-modules (gnu build marionette)
196 (ice-9 popen)
197 (ice-9 rdelim)
198 (srfi srfi-64))
199
200 (define marionette
201 (make-marionette (list #$(virtual-machine os))))
202
203 (mkdir #$output)
204 (chdir #$output)
205
206 (test-begin "openvswitch")
207
208 ;; Make sure the bridge is created.
209 (test-assert "br0 exists"
210 (marionette-eval
6720616d
MB
211 '(zero? (system* #$(file-append openvswitch "/bin/ovs-vsctl")
212 "br-exists" "br0"))
671dbdb9
MB
213 marionette))
214
215 ;; Make sure eth0 is connected to the bridge.
216 (test-equal "eth0 is connected to br0"
217 "br0"
218 (marionette-eval
219 '(begin
220 (use-modules (ice-9 popen) (ice-9 rdelim))
221 (let* ((port (open-pipe*
222 OPEN_READ
223 (string-append #$openvswitch "/bin/ovs-vsctl")
224 "port-to-br" "eth0"))
225 (output (read-line port)))
226 (close-pipe port)
227 output))
228 marionette))
229
230 ;; Make sure the virtual interface got a static IP.
231 (test-assert "networking has started on ovs0"
232 (marionette-eval
233 '(begin
234 (use-modules (gnu services herd)
235 (srfi srfi-1))
236 (live-service-running
237 (find (lambda (live)
238 (memq 'networking-ovs0
239 (live-service-provision live)))
240 (current-services))))
241 marionette))
242
243 (test-end)
244 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
245
246 (gexp->derivation "openvswitch-test" test))
247
248(define %test-openvswitch
249 (system-test
250 (name "openvswitch")
251 (description "Test a running OpenvSwitch configuration.")
252 (value (run-openvswitch-test))))
f1104d90
CM
253
254\f
255;;;
256;;; DHCP Daemon
257;;;
258
259(define minimal-dhcpd-v4-config-file
260 (plain-file "dhcpd.conf"
261 "\
262default-lease-time 600;
263max-lease-time 7200;
264
265subnet 192.168.1.0 netmask 255.255.255.0 {
266 range 192.168.1.100 192.168.1.200;
267 option routers 192.168.1.1;
268 option domain-name-servers 192.168.1.2, 192.168.1.3;
269 option domain-name \"dummy.domain.name.abc123xyz\";
270}
271"))
272
273(define dhcpd-v4-configuration
274 (dhcpd-configuration
275 (config-file minimal-dhcpd-v4-config-file)
276 (version "4")
ef0f5ff2 277 (interfaces '("ens3"))))
f1104d90
CM
278
279(define %dhcpd-os
280 (simple-operating-system
ef0f5ff2 281 (static-networking-service "ens3" "192.168.1.4"
f1104d90
CM
282 #:netmask "255.255.255.0"
283 #:gateway "192.168.1.1"
284 #:name-servers '("192.168.1.2" "192.168.1.3"))
285 (service dhcpd-service-type dhcpd-v4-configuration)))
286
287(define (run-dhcpd-test)
288 (define os
289 (marionette-operating-system %dhcpd-os
290 #:imported-modules '((gnu services herd))))
291
292 (define test
293 (with-imported-modules '((gnu build marionette))
294 #~(begin
295 (use-modules (gnu build marionette)
296 (ice-9 popen)
297 (ice-9 rdelim)
298 (srfi srfi-64))
299
300 (define marionette
301 (make-marionette (list #$(virtual-machine os))))
302
303 (mkdir #$output)
304 (chdir #$output)
305
306 (test-begin "dhcpd")
307
308 (test-assert "pid file exists"
309 (marionette-eval
310 '(file-exists?
311 #$(dhcpd-configuration-pid-file dhcpd-v4-configuration))
312 marionette))
313
314 (test-assert "lease file exists"
315 (marionette-eval
316 '(file-exists?
317 #$(dhcpd-configuration-lease-file dhcpd-v4-configuration))
318 marionette))
319
320 (test-assert "run directory exists"
321 (marionette-eval
322 '(file-exists?
323 #$(dhcpd-configuration-run-directory dhcpd-v4-configuration))
324 marionette))
325
326 (test-assert "dhcpd is alive"
327 (marionette-eval
328 '(begin
329 (use-modules (gnu services herd)
330 (srfi srfi-1))
331 (live-service-running
332 (find (lambda (live)
333 (memq 'dhcpv4-daemon
334 (live-service-provision live)))
335 (current-services))))
336 marionette))
337
338 (test-end)
339 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
340
341 (gexp->derivation "dhcpd-test" test))
342
343(define %test-dhcpd
344 (system-test
345 (name "dhcpd")
346 (description "Test a running DHCP daemon configuration.")
347 (value (run-dhcpd-test))))
5dfd80e1
CM
348
349\f
350;;;
351;;; Services related to Tor
352;;;
353
354(define %tor-os
355 (simple-operating-system
84a2de36 356 (service tor-service-type)))
5dfd80e1 357
b0f951e4
CM
358(define %tor-os/unix-socks-socket
359 (simple-operating-system
360 (service tor-service-type
361 (tor-configuration
3bcb305b 362 (socks-socket-type 'unix)))))
b0f951e4 363
5dfd80e1
CM
364(define (run-tor-test)
365 (define os
366 (marionette-operating-system %tor-os
367 #:imported-modules '((gnu services herd))
368 #:requirements '(tor)))
369
b0f951e4
CM
370 (define os/unix-socks-socket
371 (marionette-operating-system %tor-os/unix-socks-socket
372 #:imported-modules '((gnu services herd))
373 #:requirements '(tor)))
374
5dfd80e1
CM
375 (define test
376 (with-imported-modules '((gnu build marionette))
377 #~(begin
378 (use-modules (gnu build marionette)
379 (ice-9 popen)
380 (ice-9 rdelim)
381 (srfi srfi-64))
382
383 (define marionette
384 (make-marionette (list #$(virtual-machine os))))
385
b0f951e4 386 (define (tor-is-alive? marionette)
5dfd80e1
CM
387 (marionette-eval
388 '(begin
389 (use-modules (gnu services herd)
390 (srfi srfi-1))
391 (live-service-running
392 (find (lambda (live)
393 (memq 'tor
394 (live-service-provision live)))
395 (current-services))))
396 marionette))
397
b0f951e4
CM
398 (mkdir #$output)
399 (chdir #$output)
400
401 (test-begin "tor")
402
403 ;; Test the usual Tor service.
404
405 (test-assert "tor is alive"
406 (tor-is-alive? marionette))
407
408 (test-assert "tor is listening"
409 (let ((default-port 9050))
410 (wait-for-tcp-port default-port marionette)))
411
412 ;; Don't run two VMs at once.
413 (marionette-control "quit" marionette)
414
415 ;; Test the Tor service using a SOCKS socket.
416
417 (let* ((socket-directory "/tmp/more-sockets")
418 (_ (mkdir socket-directory))
419 (marionette/unix-socks-socket
420 (make-marionette
421 (list #$(virtual-machine os/unix-socks-socket))
422 ;; We can't use the same socket directory as the first
423 ;; marionette.
424 #:socket-directory socket-directory)))
425 (test-assert "tor is alive, even when using a SOCKS socket"
426 (tor-is-alive? marionette/unix-socks-socket))
427
428 (test-assert "tor is listening, even when using a SOCKS socket"
429 (wait-for-unix-socket "/var/run/tor/socks-sock"
430 marionette/unix-socks-socket)))
431
5dfd80e1
CM
432 (test-end)
433 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
434
435 (gexp->derivation "tor-test" test))
436
437(define %test-tor
438 (system-test
439 (name "tor")
440 (description "Test a running Tor daemon configuration.")
441 (value (run-tor-test))))
9926b8f8
AI
442
443(define* (run-iptables-test)
444 "Run tests of 'iptables-service-type'."
445 (define iptables-rules
446 "*filter
447:INPUT ACCEPT
448:FORWARD ACCEPT
449:OUTPUT ACCEPT
450-A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp-port-unreachable
451COMMIT
452")
453
454 (define ip6tables-rules
455 "*filter
456:INPUT ACCEPT
457:FORWARD ACCEPT
458:OUTPUT ACCEPT
459-A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp6-port-unreachable
460COMMIT
461")
462
463 (define inetd-echo-port 7)
464
465 (define os
466 (marionette-operating-system
467 (simple-operating-system
39d7fdce 468 (service dhcp-client-service-type)
9926b8f8
AI
469 (service inetd-service-type
470 (inetd-configuration
471 (entries (list
472 (inetd-entry
473 (name "echo")
474 (socket-type 'stream)
475 (protocol "tcp")
476 (wait? #f)
477 (user "root"))))))
478 (service iptables-service-type
479 (iptables-configuration
480 (ipv4-rules (plain-file "iptables.rules" iptables-rules))
481 (ipv6-rules (plain-file "ip6tables.rules" ip6tables-rules)))))
482 #:imported-modules '((gnu services herd))
483 #:requirements '(inetd iptables)))
484
485 (define test
486 (with-imported-modules '((gnu build marionette))
487 #~(begin
488 (use-modules (srfi srfi-64)
489 (gnu build marionette))
490 (define marionette
491 (make-marionette (list #$(virtual-machine os))))
492
493 (define (dump-iptables iptables-save marionette)
494 (marionette-eval
495 `(begin
496 (use-modules (ice-9 popen)
497 (ice-9 rdelim)
498 (ice-9 regex))
499 (call-with-output-string
500 (lambda (out)
501 (call-with-port
502 (open-pipe* OPEN_READ ,iptables-save)
503 (lambda (in)
504 (let loop ((line (read-line in)))
505 ;; iptables-save does not output rules in the exact
506 ;; same format we loaded using iptables-restore. It
507 ;; adds comments, packet counters, etc. We remove
508 ;; these additions.
509 (unless (eof-object? line)
510 (cond
511 ;; Remove comments
512 ((string-match "^#" line) #t)
513 ;; Remove packet counters
514 ((string-match "^:([A-Z]*) ([A-Z]*) .*" line)
515 => (lambda (match-record)
516 (format out ":~a ~a~%"
517 (match:substring match-record 1)
518 (match:substring match-record 2))))
519 ;; Pass other lines without modification
520 (else (display line out)
521 (newline out)))
522 (loop (read-line in)))))))))
523 marionette))
524
525 (mkdir #$output)
526 (chdir #$output)
527
528 (test-begin "iptables")
529
530 (test-equal "iptables-save dumps the same rules that were loaded"
531 (dump-iptables #$(file-append iptables "/sbin/iptables-save")
532 marionette)
533 #$iptables-rules)
534
535 (test-equal "ip6tables-save dumps the same rules that were loaded"
536 (dump-iptables #$(file-append iptables "/sbin/ip6tables-save")
537 marionette)
538 #$ip6tables-rules)
539
540 (test-error "iptables firewall blocks access to inetd echo service"
541 'misc-error
542 (wait-for-tcp-port inetd-echo-port marionette #:timeout 5))
543
544 ;; TODO: This test freezes up at the login prompt without any
545 ;; relevant messages on the console. Perhaps it is waiting for some
546 ;; timeout. Find and fix this issue.
547 ;; (test-assert "inetd echo service is accessible after iptables firewall is stopped"
548 ;; (begin
549 ;; (marionette-eval
550 ;; '(begin
551 ;; (use-modules (gnu services herd))
552 ;; (stop-service 'iptables))
553 ;; marionette)
554 ;; (wait-for-tcp-port inetd-echo-port marionette #:timeout 5)))
555
556 (test-end)
557 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
558
559 (gexp->derivation "iptables" test))
560
561(define %test-iptables
562 (system-test
563 (name "iptables")
564 (description "Test a running iptables daemon.")
565 (value (run-iptables-test))))