projects/guix·blob javascript-team · refs

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