projects/guix·blob java-team · refs

1427 lines · 47.8 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 Thomas Danckaert <[email protected]>
3;;; Copyright © 2017, 2020 Marius Bakke <[email protected]>
4;;; Copyright © 2018 Chris Marusich <[email protected]>
5;;; Copyright © 2018 Arun Isaac <[email protected]>
6;;; Copyright © 2021 Maxime Devos <[email protected]>
7;;; Copyright © 2021, 2023-2024 Ludovic Courtès <[email protected]>
8;;; Copyright © 2025 Maxim Cournoyer <[email protected]>
9;;;
10;;; This file is part of GNU Guix.
11;;;
12;;; GNU Guix is free software; you can redistribute it and/or modify it
13;;; under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 3 of the License, or (at
15;;; your option) any later version.
16;;;
17;;; GNU Guix is distributed in the hope that it will be useful, but
18;;; WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
24
25(define-module (gnu tests networking)
26 #:use-module (gnu tests)
27 #:use-module (gnu system)
28 #:use-module (gnu system vm)
29 #:use-module (gnu services)
30 #:use-module (gnu services base)
31 #:use-module (gnu services dns)
32 #:use-module (gnu services networking)
33 #:use-module (gnu services ssh)
34 #:use-module (guix gexp)
35 #:use-module (guix store)
36 #:use-module (guix monads)
37 #:use-module (guix modules)
38 #:use-module (gnu packages admin)
39 #:use-module (gnu packages bash)
40 #:use-module (gnu packages dns)
41 #:use-module (gnu packages linux)
42 #:use-module (gnu packages networking)
43 #:use-module (gnu packages guile)
44 #:use-module (gnu services shepherd)
45 #:use-module (ice-9 match)
46 #:export (%test-static-networking
47 %test-static-networking-failure
48 %test-static-networking-advanced
49 %test-network-manager
50 %test-network-manager-dnsmasq
51 %test-inetd
52 %test-openvswitch
53 %test-dhcpd
54 %test-dhcpcd
55 %test-dnsmasq
56 %test-tor
57 %test-iptables
58 %test-nftables
59 %test-ipfs))
60
61
62;;;
63;;; Static networking.
64;;;
65
66(define (run-static-networking-test vm)
67 (define test
68 (with-imported-modules '((gnu build marionette)
69 (guix build syscalls))
70 #~(begin
71 (use-modules (gnu build marionette)
72 (guix build syscalls)
73 (srfi srfi-64))
74
75 (define marionette
76 (make-marionette
77 '(#$vm "-nic" "user,model=virtio-net-pci")))
78
79 (test-runner-current (system-test-runner #$output))
80 (test-begin "static-networking")
81
82 (test-assert "service is up"
83 (marionette-eval
84 '(begin
85 (use-modules (gnu services herd))
86 (start-service 'networking))
87 marionette))
88
89 (test-assert "network interfaces"
90 (marionette-eval
91 '(begin
92 (use-modules (guix build syscalls))
93 (network-interface-names))
94 marionette))
95
96 (test-equal "address of eth0"
97 "10.0.2.15"
98 (marionette-eval
99 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
100 (addr (network-interface-address sock "eth0")))
101 (close-port sock)
102 (inet-ntop (sockaddr:fam addr) (sockaddr:addr addr)))
103 marionette))
104
105 (test-equal "netmask of eth0"
106 "255.255.255.0"
107 (marionette-eval
108 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
109 (mask (network-interface-netmask sock "eth0")))
110 (close-port sock)
111 (inet-ntop (sockaddr:fam mask) (sockaddr:addr mask)))
112 marionette))
113
114 (test-equal "eth0 is up"
115 IFF_UP
116 (marionette-eval
117 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
118 (flags (network-interface-flags sock "eth0")))
119 (logand flags IFF_UP))
120 marionette))
121
122 (test-end))))
123
124 (gexp->derivation "static-networking" test))
125
126(define %test-static-networking
127 (system-test
128 (name "static-networking")
129 (description "Test the 'static-networking' service.")
130 (value
131 (let ((os (marionette-operating-system
132 (simple-operating-system
133 (service static-networking-service-type
134 (list %qemu-static-networking)))
135 #:imported-modules '((gnu services herd)
136 (guix combinators)))))
137 (run-static-networking-test (virtual-machine os))))))
138
139
140(define %static-networking-with-nonexistent-device
141 ;; Similar to %QEMU-STATIC-NETWORKING except that the device does not exist.
142 (static-networking
143 (addresses (list (network-address
144 (device "does-not-exist") ;<- really
145 (value "10.0.2.15/24"))))
146 (routes (list (network-route
147 (destination "default")
148 (gateway "10.0.2.2"))))
149 (requirement '())
150 (provision '(networking))
151 (name-servers '("10.0.2.3"))))
152
153(define (run-static-networking-failure-test vm)
154 (define test
155 (with-imported-modules '((gnu build marionette)
156 (guix build syscalls))
157 #~(begin
158 (use-modules (gnu build marionette)
159 (guix build syscalls)
160 (srfi srfi-64))
161
162 (define marionette
163 (make-marionette '(#$vm)))
164
165 (test-runner-current (system-test-runner #$output))
166 (test-begin "static-networking")
167
168 (test-equal "service fails to start"
169 #f
170 ;; The 'start' method of the 'networking' service should fail
171 ;; within a minute or so. Previously it would never complete:
172 ;; <https://issues.guix.gnu.org/71173>.
173 (marionette-eval
174 '(begin
175 (use-modules (gnu services herd))
176 (alarm 180) ;must complete in a timely fashion
177 (start-service 'networking))
178 marionette))
179
180 (test-equal "network interfaces"
181 '("lo")
182 (marionette-eval
183 '(begin
184 (use-modules (guix build syscalls))
185 (network-interface-names))
186 marionette))
187
188 (test-end))))
189
190 (gexp->derivation "static-networking-failure" test))
191
192(define %test-static-networking-failure
193 (system-test
194 (name "static-networking-failure")
195 (description "Test the behavior of the 'static-networking' service when
196passed an invalid device.")
197 (value
198 (let ((os (marionette-operating-system
199 (simple-operating-system
200 (service static-networking-service-type
201 (list %static-networking-with-nonexistent-device)))
202 #:imported-modules '((gnu services herd)
203 (guix combinators)))))
204 (run-static-networking-failure-test (virtual-machine os))))))
205
206
207(define (run-static-networking-advanced-test vm)
208 (define test
209 (with-imported-modules '((gnu build marionette)
210 (guix build syscalls))
211 #~(begin
212 (use-modules (gnu build marionette)
213 (guix build syscalls)
214 (srfi srfi-64))
215
216 (define marionette
217 (make-marionette
218 '(#$vm "-net" "nic,model=e1000,macaddr=98:11:22:33:44:55"
219 "-net" "nic,model=e1000,macaddr=98:11:22:33:44:56")))
220
221 (test-runner-current (system-test-runner #$output))
222 (test-begin "static-networking-advanced")
223
224 (test-assert "service is up"
225 (marionette-eval
226 '(begin
227 (use-modules (gnu services herd))
228 (start-service 'networking))
229 marionette))
230
231 (test-assert "network interfaces"
232 (marionette-eval
233 '(begin
234 (use-modules (guix build syscalls))
235 (network-interface-names))
236 marionette))
237
238 (test-equal "bond0 bonding mode"
239 "802.3ad 4"
240 (marionette-eval
241 '(begin
242 (use-modules (ice-9 rdelim))
243 (call-with-input-file "/sys/class/net/bond0/bonding/mode" read-line))
244 marionette))
245
246 (test-equal "bond0 bonding lacp_rate"
247 "fast 1"
248 (marionette-eval
249 '(begin
250 (use-modules (ice-9 rdelim))
251 (call-with-input-file "/sys/class/net/bond0/bonding/lacp_rate" read-line))
252 marionette))
253
254 (test-equal "bond0 bonding miimon"
255 "100"
256 (marionette-eval
257 '(begin
258 (use-modules (ice-9 rdelim))
259 (call-with-input-file "/sys/class/net/bond0/bonding/miimon" read-line))
260 marionette))
261
262 (test-equal "bond0 bonding slaves"
263 "a b"
264 (marionette-eval
265 '(begin
266 (use-modules (ice-9 rdelim))
267 (call-with-input-file "/sys/class/net/bond0/bonding/slaves" read-line))
268 marionette))
269
270 ;; The hw mac address will come from the first slave bonded to the
271 ;; channel.
272 (test-equal "bond0 mac address"
273 "98:11:22:33:44:55"
274 (marionette-eval
275 '(begin
276 (use-modules (ice-9 rdelim))
277 (call-with-input-file "/sys/class/net/bond0/address" read-line))
278 marionette))
279
280 (test-equal "bond0.1055 is up"
281 IFF_UP
282 (marionette-eval
283 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
284 (flags (network-interface-flags sock "bond0.1055")))
285 (logand flags IFF_UP))
286 marionette))
287
288 (test-equal "bond0.1055 address is correct"
289 "192.168.1.4"
290 (marionette-eval
291 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
292 (addr (network-interface-address sock "bond0.1055")))
293 (close-port sock)
294 (inet-ntop (sockaddr:fam addr) (sockaddr:addr addr)))
295 marionette))
296
297 (test-equal "bond0.1055 netmask is correct"
298 "255.255.255.0"
299 (marionette-eval
300 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
301 (mask (network-interface-netmask sock "bond0.1055")))
302 (close-port sock)
303 (inet-ntop (sockaddr:fam mask) (sockaddr:addr mask)))
304 marionette))
305 (test-end))))
306
307 (gexp->derivation "static-networking-advanced" test))
308
309(define %test-static-networking-advanced
310 (system-test
311 (name "static-networking-advanced")
312 (description "Test the 'static-networking' service with advanced features like bonds, vlans etc...")
313 (value
314 (let ((os (marionette-operating-system
315 (simple-operating-system
316 (service static-networking-service-type
317 (list (static-networking
318 (links (list
319
320 (network-link
321 (mac-address "98:11:22:33:44:55")
322 (arguments '((name . "a"))))
323
324 (network-link
325 (mac-address "98:11:22:33:44:56")
326 (arguments '((name . "b"))))
327
328 (network-link
329 (name "bond0")
330 (type 'bond)
331 (arguments '((mode . "802.3ad")
332 (miimon . 100)
333 (lacp-active . "on")
334 (lacp-rate . "fast"))))
335
336 (network-link
337 (name "a")
338 (arguments '((master . "bond0"))))
339
340 (network-link
341 (name "b")
342 (arguments '((master . "bond0"))))
343
344 (network-link
345 (name "bond0.1055")
346 (type 'vlan)
347 (arguments '((id . 1055)
348 (link . "bond0"))))))
349
350 (addresses (list (network-address
351 (value "192.168.1.4/24")
352 (device "bond0.1055"))))))))
353 #:imported-modules '((gnu services herd)
354 (guix combinators)))))
355 (run-static-networking-advanced-test (virtual-machine os))))))
356
357
358;;;
359;;; NetworkManager.
360;;;
361
362(define (run-network-manager-test vm)
363 (define test
364 (with-imported-modules '((gnu build marionette)
365 (guix build syscalls))
366 #~(begin
367 (use-modules (gnu build marionette)
368 (guix build syscalls)
369 (srfi srfi-64))
370
371 (define marionette
372 (make-marionette
373 '(#$vm "-nic" "user,model=virtio-net-pci")))
374
375 (test-runner-current (system-test-runner #$output))
376 (test-begin "network-manager")
377
378 (test-assert "service is up"
379 (marionette-eval
380 '(begin
381 (use-modules (gnu services herd))
382 (start-service 'NetworkManager))
383 marionette))
384
385 (test-assert "network interfaces"
386 (marionette-eval
387 '(begin
388 (use-modules (guix build syscalls))
389 (network-interface-names))
390 marionette))
391
392 (test-equal "eth0 is up"
393 IFF_UP
394 (marionette-eval
395 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
396 (flags (network-interface-flags sock "eth0")))
397 (logand flags IFF_UP))
398 marionette))
399
400 (test-end))))
401
402 (gexp->derivation "network-manager" test))
403
404(define %test-network-manager
405 (system-test
406 (name "network-manager")
407 (description "Test the 'network-manager' service.")
408 (value
409 (let ((os (marionette-operating-system
410 (simple-operating-system
411 (service network-manager-service-type)
412 (service wpa-supplicant-service-type))
413 #:imported-modules '((gnu services herd)
414 (guix combinators)))))
415 (run-network-manager-test (virtual-machine os))))))
416
417
418(define (run-network-manager-dnsmasq-test vm)
419 (define test
420 (with-imported-modules '((gnu build marionette)
421 (guix build syscalls))
422 #~(begin
423 (use-modules (gnu build marionette)
424 (guix build syscalls)
425 (srfi srfi-64))
426
427 (define marionette
428 (make-marionette
429 '(#$vm "-nic" "user,model=virtio-net-pci")))
430
431 (test-runner-current (system-test-runner #$output))
432 (test-begin "network-manager-dnsmasq")
433
434 (test-assert "service is up"
435 (marionette-eval
436 '(begin
437 (use-modules (gnu services herd))
438 (start-service 'NetworkManager))
439 marionette))
440
441 (test-assert "network interfaces"
442 (marionette-eval
443 '(begin
444 (use-modules (guix build syscalls))
445 (network-interface-names))
446 marionette))
447
448 (test-equal "eth0 is up"
449 IFF_UP
450 (marionette-eval
451 '(let* ((sock (socket AF_INET SOCK_STREAM 0))
452 (flags (network-interface-flags sock "eth0")))
453 (logand flags IFF_UP))
454 marionette))
455
456 (test-assert "dnsmasq is running"
457 (marionette-eval
458 `(zero? (system* ,#$(file-append procps "/bin/pgrep")
459 "--count" "--exact" "dnsmasq"))
460 marionette))
461
462 (test-equal "resolves custom address"
463 "1.2.3.4"
464 (marionette-eval
465 '(begin
466 (use-modules (ice-9 popen)
467 (rnrs io ports))
468
469 (let* ((resolve-cmd "dig @127.0.0.1 example.test +short")
470 (port (open-input-pipe resolve-cmd))
471 (out (get-string-all port)))
472 (close-port port)
473 (string-drop-right out 1))) ;; drop newline
474 marionette))
475
476 (test-end))))
477
478 (gexp->derivation "network-manager-dnsmasq" test))
479
480(define %test-network-manager-dnsmasq
481 (system-test
482 (name "network-manager-dnsmasq")
483 (description "Test the 'network-manager' service using dnsmasq.")
484 (value
485 (let* ((base-os (simple-operating-system
486 (service network-manager-service-type
487 (network-manager-configuration
488 (dns "dnsmasq")
489 (dnsmasq-configuration-files
490 `(("dnsmasq.conf"
491 ,(plain-file "dnsmasq.conf" "address=/example.test/1.2.3.4\n"))))))
492 (service wpa-supplicant-service-type)))
493 (os (marionette-operating-system
494 (operating-system
495 (inherit base-os)
496 (packages
497 (append (list (list isc-bind "utils"))
498 (operating-system-packages base-os))))
499 #:imported-modules '((gnu services herd)
500 (guix combinators)))))
501 (run-network-manager-dnsmasq-test (virtual-machine os))))))
502
503
504;;;
505;;; Inetd.
506;;;
507
508(define %inetd-os
509 ;; Operating system with 2 inetd services.
510 (simple-operating-system
511 (service dhcpcd-service-type)
512 (service inetd-service-type
513 (inetd-configuration
514 (entries (list
515 (inetd-entry
516 (name "echo")
517 (socket-type 'stream)
518 (protocol "tcp")
519 (wait? #f)
520 (user "root"))
521 (inetd-entry
522 (name "dict")
523 (socket-type 'stream)
524 (protocol "tcp")
525 (wait? #f)
526 (user "root")
527 (program (file-append bash
528 "/bin/bash"))
529 (arguments
530 (list "bash" (plain-file "my-dict.sh" "\
531while read line
532do
533 if [[ $line =~ ^DEFINE\\ (.*)$ ]]
534 then
535 case ${BASH_REMATCH[1]} in
536 Guix)
537 echo GNU Guix is a package management tool for the GNU system.
538 ;;
539 G-expression)
540 echo Like an S-expression but with a G.
541 ;;
542 *)
543 echo NO DEFINITION FOUND
544 ;;
545 esac
546 else
547 echo ERROR
548 fi
549done" ))))))))))
550
551(define* (run-inetd-test)
552 "Run tests in %INETD-OS, where the inetd service provides an echo service on
553port 7, and a dict service on port 2628."
554 (define os
555 (marionette-operating-system %inetd-os))
556
557 (define vm
558 (virtual-machine
559 (operating-system os)
560 (port-forwardings `((8007 . 7)
561 (8628 . 2628)))))
562
563 (define test
564 (with-imported-modules '((gnu build marionette))
565 #~(begin
566 (use-modules (ice-9 rdelim)
567 (srfi srfi-64)
568 (gnu build marionette))
569 (define marionette
570 (make-marionette (list #$vm)))
571
572 (test-runner-current (system-test-runner #$output))
573 (test-begin "inetd")
574
575 ;; Make sure the PID file is created.
576 (test-assert "PID file"
577 (wait-for-file "/var/run/inetd.pid" marionette
578 #:timeout 30))
579
580 ;; Test the echo service.
581 (test-equal "echo response"
582 "Hello, Guix!"
583 (let ((echo (socket PF_INET SOCK_STREAM 0))
584 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8007)))
585 (connect echo addr)
586 (display "Hello, Guix!\n" echo)
587 (let ((response (read-line echo)))
588 (close echo)
589 response)))
590
591 ;; Test the dict service
592 (test-equal "dict response"
593 "GNU Guix is a package management tool for the GNU system."
594 (let ((dict (socket PF_INET SOCK_STREAM 0))
595 (addr (make-socket-address AF_INET INADDR_LOOPBACK 8628)))
596 (connect dict addr)
597 (display "DEFINE Guix\n" dict)
598 (let ((response (read-line dict)))
599 (close dict)
600 response)))
601
602 (test-end))))
603
604 (gexp->derivation "inetd-test" test))
605
606(define %test-inetd
607 (system-test
608 (name "inetd")
609 (description "Connect to a host with an INETD server.")
610 (value (run-inetd-test))))
611
612
613;;;
614;;; Open vSwitch
615;;;
616
617(define setup-openvswitch
618 #~(let ((ovs-vsctl (lambda (str)
619 (zero? (apply system*
620 #$(file-append openvswitch "/bin/ovs-vsctl")
621 (string-tokenize str)))))
622 (add-native-port (lambda (if)
623 (string-append "--may-exist add-port br0 " if
624 " vlan_mode=native-untagged"
625 " -- set Interface " if
626 " type=internal"))))
627 (and (ovs-vsctl "--may-exist add-br br0")
628 ;; Connect eth0 as an "untagged" port (no VLANs).
629 (ovs-vsctl "--may-exist add-port br0 eth0 vlan_mode=native-untagged")
630 (ovs-vsctl (add-native-port "ovs0")))))
631
632(define openvswitch-configuration-service
633 (simple-service 'openvswitch-configuration shepherd-root-service-type
634 (list (shepherd-service
635 (provision '(openvswitch-configuration))
636 (requirement '(vswitchd))
637 (start #~(lambda ()
638 #$setup-openvswitch))
639 (respawn? #f)))))
640
641(define %openvswitch-os
642 (operating-system
643 (inherit (simple-operating-system
644 (simple-service 'openswitch-networking
645 static-networking-service-type
646 (list (static-networking
647 (addresses (list (network-address
648 (value "10.1.1.1/24")
649 (device "ovs0"))))
650 (requirement '(openvswitch-configuration)))))
651 (service openvswitch-service-type)
652 openvswitch-configuration-service))
653 ;; Ensure the interface name does not change depending on the driver.
654 (kernel-arguments (cons "net.ifnames=0" %default-kernel-arguments))))
655
656(define (run-openvswitch-test)
657 (define os
658 (marionette-operating-system %openvswitch-os
659 #:imported-modules '((gnu services herd)
660 (guix build syscalls))))
661
662 (define test
663 (with-imported-modules '((gnu build marionette)
664 (guix build syscalls))
665 #~(begin
666 (use-modules (gnu build marionette)
667 (guix build syscalls)
668 (ice-9 popen)
669 (ice-9 rdelim)
670 (srfi srfi-64))
671
672 (define marionette
673 (make-marionette (list #$(virtual-machine os))))
674
675 (test-runner-current (system-test-runner #$output))
676 (test-begin "openvswitch")
677
678 ;; Wait for our configuration to be active (it sets up br0).
679 (test-assert "openvswitch-configuration is running"
680 (marionette-eval
681 '(begin
682 (use-modules (gnu services herd))
683 (wait-for-service 'openvswitch-configuration))
684 marionette))
685
686 ;; Make sure the bridge is created.
687 (test-assert "br0 exists"
688 (marionette-eval
689 '(zero? (system* #$(file-append openvswitch "/bin/ovs-vsctl")
690 "br-exists" "br0"))
691 marionette))
692
693 ;; Make sure eth0 is connected to the bridge.
694 (test-equal "eth0 is connected to br0"
695 "br0"
696 (marionette-eval
697 '(begin
698 (use-modules (ice-9 popen) (ice-9 rdelim))
699 (let* ((port (open-pipe*
700 OPEN_READ
701 (string-append #$openvswitch "/bin/ovs-vsctl")
702 "port-to-br" "eth0"))
703 (output (read-line port)))
704 (close-pipe port)
705 output))
706 marionette))
707
708 ;; Make sure the virtual interface got a static IP.
709 (test-assert "networking has started on ovs0"
710 (marionette-eval
711 '(begin
712 (use-modules (gnu services herd)
713 (srfi srfi-1))
714 (live-service-running
715 (find (lambda (live)
716 (memq 'networking
717 (live-service-provision live)))
718 (current-services))))
719 marionette))
720
721 (test-equal "ovs0 is up"
722 IFF_UP
723 (marionette-eval
724 '(begin
725 (use-modules (guix build syscalls))
726
727 (let* ((sock (socket AF_INET SOCK_STREAM 0))
728 (flags (network-interface-flags sock "ovs0")))
729 (close-port sock)
730 (logand flags IFF_UP)))
731 marionette))
732
733 (test-end))))
734
735 (gexp->derivation "openvswitch-test" test))
736
737(define %test-openvswitch
738 (system-test
739 (name "openvswitch")
740 (description "Test a running OpenvSwitch configuration.")
741 (value (run-openvswitch-test))))
742
743
744;;;
745;;; DHCP Daemon
746;;;
747
748(define minimal-dhcpd-v4-config-file
749 (plain-file "dhcpd.conf"
750 "\
751default-lease-time 600;
752max-lease-time 7200;
753
754subnet 192.168.1.0 netmask 255.255.255.0 {
755 range 192.168.1.100 192.168.1.200;
756 option routers 192.168.1.1;
757 option domain-name-servers 192.168.1.2, 192.168.1.3;
758 option domain-name \"dummy.domain.name.abc123xyz\";
759}
760"))
761
762(define dhcpd-v4-configuration
763 (dhcpd-configuration
764 (config-file minimal-dhcpd-v4-config-file)
765 (version "4")
766 (interfaces '("ens3"))))
767
768(define %dhcpd-os
769 (simple-operating-system
770 (service static-networking-service-type
771 (list (static-networking
772 (addresses (list (network-address
773 (value "192.168.1.4/24")
774 (device "ens3"))))
775 (routes (list (network-route
776 (destination "default")
777 (gateway "192.168.1.1"))))
778 (name-servers '("192.168.1.2" "192.168.1.3")))))
779 (service dhcpd-service-type dhcpd-v4-configuration)))
780
781(define (run-dhcpd-test)
782 (define os
783 (marionette-operating-system %dhcpd-os
784 #:imported-modules '((gnu services herd))))
785
786 (define test
787 (with-imported-modules '((gnu build marionette))
788 #~(begin
789 (use-modules (gnu build marionette)
790 (srfi srfi-64))
791
792 (define marionette
793 (make-marionette (list #$(virtual-machine os))))
794
795 (test-runner-current (system-test-runner #$output))
796 (test-begin "dhcpd")
797
798 (test-assert "pid file exists"
799 (wait-for-file
800 '#$(dhcpd-configuration-pid-file dhcpd-v4-configuration)
801 marionette))
802
803 (test-assert "lease file exists"
804 (wait-for-file
805 '#$(dhcpd-configuration-lease-file dhcpd-v4-configuration)
806 marionette
807 #:read '(@ (ice-9 textual-ports) get-string-all)))
808
809 (test-assert "run directory exists"
810 (marionette-eval
811 '(file-exists?
812 #$(dhcpd-configuration-run-directory dhcpd-v4-configuration))
813 marionette))
814
815 (test-assert "dhcpd is alive"
816 (marionette-eval
817 '(begin
818 (use-modules (gnu services herd))
819 (wait-for-service 'dhcpv4-daemon))
820 marionette))
821
822 (test-end))))
823
824 (gexp->derivation "dhcpd-test" test))
825
826(define %test-dhcpd
827 (system-test
828 (name "dhcpd")
829 (description "Test a running DHCP daemon configuration.")
830 (value (run-dhcpd-test))))
831
832
833
834;;;
835;;; dnsmasq tests.
836;;;
837
838
839(define dnsmasq-os-configuration
840 (dnsmasq-configuration))
841
842(define %dnsmasq-os
843 (simple-operating-system
844 (service dhcpcd-service-type)
845 (service dnsmasq-service-type
846 (dnsmasq-configuration
847 (extra-options
848 (list "--log-facility=/tmp/dnsmasq.log"))))))
849
850
851(define (run-dnsmasq-test)
852 (define os
853 (marionette-operating-system %dnsmasq-os
854 #:imported-modules '((gnu services herd))))
855
856 (define test
857 (with-imported-modules '((gnu build marionette))
858 #~(begin
859 (use-modules (gnu build marionette)
860 (srfi srfi-64))
861
862 (define marionette
863 (make-marionette (list #$(virtual-machine os))))
864
865 (test-runner-current (system-test-runner #$output))
866 (test-begin "dnsmasq")
867
868 (test-assert "dnsmasq is alive"
869 (marionette-eval
870 '(begin
871 (use-modules (gnu services herd))
872 (wait-for-service 'dnsmasq))
873 marionette))
874
875 (test-assert "pid file exists"
876 (wait-for-file
877 '#$(dnsmasq-configuration-pid-file dnsmasq-os-configuration)
878 marionette))
879
880 (test-assert "send SIGHUP"
881 (positive?
882 (marionette-eval
883 '(begin
884 (use-modules (ice-9 rdelim))
885 (system* "sync")
886 (let* ((port (open-input-file "/tmp/dnsmasq.log")))
887 (seek port 0 SEEK_END)
888 (system* "herd" "reload" "dnsmasq")
889 (system* "sync")
890 (let ((line (read-line port)))
891 (close-port port)
892 (string-contains line "read /etc/hosts"))))
893 marionette)))
894
895 (test-assert "send SIGUSR1"
896 (positive?
897 (marionette-eval
898 '(begin
899 (use-modules (ice-9 rdelim))
900 (system* "sync")
901 (let* ((port (open-input-file "/tmp/dnsmasq.log")))
902 (seek port 0 SEEK_END)
903 (system* "herd" "stats" "dnsmasq")
904 (system* "sync")
905 (let ((line (read-line port)))
906 (close-port port)
907 (string-contains-ci line "time"))))
908 marionette)))
909
910 (test-assert "dnsmasq is alive"
911 (marionette-eval
912 '(begin
913 (use-modules (gnu services herd))
914 (wait-for-service 'dnsmasq))
915 marionette))
916
917 (test-end))))
918
919 (gexp->derivation "dnsmasq-test" test))
920
921(define %test-dnsmasq
922 (system-test
923 (name "dnsmasq")
924 (description "Test a running dnsmasq daemon configuration.")
925 (value (run-dnsmasq-test))))
926
927
928
929;;;
930;;; DHCPCD Daemon
931;;;
932
933(define %dhcpcd-os
934 (let ((base-os
935 (simple-operating-system
936 (service dhcpcd-service-type
937 (dhcpcd-configuration
938 (command-arguments '("--debug" "--logfile" "/dev/console"))
939 (interfaces '("ens3")))))))
940 (operating-system
941 (inherit base-os)
942 (packages
943 (append (list dhcpcd iproute)
944 (operating-system-packages base-os))))))
945
946(define (run-dhcpcd-test)
947 "Run tests in %dhcpcd-os with a running dhcpcd daemon on localhost."
948 (define os
949 (marionette-operating-system
950 %dhcpcd-os
951 #:imported-modules '((gnu services herd))))
952
953 (define vm
954 (virtual-machine os))
955
956 (define test
957 (with-imported-modules '((gnu build marionette))
958 #~(begin
959 (use-modules (srfi srfi-64)
960 (gnu build marionette))
961 (define marionette
962 (make-marionette (list #$vm)))
963
964 (define (wait-for-lease)
965 (marionette-eval
966 '(begin
967 (use-modules (ice-9 popen) (ice-9 rdelim))
968
969 (let loop ((i 15))
970 (if (> i 0)
971 (let* ((port (open-input-pipe "dhcpcd --dumplease ens3"))
972 (output (read-string port)))
973 (close-port port)
974 (unless (string-contains output "reason=BOUND")
975 (sleep 1)
976 (loop (- i 1))))
977 (error "failed to obtain a DHCP lease"))))
978 marionette))
979
980 (test-runner-current (system-test-runner #$output))
981 (test-begin "dhcpcd")
982
983 (test-assert "service is running"
984 (marionette-eval
985 '(begin
986 (use-modules (gnu services herd))
987
988 ;; Make sure the 'dhcpcd' command is found.
989 (setenv "PATH" "/run/current-system/profile/sbin")
990
991 (wait-for-service 'networking))
992 marionette))
993
994 (test-assert "IPC socket exists"
995 (marionette-eval
996 '(file-exists? "/var/run/dhcpcd/ens3.sock")
997 marionette))
998
999 (test-equal "IPC is functional"
1000 0
1001 (marionette-eval
1002 '(status:exit-val
1003 (system* "dhcpcd" "--dumplease" "ens3"))
1004 marionette))
1005
1006 (test-equal "aquires IPv4 address via DHCP"
1007 1
1008 (and
1009 (wait-for-lease)
1010 (marionette-eval
1011 '(begin
1012 (use-modules (ice-9 popen) (ice-9 rdelim))
1013
1014 (let* ((port (open-input-pipe "ip -4 address show dev ens3"))
1015 (lines (string-split (read-string port) #\newline)))
1016 (close-port port)
1017 (length
1018 (filter (lambda (line)
1019 (string-contains line "scope global dynamic"))
1020 lines))))
1021 marionette)))
1022
1023 (test-end))))
1024 (gexp->derivation "dhcpcd-test" test))
1025
1026(define %test-dhcpcd
1027 (system-test
1028 (name "dhcpcd")
1029 (description "Test that the dhcpcd obtains IP DHCP leases.")
1030 (value (run-dhcpcd-test))))
1031
1032
1033;;;
1034;;; Services related to Tor
1035;;;
1036
1037(define %tor-os
1038 (simple-operating-system
1039 (service tor-service-type)))
1040
1041(define %tor-os/unix-socks-socket
1042 (simple-operating-system
1043 (service tor-service-type
1044 (tor-configuration
1045 (socks-socket-type 'unix)))))
1046
1047(define (run-tor-test)
1048 (define os
1049 (marionette-operating-system %tor-os
1050 #:imported-modules '((gnu services herd))
1051 #:requirements '(tor)))
1052
1053 (define os/unix-socks-socket
1054 (marionette-operating-system %tor-os/unix-socks-socket
1055 #:imported-modules '((gnu services herd))
1056 #:requirements '(tor)))
1057
1058 (define test
1059 (with-imported-modules '((gnu build marionette))
1060 #~(begin
1061 (use-modules (gnu build marionette)
1062 (ice-9 popen)
1063 (ice-9 rdelim)
1064 (srfi srfi-64))
1065
1066 (define marionette
1067 (make-marionette (list #$(virtual-machine os))))
1068
1069 (define (tor-is-alive? marionette)
1070 (marionette-eval
1071 '(begin
1072 (use-modules (gnu services herd)
1073 (srfi srfi-1))
1074 (live-service-running
1075 (find (lambda (live)
1076 (memq 'tor
1077 (live-service-provision live)))
1078 (current-services))))
1079 marionette))
1080
1081 (test-runner-current (system-test-runner #$output))
1082 (test-begin "tor")
1083
1084 ;; Test the usual Tor service.
1085
1086 (test-assert "tor is alive"
1087 (tor-is-alive? marionette))
1088
1089 (test-assert "tor is listening"
1090 (let ((default-port 9050))
1091 (wait-for-tcp-port default-port marionette)))
1092
1093 ;; Don't run two VMs at once.
1094 (marionette-control "quit" marionette)
1095
1096 ;; Test the Tor service using a SOCKS socket.
1097
1098 (let* ((socket-directory "/tmp/more-sockets")
1099 (_ (mkdir socket-directory))
1100 (marionette/unix-socks-socket
1101 (make-marionette
1102 (list #$(virtual-machine os/unix-socks-socket))
1103 ;; We can't use the same socket directory as the first
1104 ;; marionette.
1105 #:socket-directory socket-directory)))
1106 (test-assert "tor is alive, even when using a SOCKS socket"
1107 (tor-is-alive? marionette/unix-socks-socket))
1108
1109 (test-assert "tor is listening, even when using a SOCKS socket"
1110 (wait-for-unix-socket "/var/run/tor/socks-sock"
1111 marionette/unix-socks-socket)))
1112
1113 (test-end))))
1114
1115 (gexp->derivation "tor-test" test))
1116
1117(define %test-tor
1118 (system-test
1119 (name "tor")
1120 (description "Test a running Tor daemon configuration.")
1121 (value (run-tor-test))))
1122
1123(define %inetd-echo-port 7)
1124
1125(define* (run-iptables-test)
1126 "Run tests of 'iptables-service-type'."
1127 (define iptables-rules
1128 "*filter
1129:INPUT ACCEPT
1130:FORWARD ACCEPT
1131:OUTPUT ACCEPT
1132-A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp-port-unreachable
1133COMMIT
1134")
1135
1136 (define ip6tables-rules
1137 "*filter
1138:INPUT ACCEPT
1139:FORWARD ACCEPT
1140:OUTPUT ACCEPT
1141-A INPUT -p tcp -m tcp --dport 7 -j REJECT --reject-with icmp6-port-unreachable
1142COMMIT
1143")
1144
1145 (define os
1146 (marionette-operating-system
1147 (simple-operating-system
1148 (service dhcpcd-service-type)
1149 (service inetd-service-type
1150 (inetd-configuration
1151 (entries (list
1152 (inetd-entry
1153 (name "echo")
1154 (socket-type 'stream)
1155 (protocol "tcp")
1156 (wait? #f)
1157 (user "root"))))))
1158 (service iptables-service-type
1159 (iptables-configuration
1160 (ipv4-rules (plain-file "iptables.rules" iptables-rules))
1161 (ipv6-rules (plain-file "ip6tables.rules" ip6tables-rules)))))
1162 #:imported-modules '((gnu services herd))
1163 #:requirements '(inetd iptables)))
1164
1165 (define test
1166 (with-imported-modules '((gnu build marionette))
1167 #~(begin
1168 (use-modules (srfi srfi-64)
1169 (gnu build marionette))
1170 (define marionette
1171 (make-marionette (list #$(virtual-machine os))))
1172
1173 (define (dump-iptables iptables-save marionette)
1174 (marionette-eval
1175 `(begin
1176 (use-modules (ice-9 popen)
1177 (ice-9 rdelim)
1178 (ice-9 regex))
1179 (call-with-output-string
1180 (lambda (out)
1181 (call-with-port
1182 (open-pipe* OPEN_READ ,iptables-save)
1183 (lambda (in)
1184 (let loop ((line (read-line in)))
1185 ;; iptables-save does not output rules in the exact
1186 ;; same format we loaded using iptables-restore. It
1187 ;; adds comments, packet counters, etc. We remove
1188 ;; these additions.
1189 (unless (eof-object? line)
1190 (cond
1191 ;; Remove comments
1192 ((string-match "^#" line) #t)
1193 ;; Remove packet counters
1194 ((string-match "^:([A-Z]*) ([A-Z]*) .*" line)
1195 => (lambda (match-record)
1196 (format out ":~a ~a~%"
1197 (match:substring match-record 1)
1198 (match:substring match-record 2))))
1199 ;; Pass other lines without modification
1200 (else (display line out)
1201 (newline out)))
1202 (loop (read-line in)))))))))
1203 marionette))
1204
1205 (test-runner-current (system-test-runner #$output))
1206 (test-begin "iptables")
1207
1208 (test-equal "iptables-save dumps the same rules that were loaded"
1209 (dump-iptables #$(file-append iptables "/sbin/iptables-save")
1210 marionette)
1211 #$iptables-rules)
1212
1213 (test-equal "ip6tables-save dumps the same rules that were loaded"
1214 (dump-iptables #$(file-append iptables "/sbin/ip6tables-save")
1215 marionette)
1216 #$ip6tables-rules)
1217
1218 (test-error "iptables firewall blocks access to inetd echo service"
1219 'misc-error
1220 (wait-for-tcp-port #$%inetd-echo-port marionette
1221 #:timeout 5))
1222
1223 ;; TODO: This test freezes up at the login prompt without any
1224 ;; relevant messages on the console. Perhaps it is waiting for some
1225 ;; timeout. Find and fix this issue.
1226 ;; (test-assert "inetd echo service is accessible after iptables firewall is stopped"
1227 ;; (begin
1228 ;; (marionette-eval
1229 ;; '(begin
1230 ;; (use-modules (gnu services herd))
1231 ;; (stop-service 'iptables))
1232 ;; marionette)
1233 ;; (wait-for-tcp-port #$%inetd-echo-port marionette #:timeout 5)))
1234
1235 (test-end))))
1236
1237 (gexp->derivation "iptables" test))
1238
1239(define %test-iptables
1240 (system-test
1241 (name "iptables")
1242 (description "Test a running iptables daemon.")
1243 (value (run-iptables-test))))
1244
1245
1246;;;
1247;;; nftables.
1248;;;
1249
1250(define (make-nftables-os ruleset)
1251 (simple-operating-system
1252 (service dhcpcd-service-type)
1253 (service inetd-service-type
1254 (inetd-configuration
1255 (entries (list
1256 (inetd-entry
1257 (name "echo")
1258 (socket-type 'stream)
1259 (protocol "tcp")
1260 (wait? #f)
1261 (user "root"))))))
1262 (service openssh-service-type)
1263 (service nftables-service-type
1264 (nftables-configuration
1265 (debug-levels '(all))
1266 (ruleset ruleset)))))
1267
1268(define %default-nftables-ruleset-for-tests
1269 ;; This is like the %default-nftables-ruleset, but without allowing any
1270 ;; connections from the loopback interface.
1271 (plain-file "nftables.conf" "\
1272table inet filter {
1273 chain input {
1274 type filter hook input priority 0; policy drop;
1275
1276 # early drop of invalid connections
1277 ct state invalid drop
1278
1279 # allow established/related connections
1280 ct state { established, related } accept
1281
1282 # allow from loopback
1283 # iif lo accept # COMMENTED OUT FOR TESTS
1284 # drop connections to lo not coming from lo
1285 iif != lo ip daddr 127.0.0.1/8 drop
1286 iif != lo ip6 daddr ::1/128 drop
1287
1288 # allow icmp
1289 ip protocol icmp accept
1290 ip6 nexthdr icmpv6 accept
1291
1292 # allow ssh
1293 tcp dport ssh accept
1294
1295 # reject everything else
1296 reject with icmpx type port-unreachable
1297 }
1298 chain forward {
1299 type filter hook forward priority 0; policy drop;
1300 }
1301 chain output {
1302 type filter hook output priority 0; policy accept;
1303 }
1304}"))
1305
1306(define %nftables-os
1307 (make-nftables-os %default-nftables-ruleset-for-tests))
1308
1309(define (run-nftables-test)
1310 (define os
1311 (marionette-operating-system
1312 %nftables-os
1313 #:imported-modules '((gnu services herd))
1314 #:requirements '(inetd nftables ssh)))
1315
1316 (define test
1317 (with-imported-modules '((gnu build marionette))
1318 #~(begin
1319 (use-modules (gnu build marionette)
1320 (srfi srfi-64))
1321 (define marionette
1322 (make-marionette (list #$(virtual-machine os))))
1323
1324 (test-runner-current (system-test-runner #$output))
1325 (test-begin "nftables")
1326
1327 (test-error "nftables blocks access to inetd echo service"
1328 'misc-error
1329 (wait-for-tcp-port #$%inetd-echo-port marionette
1330 #:timeout 5))
1331
1332 (test-assert "nftables allows access to SSH TCP port 22"
1333 (wait-for-tcp-port 22 marionette))
1334
1335 (test-end))))
1336
1337 (gexp->derivation "nftables-test" test))
1338
1339(define %test-nftables
1340 (system-test
1341 (name "nftables")
1342 (description "Test the nftables service properly allow or block
1343connection to ports.")
1344 (value (run-nftables-test))))
1345
1346
1347;;;
1348;;; IPFS service
1349;;;
1350
1351(define %ipfs-os
1352 (simple-operating-system
1353 (service ipfs-service-type)))
1354
1355(define (run-ipfs-test)
1356 (define os
1357 (marionette-operating-system %ipfs-os
1358 #:imported-modules (source-module-closure
1359 '((gnu services herd)
1360 (guix ipfs)))
1361 #:extensions (list guile-json-4)
1362 #:requirements '(ipfs)))
1363
1364 (define test
1365 (with-imported-modules '((gnu build marionette))
1366 #~(begin
1367 (use-modules (gnu build marionette)
1368 (rnrs bytevectors)
1369 (srfi srfi-64)
1370 (ice-9 binary-ports))
1371
1372 (define marionette
1373 (make-marionette (list #$(virtual-machine os))))
1374
1375 (define (ipfs-is-alive?)
1376 (marionette-eval
1377 '(begin
1378 (use-modules (gnu services herd)
1379 (srfi srfi-1))
1380 (live-service-running
1381 (find (lambda (live)
1382 (memq 'ipfs
1383 (live-service-provision live)))
1384 (current-services))))
1385 marionette))
1386
1387 ;; The default API endpoint port 5001 is used,
1388 ;; so there is no need to parameterize %ipfs-base-url.
1389 (define (add-data data)
1390 (marionette-eval `(content-name (add-data ,data)) marionette))
1391 (define (read-contents object)
1392 (marionette-eval
1393 `(let* ((input (read-contents ,object))
1394 (all-input (get-bytevector-all input)))
1395 (close-port input)
1396 all-input)
1397 marionette))
1398
1399 (marionette-eval '(use-modules (guix ipfs)) marionette)
1400 (test-runner-current (system-test-runner #$output))
1401 (test-begin "ipfs")
1402
1403 ;; Test the IPFS service.
1404
1405 (test-assert "ipfs is alive" (ipfs-is-alive?))
1406
1407 (test-assert "ipfs is listening on the gateway"
1408 (let ((default-port 8082))
1409 (wait-for-tcp-port default-port marionette)))
1410
1411 (test-assert "ipfs is listening on the API endpoint"
1412 (let ((default-port 5001))
1413 (wait-for-tcp-port default-port marionette)))
1414
1415 (define test-bv (string->utf8 "hello ipfs!"))
1416 (test-equal "can upload and download a file to/from ipfs"
1417 test-bv
1418 (read-contents (add-data test-bv)))
1419
1420 (test-end))))
1421 (gexp->derivation "ipfs-test" test))
1422
1423(define %test-ipfs
1424 (system-test
1425 (name "ipfs")
1426 (description "Test a running IPFS daemon configuration.")
1427 (value (run-ipfs-test))))