]>
git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # systemd-networkd tests
5 # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
6 # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
8 # To run an individual test, specify it as a command line argument in the form
9 # of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
10 # function called test_ipv6_mtu(). To run just that test use:
12 # run0 ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
14 # Similarly, other individual tests can be run, e.g.:
16 # run0 ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
18 # To run the test with the executables (systemd-networkd, networkctl, systemd-udevd and so on)
19 # in your build directory, --build-dir=/path/to/build/ option can be used:
21 # run0 ./systemd-networkd-tests.py --build-dir=/path/to/build NetworkdNetworkTests.test_address_static
23 # Note, unlike the long getopt option handling, the path must be specified after '=', rather than space.
24 # Otherwise the path is recognized as a test case, and the test run will fail.
49 network_unit_dir
= '/run/systemd/network'
50 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
51 networkd_ci_temp_dir
= '/run/networkd-ci'
52 udev_rules_dir
= '/run/udev/rules.d'
53 credstore_dir
= '/run/credstore'
55 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
56 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
57 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
59 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
60 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
62 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
64 systemd_lib_paths
= [ '/usr/lib/systemd' , '/lib/systemd' ]
65 which_paths
= ':' . join ( systemd_lib_paths
+ os
. getenv ( 'PATH' , os
. defpath
). lstrip ( ':' ). split ( ':' ))
67 networkd_bin
= shutil
. which ( 'systemd-networkd' , path
= which_paths
)
68 resolved_bin
= shutil
. which ( 'systemd-resolved' , path
= which_paths
)
69 timesyncd_bin
= shutil
. which ( 'systemd-timesyncd' , path
= which_paths
)
70 wait_online_bin
= shutil
. which ( 'systemd-networkd-wait-online' , path
= which_paths
)
71 networkctl_bin
= shutil
. which ( 'networkctl' , path
= which_paths
)
72 resolvectl_bin
= shutil
. which ( 'resolvectl' , path
= which_paths
)
73 timedatectl_bin
= shutil
. which ( 'timedatectl' , path
= which_paths
)
74 udevadm_bin
= shutil
. which ( 'udevadm' , path
= which_paths
)
75 test_ndisc_send
= None
88 show_journal
= True # When true, show journal on stopping networkd.
106 saved_ipv4_rules
= None
107 saved_ipv6_rules
= None
108 saved_timezone
= None
111 if os
. path
. exists ( path
):
115 shutil
. rmtree ( path
, ignore_errors
= True )
118 shutil
. copy ( src
, dst
)
121 shutil
. copytree ( src
, dst
, copy_function
= shutil
. copy
, dirs_exist_ok
= True )
124 os
. makedirs ( path
, exist_ok
= True )
127 pathlib
. Path ( path
). touch ()
129 # pylint: disable=R1710
130 def check_output (* command
, ** kwargs
):
131 # This checks the result and returns stdout (and stderr) on success.
132 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
133 ret
= subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. PIPE
, stderr
= subprocess
. STDOUT
, ** kwargs
)
134 if ret
. returncode
== 0 :
135 return ret
. stdout
. rstrip ()
136 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
138 ret
. check_returncode ()
140 def call (* command
, ** kwargs
):
141 # This returns returncode. stdout and stderr are merged and shown in console
142 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
143 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stderr
= subprocess
. STDOUT
, ** kwargs
). returncode
145 def call_check (* command
, ** kwargs
):
146 # Same as call() above, but it triggers CalledProcessError if rc != 0
147 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
148 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stderr
= subprocess
. STDOUT
, ** kwargs
). check_returncode ()
150 def call_quiet (* command
, ** kwargs
):
151 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
152 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. DEVNULL
, stderr
= subprocess
. DEVNULL
, ** kwargs
). returncode
154 def run (* command
, ** kwargs
):
155 # This returns CompletedProcess instance.
156 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
157 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. PIPE
, stderr
= subprocess
. PIPE
, ** kwargs
)
159 def check_json ( string
):
162 except json
. JSONDecodeError
:
163 print ( f
"String is not a valid JSON: ' {string} '" )
166 def is_module_available (* module_names
):
167 for module_name
in module_names
:
168 lsmod_output
= check_output ( 'lsmod' )
169 module_re
= re
. compile ( rf
'^{re.escape(module_name)} \b ' , re
. MULTILINE
)
170 if not module_re
. search ( lsmod_output
) and call_quiet ( 'modprobe' , module_name
) != 0 :
174 def expectedFailureIfModuleIsNotAvailable (* module_names
):
176 return func
if is_module_available (* module_names
) else unittest
. expectedFailure ( func
)
180 def expectedFailureIfERSPANv0IsNotSupported ():
181 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
183 rc
= call_quiet ( 'ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 0' )
184 remove_link ( 'erspan99' )
185 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
189 def expectedFailureIfERSPANv2IsNotSupported ():
190 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
192 rc
= call_quiet ( 'ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 2' )
193 remove_link ( 'erspan99' )
194 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
198 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable ():
200 rc
= call_quiet ( 'ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7' )
201 call_quiet ( 'ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7' )
202 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
206 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable ():
208 # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols.
209 # Hence. here we use explicit number: 6 == tcp.
210 rc
= call_quiet ( 'ip rule add not from 192.168.100.19 ipproto 6 table 7' )
211 call_quiet ( 'ip rule del not from 192.168.100.19 ipproto 6 table 7' )
212 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
216 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable ():
219 if call_quiet ( 'ip rule add from 192.168.100.19 table 7 uidrange 200-300' ) == 0 :
220 ret
= run ( 'ip rule list from 192.168.100.19 table 7' )
221 supported
= ret
. returncode
== 0 and 'uidrange 200-300' in ret
. stdout
222 call_quiet ( 'ip rule del from 192.168.100.19 table 7 uidrange 200-300' )
223 return func
if supported
else unittest
. expectedFailure ( func
)
227 def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable ():
229 rc
= call_quiet ( 'ip rule add not from 192.168.100.19 l3mdev' )
230 call_quiet ( 'ip rule del not from 192.168.100.19 l3mdev' )
231 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
235 def expectedFailureIfNexthopIsNotAvailable ():
237 rc
= call_quiet ( 'ip nexthop list' )
238 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
242 def expectedFailureIfRTA_VIAIsNotSupported ():
244 call_quiet ( 'ip link add dummy98 type dummy' )
245 call_quiet ( 'ip link set up dev dummy98' )
246 call_quiet ( 'ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98' )
247 rc
= call_quiet ( 'ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98' )
248 remove_link ( 'dummy98' )
249 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
253 def expectedFailureIfAlternativeNameIsNotAvailable ():
255 call_quiet ( 'ip link add dummy98 type dummy' )
257 call_quiet ( 'ip link prop add dev dummy98 altname hogehogehogehogehoge' ) == 0 and \
258 call_quiet ( 'ip link show dev hogehogehogehogehoge' ) == 0
259 remove_link ( 'dummy98' )
260 return func
if supported
else unittest
. expectedFailure ( func
)
264 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ():
266 def finalize ( func
, supported
):
267 call_quiet ( 'rmmod netdevsim' )
268 return func
if supported
else unittest
. expectedFailure ( func
)
270 call_quiet ( 'rmmod netdevsim' )
271 if call_quiet ( 'modprobe netdevsim' ) != 0 :
272 return finalize ( func
, False )
275 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
278 return finalize ( func
, False )
280 return finalize ( func
, os
. path
. exists ( '/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs' ))
284 def expectedFailureIfKernelReturnsInvalidFlags ():
286 This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
287 fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
290 call_quiet ( 'ip link add dummy98 type dummy' )
291 call_quiet ( 'ip link set up dev dummy98' )
292 call_quiet ( 'ip address add 192.0.2.1/24 dev dummy98 noprefixroute' )
293 output
= check_output ( 'ip address show dev dummy98' )
294 remove_link ( 'dummy98' )
295 return func
if 'noprefixroute' in output
else unittest
. expectedFailure ( func
)
299 # pylint: disable=C0415
300 def compare_kernel_version ( min_kernel_version
):
303 from packaging
import version
305 print ( 'Failed to import either platform or packaging module, assuming the comparison failed' )
308 # Get only the actual kernel version without any build/distro/arch stuff
309 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
310 kver
= platform
. release (). split ( '-' )[ 0 ]
311 # Get also rid of '+'
312 kver
= kver
. split ( '+' )[ 0 ]
314 return version
. parse ( kver
) >= version
. parse ( min_kernel_version
)
316 def copy_network_unit (* units
, copy_dropins
= True ):
318 Copy networkd unit files into the testbed.
320 Any networkd unit file type can be specified, as well as drop-in files.
322 By default, all drop-ins for a specified unit file are copied in;
323 to avoid that specify dropins=False.
325 When a drop-in file is specified, its unit file is also copied in automatically.
328 mkdir_p ( network_unit_dir
)
330 if copy_dropins
and os
. path
. exists ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' )):
331 cp_r ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' ), os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
333 if unit
. endswith ( '.conf' ):
335 unit
= os
. path
. dirname ( dropin
). rstrip ( '.d' )
336 dropindir
= os
. path
. join ( network_unit_dir
, unit
+ '.d' )
338 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), dropindir
)
340 cp ( os
. path
. join ( networkd_ci_temp_dir
, unit
), network_unit_dir
)
342 if unit
. endswith ( '.link' ):
348 def copy_credential ( src
, target
):
349 mkdir_p ( credstore_dir
)
350 cp ( os
. path
. join ( networkd_ci_temp_dir
, src
),
351 os
. path
. join ( credstore_dir
, target
))
353 def remove_network_unit (* units
):
355 Remove previously copied unit files from the testbed.
357 Drop-ins will be removed automatically.
361 rm_f ( os
. path
. join ( network_unit_dir
, unit
))
362 rm_rf ( os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
364 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
370 def touch_network_unit (* units
):
372 touch ( os
. path
. join ( network_unit_dir
, unit
))
374 def clear_network_units ():
376 if os
. path
. exists ( network_unit_dir
):
377 units
= os
. listdir ( network_unit_dir
)
379 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
382 rm_rf ( network_unit_dir
)
387 def copy_networkd_conf_dropin (* dropins
):
388 """Copy networkd.conf dropin files into the testbed."""
389 mkdir_p ( networkd_conf_dropin_dir
)
390 for dropin
in dropins
:
391 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
393 def remove_networkd_conf_dropin (* dropins
):
394 """Remove previously copied networkd.conf dropin files from the testbed."""
395 for dropin
in dropins
:
396 rm_f ( os
. path
. join ( networkd_conf_dropin_dir
, dropin
))
398 def clear_networkd_conf_dropins ():
399 rm_rf ( networkd_conf_dropin_dir
)
401 def setup_systemd_udev_rules ():
402 if not build_dir
and not source_dir
:
405 mkdir_p ( udev_rules_dir
)
407 for path
in [ build_dir
, source_dir
]:
411 path
= os
. path
. join ( path
, "rules.d" )
412 print ( f
"Copying udev rules from {path} to {udev_rules_dir} " )
414 for rule
in os
. listdir ( path
):
415 if not rule
. endswith ( ".rules" ):
417 cp ( os
. path
. join ( path
, rule
), udev_rules_dir
)
419 def clear_networkd_state_files ():
420 rm_rf ( '/var/lib/systemd/network/' )
422 def copy_udev_rule (* rules
):
423 """Copy udev rules"""
424 mkdir_p ( udev_rules_dir
)
426 cp ( os
. path
. join ( networkd_ci_temp_dir
, rule
), udev_rules_dir
)
428 def remove_udev_rule (* rules
):
429 """Remove previously copied udev rules"""
431 rm_f ( os
. path
. join ( udev_rules_dir
, rule
))
433 def clear_udev_rules ():
434 rm_rf ( udev_rules_dir
)
436 def save_active_units ():
437 for u
in [ 'systemd-networkd.socket' , 'systemd-networkd.service' ,
438 'systemd-resolved.service' , 'systemd-timesyncd.service' ,
439 'firewalld.service' ]:
440 if call ( f
'systemctl is-active --quiet {u} ' ) == 0 :
441 call ( f
'systemctl stop {u} ' )
442 active_units
. append ( u
)
444 def restore_active_units ():
445 if 'systemd-networkd.socket' in active_units
:
446 call ( 'systemctl stop systemd-networkd.socket systemd-networkd.service' )
447 for u
in active_units
:
448 call ( f
'systemctl restart {u} ' )
450 def create_unit_dropin ( unit
, contents
):
451 mkdir_p ( f
'/run/systemd/system/ {unit} .d' )
452 with
open ( f
'/run/systemd/system/ {unit} .d/00-override.conf' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
453 f
. write ( ' \n ' . join ( contents
))
455 def create_service_dropin ( service
, command
, additional_settings
= None ):
456 drop_in
= [ '[Service]' ]
460 f
'ExecStart= {valgrind_cmd}{command} ' ,
463 drop_in
+= [ 'Environment=SYSTEMD_LOG_LEVEL=debug' ]
465 drop_in
+= [ f
'Environment=ASAN_OPTIONS=" {asan_options} "' ]
467 drop_in
+= [ f
'Environment=LSAN_OPTIONS=" {lsan_options} "' ]
469 drop_in
+= [ f
'Environment=UBSAN_OPTIONS=" {ubsan_options} "' ]
470 if asan_options
or lsan_options
or ubsan_options
:
471 drop_in
+= [ 'SystemCallFilter=' ]
472 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
473 drop_in
+= [ 'MemoryDenyWriteExecute=no' ]
476 'Environment=SYSTEMD_MEMPOOL=0' ,
484 if additional_settings
:
485 drop_in
+= additional_settings
487 create_unit_dropin ( f
' {service} .service' , drop_in
)
489 def setup_system_units ():
490 if build_dir
or source_dir
:
491 mkdir_p ( '/run/systemd/system/' )
494 'systemd-networkd.service' ,
495 'systemd-networkd.socket' ,
496 'systemd-networkd-persistent-storage.service' ,
497 'systemd-resolved.service' ,
498 'systemd-timesyncd.service' ,
499 'systemd-udevd.service' ,
501 for path
in [ build_dir
, source_dir
]:
505 fullpath
= os
. path
. join ( os
. path
. join ( path
, "units" ), unit
)
506 if os
. path
. exists ( fullpath
):
507 print ( f
"Copying unit file from {fullpath} to /run/systemd/system/" )
508 cp ( fullpath
, '/run/systemd/system/' )
511 create_service_dropin ( 'systemd-networkd' , networkd_bin
,
514 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes' ,
516 'StartLimitIntervalSec=0' ])
517 create_service_dropin ( 'systemd-resolved' , resolved_bin
)
518 create_service_dropin ( 'systemd-timesyncd' , timesyncd_bin
)
520 # TODO: also run udevd with sanitizers, valgrind, or coverage
522 'systemd-udevd.service' ,
526 f
'ExecStart=@ {udevadm_bin} systemd-udevd' ,
530 'systemd-networkd.socket' ,
533 'StartLimitIntervalSec=0' ,
537 'systemd-networkd-persistent-storage.service' ,
540 'StartLimitIntervalSec=0' ,
543 f
'ExecStart= {networkctl_bin} persistent-storage yes' ,
545 f
'ExecStop= {networkctl_bin} persistent-storage no' ,
546 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug
else '' ,
550 check_output ( 'systemctl daemon-reload' )
551 print ( check_output ( 'systemctl cat systemd-networkd.service' ))
552 print ( check_output ( 'systemctl cat systemd-networkd-persistent-storage.service' ))
553 print ( check_output ( 'systemctl cat systemd-resolved.service' ))
554 print ( check_output ( 'systemctl cat systemd-timesyncd.service' ))
555 print ( check_output ( 'systemctl cat systemd-udevd.service' ))
556 check_output ( 'systemctl restart systemd-resolved.service' )
557 check_output ( 'systemctl restart systemd-timesyncd.service' )
558 check_output ( 'systemctl restart systemd-udevd.service' )
560 def clear_system_units ():
562 rm_f ( f
'/run/systemd/system/ {name} ' )
563 rm_rf ( f
'/run/systemd/system/ {name} .d' )
565 rm_unit ( 'systemd-networkd.service' )
566 rm_unit ( 'systemd-networkd.socket' )
567 rm_unit ( 'systemd-networkd-persistent-storage.service' )
568 rm_unit ( 'systemd-resolved.service' )
569 rm_unit ( 'systemd-timesyncd.service' )
570 rm_unit ( 'systemd-udevd.service' )
571 check_output ( 'systemctl daemon-reload' )
572 check_output ( 'systemctl restart systemd-udevd.service' )
574 def link_exists (* links
):
576 if call_quiet ( f
'ip link show {link} ' ) != 0 :
580 def link_resolve ( link
):
581 return check_output ( f
'ip link show {link} ' ). split ( ':' )[ 1 ]. strip (). split ( '@' )[ 0 ]
583 def remove_link (* links
, protect
= False ):
585 if protect
and link
in protected_links
:
587 if link_exists ( link
):
588 call ( f
'ip link del dev {link} ' )
590 def save_existing_links ():
591 links
= os
. listdir ( '/sys/class/net' )
593 if link_exists ( link
):
594 protected_links
. add ( link
)
596 print ( '### The following links will be protected:' )
597 print ( ', ' . join ( sorted ( list ( protected_links
))))
599 def unmanage_existing_links ():
600 mkdir_p ( network_unit_dir
)
602 with
open ( os
. path
. join ( network_unit_dir
, '00-unmanaged.network' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
604 for link
in protected_links
:
605 f
. write ( f
'Name= {link} \n ' )
606 f
. write ( ' \n [Link] \n Unmanaged=yes \n ' )
609 links
= os
. listdir ( '/sys/class/net' )
610 remove_link (* links
, protect
= True )
612 def flush_nexthops ():
613 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
614 # Hence, we cannot restore nexthops in a simple way.
615 # Let's assume there is no nexthop used in the system
616 call_quiet ( 'ip nexthop flush' )
619 # pylint: disable=global-statement
621 saved_routes
= check_output ( 'ip route show table all' )
622 print ( '### The following routes will be protected:' )
627 output
= check_output ( 'ip route show table all' )
628 for line
in output
. splitlines ():
629 if line
in saved_routes
:
631 if 'proto kernel' in line
:
633 if ' dev ' in line
and not ' dev lo ' in line
:
637 print ( '### Removing routes that did not exist when the test started.' )
639 call ( f
'ip route del {line} ' )
641 def save_routing_policy_rules ():
642 # pylint: disable=global-statement
643 global saved_ipv4_rules
, saved_ipv6_rules
645 output
= check_output ( f
'ip - {ipv} rule show' )
646 print ( f
'### The following IPv {ipv} routing policy rules will be protected:' )
650 saved_ipv4_rules
= save ( 4 )
651 saved_ipv6_rules
= save ( 6 )
653 def flush_routing_policy_rules ():
654 def flush ( ipv
, saved_rules
):
656 output
= check_output ( f
'ip - {ipv} rule show' )
657 for line
in output
. splitlines ():
658 if line
in saved_rules
:
662 print ( f
'### Removing IPv {ipv} routing policy rules that did not exist when the test started.' )
664 words
= line
. replace ( 'lookup [l3mdev-table]' , 'l3mdev' ). replace ( '[detached]' , '' ). split ()
665 priority
= words
[ 0 ]. rstrip ( ':' )
666 call ( f
'ip - {ipv} rule del priority {priority} ' + ' ' . join ( words
[ 1 :]))
668 flush ( 4 , saved_ipv4_rules
)
669 flush ( 6 , saved_ipv6_rules
)
671 def flush_fou_ports ():
672 ret
= run ( 'ip fou show' )
673 if ret
. returncode
!= 0 :
674 return # fou may not be supported
675 for line
in ret
. stdout
. splitlines ():
676 port
= line
. split ()[ 1 ]
677 call ( f
'ip fou del port {port} ' )
679 def flush_l2tp_tunnels ():
681 ret
= run ( 'ip l2tp show tunnel' )
682 if ret
. returncode
!= 0 :
683 return # l2tp may not be supported
684 for line
in ret
. stdout
. splitlines ():
686 if words
[ 0 ] == 'Tunnel' :
687 tid
= words
[ 1 ]. rstrip ( ',' )
688 call ( f
'ip l2tp del tunnel tunnel_id {tid} ' )
691 # Removing L2TP tunnel is asynchronous and slightly takes a time.
694 r
= run ( f
'ip l2tp show tunnel tunnel_id {tid} ' )
695 if r
. returncode
!= 0 or len ( r
. stdout
. rstrip ()) == 0 :
699 print ( f
'Cannot remove L2TP tunnel {tid} , ignoring.' )
702 # pylint: disable=global-statement
703 global saved_timezone
704 r
= run (* timedatectl_cmd
, 'show' , '--value' , '--property' , 'Timezone' , env
= env
)
705 if r
. returncode
== 0 :
706 saved_timezone
= r
. stdout
. rstrip ()
707 print ( f
'### Saved timezone: {saved_timezone} ' )
709 def restore_timezone ():
711 call (* timedatectl_cmd
, 'set-timezone' , f
' {saved_timezone} ' , env
= env
)
713 def read_link_attr (* args
):
714 with
open ( os
. path
. join ( '/sys/class/net' , * args
), encoding
= 'utf-8' ) as f
:
715 return f
. readline (). strip ()
717 def read_manager_state_file ():
718 with
open ( '/run/systemd/netif/state' , encoding
= 'utf-8' ) as f
:
721 def read_link_state_file ( link
):
722 ifindex
= read_link_attr ( link
, 'ifindex' )
723 path
= os
. path
. join ( '/run/systemd/netif/links' , ifindex
)
724 with
open ( path
, encoding
= 'utf-8' ) as f
:
727 def read_ip_sysctl_attr ( link
, attribute
, ipv
):
728 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'conf' , link
, attribute
), encoding
= 'utf-8' ) as f
:
729 return f
. readline (). strip ()
731 def read_ip_neigh_sysctl_attr ( link
, attribute
, ipv
):
732 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'neigh' , link
, attribute
), encoding
= 'utf-8' ) as f
:
733 return f
. readline (). strip ()
735 def read_ipv6_sysctl_attr ( link
, attribute
):
736 return read_ip_sysctl_attr ( link
, attribute
, 'ipv6' )
738 def read_ipv6_neigh_sysctl_attr ( link
, attribute
):
739 return read_ip_neigh_sysctl_attr ( link
, attribute
, 'ipv6' )
741 def read_ipv4_sysctl_attr ( link
, attribute
):
742 return read_ip_sysctl_attr ( link
, attribute
, 'ipv4' )
744 def read_mpls_sysctl_attr ( link
, attribute
):
745 return read_ip_sysctl_attr ( link
, attribute
, 'mpls' )
747 def stop_by_pid_file ( pid_file
):
748 if not os
. path
. exists ( pid_file
):
750 with
open ( pid_file
, 'r' , encoding
= 'utf-8' ) as f
:
751 pid
= f
. read (). rstrip ( ' \t\r\n \0' )
752 os
. kill ( int ( pid
), signal
. SIGTERM
)
756 print ( f
"PID {pid} is still alive, waiting..." )
759 if e
. errno
== errno
. ESRCH
:
761 print ( f
"Unexpected exception when waiting for {pid} to die: {e.errno}" )
764 def dnr_v4_instance_data ( adn
, addrs
= None , prio
= 1 , alpns
=( "dot" ,), dohpath
= None ):
766 pack
= lambda c
, w
= 1 : struct
. pack ( '>' + "_BH_I" [ w
], len ( c
)) + c
767 pyton
= lambda n
, w
= 2 : struct
. pack ( '>' + "_BH_I" [ w
], n
)
768 ipv4
= ipaddress
. IPv4Address
769 class SvcParam ( enum
. Enum
):
775 adn
= adn
. rstrip ( '.' ) + '.'
776 data
+= pack ( b
. join ( pack ( label
. encode ( 'ascii' )) for label
in adn
. split ( '.' )))
778 if not addrs
: # adn-only mode
781 data
+= pack ( b
. join ( ipv4 ( addr
). packed
for addr
in addrs
))
782 data
+= pyton ( SvcParam
. ALPN
. value
) + pack ( b
. join ( pack ( alpn
. encode ( 'ascii' )) for alpn
in alpns
), 2 )
783 if dohpath
is not None :
784 data
+= pyton ( SvcParam
. DOHPATH
. value
) + pack ( dohpath
. encode ( 'utf-8' ), 2 )
788 def dnr_v6_instance_data ( adn
, addrs
= None , prio
= 1 , alpns
=( "dot" ,), dohpath
= None ):
790 pack
= lambda c
, w
= 1 : struct
. pack ( '>' + "_BH_I" [ w
], len ( c
)) + c
791 pyton
= lambda n
, w
= 2 : struct
. pack ( '>' + "_BH_I" [ w
], n
)
792 ipv6
= ipaddress
. IPv6Address
793 class SvcParam ( enum
. Enum
):
799 adn
= adn
. rstrip ( '.' ) + '.'
800 data
+= pack ( b
. join ( pack ( label
. encode ( 'ascii' )) for label
in adn
. split ( '.' )), 2 )
802 if not addrs
: # adn-only mode
805 data
+= pack ( b
. join ( ipv6 ( addr
). packed
for addr
in addrs
), 2 )
806 data
+= pyton ( SvcParam
. ALPN
. value
) + pack ( b
. join ( pack ( alpn
. encode ( 'ascii' )) for alpn
in alpns
), 2 )
807 if dohpath
is not None :
808 data
+= pyton ( SvcParam
. DOHPATH
. value
) + pack ( dohpath
. encode ( 'utf-8' ), 2 )
812 def start_dnsmasq (* additional_options
, interface
= 'veth-peer' , ra_mode
= None , ipv4_range
= '192.168.5.10,192.168.5.200' , ipv4_router
= '192.168.5.1' , ipv6_range
= '2600::10,2600::20' ):
814 ra_mode
= f
', {ra_mode} '
820 f
'--log-facility= {dnsmasq_log_file} ' ,
821 '--log-queries=extra' ,
823 f
'--pid-file= {dnsmasq_pid_file} ' ,
824 '--conf-file=/dev/null' ,
826 f
'--interface= {interface} ' ,
827 f
'--dhcp-leasefile= {dnsmasq_lease_file} ' ,
829 f
'--dhcp-range= {ipv6_range}{ra_mode} ,2m' ,
830 f
'--dhcp-range= {ipv4_range} ,2m' ,
831 '--dhcp-option=option:mtu,1492' ,
832 f
'--dhcp-option=option:router, {ipv4_router} ' ,
835 ) + additional_options
836 check_output (* command
)
839 stop_by_pid_file ( dnsmasq_pid_file
)
840 rm_f ( dnsmasq_lease_file
)
841 rm_f ( dnsmasq_log_file
)
843 def read_dnsmasq_log_file ():
844 with
open ( dnsmasq_log_file
, encoding
= 'utf-8' ) as f
:
847 def start_isc_dhcpd ( conf_file
, ipv
, interface
= 'veth-peer' ):
848 conf_file_path
= os
. path
. join ( networkd_ci_temp_dir
, conf_file
)
849 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface} '
850 touch ( isc_dhcpd_lease_file
)
851 check_output ( isc_dhcpd_command
)
853 def stop_isc_dhcpd ():
854 stop_by_pid_file ( isc_dhcpd_pid_file
)
855 rm_f ( isc_dhcpd_lease_file
)
857 def get_dbus_link_path ( link
):
858 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
859 '/org/freedesktop/network1' , 'org.freedesktop.network1.Manager' ,
860 'GetLinkByName' , 's' , link
])
862 assert out
. startswith ( b
'io ' )
864 assert out
. endswith ( b
'"' )
866 return out
[:- 1 ]. split ( '"' )[ 1 ]
868 def get_dhcp_client_state ( link
, family
):
869 link_path
= get_dbus_link_path ( link
)
871 out
= subprocess
. check_output ([ 'busctl' , 'get-property' , 'org.freedesktop.network1' ,
872 link_path
, f
'org.freedesktop.network1.DHCPv {family} Client' , 'State' ])
873 assert out
. startswith ( b
's "' )
875 assert out
. endswith ( b
'"' )
876 return out
[ 3 :- 1 ]. decode ()
878 def get_dhcp4_client_state ( link
):
879 return get_dhcp_client_state ( link
, '4' )
881 def get_dhcp6_client_state ( link
):
882 return get_dhcp_client_state ( link
, '6' )
884 def get_link_description ( link
):
885 link_path
= get_dbus_link_path ( link
)
887 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
888 link_path
, 'org.freedesktop.network1.Link' , 'Describe' ])
889 assert out
. startswith ( b
's "' )
891 assert out
. endswith ( b
'"' )
892 json_raw
= out
[ 2 :]. decode ()
894 description
= json
. loads ( json_raw
) # Convert from escaped sequences to json
895 check_json ( description
)
896 return json
. loads ( description
) # Now parse the json
898 def start_radvd (* additional_options
, config_file
):
899 config_file_path
= os
. path
. join ( networkd_ci_temp_dir
, 'radvd' , config_file
)
902 f
'--pidfile= {radvd_pid_file} ' ,
903 f
'--config= {config_file_path} ' ,
904 '--logmethod=stderr' ,
905 ) + additional_options
906 check_output (* command
)
909 stop_by_pid_file ( radvd_pid_file
)
911 def radvd_check_config ( config_file
):
912 if not shutil
. which ( 'radvd' ):
913 print ( 'radvd is not installed, assuming the config check failed' )
916 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
917 # set up (one instance is @unittest.skipX())
918 config_file_path
= os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf/radvd' , config_file
)
919 return call ( f
'radvd --config= {config_file_path} --configtest' ) == 0
921 def networkd_invocation_id ():
922 return check_output ( 'systemctl show --value -p InvocationID systemd-networkd.service' )
925 return check_output ( 'systemctl show --value -p MainPID systemd-networkd.service' )
927 def read_networkd_log ( invocation_id
= None , since
= None ):
928 if not invocation_id
:
929 invocation_id
= networkd_invocation_id ()
933 '--output=short-monotonic' ,
934 f
'_SYSTEMD_INVOCATION_ID= {invocation_id} ' ,
937 command
. append ( f
'--since= {since} ' )
938 check_output ( 'journalctl --sync' )
939 return check_output (* command
)
941 def networkd_is_failed ():
942 return call_quiet ( 'systemctl is-failed -q systemd-networkd.service' ) != 1
944 def stop_networkd ( show_logs
= True , check_failed
= True ):
946 show_logs
= show_logs
and show_journal
948 invocation_id
= networkd_invocation_id ()
951 check_output ( 'systemctl stop systemd-networkd.socket' )
952 check_output ( 'systemctl stop systemd-networkd.service' )
954 call ( 'systemctl stop systemd-networkd.socket' )
955 call ( 'systemctl stop systemd-networkd.service' )
958 print ( read_networkd_log ( invocation_id
))
960 # Check if networkd exits cleanly.
962 assert not networkd_is_failed ()
964 def start_networkd ():
965 check_output ( 'systemctl start systemd-networkd' )
966 invocation_id
= networkd_invocation_id ()
968 print ( f
'Started systemd-networkd.service: PID= {pid} , Invocation ID= {invocation_id} ' )
970 def restart_networkd ( show_logs
= True ):
972 show_logs
= show_logs
and show_journal
974 invocation_id
= networkd_invocation_id ()
975 check_output ( 'systemctl restart systemd-networkd.service' )
977 print ( read_networkd_log ( invocation_id
))
979 invocation_id
= networkd_invocation_id ()
981 print ( f
'Restarted systemd-networkd.service: PID= {pid} , Invocation ID= {invocation_id} ' )
984 return int ( check_output ( 'systemctl show --value -p MainPID systemd-networkd.service' ))
986 def networkctl (* args
):
987 # Do not call networkctl if networkd is in failed state.
988 # Otherwise, networkd may be restarted and we may get wrong results.
989 assert not networkd_is_failed ()
990 return check_output (*( networkctl_cmd
+ list ( args
)), env
= env
)
992 def networkctl_status (* args
):
993 return networkctl ( '-n' , '0' , 'status' , * args
)
995 def networkctl_json (* args
):
996 return networkctl ( '--json=short' , 'status' , * args
)
998 def networkctl_reconfigure (* links
):
999 networkctl ( 'reconfigure' , * links
)
1001 def networkctl_reload ():
1002 networkctl ( 'reload' )
1004 def resolvectl (* args
):
1005 return check_output (*( resolvectl_cmd
+ list ( args
)), env
= env
)
1007 def timedatectl (* args
):
1008 return check_output (*( timedatectl_cmd
+ list ( args
)), env
= env
)
1011 return check_output (*( udevadm_cmd
+ list ( args
)))
1013 def udevadm_reload ():
1014 udevadm ( 'control' , '--reload' )
1016 def udevadm_trigger (* args
, action
= 'add' ):
1017 udevadm ( 'trigger' , '--settle' , f
'--action= {action} ' , * args
)
1020 # Protect existing links
1021 unmanage_existing_links ()
1023 # We usually show something in each test. So, let's break line to make the title of a test and output
1024 # from the test mixed. Then, flush stream buffer and journals.
1027 check_output ( 'journalctl --sync' )
1029 def tear_down_common ():
1030 # 1. stop DHCP/RA servers
1036 call_quiet ( 'rmmod netdevsim' )
1037 call_quiet ( 'rmmod sch_teql' )
1039 # 3. remove network namespace
1040 call_quiet ( 'ip netns del ns99' )
1043 flush_l2tp_tunnels ()
1047 stop_networkd ( check_failed
= False )
1050 clear_network_units ()
1051 clear_networkd_conf_dropins ()
1052 clear_networkd_state_files ()
1057 flush_routing_policy_rules ()
1060 # 8. flush stream buffer and journals to make not any output from the test with the next one
1062 check_output ( 'journalctl --sync' )
1064 # 9. check the status of networkd
1065 assert not networkd_is_failed ()
1068 rm_rf ( networkd_ci_temp_dir
)
1069 cp_r ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf' ), networkd_ci_temp_dir
)
1071 clear_network_units ()
1072 clear_networkd_conf_dropins ()
1073 clear_networkd_state_files ()
1076 setup_systemd_udev_rules ()
1077 copy_udev_rule ( '00-debug-net.rules' )
1079 # Save current state
1081 save_existing_links ()
1083 save_routing_policy_rules ()
1086 setup_system_units ()
1088 def tearDownModule ():
1089 rm_rf ( networkd_ci_temp_dir
)
1091 clear_network_units ()
1092 clear_networkd_conf_dropins ()
1093 clear_networkd_state_files ()
1097 clear_system_units ()
1098 restore_active_units ()
1100 # Flush stream buffer and journals before showing the test summary.
1102 check_output ( 'journalctl --sync' )
1105 # pylint: disable=no-member
1107 def check_link_exists ( self
, * link
, expected
= True ):
1109 self
. assertTrue ( link_exists (* link
))
1111 self
. assertFalse ( link_exists (* link
))
1113 def check_link_attr ( self
, * args
):
1114 self
. assertEqual ( read_link_attr (* args
[:- 1 ]), args
[- 1 ])
1116 def check_bridge_port_attr ( self
, master
, port
, attribute
, expected
, allow_enoent
= False ):
1117 path
= os
. path
. join ( '/sys/devices/virtual/net' , master
, 'lower_' + port
, 'brport' , attribute
)
1118 if allow_enoent
and not os
. path
. exists ( path
):
1120 with
open ( path
, encoding
= 'utf-8' ) as f
:
1121 self
. assertEqual ( f
. readline (). strip (), expected
)
1123 def check_ipv4_sysctl_attr ( self
, link
, attribute
, expected
):
1124 self
. assertEqual ( read_ipv4_sysctl_attr ( link
, attribute
), expected
)
1126 def check_ipv6_sysctl_attr ( self
, link
, attribute
, expected
):
1127 self
. assertEqual ( read_ipv6_sysctl_attr ( link
, attribute
), expected
)
1129 def check_ipv6_neigh_sysctl_attr ( self
, link
, attribute
, expected
):
1130 self
. assertEqual ( read_ipv6_neigh_sysctl_attr ( link
, attribute
), expected
)
1132 def check_mpls_sysctl_attr ( self
, link
, attribute
, expected
):
1133 self
. assertEqual ( read_mpls_sysctl_attr ( link
, attribute
), expected
)
1135 def wait_links ( self
, * links
, trial
= 40 ):
1136 for _
in range ( trial
):
1137 if link_exists (* links
):
1141 self
. fail ( 'Timed out waiting for all links to be created: ' + ', ' . join ( list ( links
)))
1143 def wait_activated ( self
, link
, state
= 'down' , trial
= 40 ):
1144 # wait for the interface is activated.
1145 needle
= f
' {link} : Bringing link {state} '
1146 flag
= state
. upper ()
1147 self
. wait_links ( link
, trial
= trial
)
1148 self
. check_networkd_log ( needle
, trial
= trial
)
1149 for _
in range ( trial
):
1150 if flag
in check_output ( f
'ip link show {link} ' ):
1154 self
. fail ( f
'Timed out waiting for {link} activated.' )
1156 def wait_operstate ( self
, link
, operstate
= 'degraded' , setup_state
= 'configured' , setup_timeout
= 5 , fail_assert
= True ):
1157 """Wait for the link to reach the specified operstate and/or setup state.
1159 Specify None or '' for either operstate or setup_state to ignore that state.
1160 This will recheck until the state conditions are met or the timeout expires.
1162 If the link successfully matches the requested state, this returns True.
1163 If this times out waiting for the link to match, the behavior depends on the
1164 'fail_assert' parameter; if True, this causes a test assertion failure,
1165 otherwise this returns False. The default is to cause assertion failure.
1167 Note that this function matches on *exactly* the given operstate and setup_state.
1168 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1173 setup_state
= r
'\S+'
1175 for _
in range ( setup_timeout
* 2 ):
1176 if not link_exists ( link
):
1179 output
= networkctl_status ( link
)
1180 if re
. search ( rf
'(?m)^\s*State:\s+ {operstate} \s+\( {setup_state} \)\s*$' , output
):
1185 self
. fail ( f
'Timed out waiting for {link} to reach state {operstate} / {setup_state} ' )
1188 def wait_online ( self
, * links_with_operstate
, timeout
= '20s' , bool_any
= False , ipv4
= False , ipv6
= False , setup_state
= 'configured' , setup_timeout
= 5 , bool_dns
= False ):
1189 """Wait for the links to reach the specified operstate and/or setup state.
1191 This is similar to wait_operstate() but can be used for multiple links,
1192 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1193 The operstate should be specified in the link name, like 'eth0:degraded'.
1194 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1196 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1197 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1199 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1200 If this is set, no setup_state checks are done.
1202 Set 'bool_dns' to True to wait for DNS servers to be accessible.
1204 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1205 This is applied only for the operational state 'degraded' or above.
1207 Note that this function waits for the links to reach *or exceed* the given operstate.
1208 However, the setup_state, if specified, must be matched *exactly*.
1210 This returns if the links reached the requested operstate/setup_state; otherwise it
1211 raises CalledProcessError or fails test assertion.
1213 args
= wait_online_cmd
+ [ f
'--timeout= {timeout} ' ] + [ f
'--interface= {link} ' for link
in links_with_operstate
] + [ f
'--ignore= {link} ' for link
in protected_links
]
1223 check_output (* args
, env
= wait_online_env
)
1224 except subprocess
. CalledProcessError
:
1225 if networkd_is_failed ():
1226 print ( '!!!!! systemd-networkd.service is failed !!!!!' )
1227 call ( 'systemctl status systemd-networkd.service' )
1229 # show detailed status on failure
1230 for link
in links_with_operstate
:
1231 name
= link
. split ( ':' )[ 0 ]
1232 if link_exists ( name
):
1233 print ( networkctl_status ( name
))
1235 print ( f
'Interface {name} not found.' )
1237 if not bool_any
and setup_state
:
1238 for link
in links_with_operstate
:
1239 self
. wait_operstate ( link
. split ( ':' )[ 0 ], None , setup_state
, setup_timeout
)
1241 def wait_address ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1242 for _
in range ( timeout_sec
* 2 ):
1243 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1244 if re
. search ( address_regex
, output
) and 'tentative' not in output
:
1248 self
. assertRegex ( output
, address_regex
)
1250 def wait_address_dropped ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1251 for _
in range ( timeout_sec
* 2 ):
1252 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1253 if not re
. search ( address_regex
, output
):
1257 self
. assertNotRegex ( output
, address_regex
)
1259 def wait_route ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1260 for _
in range ( timeout_sec
* 2 ):
1261 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1262 if re
. search ( route_regex
, output
):
1266 self
. assertRegex ( output
, route_regex
)
1268 def wait_route_dropped ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1269 for _
in range ( timeout_sec
* 2 ):
1270 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1271 if not re
. search ( route_regex
, output
):
1275 self
. assertNotRegex ( output
, route_regex
)
1277 def check_netlabel ( self
, interface
, address
, label
= 'system_u:object_r:root_t:s0' ):
1278 if not shutil
. which ( 'selinuxenabled' ):
1279 print ( '## Checking NetLabel skipped: selinuxenabled command not found.' )
1280 elif call_quiet ( 'selinuxenabled' ) != 0 :
1281 print ( '## Checking NetLabel skipped: SELinux disabled.' )
1282 elif not shutil
. which ( 'netlabelctl' ): # not packaged by all distros
1283 print ( '## Checking NetLabel skipped: netlabelctl command not found.' )
1285 output
= check_output ( 'netlabelctl unlbl list' )
1287 self
. assertRegex ( output
, f
'interface: {interface} ,address: {address} ,label:" {label} "' )
1289 def setup_nftset ( self
, filter_name
, filter_type
, flags
= '' ):
1290 if not shutil
. which ( 'nft' ):
1291 print ( '## Setting up NFT sets skipped: nft command not found.' )
1293 if call ( f
'nft add table inet sd_test' ) != 0 :
1294 print ( '## Setting up NFT table failed.' )
1296 if call ( f
'nft add set inet sd_test {filter_name} {{ type {filter_type} ; {flags} }}' ) != 0 :
1297 print ( '## Setting up NFT sets failed.' )
1300 def teardown_nftset ( self
, * filters
):
1301 if not shutil
. which ( 'nft' ):
1302 print ( '## Tearing down NFT sets skipped: nft command not found.' )
1304 for filter_name
in filters
:
1305 if call ( f
'nft delete set inet sd_test {filter_name} ' ) != 0 :
1306 print ( '## Tearing down NFT sets failed.' )
1308 if call ( f
'nft delete table inet sd_test' ) != 0 :
1309 print ( '## Tearing down NFT table failed.' )
1312 def check_nftset ( self
, filter_name
, contents
):
1313 if not shutil
. which ( 'nft' ):
1314 print ( '## Checking NFT sets skipped: nft command not found.' )
1316 output
= check_output ( f
'nft list set inet sd_test {filter_name} ' )
1318 self
. assertRegex ( output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*' )
1320 def check_networkd_log ( self
, contents
, since
= None , trial
= 20 ):
1321 for _
in range ( trial
):
1322 if contents
in read_networkd_log ( since
= since
):
1326 self
. fail ( f
'" {contents} " not found in journal.' )
1328 def networkctl_check_unit ( self
, ifname
, netdev_file
= None , network_file
= None , link_file
= None ):
1329 output
= networkctl_status ( ifname
)
1332 self
. assertRegex ( output
, rf
'NetDev File: .*/ {netdev_file} \.netdev' )
1334 self
. assertNotIn ( 'NetDev File:' , output
)
1336 self
. assertRegex ( output
, rf
'Network File: .*/ {network_file} \.network' )
1338 self
. assertIn ( 'Network File: n/a' , output
)
1340 self
. assertRegex ( output
, rf
'Link File: .*/ {link_file} \.link' )
1342 class NetworkctlTests ( unittest
. TestCase
, Utilities
):
1350 @expectedFailureIfAlternativeNameIsNotAvailable ()
1351 def test_altname ( self
):
1352 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' , '12-dummy.netdev' , '12-dummy.link' )
1354 self
. wait_online ( 'dummy98:degraded' )
1356 output
= networkctl_status ( 'dummy98' )
1357 self
. assertRegex ( output
, 'hogehogehogehogehogehoge' )
1359 @expectedFailureIfAlternativeNameIsNotAvailable ()
1360 def test_rename_to_altname ( self
):
1361 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' ,
1362 '12-dummy.netdev' , '12-dummy-rename-to-altname.link' )
1364 self
. wait_online ( 'dummyalt:degraded' )
1366 output
= networkctl_status ( 'dummyalt' )
1367 self
. assertIn ( 'hogehogehogehogehogehoge' , output
)
1368 self
. assertNotIn ( 'dummy98' , output
)
1370 def test_reconfigure ( self
):
1371 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1373 self
. wait_online ( 'dummy98:routable' )
1375 output
= check_output ( 'ip -4 address show dev dummy98' )
1377 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1378 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1379 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1381 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
1382 check_output ( 'ip address del 10.1.2.4/16 dev dummy98' )
1383 check_output ( 'ip address del 10.2.2.4/16 dev dummy98' )
1385 networkctl_reconfigure ( 'dummy98' )
1386 self
. wait_online ( 'dummy98:routable' )
1388 output
= check_output ( 'ip -4 address show dev dummy98' )
1390 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1391 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1392 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1394 remove_network_unit ( '25-address-static.network' )
1397 self
. wait_operstate ( 'dummy98' , 'degraded' , setup_state
= 'unmanaged' )
1399 output
= check_output ( 'ip -4 address show dev dummy98' )
1401 self
. assertNotIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1402 self
. assertNotIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1403 self
. assertNotIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1405 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
1407 self
. wait_online ( 'dummy98:routable' )
1409 output
= check_output ( 'ip -4 address show dev dummy98' )
1411 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1412 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1413 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1415 def test_renew ( self
):
1417 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
1418 output
= networkctl_status ( 'veth99' )
1420 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
1421 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
1422 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
1423 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
1425 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
1428 check_json ( networkctl_json ( '--lines=0' , '--stats' , '--all' , '--full' ))
1430 for verb
in [ 'renew' , 'forcerenew' ]:
1431 networkctl ( verb
, 'veth99' )
1433 networkctl ( verb
, 'veth99' , 'veth99' , 'veth99' )
1436 def test_up_down ( self
):
1437 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1439 self
. wait_online ( 'dummy98:routable' )
1441 networkctl ( 'down' , 'dummy98' )
1442 self
. wait_online ( 'dummy98:off' )
1443 networkctl ( 'up' , 'dummy98' )
1444 self
. wait_online ( 'dummy98:routable' )
1445 networkctl ( 'down' , 'dummy98' , 'dummy98' , 'dummy98' )
1446 self
. wait_online ( 'dummy98:off' )
1447 networkctl ( 'up' , 'dummy98' , 'dummy98' , 'dummy98' )
1448 self
. wait_online ( 'dummy98:routable' )
1450 def test_reload ( self
):
1453 copy_network_unit ( '11-dummy.netdev' )
1455 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'unmanaged' )
1457 copy_network_unit ( '11-dummy.network' )
1459 self
. wait_online ( 'test1:degraded' )
1461 remove_network_unit ( '11-dummy.network' )
1463 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1465 remove_network_unit ( '11-dummy.netdev' )
1467 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1469 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1471 self
. wait_operstate ( 'test1' , 'degraded' )
1473 def test_glob ( self
):
1474 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1477 self
. wait_online ( 'test1:degraded' )
1479 output
= networkctl ( 'list' )
1480 self
. assertRegex ( output
, '1 lo ' )
1481 self
. assertRegex ( output
, 'test1' )
1483 output
= networkctl ( 'list' , 'test1' )
1484 self
. assertNotRegex ( output
, '1 lo ' )
1485 self
. assertRegex ( output
, 'test1' )
1487 output
= networkctl ( 'list' , 'te*' )
1488 self
. assertNotRegex ( output
, '1 lo ' )
1489 self
. assertRegex ( output
, 'test1' )
1491 output
= networkctl_status ( 'te*' )
1492 self
. assertNotRegex ( output
, '1: lo ' )
1493 self
. assertRegex ( output
, 'test1' )
1495 output
= networkctl_status ( 'tes[a-z][0-9]' )
1496 self
. assertNotRegex ( output
, '1: lo ' )
1497 self
. assertRegex ( output
, 'test1' )
1500 copy_network_unit ( '11-dummy-mtu.netdev' , '11-dummy.network' )
1503 self
. wait_online ( 'test1:degraded' )
1505 output
= networkctl_status ( 'test1' )
1506 self
. assertRegex ( output
, 'MTU: 1600' )
1508 def test_type ( self
):
1509 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1511 self
. wait_online ( 'test1:degraded' )
1513 output
= networkctl_status ( 'test1' )
1515 self
. assertRegex ( output
, 'Type: ether' )
1517 output
= networkctl_status ( 'lo' )
1519 self
. assertRegex ( output
, 'Type: loopback' )
1521 def test_unit_file ( self
):
1522 copy_network_unit ( '11-test-unit-file.netdev' , '11-test-unit-file.network' , '11-test-unit-file.link' )
1524 self
. wait_online ( 'test1:degraded' )
1526 output
= networkctl_status ( 'test1' )
1528 self
. assertIn ( 'Link File: /run/systemd/network/11-test-unit-file.link' , output
)
1529 self
. assertIn ( '/run/systemd/network/11-test-unit-file.link.d/dropin.conf' , output
)
1530 self
. assertIn ( 'Network File: /run/systemd/network/11-test-unit-file.network' , output
)
1531 self
. assertIn ( '/run/systemd/network/11-test-unit-file.network.d/dropin.conf' , output
)
1533 self
. check_networkd_log ( 'test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).' )
1535 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1536 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1537 # Let's reprocess the interface and drop the property.
1538 udevadm_trigger ( '/sys/class/net/lo' )
1539 output
= networkctl_status ( 'lo' )
1541 self
. assertIn ( 'Link File: n/a' , output
)
1542 self
. assertIn ( 'Network File: n/a' , output
)
1544 def test_delete_links ( self
):
1545 copy_network_unit ( '11-dummy.netdev' , '25-veth.netdev' )
1547 self
. wait_links ( 'test1' , 'veth99' , 'veth-peer' )
1548 networkctl ( 'delete' , 'test1' , 'veth99' )
1549 self
. check_link_exists ( 'test1' , 'veth99' , 'veth-peer' , expected
= False )
1551 def test_label ( self
):
1554 class NetworkdMatchTests ( unittest
. TestCase
, Utilities
):
1562 @expectedFailureIfAlternativeNameIsNotAvailable ()
1563 def test_match ( self
):
1564 copy_network_unit ( '12-dummy-mac.netdev' ,
1565 '12-dummy-match-mac-01.network' ,
1566 '12-dummy-match-mac-02.network' ,
1567 '12-dummy-match-renamed.network' ,
1568 '12-dummy-match-altname.network' ,
1569 '12-dummy-altname.link' )
1572 self
. wait_online ( 'dummy98:routable' )
1573 output
= networkctl_status ( 'dummy98' )
1574 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-01.network' , output
)
1575 output
= check_output ( 'ip -4 address show dev dummy98' )
1576 self
. assertIn ( '10.0.0.1/16' , output
)
1578 check_output ( 'ip link set dev dummy98 down' )
1579 check_output ( 'ip link set dev dummy98 address 12:34:56:78:9a:02' )
1581 self
. wait_address ( 'dummy98' , '10.0.0.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1582 self
. wait_online ( 'dummy98:routable' )
1583 output
= networkctl_status ( 'dummy98' )
1584 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-02.network' , output
)
1586 check_output ( 'ip link set dev dummy98 down' )
1587 check_output ( 'ip link set dev dummy98 name dummy98-1' )
1589 self
. wait_address ( 'dummy98-1' , '10.0.1.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1590 self
. wait_online ( 'dummy98-1:routable' )
1591 output
= networkctl_status ( 'dummy98-1' )
1592 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-renamed.network' , output
)
1594 check_output ( 'ip link set dev dummy98-1 down' )
1595 check_output ( 'ip link set dev dummy98-1 name dummy98-2' )
1596 udevadm_trigger ( '/sys/class/net/dummy98-2' )
1598 self
. wait_address ( 'dummy98-2' , '10.0.2.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1599 self
. wait_online ( 'dummy98-2:routable' )
1600 output
= networkctl_status ( 'dummy98-2' )
1601 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-altname.network' , output
)
1603 def test_match_udev_property ( self
):
1604 copy_network_unit ( '12-dummy.netdev' , '13-not-match-udev-property.network' , '14-match-udev-property.network' )
1606 self
. wait_online ( 'dummy98:routable' )
1608 output
= networkctl_status ( 'dummy98' )
1610 self
. assertRegex ( output
, 'Network File: /run/systemd/network/14-match-udev-property' )
1612 class WaitOnlineTests ( unittest
. TestCase
, Utilities
):
1620 def test_wait_online_any ( self
):
1621 copy_network_unit ( '25-bridge.netdev' , '25-bridge.network' , '11-dummy.netdev' , '11-dummy.network' )
1624 self
. wait_online ( 'bridge99' , 'test1:degraded' , bool_any
= True )
1626 self
. wait_operstate ( 'bridge99' , '(off|no-carrier)' , setup_state
= 'configuring' )
1627 self
. wait_operstate ( 'test1' , 'degraded' )
1629 def do_test_wait_online_dns (
1633 expect_timeout
= False ,
1634 network_dropin
= None ,
1636 global wait_online_env
1638 if network_dropin
is not None :
1639 network_dropin_path
= os
. path
. join (
1641 '25-dhcp-client-use-dns-ipv4.network.d/test.conf'
1643 mkdir_p ( os
. path
. dirname ( network_dropin_path
))
1644 with
open ( network_dropin_path
, 'w' ) as f
:
1645 f
. write ( network_dropin
)
1649 '25-dhcp-client-use-dns-ipv4.network' ,
1650 '25-dhcp-server.network'
1653 self
. wait_online ( 'veth-peer:routable' )
1655 # Unless given, clear global DNS configuration
1656 resolved_dropin
= '/run/systemd/resolved.conf.d/global-dns.conf'
1657 mkdir_p ( os
. path
. dirname ( resolved_dropin
))
1658 with
open ( resolved_dropin
, 'w' ) as f
:
1661 f
'DNS= {global_dns} \n '
1662 f
'FallbackDNS= {fallback_dns} \n '
1664 self
. addCleanup ( os
. remove
, resolved_dropin
)
1665 check_output ( 'systemctl reload systemd-resolved' )
1668 wait_online_env_copy
= wait_online_env
. copy ()
1670 wait_online_env
[ 'SYSTEMD_LOG_LEVEL' ] = 'debug'
1671 wait_online_env
[ 'SYSTEMD_LOG_TARGET' ] = 'console'
1673 self
. wait_online ( 'veth99:routable' , bool_dns
= True )
1676 # The above should have thrown an exception.
1678 'Expected systemd-networkd-wait-online to time out'
1681 except subprocess
. CalledProcessError
as e
:
1685 f
'veth99: No link-specific DNS server is accessible' ,
1686 f
'Missing expected log message: \n {e.output}'
1690 f
'Command timed out: \n {e.output}'
1693 wait_online_env
= wait_online_env_copy
1695 def test_wait_online_dns ( self
):
1696 ''' test systemd-networkd-wait-online with --dns '''
1697 self
. do_test_wait_online_dns ()
1699 def test_wait_online_dns_global ( self
):
1701 test systemd-networkd-wait-online with --dns, expect pass due to global DNS
1704 # Set UseDNS=no, and allow global DNS to be used.
1705 self
. do_test_wait_online_dns (
1706 global_dns
= '192.168.5.1' ,
1713 def test_wait_online_dns_expect_timeout ( self
):
1714 ''' test systemd-networkd-wait-online with --dns, and expect timeout '''
1716 # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
1717 self
. do_test_wait_online_dns (
1718 expect_timeout
= True ,
1721 'DNSDefaultRoute=yes \n '
1727 def test_wait_online_dns_expect_timeout_global ( self
):
1729 test systemd-networkd-wait-online with --dns, and expect timeout
1733 # Configure Domains=~., and expect timeout despite global DNS servers
1735 self
. do_test_wait_online_dns (
1736 expect_timeout
= True ,
1737 global_dns
= '192.168.5.1' ,
1747 class NetworkdNetDevTests ( unittest
. TestCase
, Utilities
):
1755 def test_dropin_and_name_conflict ( self
):
1756 copy_network_unit ( '10-dropin-test.netdev' , '15-name-conflict-test.netdev' )
1759 self
. wait_online ( 'dropin-test:off' , setup_state
= 'unmanaged' )
1761 output
= check_output ( 'ip link show dropin-test' )
1763 self
. assertRegex ( output
, '00:50:56:c0:00:28' )
1765 @expectedFailureIfModuleIsNotAvailable ( 'bareudp' )
1766 def test_bareudp ( self
):
1767 copy_network_unit ( '25-bareudp.netdev' , '26-netdev-link-local-addressing-yes.network' )
1770 self
. wait_online ( 'bareudp99:degraded' )
1771 self
. networkctl_check_unit ( 'bareudp99' , '25-bareudp' , '26-netdev-link-local-addressing-yes' )
1773 output
= check_output ( 'ip -d link show bareudp99' )
1775 self
. assertRegex ( output
, 'dstport 1000 ' )
1776 self
. assertRegex ( output
, 'ethertype ip ' )
1777 self
. assertRegex ( output
, 'srcportmin 1001 ' )
1779 touch_network_unit ( '25-bareudp.netdev' , '26-netdev-link-local-addressing-yes.network' )
1781 self
. wait_online ( 'bareudp99:degraded' )
1783 @expectedFailureIfModuleIsNotAvailable ( 'batman-adv' )
1784 def test_batadv ( self
):
1785 copy_network_unit ( '25-batadv.netdev' , '26-netdev-link-local-addressing-yes.network' )
1788 self
. wait_online ( 'batadv99:degraded' )
1789 self
. networkctl_check_unit ( 'batadv99' , '25-batadv' , '26-netdev-link-local-addressing-yes' )
1791 output
= check_output ( 'ip -d link show batadv99' )
1793 self
. assertRegex ( output
, 'batadv' )
1795 touch_network_unit ( '25-batadv.netdev' , '26-netdev-link-local-addressing-yes.network' )
1797 self
. wait_online ( 'batadv99:degraded' )
1799 def test_bridge ( self
):
1800 copy_network_unit ( '25-bridge.netdev' , '25-bridge-configure-without-carrier.network' )
1803 self
. wait_online ( 'bridge99:no-carrier' )
1804 self
. networkctl_check_unit ( 'bridge99' , '25-bridge' , '25-bridge-configure-without-carrier' )
1806 tick
= os
. sysconf ( 'SC_CLK_TCK' )
1807 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'hello_time' )) / tick
))
1808 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'max_age' )) / tick
))
1809 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'forward_delay' )) / tick
))
1810 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'ageing_time' )) / tick
))
1811 self
. assertEqual ( 9 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'priority' )))
1812 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_querier' )))
1813 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_snooping' )))
1814 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'stp_state' )))
1815 self
. assertEqual ( 3 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_igmp_version' )))
1816 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'no_linklocal_learn' )))
1818 output
= networkctl_status ( 'bridge99' )
1820 self
. assertRegex ( output
, 'Priority: 9' )
1821 self
. assertRegex ( output
, 'STP: yes' )
1822 self
. assertRegex ( output
, 'Multicast IGMP Version: 3' )
1823 if 'FDB Max Learned' in output
:
1824 self
. assertRegex ( output
, 'FDB Max Learned: 4' )
1826 output
= check_output ( 'ip -d link show bridge99' )
1828 self
. assertIn ( 'vlan_filtering 1 ' , output
)
1829 self
. assertIn ( 'vlan_protocol 802.1ad ' , output
)
1830 self
. assertIn ( 'vlan_default_pvid 9 ' , output
)
1831 if 'fdb_max_learned' in output
:
1832 self
. assertIn ( 'fdb_max_learned 4 ' , output
)
1834 def test_bond ( self
):
1835 copy_network_unit ( '25-bond.netdev' , '25-bond-balanced-tlb.netdev' , '25-bond-property.netdev' )
1838 self
. wait_online ( 'bond99:off' , 'bond98:off' , 'bond97:off' , setup_state
= 'unmanaged' )
1839 self
. networkctl_check_unit ( 'bond99' , '25-bond' )
1840 self
. networkctl_check_unit ( 'bond98' , '25-bond-balanced-tlb' )
1841 self
. networkctl_check_unit ( 'bond97' , '25-bond-property' )
1843 self
. check_link_attr ( 'bond99' , 'bonding' , 'mode' , '802.3ad 4' )
1844 self
. check_link_attr ( 'bond99' , 'bonding' , 'xmit_hash_policy' , 'layer3+4 1' )
1845 self
. check_link_attr ( 'bond99' , 'bonding' , 'miimon' , '1000' )
1846 self
. check_link_attr ( 'bond99' , 'bonding' , 'lacp_rate' , 'fast 1' )
1847 self
. check_link_attr ( 'bond99' , 'bonding' , 'updelay' , '2000' )
1848 self
. check_link_attr ( 'bond99' , 'bonding' , 'downdelay' , '2000' )
1849 self
. check_link_attr ( 'bond99' , 'bonding' , 'resend_igmp' , '4' )
1850 self
. check_link_attr ( 'bond99' , 'bonding' , 'min_links' , '1' )
1851 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_sys_prio' , '1218' )
1852 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_user_port_key' , '811' )
1853 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_system' , '00:11:22:33:44:55' )
1855 self
. check_link_attr ( 'bond98' , 'bonding' , 'mode' , 'balance-tlb 5' )
1856 self
. check_link_attr ( 'bond98' , 'bonding' , 'tlb_dynamic_lb' , '1' )
1858 output
= networkctl_status ( 'bond99' )
1860 self
. assertIn ( 'Mode: 802.3ad' , output
)
1861 self
. assertIn ( 'Miimon: 1s' , output
)
1862 self
. assertIn ( 'Updelay: 2s' , output
)
1863 self
. assertIn ( 'Downdelay: 2s' , output
)
1865 output
= networkctl_status ( 'bond98' )
1867 self
. assertIn ( 'Mode: balance-tlb' , output
)
1869 output
= networkctl_status ( 'bond97' )
1872 self
. check_link_attr ( 'bond97' , 'bonding' , 'arp_missed_max' , '10' )
1873 self
. check_link_attr ( 'bond97' , 'bonding' , 'peer_notif_delay' , '300000' )
1875 def check_vlan ( self
, id , flags
):
1876 self
. wait_online ( 'test1:degraded' , 'vlan99:routable' )
1877 self
. networkctl_check_unit ( 'vlan99' , '21-vlan' , '21-vlan' )
1878 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '21-vlan-test1' )
1880 output
= check_output ( 'ip -d link show test1' )
1882 self
. assertRegex ( output
, ' mtu 2000 ' )
1884 output
= check_output ( 'ip -d link show vlan99' )
1886 self
. assertIn ( ' mtu 2000 ' , output
)
1888 self
. assertIn ( 'REORDER_HDR' , output
)
1889 self
. assertIn ( 'LOOSE_BINDING' , output
)
1890 self
. assertIn ( 'GVRP' , output
)
1891 self
. assertIn ( 'MVRP' , output
)
1893 self
. assertNotIn ( 'REORDER_HDR' , output
)
1894 self
. assertNotIn ( 'LOOSE_BINDING' , output
)
1895 self
. assertNotIn ( 'GVRP' , output
)
1896 self
. assertNotIn ( 'MVRP' , output
)
1897 self
. assertIn ( f
' id {id} ' , output
)
1898 self
. assertIn ( 'ingress-qos-map { 4:100 7:13 }' , output
)
1899 self
. assertIn ( 'egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }' , output
)
1901 output
= check_output ( 'ip -4 address show dev test1' )
1903 self
. assertRegex ( output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1' )
1904 self
. assertRegex ( output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1' )
1906 output
= check_output ( 'ip -4 address show dev vlan99' )
1908 self
. assertRegex ( output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99' )
1910 def test_vlan ( self
):
1911 copy_network_unit ( '21-vlan.netdev' , '11-dummy.netdev' ,
1912 '21-vlan.network' , '21-vlan-test1.network' )
1914 self
. check_vlan ( id = 99 , flags
= True )
1916 # Test for reloading .netdev file. See issue #34907.
1917 with
open ( os
. path
. join ( network_unit_dir
, '21-vlan.netdev.d/override.conf' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1918 f
. write ( '[VLAN] \n Id=42 \n ' )
1920 # VLAN ID cannot be changed, so we need to remove the existing netdev.
1921 check_output ( "ip link del vlan99" )
1923 self
. check_vlan ( id = 42 , flags
= True )
1925 with
open ( os
. path
. join ( network_unit_dir
, '21-vlan.netdev.d/override.conf' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1930 'ReorderHeader=no \n ' )
1932 # flags can be changed, hence it is not necessary to remove the existing netdev.
1934 self
. check_vlan ( id = 42 , flags
= False )
1936 def test_vlan_on_bond ( self
):
1937 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1938 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1940 copy_network_unit ( '21-bond-802.3ad.netdev' , '21-bond-802.3ad.network' ,
1941 '21-vlan-on-bond.netdev' , '21-vlan-on-bond.network' )
1943 self
. wait_online ( 'bond99:off' )
1944 self
. wait_operstate ( 'vlan99' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 10 )
1945 self
. networkctl_check_unit ( 'vlan99' , '21-vlan-on-bond' , '21-vlan-on-bond' )
1946 self
. networkctl_check_unit ( 'bond99' , '21-bond-802.3ad' , '21-bond-802.3ad' )
1948 self
. check_networkd_log ( 'vlan99: Could not bring up interface, ignoring: Network is down' )
1950 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '21-dummy-bond-slave.network' )
1952 self
. wait_online ( 'test1:enslaved' , 'dummy98:enslaved' , 'bond99:carrier' , 'vlan99:routable' )
1954 def test_macvtap ( self
):
1956 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1962 print ( f
'### test_macvtap(mode= {mode} )' )
1963 with self
. subTest ( mode
= mode
):
1964 copy_network_unit ( '21-macvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1965 '11-dummy.netdev' , '25-macvtap.network' )
1966 with
open ( os
. path
. join ( network_unit_dir
, '21-macvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1967 f
. write ( '[MACVTAP] \n Mode=' + mode
)
1970 self
. wait_online ( 'macvtap99:degraded' ,
1971 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1972 self
. networkctl_check_unit ( 'macvtap99' , '21-macvtap' , '26-netdev-link-local-addressing-yes' )
1973 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-macvtap' )
1975 output
= check_output ( 'ip -d link show macvtap99' )
1977 self
. assertRegex ( output
, 'macvtap mode ' + mode
+ ' ' )
1979 touch_network_unit ( '21-macvtap.netdev' )
1981 self
. wait_online ( 'macvtap99:degraded' ,
1982 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1984 @expectedFailureIfModuleIsNotAvailable ( 'macvlan' )
1985 def test_macvlan ( self
):
1987 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1993 print ( f
'### test_macvlan(mode= {mode} )' )
1994 with self
. subTest ( mode
= mode
):
1995 copy_network_unit ( '21-macvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1996 '11-dummy.netdev' , '25-macvlan.network' )
1997 with
open ( os
. path
. join ( network_unit_dir
, '21-macvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1998 f
. write ( '[MACVLAN] \n Mode=' + mode
)
2001 self
. wait_online ( 'macvlan99:degraded' ,
2002 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
2003 self
. networkctl_check_unit ( 'macvlan99' , '21-macvlan' , '26-netdev-link-local-addressing-yes' )
2004 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-macvlan' )
2006 output
= check_output ( 'ip -d link show test1' )
2008 self
. assertIn ( ' mtu 2000 ' , output
)
2010 output
= check_output ( 'ip -d link show macvlan99' )
2012 self
. assertIn ( ' mtu 2000 ' , output
)
2013 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
2015 remove_link ( 'test1' )
2018 check_output ( "ip link add test1 type dummy" )
2019 self
. wait_online ( 'macvlan99:degraded' ,
2020 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
2022 output
= check_output ( 'ip -d link show test1' )
2024 self
. assertIn ( ' mtu 2000 ' , output
)
2026 output
= check_output ( 'ip -d link show macvlan99' )
2028 self
. assertIn ( ' mtu 2000 ' , output
)
2029 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
2030 self
. assertIn ( ' bcqueuelen 1234 ' , output
)
2031 if ' bclim ' in output
: # This is new in kernel and iproute2 v6.4
2032 self
. assertIn ( ' bclim 2147483647 ' , output
)
2034 touch_network_unit ( '21-macvlan.netdev' )
2036 self
. wait_online ( 'macvlan99:degraded' ,
2037 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
2039 @expectedFailureIfModuleIsNotAvailable ( 'ipvlan' )
2040 def test_ipvlan ( self
):
2042 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
2048 print ( f
'### test_ipvlan(mode= {mode} , flag= {flag} )' )
2049 with self
. subTest ( mode
= mode
, flag
= flag
):
2050 copy_network_unit ( '25-ipvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2051 '11-dummy.netdev' , '25-ipvlan.network' )
2052 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2053 f
. write ( '[IPVLAN] \n Mode=' + mode
+ ' \n Flags=' + flag
)
2056 self
. wait_online ( 'ipvlan99:degraded' , 'test1:degraded' )
2057 self
. networkctl_check_unit ( 'ipvlan99' , '25-ipvlan' , '26-netdev-link-local-addressing-yes' )
2058 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-ipvlan' )
2060 output
= check_output ( 'ip -d link show ipvlan99' )
2062 self
. assertRegex ( output
, 'ipvlan *mode ' + mode
. lower () + ' ' + flag
)
2064 touch_network_unit ( '25-ipvlan.netdev' )
2066 self
. wait_online ( 'ipvlan99:degraded' , 'test1:degraded' )
2068 @expectedFailureIfModuleIsNotAvailable ( 'hsr' )
2071 for proto
, supervision
in [[ 'hsr' , 9 ], [ 'prp' , 127 ]]:
2077 print ( f
'### test_hsr(proto= {proto} , supervision= {supervision} )' )
2078 with self
. subTest ( proto
= proto
, supervision
= supervision
):
2079 copy_network_unit ( '25-hsr.netdev' , '25-hsr.network' ,
2080 '11-dummy.netdev' , '11-dummy.network' ,
2081 '12-dummy.netdev' , '12-dummy-no-address.network' )
2082 with
open ( os
. path
. join ( network_unit_dir
, '25-hsr.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2083 f
. write ( 'Protocol=' + proto
+ ' \n Supervision=' + str ( supervision
))
2086 self
. wait_online ( 'hsr99:degraded' )
2087 self
. networkctl_check_unit ( 'hsr99' , '25-hsr' , '25-hsr' )
2088 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '11-dummy' )
2089 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy-no-address' )
2091 output
= check_output ( 'ip -d link show hsr99' )
2093 self
. assertRegex ( output
, 'hsr slave1 test1 slave2 dummy98' )
2094 self
. assertRegex ( output
, f
'supervision 01:15:4e:00:01:{supervision:02x}' )
2095 self
. assertRegex ( output
, 'proto ' + ( '0' if proto
== 'hsr' else '1' ) + ' ' )
2097 touch_network_unit ( '25-hsr.netdev' )
2099 self
. wait_online ( 'hsr99:degraded' )
2101 @expectedFailureIfModuleIsNotAvailable ( 'ipvtap' )
2102 def test_ipvtap ( self
):
2104 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
2110 print ( f
'### test_ipvtap(mode= {mode} , flag= {flag} )' )
2111 with self
. subTest ( mode
= mode
, flag
= flag
):
2112 copy_network_unit ( '25-ipvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2113 '11-dummy.netdev' , '25-ipvtap.network' )
2114 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2115 f
. write ( '[IPVTAP] \n Mode=' + mode
+ ' \n Flags=' + flag
)
2118 self
. wait_online ( 'ipvtap99:degraded' , 'test1:degraded' )
2119 self
. networkctl_check_unit ( 'ipvtap99' , '25-ipvtap' , '26-netdev-link-local-addressing-yes' )
2120 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-ipvtap' )
2122 output
= check_output ( 'ip -d link show ipvtap99' )
2124 self
. assertRegex ( output
, 'ipvtap *mode ' + mode
. lower () + ' ' + flag
)
2126 touch_network_unit ( '25-ipvtap.netdev' )
2128 self
. wait_online ( 'ipvtap99:degraded' , 'test1:degraded' )
2130 def test_veth ( self
):
2131 copy_network_unit ( '25-veth.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2132 '25-veth-mtu.netdev' )
2135 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' , 'veth-mtu:degraded' , 'veth-mtu-peer:degraded' )
2136 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '26-netdev-link-local-addressing-yes' )
2137 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '26-netdev-link-local-addressing-yes' )
2138 self
. networkctl_check_unit ( 'veth-mtu' , '25-veth-mtu' , '26-netdev-link-local-addressing-yes' )
2139 self
. networkctl_check_unit ( 'veth-mtu-peer' , '25-veth-mtu' , '26-netdev-link-local-addressing-yes' )
2141 output
= check_output ( 'ip -d link show veth99' )
2143 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bc' )
2144 output
= check_output ( 'ip -d link show veth-peer' )
2146 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bd' )
2148 output
= check_output ( 'ip -d link show veth-mtu' )
2150 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:be' )
2151 self
. assertRegex ( output
, 'mtu 1800' )
2152 output
= check_output ( 'ip -d link show veth-mtu-peer' )
2154 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bf' )
2155 self
. assertRegex ( output
, 'mtu 1800' )
2159 '26-netdev-link-local-addressing-yes.network' ,
2160 '25-veth-mtu.netdev' )
2164 'veth-peer:degraded' ,
2165 'veth-mtu:degraded' ,
2166 'veth-mtu-peer:degraded' )
2168 def check_tuntap ( self
, attached
):
2169 pid
= networkd_pid ()
2170 name
= psutil
. Process ( pid
). name ()[: 15 ]
2172 output
= check_output ( 'ip -d -oneline tuntap show' )
2174 self
. assertRegex ( output
, r
'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:' )
2175 self
. assertRegex ( output
, r
'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:' )
2178 self
. assertRegex ( output
, fr
'testtap99: .* {name} \( {pid} \)' )
2179 self
. assertRegex ( output
, fr
'testtun99: .* {name} \( {pid} \)' )
2180 self
. assertRegex ( output
, r
'testtap99: .*systemd\(1\)' )
2181 self
. assertRegex ( output
, r
'testtun99: .*systemd\(1\)' )
2183 output
= check_output ( 'ip -d link show testtun99' )
2185 # Old ip command does not support IFF_ flags
2186 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
2187 self
. assertIn ( 'UP,LOWER_UP' , output
)
2189 output
= check_output ( 'ip -d link show testtap99' )
2191 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
2192 self
. assertIn ( 'UP,LOWER_UP' , output
)
2195 self
. assertNotIn ( f
' {name} ( {pid} )' , output
)
2196 self
. assertNotIn ( 'systemd(1)' , output
)
2199 output
= check_output ( 'ip -d link show testtun99' )
2201 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
2202 if 'NO-CARRIER' in output
:
2209 output
= check_output ( 'ip -d link show testtap99' )
2211 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
2212 if 'NO-CARRIER' in output
:
2218 def test_tuntap ( self
):
2219 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '26-netdev-link-local-addressing-yes.network' )
2221 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
2222 self
. networkctl_check_unit ( 'testtap99' , '25-tap' , '26-netdev-link-local-addressing-yes' )
2223 self
. networkctl_check_unit ( 'testtun99' , '25-tun' , '26-netdev-link-local-addressing-yes' )
2225 self
. check_tuntap ( True )
2227 touch_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '26-netdev-link-local-addressing-yes.network' )
2229 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
2231 self
. check_tuntap ( True )
2233 remove_network_unit ( '26-netdev-link-local-addressing-yes.network' )
2235 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' , setup_state
= 'unmanaged' )
2236 self
. networkctl_check_unit ( 'testtap99' , '25-tap' )
2237 self
. networkctl_check_unit ( 'testtun99' , '25-tun' )
2239 self
. check_tuntap ( True )
2241 clear_network_units ()
2242 unmanage_existing_links ()
2244 self
. wait_online ( 'testtun99:off' , 'testtap99:off' , setup_state
= 'unmanaged' )
2245 self
. networkctl_check_unit ( 'testtap99' )
2246 self
. networkctl_check_unit ( 'testtun99' )
2248 self
. check_tuntap ( False )
2250 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
2252 copy_network_unit ( '25-vrf.netdev' , '26-netdev-link-local-addressing-yes.network' )
2255 self
. wait_online ( 'vrf99:carrier' )
2256 self
. networkctl_check_unit ( 'vrf99' , '25-vrf' , '26-netdev-link-local-addressing-yes' )
2258 touch_network_unit ( '25-vrf.netdev' , '26-netdev-link-local-addressing-yes.network' )
2260 self
. wait_online ( 'vrf99:carrier' )
2262 @expectedFailureIfModuleIsNotAvailable ( 'vcan' )
2263 def test_vcan ( self
):
2264 copy_network_unit ( '25-vcan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2265 '25-vcan98.netdev' , '25-vcan98.network' )
2268 self
. wait_online ( 'vcan99:carrier' , 'vcan98:carrier' )
2269 # For can devices, 'carrier' is the default required operational state.
2270 self
. wait_online ( 'vcan99' , 'vcan98' )
2271 self
. networkctl_check_unit ( 'vcan99' , '25-vcan' , '26-netdev-link-local-addressing-yes' )
2272 self
. networkctl_check_unit ( 'vcan98' , '25-vcan98' , '25-vcan98' )
2274 # https://github.com/systemd/systemd/issues/30140
2275 output
= check_output ( 'ip -d link show vcan99' )
2277 self
. assertIn ( 'mtu 16 ' , output
)
2279 output
= check_output ( 'ip -d link show vcan98' )
2281 self
. assertIn ( 'mtu 16 ' , output
)
2285 '26-netdev-link-local-addressing-yes.network' ,
2287 '25-vcan98.network' )
2289 self
. wait_online ( 'vcan99:carrier' , 'vcan98:carrier' )
2291 @expectedFailureIfModuleIsNotAvailable ( 'vxcan' )
2292 def test_vxcan ( self
):
2293 copy_network_unit ( '25-vxcan.netdev' , '26-netdev-link-local-addressing-yes.network' )
2296 self
. wait_online ( 'vxcan99:carrier' , 'vxcan-peer:carrier' )
2297 # For can devices, 'carrier' is the default required operational state.
2298 self
. wait_online ( 'vxcan99' , 'vxcan-peer' )
2299 self
. networkctl_check_unit ( 'vxcan99' , '25-vxcan' , '26-netdev-link-local-addressing-yes' )
2300 self
. networkctl_check_unit ( 'vxcan-peer' , '25-vxcan' , '26-netdev-link-local-addressing-yes' )
2302 touch_network_unit ( '25-vxcan.netdev' , '26-netdev-link-local-addressing-yes.network' )
2304 self
. wait_online ( 'vxcan99:carrier' , 'vxcan-peer:carrier' )
2306 @expectedFailureIfModuleIsNotAvailable ( 'wireguard' )
2307 def test_wireguard ( self
):
2308 copy_credential ( '25-wireguard-endpoint-peer0-cred.txt' , 'network.wireguard.peer0.endpoint' )
2309 copy_credential ( '25-wireguard-preshared-key-peer2-cred.txt' , 'network.wireguard.peer2.psk' )
2310 copy_credential ( '25-wireguard-no-peer-private-key-cred.txt' , 'network.wireguard.private.25-wireguard-no-peer' )
2312 copy_network_unit ( '25-wireguard.netdev' , '25-wireguard.network' ,
2313 '25-wireguard-23-peers.netdev' , '25-wireguard-23-peers.network' ,
2314 '25-wireguard-public-key.txt' , '25-wireguard-preshared-key.txt' , '25-wireguard-private-key.txt' ,
2315 '25-wireguard-no-peer.netdev' , '25-wireguard-no-peer.network' )
2317 self
. wait_online ( 'wg99:routable' , 'wg98:routable' , 'wg97:carrier' )
2318 self
. networkctl_check_unit ( 'wg99' , '25-wireguard' , '25-wireguard' )
2319 self
. networkctl_check_unit ( 'wg98' , '25-wireguard-23-peers' , '25-wireguard-23-peers' )
2320 self
. networkctl_check_unit ( 'wg97' , '25-wireguard-no-peer' , '25-wireguard-no-peer' )
2322 output
= check_output ( 'ip -4 address show dev wg99' )
2324 self
. assertIn ( 'inet 192.168.124.1/24 scope global wg99' , output
)
2326 output
= check_output ( 'ip -4 address show dev wg99' )
2328 self
. assertIn ( 'inet 169.254.11.1/24 scope link wg99' , output
)
2330 output
= check_output ( 'ip -6 address show dev wg99' )
2332 self
. assertIn ( 'inet6 fe80::1/64 scope link' , output
)
2334 output
= check_output ( 'ip -4 address show dev wg98' )
2336 self
. assertIn ( 'inet 192.168.123.123/24 scope global wg98' , output
)
2338 output
= check_output ( 'ip -6 address show dev wg98' )
2340 self
. assertIn ( 'inet6 fd8d:4d6d:3ccb:500::1/64 scope global' , output
)
2342 output
= check_output ( 'ip -4 route show dev wg99 table 1234' )
2344 self
. assertIn ( '192.168.26.0/24 proto static scope link metric 123' , output
)
2346 output
= check_output ( 'ip -6 route show dev wg99 table 1234' )
2348 self
. assertIn ( 'fd31:bf08:57cb::/48 proto static metric 123 pref medium' , output
)
2350 output
= check_output ( 'ip -6 route show dev wg98 table 1234' )
2352 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium' , output
)
2353 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium' , output
)
2354 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium' , output
)
2355 self
. assertIn ( 'fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium' , output
)
2356 self
. assertIn ( 'fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium' , output
)
2357 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium' , output
)
2358 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium' , output
)
2359 self
. assertIn ( 'fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium' , output
)
2360 self
. assertIn ( 'fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium' , output
)
2361 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium' , output
)
2362 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium' , output
)
2363 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium' , output
)
2364 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium' , output
)
2365 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium' , output
)
2366 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium' , output
)
2367 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium' , output
)
2368 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium' , output
)
2369 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium' , output
)
2370 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium' , output
)
2371 self
. assertIn ( 'fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium' , output
)
2372 self
. assertIn ( 'fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium' , output
)
2373 self
. assertIn ( 'fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium' , output
)
2374 self
. assertIn ( 'fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium' , output
)
2375 self
. assertIn ( 'fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium' , output
)
2376 self
. assertIn ( 'fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium' , output
)
2377 self
. assertIn ( 'fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium' , output
)
2378 self
. assertIn ( 'fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium' , output
)
2379 self
. assertIn ( 'fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium' , output
)
2380 self
. assertIn ( 'fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium' , output
)
2381 self
. assertIn ( 'fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium' , output
)
2382 self
. assertIn ( 'fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium' , output
)
2383 self
. assertIn ( 'fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium' , output
)
2384 self
. assertIn ( 'fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium' , output
)
2385 self
. assertIn ( 'fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium' , output
)
2386 self
. assertIn ( 'fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium' , output
)
2387 self
. assertIn ( 'fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium' , output
)
2388 self
. assertIn ( 'fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium' , output
)
2389 self
. assertIn ( 'fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium' , output
)
2390 self
. assertIn ( 'fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium' , output
)
2391 self
. assertIn ( 'fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium' , output
)
2392 self
. assertIn ( 'fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium' , output
)
2393 self
. assertIn ( 'fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium' , output
)
2394 self
. assertIn ( 'fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium' , output
)
2395 self
. assertIn ( 'fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium' , output
)
2396 self
. assertIn ( 'fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium' , output
)
2397 self
. assertIn ( 'fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium' , output
)
2399 if shutil
. which ( 'wg' ):
2402 output
= check_output ( 'wg show wg99 listen-port' )
2403 self
. assertEqual ( output
, '51820' )
2404 output
= check_output ( 'wg show wg99 fwmark' )
2405 self
. assertEqual ( output
, '0x4d2' )
2406 output
= check_output ( 'wg show wg99 private-key' )
2407 self
. assertEqual ( output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=' )
2408 output
= check_output ( 'wg show wg99 allowed-ips' )
2409 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 192.168.124.3/32' , output
)
2410 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t 192.168.124.2/32' , output
)
2411 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t fdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128' , output
)
2412 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.26.0/24 fd31:bf08:57cb::/48' , output
)
2413 output
= check_output ( 'wg show wg99 persistent-keepalive' )
2414 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t off' , output
)
2415 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t off' , output
)
2416 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t off' , output
)
2417 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 20' , output
)
2418 output
= check_output ( 'wg show wg99 endpoints' )
2419 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t (none)' , output
)
2420 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t (none)' , output
)
2421 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t (none)' , output
)
2422 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.27.3:51820' , output
)
2423 output
= check_output ( 'wg show wg99 preshared-keys' )
2424 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=' , output
)
2425 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t it7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=' , output
)
2426 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=' , output
)
2427 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=' , output
)
2429 output
= check_output ( 'wg show wg98 private-key' )
2430 self
. assertEqual ( output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=' )
2432 output
= check_output ( 'wg show wg97 listen-port' )
2433 self
. assertEqual ( output
, '51821' )
2434 output
= check_output ( 'wg show wg97 fwmark' )
2435 self
. assertEqual ( output
, '0x4d3' )
2438 '25-wireguard.netdev' , '25-wireguard.network' ,
2439 '25-wireguard-23-peers.netdev' , '25-wireguard-23-peers.network' ,
2440 '25-wireguard-public-key.txt' , '25-wireguard-preshared-key.txt' , '25-wireguard-private-key.txt' ,
2441 '25-wireguard-no-peer.netdev' , '25-wireguard-no-peer.network' )
2443 self
. wait_online ( 'wg99:routable' , 'wg98:routable' , 'wg97:carrier' )
2445 def test_geneve ( self
):
2446 copy_network_unit ( '25-geneve.netdev' , '26-netdev-link-local-addressing-yes.network' )
2449 self
. wait_online ( 'geneve99:degraded' )
2450 self
. networkctl_check_unit ( 'geneve99' , '25-geneve' , '26-netdev-link-local-addressing-yes' )
2452 output
= check_output ( 'ip -d link show geneve99' )
2454 self
. assertRegex ( output
, '192.168.22.1' )
2455 self
. assertRegex ( output
, '6082' )
2456 self
. assertRegex ( output
, 'udpcsum' )
2457 self
. assertRegex ( output
, 'udp6zerocsumrx' )
2459 touch_network_unit ( '25-geneve.netdev' , '26-netdev-link-local-addressing-yes.network' )
2461 self
. wait_online ( 'geneve99:degraded' )
2463 def _test_ipip_tunnel ( self
, mode
):
2464 copy_network_unit ( '12-dummy.netdev' , '25-ipip.network' ,
2465 '25-ipip-tunnel.netdev' , '25-tunnel.network' ,
2466 '25-ipip-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2467 '25-ipip-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2468 '25-ipip-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2471 for netdev
in [ '25-ipip-tunnel.netdev' ,
2472 '25-ipip-tunnel-local-any.netdev' ,
2473 '25-ipip-tunnel-remote-any.netdev' ,
2474 '25-ipip-tunnel-any-any.netdev' ]:
2475 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2476 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2478 mode
= 'ipip' # kernel default
2481 self
. wait_online ( 'ipiptun99:routable' , 'ipiptun98:routable' , 'ipiptun97:routable' , 'ipiptun96:routable' , 'dummy98:degraded' )
2483 output
= check_output ( 'ip -d link show ipiptun99' )
2485 self
. assertIn ( f
'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98' , output
)
2486 output
= check_output ( 'ip -d link show ipiptun98' )
2488 self
. assertIn ( f
'ipip {mode} remote 192.169.224.239 local any dev dummy98' , output
)
2489 output
= check_output ( 'ip -d link show ipiptun97' )
2491 self
. assertIn ( f
'ipip {mode} remote any local 192.168.223.238 dev dummy98' , output
)
2492 output
= check_output ( 'ip -d link show ipiptun96' )
2494 self
. assertIn ( f
'ipip {mode} remote any local any dev dummy98' , output
)
2497 '25-ipip-tunnel.netdev' ,
2498 '25-ipip-tunnel-local-any.netdev' ,
2499 '25-ipip-tunnel-remote-any.netdev' ,
2500 '25-ipip-tunnel-any-any.netdev' )
2503 'ipiptun99:routable' ,
2504 'ipiptun98:routable' ,
2505 'ipiptun97:routable' ,
2506 'ipiptun96:routable' ,
2509 def test_ipip_tunnel ( self
):
2511 for mode
in [ None , 'ipip' , 'any' ]:
2517 print ( f
'### test_ipip_tunnel(mode= {mode} )' )
2518 with self
. subTest ( mode
= mode
):
2519 self
._ test
_ ipip
_ tunnel
( mode
)
2521 def test_gre_tunnel ( self
):
2522 copy_network_unit ( '12-dummy.netdev' , '25-gretun.network' ,
2523 '25-gre-tunnel.netdev' , '25-tunnel.network' ,
2524 '25-gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2525 '25-gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2526 '25-gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2528 self
. wait_online ( 'gretun99:routable' , 'gretun98:routable' , 'gretun97:routable' , 'gretun96:routable' , 'dummy98:degraded' )
2529 self
. networkctl_check_unit ( 'gretun99' , '25-gre-tunnel' , '25-tunnel' )
2530 self
. networkctl_check_unit ( 'gretun98' , '25-gre-tunnel-local-any' , '25-tunnel-local-any' )
2531 self
. networkctl_check_unit ( 'gretun97' , '25-gre-tunnel-remote-any' , '25-tunnel-remote-any' )
2532 self
. networkctl_check_unit ( 'gretun96' , '25-gre-tunnel-any-any' , '25-tunnel-any-any' )
2533 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-gretun' )
2535 output
= check_output ( 'ip -d link show gretun99' )
2537 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2538 self
. assertRegex ( output
, 'ikey 1.2.3.103' )
2539 self
. assertRegex ( output
, 'okey 1.2.4.103' )
2540 self
. assertRegex ( output
, 'iseq' )
2541 self
. assertRegex ( output
, 'oseq' )
2542 output
= check_output ( 'ip -d link show gretun98' )
2544 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local any dev dummy98' )
2545 self
. assertRegex ( output
, 'ikey 0.0.0.104' )
2546 self
. assertRegex ( output
, 'okey 0.0.0.104' )
2547 self
. assertNotRegex ( output
, 'iseq' )
2548 self
. assertNotRegex ( output
, 'oseq' )
2549 output
= check_output ( 'ip -d link show gretun97' )
2551 self
. assertRegex ( output
, 'gre remote any local 10.65.223.238 dev dummy98' )
2552 self
. assertRegex ( output
, 'ikey 0.0.0.105' )
2553 self
. assertRegex ( output
, 'okey 0.0.0.105' )
2554 self
. assertNotRegex ( output
, 'iseq' )
2555 self
. assertNotRegex ( output
, 'oseq' )
2556 output
= check_output ( 'ip -d link show gretun96' )
2558 self
. assertRegex ( output
, 'gre remote any local any dev dummy98' )
2559 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2560 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2561 self
. assertNotRegex ( output
, 'iseq' )
2562 self
. assertNotRegex ( output
, 'oseq' )
2565 '25-gre-tunnel.netdev' ,
2566 '25-gre-tunnel-local-any.netdev' ,
2567 '25-gre-tunnel-remote-any.netdev' ,
2568 '25-gre-tunnel-any-any.netdev' )
2571 'gretun99:routable' ,
2572 'gretun98:routable' ,
2573 'gretun97:routable' ,
2574 'gretun96:routable' ,
2577 def test_ip6gre_tunnel ( self
):
2578 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretun.network' ,
2579 '25-ip6gre-tunnel.netdev' , '25-tunnel.network' ,
2580 '25-ip6gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2581 '25-ip6gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2582 '25-ip6gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2585 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2587 self
. wait_links ( 'dummy98' , 'ip6gretun99' , 'ip6gretun98' , 'ip6gretun97' , 'ip6gretun96' )
2589 output
= check_output ( 'ip -d link show ip6gretun99' )
2591 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2592 output
= check_output ( 'ip -d link show ip6gretun98' )
2594 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2595 output
= check_output ( 'ip -d link show ip6gretun97' )
2597 self
. assertRegex ( output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98' )
2598 output
= check_output ( 'ip -d link show ip6gretun96' )
2600 self
. assertRegex ( output
, 'ip6gre remote any local any dev dummy98' )
2603 '25-ip6gre-tunnel.netdev' ,
2604 '25-ip6gre-tunnel-local-any.netdev' ,
2605 '25-ip6gre-tunnel-remote-any.netdev' ,
2606 '25-ip6gre-tunnel-any-any.netdev' )
2615 def test_gretap_tunnel ( self
):
2616 copy_network_unit ( '12-dummy.netdev' , '25-gretap.network' ,
2617 '25-gretap-tunnel.netdev' , '25-tunnel.network' ,
2618 '25-gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2620 self
. wait_online ( 'gretap99:routable' , 'gretap98:routable' , 'dummy98:degraded' )
2621 self
. networkctl_check_unit ( 'gretap99' , '25-gretap-tunnel' , '25-tunnel' )
2622 self
. networkctl_check_unit ( 'gretap98' , '25-gretap-tunnel-local-any' , '25-tunnel-local-any' )
2623 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-gretap' )
2625 output
= check_output ( 'ip -d link show gretap99' )
2627 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2628 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2629 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2630 self
. assertRegex ( output
, 'iseq' )
2631 self
. assertRegex ( output
, 'oseq' )
2632 self
. assertIn ( 'nopmtudisc' , output
)
2633 self
. assertIn ( 'ignore-df' , output
)
2634 output
= check_output ( 'ip -d link show gretap98' )
2636 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local any dev dummy98' )
2637 self
. assertRegex ( output
, 'ikey 0.0.0.107' )
2638 self
. assertRegex ( output
, 'okey 0.0.0.107' )
2639 self
. assertRegex ( output
, 'iseq' )
2640 self
. assertRegex ( output
, 'oseq' )
2643 '25-gretap-tunnel.netdev' ,
2644 '25-gretap-tunnel-local-any.netdev' )
2647 'gretap99:routable' ,
2648 'gretap98:routable' ,
2651 def test_ip6gretap_tunnel ( self
):
2652 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretap.network' ,
2653 '25-ip6gretap-tunnel.netdev' , '25-tunnel.network' ,
2654 '25-ip6gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2656 self
. wait_online ( 'ip6gretap99:routable' , 'ip6gretap98:routable' , 'dummy98:degraded' )
2657 self
. networkctl_check_unit ( 'ip6gretap99' , '25-ip6gretap-tunnel' , '25-tunnel' )
2658 self
. networkctl_check_unit ( 'ip6gretap98' , '25-ip6gretap-tunnel-local-any' , '25-tunnel-local-any' )
2659 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-ip6gretap' )
2661 output
= check_output ( 'ip -d link show ip6gretap99' )
2663 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2664 output
= check_output ( 'ip -d link show ip6gretap98' )
2666 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2669 '25-ip6gretap-tunnel.netdev' ,
2670 '25-ip6gretap-tunnel-local-any.netdev' )
2673 'ip6gretap99:routable' ,
2674 'ip6gretap98:routable' ,
2677 def test_vti_tunnel ( self
):
2678 copy_network_unit ( '12-dummy.netdev' , '25-vti.network' ,
2679 '25-vti-tunnel.netdev' , '25-tunnel.network' ,
2680 '25-vti-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2681 '25-vti-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2682 '25-vti-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2684 self
. wait_online ( 'vtitun99:routable' , 'vtitun98:routable' , 'vtitun97:routable' , 'vtitun96:routable' , 'dummy98:degraded' )
2685 self
. networkctl_check_unit ( 'vtitun99' , '25-vti-tunnel' , '25-tunnel' )
2686 self
. networkctl_check_unit ( 'vtitun98' , '25-vti-tunnel-local-any' , '25-tunnel-local-any' )
2687 self
. networkctl_check_unit ( 'vtitun97' , '25-vti-tunnel-remote-any' , '25-tunnel-remote-any' )
2688 self
. networkctl_check_unit ( 'vtitun96' , '25-vti-tunnel-any-any' , '25-tunnel-any-any' )
2689 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-vti' )
2691 output
= check_output ( 'ip -d link show vtitun99' )
2693 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2694 output
= check_output ( 'ip -d link show vtitun98' )
2696 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local any dev dummy98' )
2697 output
= check_output ( 'ip -d link show vtitun97' )
2699 self
. assertRegex ( output
, 'vti remote any local 10.65.223.238 dev dummy98' )
2700 output
= check_output ( 'ip -d link show vtitun96' )
2702 self
. assertRegex ( output
, 'vti remote any local any dev dummy98' )
2705 '25-vti-tunnel.netdev' ,
2706 '25-vti-tunnel-local-any.netdev' ,
2707 '25-vti-tunnel-remote-any.netdev' ,
2708 '25-vti-tunnel-any-any.netdev' )
2711 'vtitun99:routable' ,
2712 'vtitun98:routable' ,
2713 'vtitun97:routable' ,
2714 'vtitun96:routable' ,
2717 def test_vti6_tunnel ( self
):
2718 copy_network_unit ( '12-dummy.netdev' , '25-vti6.network' ,
2719 '25-vti6-tunnel.netdev' , '25-tunnel.network' ,
2720 '25-vti6-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2721 '25-vti6-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' )
2723 self
. wait_online ( 'vti6tun99:routable' , 'vti6tun98:routable' , 'vti6tun97:routable' , 'dummy98:degraded' )
2724 self
. networkctl_check_unit ( 'vti6tun99' , '25-vti6-tunnel' , '25-tunnel' )
2725 self
. networkctl_check_unit ( 'vti6tun98' , '25-vti6-tunnel-local-any' , '25-tunnel-local-any' )
2726 self
. networkctl_check_unit ( 'vti6tun97' , '25-vti6-tunnel-remote-any' , '25-tunnel-remote-any' )
2727 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-vti6' )
2729 output
= check_output ( 'ip -d link show vti6tun99' )
2731 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2732 output
= check_output ( 'ip -d link show vti6tun98' )
2734 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98' )
2735 output
= check_output ( 'ip -d link show vti6tun97' )
2737 self
. assertRegex ( output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98' )
2740 '25-vti6-tunnel.netdev' ,
2741 '25-vti6-tunnel-local-any.netdev' ,
2742 '25-vti6-tunnel-remote-any.netdev' )
2745 'vti6tun99:routable' ,
2746 'vti6tun98:routable' ,
2747 'vti6tun97:routable' ,
2750 def _test_ip6tnl_tunnel ( self
, mode
):
2751 copy_network_unit ( '12-dummy.netdev' , '25-ip6tnl.network' ,
2752 '25-ip6tnl-tunnel.netdev' , '25-tunnel.network' ,
2753 '25-ip6tnl-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2754 '25-ip6tnl-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2755 '25-veth.netdev' , '25-ip6tnl-slaac.network' , '25-ipv6-prefix.network' ,
2756 '25-ip6tnl-tunnel-local-slaac.netdev' , '25-ip6tnl-tunnel-local-slaac.network' ,
2757 '25-ip6tnl-tunnel-external.netdev' , '26-netdev-link-local-addressing-yes.network' )
2760 for netdev
in [ '25-ip6tnl-tunnel.netdev' ,
2761 '25-ip6tnl-tunnel-local-any.netdev' ,
2762 '25-ip6tnl-tunnel-remote-any.netdev' ,
2763 '25-ip6tnl-tunnel-local-slaac.netdev' ,
2764 '25-ip6tnl-tunnel-external.netdev' ]:
2765 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2766 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2768 mode
= 'any' # kernel default
2771 self
. wait_online ( 'ip6tnl99:routable' , 'ip6tnl98:routable' , 'ip6tnl97:routable' ,
2772 'ip6tnl-slaac:degraded' , 'ip6tnl-external:degraded' ,
2773 'dummy98:degraded' , 'veth99:routable' , 'veth-peer:degraded' )
2774 self
. networkctl_check_unit ( 'ip6tnl99' , '25-ip6tnl-tunnel' , '25-tunnel' )
2775 self
. networkctl_check_unit ( 'ip6tnl98' , '25-ip6tnl-tunnel-local-any' , '25-tunnel-local-any' )
2776 self
. networkctl_check_unit ( 'ip6tnl97' , '25-ip6tnl-tunnel-remote-any' , '25-tunnel-remote-any' )
2777 self
. networkctl_check_unit ( 'ip6tnl-slaac' , '25-ip6tnl-tunnel-local-slaac' , '25-ip6tnl-tunnel-local-slaac' )
2778 self
. networkctl_check_unit ( 'ip6tnl-external' , '25-ip6tnl-tunnel-external' , '26-netdev-link-local-addressing-yes' )
2779 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-ip6tnl' )
2780 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '25-ip6tnl-slaac' )
2781 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '25-ipv6-prefix' )
2783 output
= check_output ( 'ip -d link show ip6tnl99' )
2785 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' , output
)
2786 output
= check_output ( 'ip -d link show ip6tnl98' )
2788 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98' , output
)
2789 output
= check_output ( 'ip -d link show ip6tnl97' )
2791 self
. assertIn ( f
'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98' , output
)
2792 output
= check_output ( 'ip -d link show ip6tnl-external' )
2794 self
. assertIn ( 'ip6tnl-external@NONE:' , output
)
2795 self
. assertIn ( 'ip6tnl external ' , output
)
2796 output
= check_output ( 'ip -d link show ip6tnl-slaac' )
2798 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
2800 output
= check_output ( 'ip -6 address show veth99' )
2802 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
2804 output
= check_output ( 'ip -4 route show default' )
2806 self
. assertIn ( 'default dev ip6tnl-slaac proto static' , output
)
2809 '25-ip6tnl-tunnel.netdev' ,
2810 '25-ip6tnl-tunnel-local-any.netdev' ,
2811 '25-ip6tnl-tunnel-remote-any.netdev' ,
2812 '25-ip6tnl-tunnel-local-slaac.netdev' ,
2813 '25-ip6tnl-tunnel-external.netdev' )
2816 'ip6tnl99:routable' ,
2817 'ip6tnl98:routable' ,
2818 'ip6tnl97:routable' ,
2819 'ip6tnl-slaac:degraded' ,
2820 'ip6tnl-external:degraded' ,
2823 'veth-peer:degraded' )
2825 def test_ip6tnl_tunnel ( self
):
2827 for mode
in [ None , 'ipip6' , 'ip6ip6' , 'any' ]:
2833 print ( f
'### test_ip6tnl_tunnel(mode= {mode} )' )
2834 with self
. subTest ( mode
= mode
):
2835 self
._ test
_ ip
6 tnl
_ tunnel
( mode
)
2837 def _test_sit_tunnel ( self
, mode
):
2838 copy_network_unit ( '12-dummy.netdev' , '25-sit.network' ,
2839 '25-sit-tunnel.netdev' , '25-tunnel.network' ,
2840 '25-sit-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2841 '25-sit-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2842 '25-sit-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2845 for netdev
in [ '25-sit-tunnel.netdev' ,
2846 '25-sit-tunnel-local-any.netdev' ,
2847 '25-sit-tunnel-remote-any.netdev' ,
2848 '25-sit-tunnel-any-any.netdev' ]:
2849 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2850 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2852 mode
= 'ip6ip' # kernel default
2855 self
. wait_online ( 'sittun99:routable' , 'sittun98:routable' , 'sittun97:routable' , 'sittun96:routable' , 'dummy98:degraded' )
2856 self
. networkctl_check_unit ( 'sittun99' , '25-sit-tunnel' , '25-tunnel' )
2857 self
. networkctl_check_unit ( 'sittun98' , '25-sit-tunnel-local-any' , '25-tunnel-local-any' )
2858 self
. networkctl_check_unit ( 'sittun97' , '25-sit-tunnel-remote-any' , '25-tunnel-remote-any' )
2859 self
. networkctl_check_unit ( 'sittun96' , '25-sit-tunnel-any-any' , '25-tunnel-any-any' )
2860 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-sit' )
2862 output
= check_output ( 'ip -d link show sittun99' )
2864 self
. assertIn ( f
'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98' , output
)
2865 output
= check_output ( 'ip -d link show sittun98' )
2867 self
. assertIn ( f
'sit {mode} remote 10.65.223.239 local any dev dummy98' , output
)
2868 output
= check_output ( 'ip -d link show sittun97' )
2870 self
. assertIn ( f
'sit {mode} remote any local 10.65.223.238 dev dummy98' , output
)
2871 output
= check_output ( 'ip -d link show sittun96' )
2873 self
. assertIn ( f
'sit {mode} remote any local any dev dummy98' , output
)
2876 '25-sit-tunnel.netdev' ,
2877 '25-sit-tunnel-local-any.netdev' ,
2878 '25-sit-tunnel-remote-any.netdev' ,
2879 '25-sit-tunnel-any-any.netdev' )
2882 'sittun99:routable' ,
2883 'sittun98:routable' ,
2884 'sittun97:routable' ,
2885 'sittun96:routable' ,
2888 def test_sit_tunnel ( self
):
2890 for mode
in [ None , 'ipip' , 'ip6ip' , 'any' ]:
2896 print ( f
'### test_sit_tunnel(mode= {mode} )' )
2897 with self
. subTest ( mode
= mode
):
2898 self
._ test
_ sit
_ tunnel
( mode
)
2900 def test_isatap_tunnel ( self
):
2901 copy_network_unit ( '12-dummy.netdev' , '25-isatap.network' ,
2902 '25-isatap-tunnel.netdev' , '25-tunnel.network' )
2904 self
. wait_online ( 'isataptun99:routable' , 'dummy98:degraded' )
2905 self
. networkctl_check_unit ( 'isataptun99' , '25-isatap-tunnel' , '25-tunnel' )
2906 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-isatap' )
2908 output
= check_output ( 'ip -d link show isataptun99' )
2910 self
. assertRegex ( output
, "isatap " )
2912 touch_network_unit ( '25-isatap-tunnel.netdev' )
2914 self
. wait_online ( 'isataptun99:routable' , 'dummy98:degraded' )
2916 def test_6rd_tunnel ( self
):
2917 copy_network_unit ( '12-dummy.netdev' , '25-6rd.network' ,
2918 '25-6rd-tunnel.netdev' , '25-tunnel.network' )
2920 self
. wait_online ( 'sittun99:routable' , 'dummy98:degraded' )
2921 self
. networkctl_check_unit ( 'sittun99' , '25-6rd-tunnel' , '25-tunnel' )
2922 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-6rd' )
2924 output
= check_output ( 'ip -d link show sittun99' )
2926 self
. assertRegex ( output
, '6rd-prefix 2602::/24' )
2928 touch_network_unit ( '25-6rd-tunnel.netdev' )
2930 self
. wait_online ( 'sittun99:routable' , 'dummy98:degraded' )
2932 @expectedFailureIfERSPANv0IsNotSupported ()
2933 def test_erspan_tunnel_v0 ( self
):
2934 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2935 '25-erspan0-tunnel.netdev' , '25-tunnel.network' ,
2936 '25-erspan0-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2938 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2939 self
. networkctl_check_unit ( 'erspan99' , '25-erspan0-tunnel' , '25-tunnel' )
2940 self
. networkctl_check_unit ( 'erspan98' , '25-erspan0-tunnel-local-any' , '25-tunnel-local-any' )
2941 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
2943 output
= check_output ( 'ip -d link show erspan99' )
2945 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2946 self
. assertIn ( 'erspan_ver 0' , output
)
2947 self
. assertNotIn ( 'erspan_index 123' , output
)
2948 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2949 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2950 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2951 self
. assertIn ( 'iseq' , output
)
2952 self
. assertIn ( 'nopmtudisc' , output
)
2953 self
. assertIn ( 'ignore-df' , output
)
2954 output
= check_output ( 'ip -d link show erspan98' )
2956 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2957 self
. assertIn ( 'erspan_ver 0' , output
)
2958 self
. assertNotIn ( 'erspan_index 124' , output
)
2959 self
. assertNotIn ( 'erspan_dir egress' , output
)
2960 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
2961 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2962 self
. assertIn ( 'iseq' , output
)
2965 '25-erspan0-tunnel.netdev' ,
2966 '25-erspan0-tunnel-local-any.netdev' )
2969 'erspan99:routable' ,
2970 'erspan98:routable' ,
2973 def test_erspan_tunnel_v1 ( self
):
2974 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2975 '25-erspan1-tunnel.netdev' , '25-tunnel.network' ,
2976 '25-erspan1-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2978 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2979 self
. networkctl_check_unit ( 'erspan99' , '25-erspan1-tunnel' , '25-tunnel' )
2980 self
. networkctl_check_unit ( 'erspan98' , '25-erspan1-tunnel-local-any' , '25-tunnel-local-any' )
2981 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
2983 output
= check_output ( 'ip -d link show erspan99' )
2985 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2986 self
. assertIn ( 'erspan_ver 1' , output
)
2987 self
. assertIn ( 'erspan_index 123' , output
)
2988 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2989 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2990 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2991 self
. assertIn ( 'okey 0.0.0.101' , output
)
2992 self
. assertIn ( 'iseq' , output
)
2993 self
. assertIn ( 'oseq' , output
)
2994 output
= check_output ( 'ip -d link show erspan98' )
2996 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2997 self
. assertIn ( 'erspan_ver 1' , output
)
2998 self
. assertIn ( 'erspan_index 124' , output
)
2999 self
. assertNotIn ( 'erspan_dir egress' , output
)
3000 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
3001 self
. assertIn ( 'ikey 0.0.0.102' , output
)
3002 self
. assertIn ( 'okey 0.0.0.102' , output
)
3003 self
. assertIn ( 'iseq' , output
)
3004 self
. assertIn ( 'oseq' , output
)
3007 '25-erspan1-tunnel.netdev' ,
3008 '25-erspan1-tunnel-local-any.netdev' )
3011 'erspan99:routable' ,
3012 'erspan98:routable' ,
3015 @expectedFailureIfERSPANv2IsNotSupported ()
3016 def test_erspan_tunnel_v2 ( self
):
3017 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
3018 '25-erspan2-tunnel.netdev' , '25-tunnel.network' ,
3019 '25-erspan2-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
3021 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
3022 self
. networkctl_check_unit ( 'erspan99' , '25-erspan2-tunnel' , '25-tunnel' )
3023 self
. networkctl_check_unit ( 'erspan98' , '25-erspan2-tunnel-local-any' , '25-tunnel-local-any' )
3024 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
3026 output
= check_output ( 'ip -d link show erspan99' )
3028 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
3029 self
. assertIn ( 'erspan_ver 2' , output
)
3030 self
. assertNotIn ( 'erspan_index 123' , output
)
3031 self
. assertIn ( 'erspan_dir ingress' , output
)
3032 self
. assertIn ( 'erspan_hwid 0x1f' , output
)
3033 self
. assertIn ( 'ikey 0.0.0.101' , output
)
3034 self
. assertIn ( 'okey 0.0.0.101' , output
)
3035 self
. assertIn ( 'iseq' , output
)
3036 self
. assertIn ( 'oseq' , output
)
3037 output
= check_output ( 'ip -d link show erspan98' )
3039 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
3040 self
. assertIn ( 'erspan_ver 2' , output
)
3041 self
. assertNotIn ( 'erspan_index 124' , output
)
3042 self
. assertIn ( 'erspan_dir egress' , output
)
3043 self
. assertIn ( 'erspan_hwid 0x2f' , output
)
3044 self
. assertIn ( 'ikey 0.0.0.102' , output
)
3045 self
. assertIn ( 'okey 0.0.0.102' , output
)
3046 self
. assertIn ( 'iseq' , output
)
3047 self
. assertIn ( 'oseq' , output
)
3050 '25-erspan2-tunnel.netdev' ,
3051 '25-erspan2-tunnel-local-any.netdev' )
3054 'erspan99:routable' ,
3055 'erspan98:routable' ,
3058 def test_tunnel_independent ( self
):
3059 copy_network_unit ( '25-ipip-tunnel-independent.netdev' , '26-netdev-link-local-addressing-yes.network' )
3062 self
. wait_online ( 'ipiptun99:carrier' )
3063 self
. networkctl_check_unit ( 'ipiptun99' , '25-ipip-tunnel-independent' , '26-netdev-link-local-addressing-yes' )
3065 def test_tunnel_independent_loopback ( self
):
3066 copy_network_unit ( '25-ipip-tunnel-independent-loopback.netdev' , '26-netdev-link-local-addressing-yes.network' )
3069 self
. wait_online ( 'ipiptun99:carrier' )
3070 self
. networkctl_check_unit ( 'ipiptun99' , '25-ipip-tunnel-independent-loopback' , '26-netdev-link-local-addressing-yes' )
3072 @expectedFailureIfModuleIsNotAvailable ( 'xfrm_interface' )
3073 def test_xfrm ( self
):
3074 copy_network_unit ( '12-dummy.netdev' , '25-xfrm.network' ,
3075 '25-xfrm.netdev' , '25-xfrm-independent.netdev' ,
3076 '26-netdev-link-local-addressing-yes.network' )
3079 self
. wait_online ( 'dummy98:degraded' , 'xfrm98:degraded' , 'xfrm99:degraded' )
3080 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-xfrm' )
3081 self
. networkctl_check_unit ( 'xfrm98' , '25-xfrm' , '26-netdev-link-local-addressing-yes' )
3082 self
. networkctl_check_unit ( 'xfrm99' , '25-xfrm-independent' , '26-netdev-link-local-addressing-yes' )
3084 output
= check_output ( 'ip -d link show dev xfrm98' )
3086 self
. assertIn ( 'xfrm98@dummy98:' , output
)
3087 self
. assertIn ( 'xfrm if_id 0x98 ' , output
)
3089 output
= check_output ( 'ip -d link show dev xfrm99' )
3091 self
. assertIn ( 'xfrm99@lo:' , output
)
3092 self
. assertIn ( 'xfrm if_id 0x99 ' , output
)
3094 touch_network_unit ( '25-xfrm.netdev' )
3096 self
. wait_online ( 'dummy98:degraded' , 'xfrm98:degraded' , 'xfrm99:degraded' )
3098 @expectedFailureIfModuleIsNotAvailable ( 'fou' )
3100 copy_network_unit ( '25-fou-ipproto-ipip.netdev' , '25-fou-ipproto-gre.netdev' ,
3101 '25-fou-ipip.netdev' , '25-fou-sit.netdev' ,
3102 '25-fou-gre.netdev' , '25-fou-gretap.netdev' )
3105 self
. wait_online ( 'ipiptun96:off' , 'sittun96:off' , 'gretun96:off' , 'gretap96:off' , setup_state
= 'unmanaged' )
3106 self
. networkctl_check_unit ( 'ipiptun96' , '25-fou-ipip' )
3107 self
. networkctl_check_unit ( 'sittun96' , '25-fou-sit' )
3108 self
. networkctl_check_unit ( 'gretun96' , '25-fou-gre' )
3109 self
. networkctl_check_unit ( 'gretap96' , '25-fou-gretap' )
3111 output
= check_output ( 'ip fou show' )
3113 self
. assertRegex ( output
, 'port 55555 ipproto 4' )
3114 self
. assertRegex ( output
, 'port 55556 ipproto 47' )
3116 output
= check_output ( 'ip -d link show ipiptun96' )
3118 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
3119 output
= check_output ( 'ip -d link show sittun96' )
3121 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
3122 output
= check_output ( 'ip -d link show gretun96' )
3124 self
. assertRegex ( output
, 'encap fou encap-sport 1001 encap-dport 55556' )
3125 output
= check_output ( 'ip -d link show gretap96' )
3127 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55556' )
3130 '25-fou-ipproto-ipip.netdev' , '25-fou-ipproto-gre.netdev' ,
3131 '25-fou-ipip.netdev' , '25-fou-sit.netdev' ,
3132 '25-fou-gre.netdev' , '25-fou-gretap.netdev' )
3134 self
. wait_online ( 'ipiptun96:off' , 'sittun96:off' , 'gretun96:off' , 'gretap96:off' , setup_state
= 'unmanaged' )
3136 def test_vxlan ( self
):
3137 copy_network_unit ( '11-dummy.netdev' , '25-vxlan-test1.network' ,
3138 '25-vxlan.netdev' , '25-vxlan.network' ,
3139 '25-vxlan-ipv6.netdev' , '25-vxlan-ipv6.network' ,
3140 '25-vxlan-independent.netdev' , '26-netdev-link-local-addressing-yes.network' ,
3141 '25-veth.netdev' , '25-vxlan-veth99.network' , '25-ipv6-prefix.network' ,
3142 '25-vxlan-local-slaac.netdev' , '25-vxlan-local-slaac.network' ,
3143 '25-vxlan-external.netdev' , '25-vxlan-external.network' )
3146 self
. wait_online ( 'test1:degraded' , 'veth99:routable' , 'veth-peer:degraded' ,
3147 'vxlan99:degraded' , 'vxlan98:degraded' , 'vxlan97:degraded' , 'vxlan-slaac:degraded' ,
3148 'vxlan-external:degraded' )
3149 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-vxlan-test1' )
3150 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '25-vxlan-veth99' )
3151 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '25-ipv6-prefix' )
3152 self
. networkctl_check_unit ( 'vxlan99' , '25-vxlan' , '25-vxlan' )
3153 self
. networkctl_check_unit ( 'vxlan98' , '25-vxlan-independent' , '26-netdev-link-local-addressing-yes' )
3154 self
. networkctl_check_unit ( 'vxlan97' , '25-vxlan-ipv6' , '25-vxlan-ipv6' )
3155 self
. networkctl_check_unit ( 'vxlan-slaac' , '25-vxlan-local-slaac' , '25-vxlan-local-slaac' )
3156 self
. networkctl_check_unit ( 'vxlan-external' , '25-vxlan-external' , '25-vxlan-external' )
3158 output
= check_output ( 'ip -d -d link show vxlan99' )
3160 self
. assertIn ( '999' , output
)
3161 self
. assertIn ( '5555' , output
)
3162 self
. assertIn ( 'l2miss' , output
)
3163 self
. assertIn ( 'l3miss' , output
)
3164 self
. assertIn ( 'gbp' , output
)
3165 # Since [0] some of the options use slightly different names and some
3166 # options with default values are shown only if the -d(etails) setting
3168 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
3169 self
. assertRegex ( output
, '(udpcsum|udp_csum)' )
3170 self
. assertRegex ( output
, '(udp6zerocsumtx|udp_zero_csum6_tx)' )
3171 self
. assertRegex ( output
, '(udp6zerocsumrx|udp_zero_csum6_rx)' )
3172 self
. assertRegex ( output
, '(remcsumtx|remcsum_tx)' )
3173 self
. assertRegex ( output
, '(remcsumrx|remcsum_rx)' )
3175 output
= check_output ( 'bridge fdb show dev vxlan99' )
3177 self
. assertIn ( '00:11:22:33:44:55 dst 10.0.0.5 self permanent' , output
)
3178 self
. assertIn ( '00:11:22:33:44:66 dst 10.0.0.6 self permanent' , output
)
3179 self
. assertIn ( '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent' , output
)
3181 output
= networkctl_status ( 'vxlan99' )
3183 self
. assertIn ( 'VNI: 999' , output
)
3184 self
. assertIn ( 'Destination Port: 5555' , output
)
3185 self
. assertIn ( 'Underlying Device: test1' , output
)
3187 output
= check_output ( 'bridge fdb show dev vxlan97' )
3189 self
. assertIn ( '00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent' , output
)
3190 self
. assertIn ( '00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent' , output
)
3191 self
. assertIn ( '00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent' , output
)
3193 output
= check_output ( 'ip -d link show vxlan-slaac' )
3195 self
. assertIn ( 'vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
3197 output
= check_output ( 'ip -6 address show veth99' )
3199 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
3201 output
= check_output ( 'ip -d link show vxlan-external' )
3203 self
. assertIn ( 'id 0 ' , output
)
3204 self
. assertIn ( 'external' , output
)
3205 self
. assertIn ( 'vnifilter' , output
)
3207 @unittest . skipUnless ( compare_kernel_version ( "6" ), reason
= "Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315" )
3208 def test_macsec ( self
):
3209 copy_network_unit ( '25-macsec.netdev' , '25-macsec.network' , '25-macsec.key' ,
3210 '26-macsec.network' , '12-dummy.netdev' )
3213 self
. wait_online ( 'dummy98:degraded' , 'macsec99:routable' )
3214 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '26-macsec' )
3215 self
. networkctl_check_unit ( 'macsec99' , '25-macsec' , '25-macsec' )
3217 output
= check_output ( 'ip -d link show macsec99' )
3219 self
. assertRegex ( output
, 'macsec99@dummy98' )
3220 self
. assertRegex ( output
, 'macsec sci [0-9a-f]*000b' )
3221 self
. assertRegex ( output
, 'encrypt on' )
3223 output
= check_output ( 'ip macsec show macsec99' )
3225 self
. assertRegex ( output
, 'encrypt on' )
3226 self
. assertRegex ( output
, 'TXSC: [0-9a-f]*000b on SA 1' )
3227 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000' )
3228 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000' )
3229 self
. assertRegex ( output
, 'RXSC: c619528fe6a00100, state on' )
3230 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000' )
3231 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000' )
3232 self
. assertRegex ( output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000' )
3233 self
. assertRegex ( output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000' )
3234 self
. assertNotRegex ( output
, 'key 02030405067080900000000000000000' )
3235 self
. assertRegex ( output
, 'RXSC: 8c16456c83a90002, state on' )
3236 self
. assertRegex ( output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000' )
3238 touch_network_unit ( '25-macsec.netdev' )
3240 self
. wait_online ( 'dummy98:degraded' , 'macsec99:routable' )
3242 def test_nlmon ( self
):
3243 copy_network_unit ( '25-nlmon.netdev' , '26-netdev-link-local-addressing-yes.network' )
3246 self
. wait_online ( 'nlmon99:carrier' )
3247 self
. networkctl_check_unit ( 'nlmon99' , '25-nlmon' , '26-netdev-link-local-addressing-yes' )
3249 touch_network_unit ( '25-nlmon.netdev' , '26-netdev-link-local-addressing-yes.network' )
3251 self
. wait_online ( 'nlmon99:carrier' )
3253 @expectedFailureIfModuleIsNotAvailable ( 'ifb' )
3255 copy_network_unit ( '25-ifb.netdev' , '26-netdev-link-local-addressing-yes.network' )
3258 self
. wait_online ( 'ifb99:degraded' )
3259 self
. networkctl_check_unit ( 'ifb99' , '25-ifb' , '26-netdev-link-local-addressing-yes' )
3261 touch_network_unit ( '25-ifb.netdev' , '26-netdev-link-local-addressing-yes.network' )
3263 self
. wait_online ( 'ifb99:degraded' )
3265 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
3266 def test_rps_cpu_1 ( self
):
3267 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-1.link' )
3270 self
. wait_online ( 'dummy98:carrier' )
3271 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-1' )
3273 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3275 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 2 )
3277 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
3278 def test_rps_cpu_0_1 ( self
):
3279 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-0-1.link' )
3282 self
. wait_online ( 'dummy98:carrier' )
3283 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-1' )
3285 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3287 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 3 )
3289 @unittest . skipUnless ( os
. cpu_count () >= 4 , reason
= "CPU count should be >= 4 to pass this test" )
3290 def test_rps_cpu_multi ( self
):
3291 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-multi.link' )
3294 self
. wait_online ( 'dummy98:carrier' )
3295 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-multi' )
3297 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3299 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 15 )
3301 def test_rps_cpu ( self
):
3302 cpu_count
= os
. cpu_count ()
3304 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' )
3307 self
. wait_online ( 'dummy98:carrier' )
3308 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' )
3311 copy_network_unit ( '25-rps-cpu-0.link' )
3312 udevadm_trigger ( '/sys/class/net/dummy98' )
3313 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0' )
3314 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3316 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3317 remove_network_unit ( '25-rps-cpu-0.link' )
3320 copy_network_unit ( '25-rps-cpu-all.link' )
3321 udevadm_trigger ( '/sys/class/net/dummy98' )
3322 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-all' )
3323 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3325 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3326 remove_network_unit ( '25-rps-cpu-all.link' )
3329 copy_network_unit ( '24-rps-cpu-disable.link' )
3330 udevadm_trigger ( '/sys/class/net/dummy98' )
3331 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-disable' )
3332 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3334 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 0 )
3335 remove_network_unit ( '24-rps-cpu-disable.link' )
3338 copy_network_unit ( '25-rps-cpu-all.link' )
3339 udevadm_trigger ( '/sys/class/net/dummy98' )
3340 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-all' )
3341 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3343 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3344 remove_network_unit ( '25-rps-cpu-all.link' )
3346 # empty -> unchanged
3347 copy_network_unit ( '24-rps-cpu-empty.link' )
3348 udevadm_trigger ( '/sys/class/net/dummy98' )
3349 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-empty' )
3350 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3352 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3353 remove_network_unit ( '24-rps-cpu-empty.link' )
3355 # 0, then empty -> unchanged
3356 copy_network_unit ( '25-rps-cpu-0-empty.link' )
3357 udevadm_trigger ( '/sys/class/net/dummy98' )
3358 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-empty' )
3359 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3361 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3362 remove_network_unit ( '25-rps-cpu-0-empty.link' )
3364 # 0, then invalid -> 0
3365 copy_network_unit ( '25-rps-cpu-0-invalid.link' )
3366 udevadm_trigger ( '/sys/class/net/dummy98' )
3367 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-invalid' )
3368 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3370 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3371 remove_network_unit ( '25-rps-cpu-0-invalid.link' )
3373 # invalid -> unchanged
3374 copy_network_unit ( '24-rps-cpu-invalid.link' )
3375 udevadm_trigger ( '/sys/class/net/dummy98' )
3376 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-invalid' )
3377 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3379 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3380 remove_network_unit ( '24-rps-cpu-invalid.link' )
3382 class NetworkdL2TPTests ( unittest
. TestCase
, Utilities
):
3390 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_netlink' )
3391 def test_l2tp_udp ( self
):
3392 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3393 '25-l2tp-udp.netdev' , '25-l2tp.network' )
3396 self
. wait_online ( 'test1:routable' , 'l2tp-ses1:degraded' , 'l2tp-ses2:degraded' )
3397 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-l2tp-dummy' )
3398 self
. networkctl_check_unit ( 'l2tp-ses1' , '25-l2tp-udp' , '25-l2tp' )
3399 self
. networkctl_check_unit ( 'l2tp-ses2' , '25-l2tp-udp' , '25-l2tp' )
3401 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
3403 self
. assertRegex ( output
, "Tunnel 10, encap UDP" )
3404 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
3405 self
. assertRegex ( output
, "Peer tunnel 11" )
3406 self
. assertRegex ( output
, "UDP source / dest ports: 3000/4000" )
3407 self
. assertRegex ( output
, "UDP checksum: enabled" )
3409 output
= check_output ( 'ip l2tp show session tid 10 session_id 15' )
3411 self
. assertRegex ( output
, "Session 15 in tunnel 10" )
3412 self
. assertRegex ( output
, "Peer session 16, tunnel 11" )
3413 self
. assertRegex ( output
, "interface name: l2tp-ses1" )
3415 output
= check_output ( 'ip l2tp show session tid 10 session_id 17' )
3417 self
. assertRegex ( output
, "Session 17 in tunnel 10" )
3418 self
. assertRegex ( output
, "Peer session 18, tunnel 11" )
3419 self
. assertRegex ( output
, "interface name: l2tp-ses2" )
3422 '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3423 '25-l2tp-udp.netdev' , '25-l2tp.network' )
3425 self
. wait_online ( 'test1:routable' , 'l2tp-ses1:degraded' , 'l2tp-ses2:degraded' )
3427 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_ip' , 'l2tp_netlink' )
3428 def test_l2tp_ip ( self
):
3429 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3430 '25-l2tp-ip.netdev' , '25-l2tp.network' )
3433 self
. wait_online ( 'test1:routable' , 'l2tp-ses3:degraded' , 'l2tp-ses4:degraded' )
3434 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-l2tp-dummy' )
3435 self
. networkctl_check_unit ( 'l2tp-ses3' , '25-l2tp-ip' , '25-l2tp' )
3436 self
. networkctl_check_unit ( 'l2tp-ses4' , '25-l2tp-ip' , '25-l2tp' )
3438 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
3440 self
. assertRegex ( output
, "Tunnel 10, encap IP" )
3441 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
3442 self
. assertRegex ( output
, "Peer tunnel 12" )
3444 output
= check_output ( 'ip l2tp show session tid 10 session_id 25' )
3446 self
. assertRegex ( output
, "Session 25 in tunnel 10" )
3447 self
. assertRegex ( output
, "Peer session 26, tunnel 12" )
3448 self
. assertRegex ( output
, "interface name: l2tp-ses3" )
3450 output
= check_output ( 'ip l2tp show session tid 10 session_id 27' )
3452 self
. assertRegex ( output
, "Session 27 in tunnel 10" )
3453 self
. assertRegex ( output
, "Peer session 28, tunnel 12" )
3454 self
. assertRegex ( output
, "interface name: l2tp-ses4" )
3457 '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3458 '25-l2tp-ip.netdev' , '25-l2tp.network' )
3460 self
. wait_online ( 'test1:routable' , 'l2tp-ses3:degraded' , 'l2tp-ses4:degraded' )
3462 class NetworkdNetworkTests ( unittest
. TestCase
, Utilities
):
3470 def test_ID_NET_MANAGED_BY ( self
):
3471 copy_network_unit ( '11-dummy.netdev' , '11-dummy-unmanaged.link' , '11-dummy.network' )
3473 self
. wait_online ( 'test1:off' , setup_state
= 'unmanaged' )
3475 check_output ( 'ip link set dev test1 up' )
3476 self
. wait_online ( 'test1:degraded' , setup_state
= 'unmanaged' )
3478 check_output ( 'ip link set dev test1 down' )
3479 self
. wait_online ( 'test1:off' , setup_state
= 'unmanaged' )
3481 def verify_address_static (
3511 output
= check_output ( 'ip address show dev dummy98' )
3515 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
3516 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
3517 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
3518 self
. assertIn ( 'inet6 2001:db8:0:f101::15/64 scope global' , output
)
3519 self
. assertIn ( 'inet6 2001:db8:0:f101::16/64 scope global' , output
)
3520 self
. assertIn ( 'inet6 2001:db8:0:f102::15/64 scope global' , output
)
3523 self
. assertIn ( f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1} ' , output
)
3524 self
. assertIn ( f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2} ' , output
)
3525 self
. assertIn ( f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3} ' , output
)
3528 self
. assertIn ( f
'inet 10.4.1.1/24 {broadcast1} scope global dummy98' , output
)
3529 self
. assertIn ( f
'inet 10.4.2.1/24 {broadcast2} scope global dummy98' , output
)
3530 self
. assertIn ( f
'inet 10.4.3.1/24 {broadcast3} scope global dummy98' , output
)
3533 self
. assertIn ( f
'inet 10.5.1.1 {peer1} scope global dummy98' , output
)
3534 self
. assertIn ( f
'inet 10.5.2.1 {peer2} scope global dummy98' , output
)
3535 self
. assertIn ( f
'inet 10.5.3.1 {peer3} scope global dummy98' , output
)
3536 self
. assertIn ( f
'inet6 2001:db8:0:f103::1 {peer4} scope global' , output
)
3537 self
. assertIn ( f
'inet6 2001:db8:0:f103::2 {peer5} scope global' , output
)
3538 self
. assertIn ( f
'inet6 2001:db8:0:f103::3 {peer6} scope global' , output
)
3541 self
. assertIn ( f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98' , output
)
3542 self
. assertIn ( f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98' , output
)
3545 self
. assertIn ( f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global {deprecated1} dummy98' , output
)
3546 self
. assertIn ( f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global {deprecated2} dummy98' , output
)
3547 self
. assertIn ( f
'inet6 2001:db8:0:f104::1/64 scope global {deprecated3} ' , output
)
3548 self
. assertIn ( f
'inet6 2001:db8:0:f104::2/64 scope global {deprecated4} ' , output
)
3551 self
. assertRegex ( output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98' )
3552 self
. assertRegex ( output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global' )
3554 output_route
= check_output ( 'ip -4 route show dev dummy98 10.8.1.0/24' )
3556 self
. assertIn ( f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric} ' , output_route
)
3558 output_route
= check_output ( 'ip -6 route show dev dummy98 2001:db8:0:f105::/64' )
3560 self
. assertIn ( f
'2001:db8:0:f105::/64 proto kernel metric {route_metric} ' , output_route
)
3563 self
. assertIn ( f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global {flag1} dummy98' , output
)
3564 self
. assertIn ( f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global {flag2} dummy98' , output
)
3565 self
. assertIn ( f
'inet6 2001:db8:0:f106::1/64 scope global {flag3} ' , output
)
3566 self
. assertIn ( f
'inet6 2001:db8:0:f106::2/64 scope global {flag4} ' , output
)
3569 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
3570 prefix16
= ip4_null_16
[:- len ( '.0.1' )]
3571 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
3572 prefix24
= ip4_null_24
[:- len ( '.1' )]
3573 self
. assertIn ( f
'inet {ip4_null_16} /16 brd {prefix16} .255.255 scope global subnet16' , output
)
3574 self
. assertIn ( f
'inet {ip4_null_24} /24 brd {prefix24} .255 scope global subnet24' , output
)
3575 self
. assertIn ( f
'inet6 {ip6_null_73} /73 scope global' , output
)
3576 self
. assertIn ( f
'inet6 {ip6_null_74} /74 scope global' , output
)
3579 self
. assertNotIn ( '10.4.4.1' , output
)
3580 self
. assertNotIn ( '10.5.4.1' , output
)
3581 self
. assertNotIn ( '10.5.5.1' , output
)
3582 self
. assertNotIn ( '10.8.2.1' , output
)
3583 self
. assertNotIn ( '10.9.3.1' , output
)
3584 self
. assertNotIn ( '2001:db8:0:f101::2' , output
)
3585 self
. assertNotIn ( '2001:db8:0:f103::4' , output
)
3588 self
. check_netlabel ( 'dummy98' , r
'10\.10\.1\.0/24' )
3590 check_json ( networkctl_json ())
3592 @expectedFailureIfKernelReturnsInvalidFlags ()
3593 def test_address_static ( self
):
3594 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
3595 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
3596 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
3597 self
. setup_nftset ( 'ifindex' , 'iface_index' )
3600 self
. wait_online ( 'dummy98:routable' )
3604 output
= check_output ( 'ip -4 --json address show dev dummy98' )
3605 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
3606 if i
[ 'label' ] == 'subnet16' :
3607 ip4_null_16
= i
[ 'local' ]
3608 elif i
[ 'label' ] == 'subnet24' :
3609 ip4_null_24
= i
[ 'local' ]
3610 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
3611 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
3615 output
= check_output ( 'ip -6 --json address show dev dummy98' )
3616 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
3617 if i
[ 'prefixlen' ] == 73 :
3618 ip6_null_73
= i
[ 'local' ]
3619 elif i
[ 'prefixlen' ] == 74 :
3620 ip6_null_74
= i
[ 'local' ]
3621 self
. assertTrue ( ip6_null_73
. endswith ( ':1' ))
3622 self
. assertTrue ( ip6_null_74
. endswith ( ':1' ))
3624 self
. verify_address_static (
3629 broadcast2
= ' brd 10.4.2.255' ,
3630 broadcast3
= ' brd 10.4.3.63' ,
3631 peer1
= ' peer 10.5.1.101/24' ,
3632 peer2
= ' peer 10.5.2.101/24' ,
3633 peer3
= '/24 brd 10.5.3.255' ,
3634 peer4
= ' peer 2001:db8:0:f103::101/128' ,
3635 peer5
= ' peer 2001:db8:0:f103::102/128' ,
3640 deprecated2
= ' deprecated' ,
3642 deprecated4
= ' deprecated' ,
3644 flag1
= ' noprefixroute' ,
3646 flag3
= ' noprefixroute' ,
3647 flag4
= ' home mngtmpaddr' ,
3648 ip4_null_16
= ip4_null_16
,
3649 ip4_null_24
= ip4_null_24
,
3650 ip6_null_73
= ip6_null_73
,
3651 ip6_null_74
= ip6_null_74
,
3654 self
. check_nftset ( 'addr4' , r
'10\.10\.1\.1' )
3655 self
. check_nftset ( 'network4' , r
'10\.10\.1\.0/24' )
3656 self
. check_nftset ( 'ifindex' , 'dummy98' )
3658 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
3660 copy_network_unit ( '25-address-static.network.d/10-override.conf' )
3662 self
. wait_online ( 'dummy98:routable' )
3663 self
. verify_address_static (
3664 label1
= 'new-label1' ,
3666 label3
= 'new-label3' ,
3667 broadcast1
= ' brd 10.4.1.255' ,
3669 broadcast3
= ' brd 10.4.3.31' ,
3670 peer1
= ' peer 10.5.1.102/24' ,
3671 peer2
= '/24 brd 10.5.2.255' ,
3672 peer3
= ' peer 10.5.3.102/24' ,
3673 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3675 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3678 deprecated1
= ' deprecated' ,
3680 deprecated3
= ' deprecated' ,
3684 flag2
= ' noprefixroute' ,
3685 flag3
= ' home mngtmpaddr' ,
3686 flag4
= ' noprefixroute' ,
3687 ip4_null_16
= ip4_null_16
,
3688 ip4_null_24
= ip4_null_24
,
3689 ip6_null_73
= ip6_null_73
,
3690 ip6_null_74
= ip6_null_74
,
3693 networkctl_reconfigure ( 'dummy98' )
3694 self
. wait_online ( 'dummy98:routable' )
3695 self
. verify_address_static (
3696 label1
= 'new-label1' ,
3698 label3
= 'new-label3' ,
3699 broadcast1
= ' brd 10.4.1.255' ,
3701 broadcast3
= ' brd 10.4.3.31' ,
3702 peer1
= ' peer 10.5.1.102/24' ,
3703 peer2
= '/24 brd 10.5.2.255' ,
3704 peer3
= ' peer 10.5.3.102/24' ,
3705 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3707 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3710 deprecated1
= ' deprecated' ,
3712 deprecated3
= ' deprecated' ,
3716 flag2
= ' noprefixroute' ,
3717 flag3
= ' home mngtmpaddr' ,
3718 flag4
= ' noprefixroute' ,
3719 ip4_null_16
= ip4_null_16
,
3720 ip4_null_24
= ip4_null_24
,
3721 ip6_null_73
= ip6_null_73
,
3722 ip6_null_74
= ip6_null_74
,
3726 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
3727 check_output ( 'ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever' )
3728 check_output ( 'ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever' )
3729 output
= check_output ( 'ip address show dev dummy98' )
3731 self
. assertNotRegex ( output
, '10.7.1.1/24 .* deprecated' )
3732 self
. assertNotRegex ( output
, '2001:db8:0:f104::1/64 .* deprecated' )
3734 # 2. reconfigure the interface, and check the deprecated flag is set again
3735 networkctl_reconfigure ( 'dummy98' )
3736 self
. wait_online ( 'dummy98:routable' )
3737 self
. verify_address_static (
3738 label1
= 'new-label1' ,
3740 label3
= 'new-label3' ,
3741 broadcast1
= ' brd 10.4.1.255' ,
3743 broadcast3
= ' brd 10.4.3.31' ,
3744 peer1
= ' peer 10.5.1.102/24' ,
3745 peer2
= '/24 brd 10.5.2.255' ,
3746 peer3
= ' peer 10.5.3.102/24' ,
3747 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3749 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3752 deprecated1
= ' deprecated' ,
3754 deprecated3
= ' deprecated' ,
3758 flag2
= ' noprefixroute' ,
3759 flag3
= ' home mngtmpaddr' ,
3760 flag4
= ' noprefixroute' ,
3761 ip4_null_16
= ip4_null_16
,
3762 ip4_null_24
= ip4_null_24
,
3763 ip6_null_73
= ip6_null_73
,
3764 ip6_null_74
= ip6_null_74
,
3767 # test for ENOBUFS issue #17012 (with reload)
3768 copy_network_unit ( '25-address-static.network.d/10-many-address.conf' )
3770 self
. wait_online ( 'dummy98:routable' )
3771 output
= check_output ( 'ip -4 address show dev dummy98' )
3772 for i
in range ( 1 , 254 ):
3773 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3775 # (with reconfigure)
3776 networkctl_reconfigure ( 'dummy98' )
3777 self
. wait_online ( 'dummy98:routable' )
3778 output
= check_output ( 'ip -4 address show dev dummy98' )
3779 for i
in range ( 1 , 254 ):
3780 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3782 # test for an empty string assignment for Address= in [Network]
3783 copy_network_unit ( '25-address-static.network.d/20-clear-addresses.conf' )
3785 self
. wait_online ( 'dummy98:routable' )
3786 output
= check_output ( 'ip -4 address show dev dummy98' )
3787 for i
in range ( 1 , 254 ):
3788 self
. assertNotIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3789 self
. assertIn ( 'inet 10.4.0.1/16 brd 10.4.255.255' , output
)
3791 def test_address_ipv4acd ( self
):
3792 check_output ( 'ip netns add ns99' )
3793 check_output ( 'ip link add veth99 type veth peer veth-peer' )
3794 check_output ( 'ip link set veth-peer netns ns99' )
3795 check_output ( 'ip link set veth99 up' )
3796 check_output ( 'ip netns exec ns99 ip link set veth-peer up' )
3797 check_output ( 'ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer' )
3799 copy_network_unit ( '25-address-ipv4acd-veth99.network' , copy_dropins
= False )
3801 self
. wait_online ( 'veth99:routable' )
3803 output
= check_output ( 'ip -4 address show dev veth99' )
3805 self
. assertNotIn ( '192.168.100.10/24' , output
)
3806 self
. assertIn ( '192.168.100.11/24' , output
)
3808 copy_network_unit ( '25-address-ipv4acd-veth99.network.d/conflict-address.conf' )
3810 self
. wait_operstate ( 'veth99' , operstate
= 'routable' , setup_state
= 'configuring' , setup_timeout
= 10 )
3812 output
= check_output ( 'ip -4 address show dev veth99' )
3814 self
. assertNotIn ( '192.168.100.10/24' , output
)
3815 self
. assertIn ( '192.168.100.11/24' , output
)
3817 def test_address_peer_ipv4 ( self
):
3818 # test for issue #17304
3819 copy_network_unit ( '25-address-peer-ipv4.network' , '12-dummy.netdev' )
3821 for trial
in range ( 2 ):
3827 self
. wait_online ( 'dummy98:routable' )
3829 output
= check_output ( 'ip -4 address show dev dummy98' )
3830 self
. assertIn ( 'inet 100.64.0.1 peer 100.64.0.2/32 scope global' , output
)
3832 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
3833 def test_prefix_route ( self
):
3834 copy_network_unit ( '25-prefix-route-with-vrf.network' , '12-dummy.netdev' ,
3835 '25-prefix-route-without-vrf.network' , '11-dummy.netdev' ,
3836 '25-vrf.netdev' , '25-vrf.network' )
3837 for trial
in range ( 2 ):
3843 self
. wait_online ( 'dummy98:routable' , 'test1:routable' , 'vrf99:carrier' )
3845 output
= check_output ( 'ip route show table 42 dev dummy98' )
3846 print ( '### ip route show table 42 dev dummy98' )
3848 self
. assertRegex ( output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1' )
3849 self
. assertRegex ( output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1' )
3850 self
. assertRegex ( output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1' )
3851 self
. assertRegex ( output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1' )
3852 self
. assertRegex ( output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1' )
3853 self
. assertRegex ( output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1' )
3854 self
. assertRegex ( output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1' )
3855 output
= check_output ( 'ip -6 route show table 42 dev dummy98' )
3856 print ( '### ip -6 route show table 42 dev dummy98' )
3860 self
. assertRegex ( output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium' )
3861 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3862 self
. assertRegex ( output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium' )
3863 self
. assertRegex ( output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium' )
3864 self
. assertRegex ( output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium' )
3865 self
. assertRegex ( output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium' )
3866 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3867 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3871 output
= check_output ( 'ip route show dev test1' )
3872 print ( '### ip route show dev test1' )
3874 self
. assertRegex ( output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1' )
3875 output
= check_output ( 'ip route show table local dev test1' )
3876 print ( '### ip route show table local dev test1' )
3878 self
. assertRegex ( output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1' )
3879 self
. assertRegex ( output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1' )
3880 self
. assertRegex ( output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1' )
3881 self
. assertRegex ( output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1' )
3882 self
. assertRegex ( output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1' )
3883 self
. assertRegex ( output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1' )
3884 output
= check_output ( 'ip -6 route show dev test1' )
3885 print ( '### ip -6 route show dev test1' )
3887 self
. assertRegex ( output
, 'fdde:12:22::1 proto kernel metric 256 pref medium' )
3888 self
. assertRegex ( output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium' )
3889 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3890 output
= check_output ( 'ip -6 route show table local dev test1' )
3891 print ( '### ip -6 route show table local dev test1' )
3893 self
. assertRegex ( output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium' )
3894 self
. assertRegex ( output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium' )
3895 self
. assertRegex ( output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium' )
3896 self
. assertRegex ( output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium' )
3897 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3899 def test_configure_without_carrier ( self
):
3900 copy_network_unit ( '11-dummy.netdev' )
3902 self
. wait_operstate ( 'test1' , 'off' , '' )
3903 check_output ( 'ip link set dev test1 up carrier off' )
3905 copy_network_unit ( '25-test1.network.d/configure-without-carrier.conf' , copy_dropins
= False )
3907 self
. wait_online ( 'test1:no-carrier' )
3909 carrier_map
= { 'on' : '1' , 'off' : '0' }
3910 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3911 for carrier
in [ 'off' , 'on' , 'off' ]:
3912 with self
. subTest ( carrier
= carrier
):
3913 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3914 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3915 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3917 output
= networkctl_status ( 'test1' )
3919 self
. assertRegex ( output
, '192.168.0.15' )
3920 self
. assertRegex ( output
, '192.168.0.1' )
3921 self
. assertRegex ( output
, routable_map
[ carrier
])
3923 def test_configure_without_carrier_yes_ignore_carrier_loss_no ( self
):
3924 copy_network_unit ( '11-dummy.netdev' )
3926 self
. wait_operstate ( 'test1' , 'off' , '' )
3927 check_output ( 'ip link set dev test1 up carrier off' )
3929 copy_network_unit ( '25-test1.network' )
3931 self
. wait_online ( 'test1:no-carrier' )
3933 carrier_map
= { 'on' : '1' , 'off' : '0' }
3934 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3935 for ( carrier
, have_config
) in [( 'off' , True ), ( 'on' , True ), ( 'off' , False )]:
3936 with self
. subTest ( carrier
= carrier
, have_config
= have_config
):
3937 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3938 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3939 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3941 output
= networkctl_status ( 'test1' )
3944 self
. assertRegex ( output
, '192.168.0.15' )
3945 self
. assertRegex ( output
, '192.168.0.1' )
3947 self
. assertNotRegex ( output
, '192.168.0.15' )
3948 self
. assertNotRegex ( output
, '192.168.0.1' )
3949 self
. assertRegex ( output
, routable_map
[ carrier
])
3951 def check_routing_policy_rule_test1 ( self
):
3952 print ( '### Checking routing policy rules requested by test1' )
3954 output
= check_output ( 'ip rule list iif test1 priority 111' )
3956 self
. assertRegex ( output
, r
'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7' )
3958 output
= check_output ( 'ip -6 rule list iif test1 priority 100' )
3960 self
. assertIn ( '100: from all iif test1 lookup 8' , output
)
3962 output
= check_output ( 'ip rule list iif test1 priority 101' )
3964 self
. assertIn ( '101: from all iif test1 lookup 9' , output
)
3966 output
= check_output ( 'ip -6 rule list iif test1 priority 101' )
3968 self
. assertIn ( '101: from all iif test1 lookup 9' , output
)
3970 output
= check_output ( 'ip rule list iif test1 priority 102' )
3972 self
. assertIn ( '102: from 0.0.0.0/8 iif test1 lookup 10' , output
)
3974 output
= check_output ( 'ip rule list iif test1 priority 103' )
3976 self
. assertIn ( '103: from 10.0.0.0/16 iif test1 lookup 11 goto 111' , output
)
3978 output
= check_output ( 'ip rule list iif test1 priority 104' )
3980 self
. assertIn ( '104: from 10.1.0.0/16 iif test1 lookup 12 nop' , output
)
3982 output
= check_output ( 'ip rule list iif test1 priority 200' )
3984 self
. assertIn ( '200: from all fwmark 0/0x1 iif test1 lookup 20' , output
)
3986 output
= check_output ( 'ip rule list iif test1 priority 201' )
3988 self
. assertIn ( '201: from all fwmark 0x7/0xff iif test1 lookup 21' , output
)
3990 output
= check_output ( 'ip rule list iif test1 priority 202' )
3992 self
. assertIn ( '202: from all fwmark 0x270f iif test1 lookup 22' , output
)
3994 output
= check_output ( 'ip rule list to 192.0.2.0/26' )
3996 self
. assertIn ( 'to 192.0.2.0/26 lookup 1001' , output
)
3998 output
= check_output ( 'ip rule list to 192.0.2.64/26' )
4000 self
. assertIn ( 'to 192.0.2.64/26 lookup 1001' , output
)
4002 output
= check_output ( 'ip rule list to 192.0.2.128/26' )
4004 self
. assertIn ( 'to 192.0.2.128/26 lookup 1001' , output
)
4006 output
= check_output ( 'ip rule list to 192.0.2.192/26' )
4008 self
. assertIn ( 'to 192.0.2.192/26 lookup 1001' , output
)
4010 def check_routing_policy_rule_dummy98 ( self
):
4011 print ( '### Checking routing policy rules requested by dummy98' )
4013 output
= check_output ( 'ip rule list priority 112' )
4015 self
. assertRegex ( output
, r
'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8' )
4017 def _test_routing_policy_rule ( self
, manage_foreign_routes
):
4018 if not manage_foreign_routes
:
4019 copy_networkd_conf_dropin ( 'networkd-manage-foreign-rules-no.conf' )
4020 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' )
4024 check_output ( 'ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16' )
4025 check_output ( 'ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64' )
4028 self
. wait_online ( 'test1:degraded' )
4030 self
. check_routing_policy_rule_test1 ()
4031 check_json ( networkctl_json ())
4033 output
= check_output ( 'ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16' )
4035 if manage_foreign_routes
:
4036 self
. assertEqual ( output
, '' )
4038 self
. assertIn ( output
, '20001: from 10.10.0.0/16 lookup 9999' )
4039 check_output ( 'ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16' )
4041 output
= check_output ( 'ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64' )
4043 if manage_foreign_routes
:
4044 self
. assertEqual ( output
, '' )
4046 self
. assertIn ( output
, '20001: from 2001:db8:0:1::/64 lookup 9999' )
4047 check_output ( 'ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64' )
4049 def test_routing_policy_rule ( self
):
4051 for manage_foreign_routes
in [ True , False ]:
4057 print ( f
'### test_routing_policy_rule(manage_foreign_routes= {manage_foreign_routes} )' )
4058 with self
. subTest ( manage_foreign_routes
= manage_foreign_routes
):
4059 self
._ test
_ routing
_ policy
_ rule
( manage_foreign_routes
)
4061 def test_routing_policy_rule_restart_and_reconfigure ( self
):
4062 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' ,
4063 '25-routing-policy-rule-dummy98.network' , '12-dummy.netdev' )
4065 # For #11280 and #34068.
4067 for trial
in range ( 3 ):
4068 restart_networkd ( show_logs
=( trial
> 0 ))
4069 self
. wait_online ( 'test1:degraded' , 'dummy98:degraded' )
4071 self
. check_routing_policy_rule_test1 ()
4072 self
. check_routing_policy_rule_dummy98 ()
4074 networkctl_reconfigure ( 'test1' )
4075 self
. wait_online ( 'test1:degraded' )
4077 self
. check_routing_policy_rule_test1 ()
4078 self
. check_routing_policy_rule_dummy98 ()
4080 networkctl_reconfigure ( 'dummy98' )
4081 self
. wait_online ( 'dummy98:degraded' )
4083 self
. check_routing_policy_rule_test1 ()
4084 self
. check_routing_policy_rule_dummy98 ()
4086 def test_routing_policy_rule_reconfigure ( self
):
4087 copy_network_unit ( '25-routing-policy-rule-reconfigure2.network' , '11-dummy.netdev' )
4089 self
. wait_online ( 'test1:degraded' )
4091 output
= check_output ( 'ip rule list table 1011' )
4093 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4094 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4095 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4096 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4098 output
= check_output ( 'ip -6 rule list table 1011' )
4100 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4102 copy_network_unit ( '25-routing-policy-rule-reconfigure1.network' , '11-dummy.netdev' )
4104 self
. wait_online ( 'test1:degraded' )
4106 output
= check_output ( 'ip rule list table 1011' )
4108 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4109 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4110 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4111 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4113 output
= check_output ( 'ip -6 rule list table 1011' )
4115 self
. assertNotIn ( '10112: from all oif test1 lookup 1011' , output
)
4116 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4118 call ( 'ip rule delete priority 10111' )
4119 call ( 'ip rule delete priority 10112' )
4120 call ( 'ip rule delete priority 10113' )
4121 call ( 'ip rule delete priority 10114' )
4122 call ( 'ip -6 rule delete priority 10113' )
4124 output
= check_output ( 'ip rule list table 1011' )
4126 self
. assertEqual ( output
, '' )
4128 output
= check_output ( 'ip -6 rule list table 1011' )
4130 self
. assertEqual ( output
, '' )
4132 networkctl_reconfigure ( 'test1' )
4133 self
. wait_online ( 'test1:degraded' )
4135 output
= check_output ( 'ip rule list table 1011' )
4137 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4138 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4139 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4140 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4142 output
= check_output ( 'ip -6 rule list table 1011' )
4144 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4146 def test_routing_policy_rule_manual ( self
):
4150 '25-routing-policy-rule-manual.network' )
4152 self
. wait_operstate ( 'test1' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 20 )
4154 check_output ( 'ip link add test2 type dummy' )
4155 self
. wait_operstate ( 'test2' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 20 )
4157 networkctl ( 'up' , 'test2' )
4158 self
. wait_online ( 'test2:degraded' )
4160 # The request for the routing policy rules are bound to test1. Hence, we need to wait for the rules
4161 # being configured explicitly.
4165 output
= check_output ( 'ip -4 rule list table 51819' )
4166 if output
!= '10: from all lookup 51819 suppress_prefixlength 0 proto static' :
4169 output
= check_output ( 'ip -6 rule list table 51819' )
4170 if output
!= '10: from all lookup 51819 suppress_prefixlength 0 proto static' :
4173 output
= check_output ( 'ip -4 rule list table 51820' )
4174 if output
!= '11: not from all fwmark 0x38f lookup 51820 proto static' :
4177 output
= check_output ( 'ip -6 rule list table 51820' )
4178 if output
!= '11: not from all fwmark 0x38f lookup 51820 proto static' :
4183 self
. assertFalse ( True )
4185 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable ()
4186 def test_routing_policy_rule_port_range ( self
):
4187 copy_network_unit ( '25-fibrule-port-range.network' , '11-dummy.netdev' )
4189 self
. wait_online ( 'test1:degraded' )
4191 output
= check_output ( 'ip rule' )
4193 self
. assertIn ( '111:' , output
)
4194 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4195 self
. assertIn ( 'sport 1123-1150 ' , output
)
4196 self
. assertIn ( 'dport 3224-3290 ' , output
)
4197 self
. assertRegex ( output
, 'ipproto (tcp|ipproto-6) ' )
4198 self
. assertIn ( 'lookup 7 ' , output
)
4200 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable ()
4201 def test_routing_policy_rule_invert ( self
):
4202 copy_network_unit ( '25-fibrule-invert.network' , '11-dummy.netdev' )
4204 self
. wait_online ( 'test1:degraded' )
4206 output
= check_output ( 'ip rule' )
4208 self
. assertIn ( '111:' , output
)
4209 self
. assertIn ( 'not ' , output
)
4210 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4211 self
. assertRegex ( output
, 'ipproto (tcp|ipproto-6) ' )
4212 self
. assertIn ( 'lookup 7 ' , output
)
4214 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable ()
4215 def test_routing_policy_rule_l3mdev ( self
):
4216 copy_network_unit ( '25-fibrule-l3mdev.network' , '11-dummy.netdev' )
4218 self
. wait_online ( 'test1:degraded' )
4220 output
= check_output ( 'ip rule' )
4222 self
. assertIn ( '1500: from all lookup [l3mdev-table]' , output
)
4223 self
. assertIn ( '2000: from all lookup [l3mdev-table] unreachable' , output
)
4225 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable ()
4226 def test_routing_policy_rule_uidrange ( self
):
4227 copy_network_unit ( '25-fibrule-uidrange.network' , '11-dummy.netdev' )
4229 self
. wait_online ( 'test1:degraded' )
4231 output
= check_output ( 'ip rule' )
4233 self
. assertIn ( '111:' , output
)
4234 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4235 self
. assertIn ( 'lookup 7 ' , output
)
4236 self
. assertIn ( 'uidrange 100-200 ' , output
)
4238 def _check_route_static ( self
, test1_is_managed
: bool ):
4239 output
= networkctl_status ( 'dummy98' )
4241 output
= networkctl_status ( 'test1' )
4244 print ( '### ip -6 route show dev dummy98' )
4245 output
= check_output ( 'ip -6 route show dev dummy98' )
4247 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
4248 self
. assertIn ( '2001:1234:5:8f63::1 proto kernel' , output
)
4249 self
. assertIn ( '2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static' , output
)
4251 print ( '### ip -6 route show default' )
4252 output
= check_output ( 'ip -6 route show default' )
4254 self
. assertIn ( 'default' , output
)
4255 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff' , output
)
4257 print ( '### ip -4 route show dev dummy98' )
4258 output
= check_output ( 'ip -4 route show dev dummy98' )
4260 self
. assertIn ( '149.10.124.48/28 proto kernel scope link src 149.10.124.58' , output
)
4261 self
. assertIn ( '149.10.124.64 proto static scope link' , output
)
4262 self
. assertIn ( '169.254.0.0/16 proto static scope link metric 2048' , output
)
4263 self
. assertIn ( '192.168.1.1 proto static scope link initcwnd 20' , output
)
4264 self
. assertIn ( '192.168.1.2 proto static scope link initrwnd 30' , output
)
4265 self
. assertIn ( '192.168.1.3 proto static scope link advmss 30' , output
)
4266 self
. assertIn ( '192.168.1.4 proto static scope link hoplimit 122' , output
)
4267 self
. assertIn ( 'multicast 149.10.123.4 proto static' , output
)
4269 print ( '### ip -4 route show dev dummy98 default' )
4270 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4272 self
. assertIn ( 'default via 149.10.125.65 proto static onlink' , output
)
4273 self
. assertIn ( 'default via 149.10.124.64 proto static' , output
)
4274 self
. assertIn ( 'default proto static' , output
)
4275 self
. assertIn ( 'default via 1.1.8.104 proto static' , output
)
4277 print ( '### ip -4 route show table local dev dummy98' )
4278 output
= check_output ( 'ip -4 route show table local dev dummy98' )
4280 self
. assertIn ( 'local 149.10.123.1 proto static scope host' , output
)
4281 self
. assertIn ( 'anycast 149.10.123.2 proto static scope link' , output
)
4282 self
. assertIn ( 'broadcast 149.10.123.3 proto static scope link' , output
)
4284 print ( '### ip -4 route show type blackhole' )
4285 output
= check_output ( 'ip -4 route show type blackhole' )
4287 self
. assertIn ( 'blackhole 202.54.1.2 proto static' , output
)
4289 print ( '### ip -4 route show type unreachable' )
4290 output
= check_output ( 'ip -4 route show type unreachable' )
4292 self
. assertIn ( 'unreachable 202.54.1.3 proto static' , output
)
4294 print ( '### ip -4 route show type prohibit' )
4295 output
= check_output ( 'ip -4 route show type prohibit' )
4297 self
. assertIn ( 'prohibit 202.54.1.4 proto static' , output
)
4299 print ( '### ip -6 route show type blackhole' )
4300 output
= check_output ( 'ip -6 route show type blackhole' )
4302 self
. assertIn ( 'blackhole 2001:1234:5678::2 dev lo proto static' , output
)
4304 print ( '### ip -6 route show type unreachable' )
4305 output
= check_output ( 'ip -6 route show type unreachable' )
4307 self
. assertIn ( 'unreachable 2001:1234:5678::3 dev lo proto static' , output
)
4309 print ( '### ip -6 route show type prohibit' )
4310 output
= check_output ( 'ip -6 route show type prohibit' )
4312 self
. assertIn ( 'prohibit 2001:1234:5678::4 dev lo proto static' , output
)
4314 print ( '### ip route show 192.168.10.1' )
4315 output
= check_output ( 'ip route show 192.168.10.1' )
4317 self
. assertIn ( '192.168.10.1 proto static' , output
)
4318 self
. assertIn ( 'nexthop via 149.10.123.59 dev test1 weight 20' , output
)
4319 self
. assertIn ( 'nexthop via 149.10.123.60 dev test1 weight 30' , output
)
4320 self
. assertIn ( 'nexthop via 149.10.124.59 dev dummy98 weight 10' , output
)
4321 self
. assertIn ( 'nexthop via 149.10.124.60 dev dummy98 weight 5' , output
)
4323 print ( '### ip route show 192.168.10.2' )
4324 output
= check_output ( 'ip route show 192.168.10.2' )
4326 # old ip command does not show IPv6 gateways...
4327 self
. assertIn ( '192.168.10.2 proto static' , output
)
4328 self
. assertIn ( 'nexthop' , output
)
4329 self
. assertIn ( 'dev test1 weight 20' , output
)
4330 self
. assertIn ( 'dev test1 weight 30' , output
)
4331 self
. assertIn ( 'dev dummy98 weight 10' , output
)
4332 self
. assertIn ( 'dev dummy98 weight 5' , output
)
4334 print ( '### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff' )
4335 output
= check_output ( 'ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff' )
4337 # old ip command does not show 'nexthop' keyword and weight...
4338 self
. assertIn ( '2001:1234:5:bfff:ff:ff:ff:ff' , output
)
4339 if test1_is_managed
:
4340 self
. assertIn ( 'via 2001:1234:5:6fff:ff:ff:ff:ff dev test1' , output
)
4341 self
. assertIn ( 'via 2001:1234:5:7fff:ff:ff:ff:ff dev test1' , output
)
4342 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98' , output
)
4343 self
. assertIn ( 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98' , output
)
4345 check_json ( networkctl_json ())
4347 def _check_unreachable_routes_removed ( self
):
4348 print ( '### ip -4 route show type blackhole' )
4349 output
= check_output ( 'ip -4 route show type blackhole' )
4351 self
. assertEqual ( output
, '' )
4353 print ( '### ip -4 route show type unreachable' )
4354 output
= check_output ( 'ip -4 route show type unreachable' )
4356 self
. assertEqual ( output
, '' )
4358 print ( '### ip -4 route show type prohibit' )
4359 output
= check_output ( 'ip -4 route show type prohibit' )
4361 self
. assertEqual ( output
, '' )
4363 print ( '### ip -6 route show type blackhole' )
4364 output
= check_output ( 'ip -6 route show type blackhole' )
4366 self
. assertEqual ( output
, '' )
4368 print ( '### ip -6 route show type unreachable' )
4369 output
= check_output ( 'ip -6 route show type unreachable' )
4371 self
. assertEqual ( output
, '' )
4373 print ( '### ip -6 route show type prohibit' )
4374 output
= check_output ( 'ip -6 route show type prohibit' )
4376 self
. assertEqual ( output
, '' )
4378 check_json ( networkctl_json ())
4380 def _test_route_static ( self
, manage_foreign_routes
):
4381 if not manage_foreign_routes
:
4382 copy_networkd_conf_dropin ( 'networkd-manage-foreign-routes-no.conf' )
4384 copy_network_unit ( '25-route-static.network' , '12-dummy.netdev' ,
4385 '25-route-static-test1.network' , '11-dummy.netdev' )
4387 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4388 self
._ check
_ route
_ static
( test1_is_managed
= True )
4391 remove_network_unit ( '25-route-static-test1.network' )
4393 self
. wait_online ( 'dummy98:routable' )
4394 self
. wait_operstate ( 'test1' , setup_state
= 'unmanaged' )
4395 self
._ check
_ route
_ static
( test1_is_managed
= False )
4397 # managing test1 again
4398 copy_network_unit ( '25-route-static-test1.network' )
4400 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4401 self
._ check
_ route
_ static
( test1_is_managed
= True )
4403 # adding an unmanaged interface
4404 check_output ( 'ip link add test2 type dummy' )
4405 self
. wait_operstate ( 'test2' , operstate
= 'off' , setup_state
= 'unmanaged' )
4406 self
._ check
_ route
_ static
( test1_is_managed
= True )
4408 # reconfiguring with another config, and check all routes managed by Manager are removed
4409 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
4411 self
. wait_online ( 'dummy98:routable' )
4412 self
._ check
_u nreachable
_ routes
_ removed
()
4414 # reconfiguring the original config again
4415 remove_network_unit ( '25-address-static.network' )
4417 self
. wait_online ( 'dummy98:routable' )
4418 self
._ check
_ route
_ static
( test1_is_managed
= True )
4420 # removing interface, and check all routes managed by Manager are removed
4421 remove_link ( 'dummy98' )
4423 self
._ check
_u nreachable
_ routes
_ removed
()
4425 def test_route_static ( self
):
4427 for manage_foreign_routes
in [ True , False ]:
4433 print ( f
'### test_route_static(manage_foreign_routes= {manage_foreign_routes} )' )
4434 with self
. subTest ( manage_foreign_routes
= manage_foreign_routes
):
4435 self
._ test
_ route
_ static
( manage_foreign_routes
)
4437 def test_route_static_issue_35047 ( self
):
4439 '25-route-static-issue-35047.network' ,
4440 '25-route-static-issue-35047.network.d/step1.conf' ,
4444 self
. wait_online ( 'dummy98:routable' )
4446 print ( '### ip -4 route show table all dev dummy98' )
4447 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4449 self
. assertIn ( '192.0.2.2 proto kernel scope link src 192.0.2.1' , output
)
4450 self
. assertIn ( 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1' , output
)
4451 self
. assertIn ( '198.51.100.0/24 via 192.0.2.2 proto static' , output
)
4453 check_output ( 'ip link set dev dummy98 down' )
4454 self
. wait_route_dropped ( 'dummy98' , '192.0.2.2 proto kernel scope link src 192.0.2.1' , ipv
= '-4' , table
= 'all' , timeout_sec
= 10 )
4455 self
. wait_route_dropped ( 'dummy98' , 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1' , ipv
= '-4' , table
= 'all' , timeout_sec
= 10 )
4456 self
. wait_route_dropped ( 'dummy98' , '198.51.100.0/24 via 192.0.2.2 proto static' , ipv
= '-4' , table
= 'all' , timeout_sec
= 10 )
4458 print ( '### ip -4 route show table all dev dummy98' )
4459 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4461 self
. assertNotIn ( '192.0.2.2' , output
)
4462 self
. assertNotIn ( '192.0.2.1' , output
)
4463 self
. assertNotIn ( '198.51.100.0/24' , output
)
4465 remove_network_unit ( '25-route-static-issue-35047.network.d/step1.conf' )
4466 copy_network_unit ( '25-route-static-issue-35047.network.d/step2.conf' )
4468 self
. wait_online ( 'dummy98:routable' )
4470 print ( '### ip -4 route show table all dev dummy98' )
4471 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4473 self
. assertIn ( '192.0.2.2 proto static scope link' , output
)
4474 self
. assertIn ( 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1' , output
)
4475 self
. assertIn ( '198.51.100.0/24 via 192.0.2.2 proto static' , output
)
4477 def test_route_static_issue_37714 ( self
):
4478 copy_network_unit ( '12-dummy.netdev' , '25-route-static-issue-37714.network' )
4480 self
. wait_online ( 'dummy98:routable' )
4482 print ( '### ip -4 rule list table 249' )
4483 output
= check_output ( 'ip -4 rule list table 249' )
4485 self
. assertIn ( '32765: from 192.168.0.227 lookup 249 proto static' , output
)
4487 print ( '### ip -6 rule list table 249' )
4488 output
= check_output ( 'ip -6 rule list table 249' )
4490 self
. assertIn ( '32765: from 2000:f00::227 lookup 249 proto static' , output
)
4492 print ( '### ip -4 route show table all dev dummy98' )
4493 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4495 self
. assertIn ( 'default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink' , output
)
4496 self
. assertIn ( '192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128' , output
)
4497 self
. assertIn ( '10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink' , output
)
4498 self
. assertIn ( '192.168.0.72 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink' , output
)
4499 self
. assertIn ( '192.168.0.193 proto static scope link src 192.168.0.227 metric 128' , output
)
4500 self
. assertIn ( 'local 192.168.0.227 table local proto kernel scope host src 192.168.0.227' , output
)
4501 self
. assertIn ( 'broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227' , output
)
4503 print ( '### ip -6 route show table all dev dummy98' )
4504 output
= check_output ( 'ip -6 route show table all dev dummy98' )
4506 self
. assertIn ( '2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium' , output
)
4507 self
. assertIn ( 'default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium' , output
)
4508 self
. assertIn ( 'fe80::/64 proto kernel metric 256 pref medium' , output
)
4509 self
. assertIn ( 'local 2000:f00::227 table local proto kernel metric 0 pref medium' , output
)
4510 self
. assertRegex ( output
, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium' , output
)
4511 self
. assertIn ( 'multicast ff00::/8 table local proto kernel metric 256 pref medium' , output
)
4513 @expectedFailureIfRTA_VIAIsNotSupported ()
4514 def test_route_via_ipv6 ( self
):
4515 copy_network_unit ( '25-route-via-ipv6.network' , '12-dummy.netdev' )
4517 self
. wait_online ( 'dummy98:routable' )
4519 output
= networkctl_status ( 'dummy98' )
4522 print ( '### ip -6 route show dev dummy98' )
4523 output
= check_output ( 'ip -6 route show dev dummy98' )
4525 self
. assertRegex ( output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static' )
4526 self
. assertRegex ( output
, '2001:1234:5:8f63::1 proto kernel' )
4528 print ( '### ip -4 route show dev dummy98' )
4529 output
= check_output ( 'ip -4 route show dev dummy98' )
4531 self
. assertRegex ( output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58' )
4532 self
. assertRegex ( output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static' )
4534 @expectedFailureIfModuleIsNotAvailable ( 'tcp_dctcp' )
4535 def test_route_congctl ( self
):
4536 copy_network_unit ( '25-route-congctl.network' , '12-dummy.netdev' )
4538 self
. wait_online ( 'dummy98:routable' )
4540 print ( '### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
4541 output
= check_output ( 'ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
4543 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
4544 self
. assertIn ( 'congctl dctcp' , output
)
4546 print ( '### ip -4 route show dev dummy98 149.10.124.66' )
4547 output
= check_output ( 'ip -4 route show dev dummy98 149.10.124.66' )
4549 self
. assertIn ( '149.10.124.66 proto static' , output
)
4550 self
. assertIn ( 'congctl dctcp' , output
)
4551 self
. assertIn ( 'rto_min 300s' , output
)
4553 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
4554 def test_route_vrf ( self
):
4555 copy_network_unit ( '25-route-vrf.network' , '12-dummy.netdev' ,
4556 '25-vrf.netdev' , '25-vrf.network' )
4558 self
. wait_online ( 'dummy98:routable' , 'vrf99:carrier' )
4560 output
= check_output ( 'ip route show vrf vrf99' )
4562 self
. assertRegex ( output
, 'default via 192.168.100.1' )
4564 output
= check_output ( 'ip route show' )
4566 self
. assertNotRegex ( output
, 'default via 192.168.100.1' )
4568 def test_gateway_reconfigure ( self
):
4569 copy_network_unit ( '25-gateway-static.network' , '12-dummy.netdev' )
4571 self
. wait_online ( 'dummy98:routable' )
4572 print ( '### ip -4 route show dev dummy98 default' )
4573 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4575 self
. assertIn ( 'default via 149.10.124.59 proto static' , output
)
4576 self
. assertNotIn ( '149.10.124.60' , output
)
4578 remove_network_unit ( '25-gateway-static.network' )
4579 copy_network_unit ( '25-gateway-next-static.network' )
4581 self
. wait_online ( 'dummy98:routable' )
4582 print ( '### ip -4 route show dev dummy98 default' )
4583 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4585 self
. assertNotIn ( '149.10.124.59' , output
)
4586 self
. assertIn ( 'default via 149.10.124.60 proto static' , output
)
4588 def test_ip_route_ipv6_src_route ( self
):
4589 # a dummy device does not make the addresses go through tentative state, so we
4590 # reuse a bond from an earlier test, which does make the addresses go through
4591 # tentative state, and do our test on that
4592 copy_network_unit ( '23-active-slave.network' , '25-route-ipv6-src.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
4594 self
. wait_online ( 'dummy98:enslaved' , 'bond199:routable' )
4596 output
= check_output ( 'ip -6 route list dev bond199' )
4598 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2' , output
)
4600 def test_route_preferred_source_with_existing_address ( self
):
4602 copy_network_unit ( '25-route-preferred-source.network' , '12-dummy.netdev' )
4607 networkctl_reconfigure ( 'dummy98' )
4609 self
. wait_online ( 'dummy98:routable' )
4611 output
= check_output ( 'ip -6 route list dev dummy98' )
4613 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1' , output
)
4615 output
= check_output ( 'ip -4 route list dev dummy98' )
4617 self
. assertIn ( '10.123.0.0/16 via 192.168.30.1 proto static src 10.10.10.1' , output
)
4619 def test_ip_link_mac_address ( self
):
4620 copy_network_unit ( '25-address-link-section.network' , '12-dummy.netdev' )
4622 self
. wait_online ( 'dummy98:degraded' )
4624 output
= check_output ( 'ip link show dummy98' )
4626 self
. assertRegex ( output
, '00:01:02:aa:bb:cc' )
4628 def test_ip_link_unmanaged ( self
):
4629 copy_network_unit ( '25-link-section-unmanaged.network' , '12-dummy.netdev' )
4632 self
. wait_operstate ( 'dummy98' , 'off' , setup_state
= 'unmanaged' )
4634 def test_ipv6_address_label ( self
):
4635 copy_network_unit ( '25-ipv6-address-label-section.network' , '12-dummy.netdev' )
4636 copy_networkd_conf_dropin ( 'networkd-address-label.conf' )
4638 self
. wait_online ( 'dummy98:degraded' )
4640 output
= check_output ( 'ip addrlabel list' )
4642 self
. assertRegex ( output
, '2004:da8:1::/64 dev dummy98 label 4444' )
4643 self
. assertRegex ( output
, '2004:da8:2::/64 label 5555' )
4645 def test_ipv6_proxy_ndp ( self
):
4646 copy_network_unit ( '25-ipv6-proxy-ndp.network' , '12-dummy.netdev' )
4649 self
. wait_online ( 'dummy98:routable' )
4651 output
= check_output ( 'ip neighbor show proxy dev dummy98' )
4653 for i
in range ( 1 , 5 ):
4654 self
. assertRegex ( output
, f
'2607:5300:203:5215: {i} ::1 *proxy' )
4656 def test_ipv6_neigh_retrans_time ( self
):
4658 copy_network_unit ( '25-dummy.netdev' , '25-dummy.network' )
4661 self
. wait_online ( f
' {link} :degraded' )
4662 remove_network_unit ( '25-dummy.network' )
4664 # expect retrans_time_ms updated
4665 copy_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
4667 self
. wait_online ( f
' {link} :degraded' )
4668 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4669 remove_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
4671 # expect retrans_time_ms unchanged
4672 copy_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
4674 self
. wait_online ( f
' {link} :degraded' )
4675 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4676 remove_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
4678 # expect retrans_time_ms unchanged
4679 copy_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
4681 self
. wait_online ( f
' {link} :degraded' )
4682 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4683 remove_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
4685 # expect retrans_time_ms unchanged
4686 copy_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
4688 self
. wait_online ( f
' {link} :degraded' )
4689 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4690 remove_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
4692 # expect retrans_time_ms unchanged
4693 copy_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
4695 self
. wait_online ( f
' {link} :degraded' )
4696 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4697 remove_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
4699 # expect retrans_time_ms updated
4700 copy_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
4702 self
. wait_online ( f
' {link} :degraded' )
4703 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '4000' )
4704 remove_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
4706 def test_neighbor ( self
):
4707 copy_network_unit ( '12-dummy.netdev' , '25-neighbor-dummy.network' , '25-neighbor-dummy.network.d/10-step1.conf' ,
4708 '25-gre-tunnel-remote-any.netdev' , '25-neighbor-ip.network' ,
4709 '25-ip6gre-tunnel-remote-any.netdev' , '25-neighbor-ipv6.network' ,
4712 self
. wait_online ( 'dummy98:degraded' , 'gretun97:routable' , 'ip6gretun97:routable' )
4714 print ( '### ip neigh list dev gretun97' )
4715 output
= check_output ( 'ip neigh list dev gretun97' )
4717 self
. assertIn ( '10.0.0.22 lladdr 10.65.223.239 PERMANENT' , output
)
4718 self
. assertNotIn ( '10.0.0.23' , output
)
4720 print ( '### ip neigh list dev ip6gretun97' )
4721 output
= check_output ( 'ip neigh list dev ip6gretun97' )
4723 self
. assertRegex ( output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT' )
4724 self
. assertNotIn ( '2001:db8:0:f102::18' , output
)
4726 print ( '### ip neigh list dev dummy98' )
4727 output
= check_output ( 'ip neigh list dev dummy98' )
4729 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT' , output
)
4730 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT' , output
)
4731 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
4732 self
. assertNotIn ( '192.168.10.2' , output
)
4733 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
4735 check_json ( networkctl_json ())
4737 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
4738 # the valid configurations in 10-step1.conf.
4739 copy_network_unit ( '25-neighbor-dummy.network.d/10-step2.conf' )
4741 self
. wait_online ( 'dummy98:degraded' )
4743 print ( '### ip neigh list dev dummy98' )
4744 output
= check_output ( 'ip neigh list dev dummy98' )
4746 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT' , output
)
4747 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
4748 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
4749 self
. assertNotIn ( '192.168.10.2' , output
)
4750 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
4752 check_json ( networkctl_json ())
4754 remove_network_unit ( '25-neighbor-dummy.network.d/10-step1.conf' ,
4755 '25-neighbor-dummy.network.d/10-step2.conf' )
4756 copy_network_unit ( '25-neighbor-dummy.network.d/10-step3.conf' )
4758 self
. wait_online ( 'dummy98:degraded' )
4760 print ( '### ip neigh list dev dummy98' )
4761 output
= check_output ( 'ip neigh list dev dummy98' )
4763 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
4764 self
. assertNotIn ( '00:00:5e:00:02:65' , output
)
4765 self
. assertNotIn ( '00:00:5e:00:02:66' , output
)
4766 self
. assertNotIn ( '00:00:5e:00:03:65' , output
)
4767 self
. assertNotIn ( '2004:da8:1::1' , output
)
4769 def test_link_local_addressing ( self
):
4770 copy_network_unit ( '25-link-local-addressing-yes.network' , '11-dummy.netdev' ,
4771 '25-link-local-addressing-no.network' , '12-dummy.netdev' )
4773 self
. wait_online ( 'test1:degraded' , 'dummy98:carrier' )
4775 output
= check_output ( 'ip address show dev test1' )
4777 self
. assertRegex ( output
, 'inet .* scope link' )
4778 self
. assertRegex ( output
, 'inet6 .* scope link' )
4780 output
= check_output ( 'ip address show dev dummy98' )
4782 self
. assertNotRegex ( output
, 'inet6* .* scope link' )
4784 # Documentation/networking/ip-sysctl.txt
4786 # addr_gen_mode - INTEGER
4787 # Defines how link-local and autoconf addresses are generated.
4789 # 0: generate address based on EUI64 (default)
4790 # 1: do no generate a link-local address, use EUI64 for addresses generated
4792 # 2: generate stable privacy addresses, using the secret from
4793 # stable_secret (RFC7217)
4794 # 3: generate stable privacy addresses, using a random secret if unset
4796 self
. check_ipv6_sysctl_attr ( 'test1' , 'stable_secret' , '0123:4567:89ab:cdef:0123:4567:89ab:cdef' )
4797 self
. check_ipv6_sysctl_attr ( 'test1' , 'addr_gen_mode' , '2' )
4798 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'addr_gen_mode' , '1' )
4800 def test_link_local_addressing_ipv6ll ( self
):
4801 copy_network_unit ( '26-link-local-addressing-ipv6.network' , '12-dummy.netdev' )
4803 self
. wait_online ( 'dummy98:degraded' )
4805 # An IPv6LL address exists by default.
4806 output
= check_output ( 'ip address show dev dummy98' )
4808 self
. assertRegex ( output
, 'inet6 .* scope link' )
4810 copy_network_unit ( '25-link-local-addressing-no.network' )
4812 self
. wait_online ( 'dummy98:carrier' )
4814 # Check if the IPv6LL address is removed.
4815 output
= check_output ( 'ip address show dev dummy98' )
4817 self
. assertNotRegex ( output
, 'inet6 .* scope link' )
4819 remove_network_unit ( '25-link-local-addressing-no.network' )
4821 self
. wait_online ( 'dummy98:degraded' )
4823 # Check if a new IPv6LL address is assigned.
4824 output
= check_output ( 'ip address show dev dummy98' )
4826 self
. assertRegex ( output
, 'inet6 .* scope link' )
4828 def test_sysctl ( self
):
4829 copy_networkd_conf_dropin ( '25-global-ipv6-privacy-extensions.conf' )
4830 copy_network_unit ( '25-sysctl.network' , '12-dummy.netdev' , copy_dropins
= False )
4832 self
. wait_online ( 'dummy98:degraded' )
4834 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
4835 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '1' )
4836 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'dad_transmits' , '3' )
4837 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'hop_limit' , '5' )
4838 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'proxy_ndp' , '1' )
4839 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
4840 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp' , '1' )
4841 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp_pvlan' , '1' )
4842 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'accept_local' , '1' )
4843 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'rp_filter' , '0' )
4844 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'force_igmp_version' , '1' )
4846 copy_network_unit ( '25-sysctl.network.d/25-ipv6-privacy-extensions.conf' )
4848 self
. wait_online ( 'dummy98:degraded' )
4850 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '2' )
4852 def test_sysctl_disable_ipv6 ( self
):
4853 copy_network_unit ( '25-sysctl-disable-ipv6.network' , '12-dummy.netdev' )
4855 print ( '## Disable ipv6' )
4856 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=1' )
4857 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=1' )
4860 self
. wait_online ( 'dummy98:routable' )
4862 output
= check_output ( 'ip -4 address show dummy98' )
4864 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
4865 output
= check_output ( 'ip -6 address show dummy98' )
4867 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
4868 self
. assertRegex ( output
, 'inet6 .* scope link' )
4869 output
= check_output ( 'ip -4 route show dev dummy98' )
4871 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
4872 output
= check_output ( 'ip -6 route show default' )
4874 self
. assertRegex ( output
, 'default' )
4875 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
4877 remove_link ( 'dummy98' )
4879 print ( '## Enable ipv6' )
4880 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=0' )
4881 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=0' )
4884 self
. wait_online ( 'dummy98:routable' )
4886 output
= check_output ( 'ip -4 address show dummy98' )
4888 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
4889 output
= check_output ( 'ip -6 address show dummy98' )
4891 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
4892 self
. assertRegex ( output
, 'inet6 .* scope link' )
4893 output
= check_output ( 'ip -4 route show dev dummy98' )
4895 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
4896 output
= check_output ( 'ip -6 route show default' )
4898 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
4900 @expectedFailureIfModuleIsNotAvailable ( 'mpls_router' )
4901 def test_sysctl_mpls ( self
):
4902 check_output ( 'modprobe mpls_router' )
4903 copy_network_unit ( '25-sysctl-mpls.network' , '12-dummy.netdev' )
4905 self
. wait_online ( 'dummy98:degraded' )
4907 self
. check_mpls_sysctl_attr ( 'dummy98' , 'input' , '1' )
4909 def test_bind_carrier ( self
):
4910 copy_network_unit ( '25-bind-carrier.network' , '11-dummy.netdev' )
4913 # no bound interface.
4914 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
4915 output
= check_output ( 'ip address show test1' )
4917 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4918 self
. assertIn ( 'DOWN' , output
)
4919 self
. assertNotIn ( '192.168.10' , output
)
4921 # add one bound interface. The interface will be up.
4922 check_output ( 'ip link add dummy98 type dummy' )
4923 check_output ( 'ip link set dummy98 up' )
4924 self
. wait_online ( 'test1:routable' )
4925 output
= check_output ( 'ip address show test1' )
4927 self
. assertIn ( 'UP,LOWER_UP' , output
)
4928 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4930 # add another bound interface. The interface is still up.
4931 check_output ( 'ip link add dummy99 type dummy' )
4932 check_output ( 'ip link set dummy99 up' )
4933 self
. wait_operstate ( 'dummy99' , 'degraded' , setup_state
= 'unmanaged' )
4934 output
= check_output ( 'ip address show test1' )
4936 self
. assertIn ( 'UP,LOWER_UP' , output
)
4937 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4939 # remove one of the bound interfaces. The interface is still up
4940 remove_link ( 'dummy98' )
4941 output
= check_output ( 'ip address show test1' )
4943 self
. assertIn ( 'UP,LOWER_UP' , output
)
4944 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4946 # bring down the remaining bound interface. The interface will be down.
4947 check_output ( 'ip link set dummy99 down' )
4948 self
. wait_operstate ( 'test1' , 'off' )
4949 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
4950 output
= check_output ( 'ip address show test1' )
4952 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4953 self
. assertIn ( 'DOWN' , output
)
4954 self
. assertNotIn ( '192.168.10' , output
)
4956 # bring up the bound interface. The interface will be up.
4957 check_output ( 'ip link set dummy99 up' )
4958 self
. wait_online ( 'test1:routable' )
4959 output
= check_output ( 'ip address show test1' )
4961 self
. assertIn ( 'UP,LOWER_UP' , output
)
4962 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4964 # remove the remaining bound interface. The interface will be down.
4965 remove_link ( 'dummy99' )
4966 self
. wait_operstate ( 'test1' , 'off' )
4967 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
4968 output
= check_output ( 'ip address show test1' )
4970 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4971 self
. assertIn ( 'DOWN' , output
)
4972 self
. assertNotIn ( '192.168.10' , output
)
4974 # re-add one bound interface. The interface will be up.
4975 check_output ( 'ip link add dummy98 type dummy' )
4976 check_output ( 'ip link set dummy98 up' )
4977 self
. wait_online ( 'test1:routable' )
4978 output
= check_output ( 'ip address show test1' )
4980 self
. assertIn ( 'UP,LOWER_UP' , output
)
4981 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4983 def _test_activation_policy ( self
, interface
, test
):
4984 conffile
= '25-activation-policy.network'
4986 conffile
= f
' {conffile} .d/ {test} .conf'
4987 if interface
== 'vlan99' :
4988 copy_network_unit ( '21-vlan.netdev' , '21-vlan-test1.network' )
4989 copy_network_unit ( '11-dummy.netdev' , conffile
, copy_dropins
= False )
4992 always
= test
. startswith ( 'always' )
4993 initial_up
= test
!= 'manual' and not test
. endswith ( 'down' ) # note: default is up
4994 expect_up
= initial_up
4995 next_up
= not expect_up
4997 if test
. endswith ( 'down' ):
4998 self
. wait_activated ( interface
)
5000 for iteration
in range ( 4 ):
5001 with self
. subTest ( iteration
= iteration
, expect_up
= expect_up
):
5002 operstate
= 'routable' if expect_up
else 'off'
5003 setup_state
= 'configured' if expect_up
else ( 'configuring' if iteration
== 0 else None )
5004 self
. wait_operstate ( interface
, operstate
, setup_state
= setup_state
, setup_timeout
= 20 )
5007 self
. assertIn ( 'UP' , check_output ( f
'ip link show {interface} ' ))
5008 self
. assertIn ( '192.168.10.30/24' , check_output ( f
'ip address show {interface} ' ))
5009 self
. assertIn ( 'default via 192.168.10.1' , check_output ( f
'ip route show dev {interface} ' ))
5011 self
. assertIn ( 'DOWN' , check_output ( f
'ip link show {interface} ' ))
5014 check_output ( f
'ip link set dev {interface} up' )
5016 check_output ( f
'ip link set dev {interface} down' )
5017 expect_up
= initial_up
if always
else next_up
5018 next_up
= not next_up
5022 def test_activation_policy ( self
):
5024 for interface
in [ 'test1' , 'vlan99' ]:
5025 for test
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , '' ]:
5031 print ( f
'### test_activation_policy(interface= {interface} , test= {test} )' )
5032 with self
. subTest ( interface
= interface
, test
= test
):
5033 self
._ test
_ activation
_ policy
( interface
, test
)
5035 def _test_activation_policy_required_for_online ( self
, policy
, required
):
5036 conffile
= '25-activation-policy.network'
5037 units
= [ '11-dummy.netdev' , '12-dummy.netdev' , '12-dummy.network' , conffile
]
5039 units
+= [ f
' {conffile} .d/ {policy} .conf' ]
5041 units
+= [ f
' {conffile} .d/required- {required} .conf' ]
5042 copy_network_unit (* units
, copy_dropins
= False )
5045 if policy
. endswith ( 'down' ):
5046 self
. wait_activated ( 'test1' )
5048 if policy
. endswith ( 'down' ) or policy
== 'manual' :
5049 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
5051 self
. wait_online ( 'test1' )
5053 if policy
== 'always-down' :
5054 # if always-down, required for online is forced to no
5057 # otherwise if required for online is specified, it should match that
5058 expected
= required
== 'yes'
5060 # otherwise if only policy specified, required for online defaults to
5061 # true if policy is up, always-up, or bound
5062 expected
= policy
. endswith ( 'up' ) or policy
== 'bound'
5064 # default is true, if neither are specified
5067 output
= networkctl_status ( 'test1' )
5070 yesno
= 'yes' if expected
else 'no'
5071 self
. assertRegex ( output
, f
'Required For Online: {yesno} ' )
5073 def test_activation_policy_required_for_online ( self
):
5075 for policy
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , 'bound' , '' ]:
5076 for required
in [ 'yes' , 'no' , '' ]:
5082 print ( f
'### test_activation_policy_required_for_online(policy= {policy} , required= {required} )' )
5083 with self
. subTest ( policy
= policy
, required
= required
):
5084 self
._ test
_ activation
_ policy
_ required
_ for
_ online
( policy
, required
)
5086 def test_domain ( self
):
5087 copy_network_unit ( '12-dummy.netdev' , '24-search-domain.network' )
5089 self
. wait_online ( 'dummy98:routable' )
5091 output
= networkctl_status ( 'dummy98' )
5093 self
. assertRegex ( output
, 'Address: 192.168.42.100' )
5094 self
. assertRegex ( output
, 'DNS: 192.168.42.1' )
5095 self
. assertRegex ( output
, 'Search Domains: one' )
5097 def test_keep_configuration_yes ( self
):
5098 check_output ( 'ip link add dummy98 type dummy' )
5099 check_output ( 'ip link set dev dummy98 up' )
5100 check_output ( 'ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98' )
5101 check_output ( 'ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot' )
5103 copy_network_unit ( '24-keep-configuration-yes.network' )
5105 self
. wait_online ( 'dummy98:routable' )
5107 output
= check_output ( 'ip address show dummy98' )
5109 self
. assertIn ( 'inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98' , output
)
5111 output
= check_output ( 'ip -d -4 route show dev dummy98' )
5113 self
. assertIn ( '203.0.113.0/24 via 198.51.100.10 proto boot' , output
)
5115 def test_keep_configuration_static ( self
):
5116 check_output ( 'ip link add name dummy98 type dummy' )
5117 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
5118 check_output ( 'ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500' )
5119 output
= check_output ( 'ip address show dummy98' )
5121 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
5122 self
. assertRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
5123 output
= check_output ( 'ip route show dev dummy98' )
5126 copy_network_unit ( '24-keep-configuration-static.network' )
5128 self
. wait_online ( 'dummy98:routable' )
5130 output
= check_output ( 'ip address show dummy98' )
5132 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
5133 self
. assertNotRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
5135 def check_keep_configuration_on_restart ( self
):
5136 self
. wait_online ( 'dummy98:routable' )
5137 self
. wait_online ( 'unmanaged0:routable' , setup_state
= 'unmanaged' )
5139 print ( '### ip -6 address show dev dummy98' )
5140 output
= check_output ( 'ip -6 address show dev dummy98' )
5142 self
. assertIn ( 'inet6 2001:db8:0:f101::15/64 scope global' , output
)
5143 self
. assertIn ( 'inet6 2001:db8:1:f101::15/64 scope global deprecated' , output
)
5145 print ( '### ip -6 address show dev unmanaged0' )
5146 output
= check_output ( 'ip -6 address show dev unmanaged0' )
5148 self
. assertIn ( 'inet6 2001:db8:9999:f101::15/64 scope global' , output
)
5150 print ( '### ip -6 route show default dev dummy98' )
5151 output
= check_output ( 'ip -6 route show default dev dummy98' )
5153 self
. assertIn ( 'default via fe80::f0ca:cc1a proto static metric 1 pref medium' , output
)
5155 def test_keep_configuration_on_restart ( self
):
5156 copy_network_unit ( '12-dummy.netdev' , '85-static-ipv6.network' , '85-unmanaged.link' )
5158 # Add an unmanaged interface with an up address
5159 call ( 'ip link add unmanaged0 type dummy' )
5160 call ( 'ip link set unmanaged0 up' )
5161 call ( 'ip -4 addr add 10.20.30.40/32 dev unmanaged0' )
5162 call ( 'ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0' )
5164 # Wait for all addresses
5165 self
. wait_address ( 'unmanaged0' , 'inet 10.20.30.40/32' , scope
= 'global' , ipv
= '-4' , timeout_sec
= 10 )
5166 self
. wait_address ( 'unmanaged0' , 'inet6 2001:db8:9999:f101::15/64' , scope
= 'global' , ipv
= '-6' , timeout_sec
= 10 )
5167 self
. wait_address ( 'unmanaged0' , 'inet6 fe80::[0-9a-f:]*/64' , scope
= 'link' , ipv
= '-6' , timeout_sec
= 10 )
5169 # Wait for all routes
5170 self
. wait_route ( 'unmanaged0' , 'local 10.20.30.40 proto kernel' , table
= 'local' , ipv
= '-4' , timeout_sec
= 10 )
5171 self
. wait_route ( 'unmanaged0' , 'local fe80::[0-9a-f:]* proto kernel' , table
= 'local' , ipv
= '-6' , timeout_sec
= 10 )
5172 self
. wait_route ( 'unmanaged0' , 'multicast ff00::/8 proto kernel' , table
= 'local' , ipv
= '-6' , timeout_sec
= 10 )
5173 self
. wait_route ( 'unmanaged0' , '2001:db8:9999:f101::/64 proto kernel' , table
= 'main' , ipv
= '-6' , timeout_sec
= 10 )
5174 self
. wait_route ( 'unmanaged0' , 'fe80::/64 proto kernel' , table
= 'main' , ipv
= '-6' , timeout_sec
= 10 )
5176 # Start `ip monitor` with output to a temporary file
5177 with tempfile
. TemporaryFile ( mode
= 'r+' , prefix
= 'ip_monitor_u' ) as logfile_unmanaged
:
5178 process_u
= subprocess
. Popen ([ 'ip' , 'monitor' , 'dev' , 'unmanaged0' ], stdout
= logfile_unmanaged
, text
= True )
5181 self
. check_keep_configuration_on_restart ()
5183 # Start `ip monitor` with output to a temporary file
5184 with tempfile
. TemporaryFile ( mode
= 'r+' , prefix
= 'ip_monitor' ) as logfile
:
5185 process
= subprocess
. Popen ([ 'ip' , 'monitor' , 'dev' , 'dummy98' ], stdout
= logfile
, text
= True )
5188 self
. check_keep_configuration_on_restart ()
5190 process
. send_signal ( signal
. SIGTERM
)
5193 print ( '### ip monitor dev dummy98 BEGIN' )
5195 # Read the `ip monitor` output looking for network changes
5197 for line
in logfile
:
5198 line
= line
. rstrip ()
5200 # Check if a link went down
5201 self
. assertNotRegex ( line
, 'dummy98: .* state DOWN' )
5202 # Check if an address was removed
5203 self
. assertNotRegex ( line
, '^Deleted .* 2001:db8:' )
5204 self
. assertNotRegex ( line
, '^Deleted 2001:db8:.*/64' )
5205 # Check if the default route was removed
5206 self
. assertNotRegex ( line
, '^Deleted default via fe80::f0ca:cc1a' )
5208 print ( '### ip monitor dev dummy98 END' )
5210 process_u
. send_signal ( signal
. SIGTERM
)
5213 print ( '### ip monitor dev unmanaged0 BEGIN' )
5215 # Read the `ip monitor` output looking for network changes and check if something happened
5216 logfile_unmanaged
. seek ( 0 )
5217 self
. assertEqual ( logfile_unmanaged
. read (). rstrip (), '' )
5219 print ( '### ip monitor dev unmanaged0 END' )
5221 def test_keep_untracked_addresses ( self
):
5222 # Add an unmanaged interface with an up address
5224 copy_network_unit ( '12-dummy.netdev' , '85-static-ipv6.network' )
5226 self
. wait_online ( 'dummy98:routable' )
5228 print ( '### ip -4 addr add 10.234.77.111/32 dev dummy98' )
5229 output
= check_output ( 'ip -4 addr add 10.234.77.111/32 dev dummy98' )
5232 print ( '### ip -6 addr add 2222:3333::4444/64 dev dummy98' )
5233 output
= check_output ( 'ip -6 addr add 2222:3333::4444/64 dev dummy98' )
5238 output
= check_output ( 'ip -4 addr show dev dummy98' )
5240 self
. assertIn ( 'inet 10.234.77.111/32' , output
)
5242 output
= check_output ( 'ip -6 addr show dev dummy98' )
5244 self
. assertIn ( 'inet6 2222:3333::4444/64 scope global' , output
)
5246 def check_nexthop ( self
, manage_foreign_nexthops
, first
):
5247 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
5249 output
= check_output ( 'ip nexthop list dev veth99' )
5252 self
. assertIn ( 'id 1 via 192.168.5.1 dev veth99' , output
)
5253 self
. assertIn ( 'id 2 via 2001:1234:5:8f63::2 dev veth99' , output
)
5255 self
. assertIn ( 'id 6 via 192.168.5.1 dev veth99' , output
)
5256 self
. assertIn ( 'id 7 via 2001:1234:5:8f63::2 dev veth99' , output
)
5257 self
. assertIn ( 'id 3 dev veth99' , output
)
5258 self
. assertIn ( 'id 4 dev veth99' , output
)
5260 self
. assertRegex ( output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink' )
5262 self
. assertIn ( 'id 5 via 192.168.5.3 dev veth99' , output
)
5263 self
. assertNotRegex ( output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink' )
5264 self
. assertIn ( 'id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99' , output
)
5265 if manage_foreign_nexthops
:
5266 self
. assertRegex ( output
, r
'id [0-9]* via 192.168.5.2 dev veth99' )
5268 output
= check_output ( 'ip nexthop list dev dummy98' )
5271 self
. assertIn ( 'id 20 via 192.168.20.1 dev dummy98' , output
)
5273 self
. assertIn ( 'id 21 via 192.168.20.1 dev dummy98' , output
)
5274 if manage_foreign_nexthops
:
5275 self
. assertNotIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
5277 self
. assertIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
5279 # kernel manages blackhole nexthops on lo
5280 output
= check_output ( 'ip nexthop list dev lo' )
5283 self
. assertIn ( 'id 6 blackhole' , output
)
5284 self
. assertIn ( 'id 7 blackhole' , output
)
5286 self
. assertIn ( 'id 1 blackhole' , output
)
5287 self
. assertIn ( 'id 2 blackhole' , output
)
5289 # group nexthops are shown with -0 option
5291 output
= check_output ( 'ip -0 nexthop list id 21' )
5293 self
. assertRegex ( output
, r
'id 21 group (1,3/20|20/1,3)' )
5295 output
= check_output ( 'ip -0 nexthop list id 20' )
5297 self
. assertRegex ( output
, r
'id 20 group (5,3/21|21/5,3)' )
5299 output
= check_output ( 'ip route show dev veth99 10.10.10.10' )
5302 self
. assertEqual ( '10.10.10.10 nhid 1 via 192.168.5.1 proto static' , output
)
5304 self
. assertEqual ( '10.10.10.10 nhid 6 via 192.168.5.1 proto static' , output
)
5306 output
= check_output ( 'ip route show dev veth99 10.10.10.11' )
5309 self
. assertEqual ( '10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static' , output
)
5311 self
. assertEqual ( '10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static' , output
)
5313 output
= check_output ( 'ip route show dev veth99 10.10.10.12' )
5316 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink' , output
)
5318 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.5.3 proto static' , output
)
5320 output
= check_output ( 'ip -6 route show dev veth99 2001:1234:5:8f62::1' )
5323 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
5325 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
5327 output
= check_output ( 'ip route show 10.10.10.13' )
5330 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 6 dev lo proto static' , output
)
5332 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 1 dev lo proto static' , output
)
5334 output
= check_output ( 'ip -6 route show 2001:1234:5:8f62::2' )
5337 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium' , output
)
5339 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium' , output
)
5341 output
= check_output ( 'ip route show 10.10.10.14' )
5344 self
. assertIn ( '10.10.10.14 nhid 21 proto static' , output
)
5345 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
5347 self
. assertIn ( '10.10.10.14 nhid 20 proto static' , output
)
5348 self
. assertIn ( 'nexthop via 192.168.5.3 dev veth99 weight 3' , output
)
5349 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
5351 output
= networkctl_json ()
5353 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
5355 def _test_nexthop ( self
, manage_foreign_nexthops
):
5356 if not manage_foreign_nexthops
:
5357 copy_networkd_conf_dropin ( 'networkd-manage-foreign-nexthops-no.conf' )
5359 check_output ( 'ip link add dummy98 type dummy' )
5360 check_output ( 'ip link set dummy98 up' )
5361 check_output ( 'ip address add 192.168.20.20/24 dev dummy98' )
5362 check_output ( 'ip nexthop add id 42 via 192.168.20.2 dev dummy98' )
5364 copy_network_unit ( '25-nexthop-1.network' , '25-veth.netdev' , '25-veth-peer.network' ,
5365 '12-dummy.netdev' , '25-nexthop-dummy-1.network' )
5368 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
5370 remove_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
5371 copy_network_unit ( '25-nexthop-2.network' , '25-nexthop-dummy-2.network' )
5373 self
. check_nexthop ( manage_foreign_nexthops
, first
= False )
5375 remove_network_unit ( '25-nexthop-2.network' )
5376 copy_network_unit ( '25-nexthop-nothing.network' )
5378 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5380 output
= check_output ( 'ip nexthop list dev veth99' )
5382 self
. assertEqual ( output
, '' )
5383 output
= check_output ( 'ip nexthop list dev lo' )
5385 self
. assertEqual ( output
, '' )
5387 remove_network_unit ( '25-nexthop-nothing.network' , '25-nexthop-dummy-2.network' )
5388 copy_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
5389 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
5390 # here to test reconfiguring with different .network files does not trigger race.
5391 # See also comments in link_drop_requests().
5392 networkctl_reconfigure ( 'dummy98' ) # reconfigured with 25-nexthop-dummy-2.network
5393 networkctl_reload () # reconfigured with 25-nexthop-dummy-1.network
5395 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
5397 # Remove nexthop with ID 20
5398 check_output ( 'ip nexthop del id 20' )
5400 # Check the nexthop ID 20 is dropped from the group nexthop.
5401 output
= check_output ( 'ip -0 nexthop list id 21' )
5403 self
. assertRegex ( output
, r
'id 21 group 1,3' )
5405 # Remove nexthop with ID 21
5406 check_output ( 'ip nexthop del id 21' )
5407 copy_network_unit ( '11-dummy.netdev' , '25-nexthop-test1.network' )
5410 # 25-nexthop-test1.network requests a route with nexthop ID 21, which is removed in the above,
5411 # hence test1 should be stuck in the configuring state.
5412 self
. wait_operstate ( 'test1' , operstate
= 'routable' , setup_state
= 'configuring' )
5414 # Wait for a while, and check if the interface is still in the configuring state.
5416 output
= networkctl_status ( 'test1' )
5417 self
. assertIn ( 'State: routable (configuring)' , output
)
5419 # Check if the route which needs nexthop 21 are forgotten.
5420 output
= networkctl_json ()
5422 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
5424 # Reconfigure the interface that has nexthop with ID 20 and 21,
5425 # then the route requested by test1 can be configured.
5426 networkctl_reconfigure ( 'dummy98' )
5427 self
. wait_online ( 'test1:routable' )
5429 # Check if the requested route actually configured.
5430 output
= check_output ( 'ip route show 10.10.11.10' )
5432 self
. assertIn ( '10.10.11.10 nhid 21 proto static' , output
)
5433 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
5434 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
5436 remove_link ( 'veth99' )
5439 output
= check_output ( 'ip nexthop list dev lo' )
5441 self
. assertEqual ( output
, '' )
5443 @expectedFailureIfNexthopIsNotAvailable ()
5444 def test_nexthop ( self
):
5446 for manage_foreign_nexthops
in [ True , False ]:
5452 print ( f
'### test_nexthop(manage_foreign_nexthops= {manage_foreign_nexthops} )' )
5453 with self
. subTest ( manage_foreign_nexthops
= manage_foreign_nexthops
):
5454 self
._ test
_ nexthop
( manage_foreign_nexthops
)
5456 class NetworkdTCTests ( unittest
. TestCase
, Utilities
):
5464 @expectedFailureIfModuleIsNotAvailable ( 'sch_cake' )
5465 def test_qdisc_cake ( self
):
5466 copy_network_unit ( '25-qdisc-cake.network' , '12-dummy.netdev' )
5468 self
. wait_online ( 'dummy98:routable' )
5470 output
= check_output ( 'tc qdisc show dev dummy98' )
5472 self
. assertIn ( 'qdisc cake 3a: root' , output
)
5473 self
. assertIn ( 'bandwidth 500Mbit' , output
)
5474 self
. assertIn ( 'autorate-ingress' , output
)
5475 self
. assertIn ( 'diffserv8' , output
)
5476 self
. assertIn ( 'dual-dsthost' , output
)
5477 self
. assertIn ( ' nat' , output
)
5478 self
. assertIn ( ' wash' , output
)
5479 self
. assertIn ( ' split-gso' , output
)
5480 self
. assertIn ( ' raw' , output
)
5481 self
. assertIn ( ' atm' , output
)
5482 self
. assertIn ( 'overhead 128' , output
)
5483 self
. assertIn ( 'mpu 20' , output
)
5484 self
. assertIn ( 'fwmark 0xff00' , output
)
5485 self
. assertIn ( 'rtt 1s' , output
)
5486 self
. assertIn ( 'ack-filter-aggressive' , output
)
5488 # Test for replacing existing qdisc. See #31226.
5489 with
open ( os
. path
. join ( network_unit_dir
, '25-qdisc-cake.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5490 f
. write ( 'Bandwidth=250M \n ' )
5493 self
. wait_online ( 'dummy98:routable' )
5495 output
= check_output ( 'tc qdisc show dev dummy98' )
5497 self
. assertIn ( 'bandwidth 250Mbit' , output
)
5499 @expectedFailureIfModuleIsNotAvailable ( 'sch_codel' )
5500 def test_qdisc_codel ( self
):
5501 copy_network_unit ( '25-qdisc-codel.network' , '12-dummy.netdev' )
5503 self
. wait_online ( 'dummy98:routable' )
5505 output
= check_output ( 'tc qdisc show dev dummy98' )
5507 self
. assertRegex ( output
, 'qdisc codel 33: root' )
5508 self
. assertRegex ( output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn' )
5510 @expectedFailureIfModuleIsNotAvailable ( 'sch_drr' )
5511 def test_qdisc_drr ( self
):
5512 copy_network_unit ( '25-qdisc-drr.network' , '12-dummy.netdev' )
5514 self
. wait_online ( 'dummy98:routable' )
5516 output
= check_output ( 'tc qdisc show dev dummy98' )
5518 self
. assertRegex ( output
, 'qdisc drr 2: root' )
5519 output
= check_output ( 'tc class show dev dummy98' )
5521 self
. assertRegex ( output
, 'class drr 2:30 root quantum 2000b' )
5523 @expectedFailureIfModuleIsNotAvailable ( 'sch_ets' )
5524 def test_qdisc_ets ( self
):
5525 copy_network_unit ( '25-qdisc-ets.network' , '12-dummy.netdev' )
5527 self
. wait_online ( 'dummy98:routable' )
5529 output
= check_output ( 'tc qdisc show dev dummy98' )
5532 self
. assertRegex ( output
, 'qdisc ets 3a: root' )
5533 self
. assertRegex ( output
, 'bands 10 strict 3' )
5534 self
. assertRegex ( output
, 'quanta 1 2 3 4 5' )
5535 self
. assertRegex ( output
, 'priomap 3 4 5 6 7' )
5537 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq' )
5538 def test_qdisc_fq ( self
):
5539 copy_network_unit ( '25-qdisc-fq.network' , '12-dummy.netdev' )
5541 self
. wait_online ( 'dummy98:routable' )
5543 output
= check_output ( 'tc qdisc show dev dummy98' )
5545 self
. assertRegex ( output
, 'qdisc fq 32: root' )
5546 self
. assertRegex ( output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511' )
5547 self
. assertRegex ( output
, 'quantum 1500' )
5548 self
. assertRegex ( output
, 'initial_quantum 13000' )
5549 self
. assertRegex ( output
, 'maxrate 1Mbit' )
5551 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_codel' )
5552 def test_qdisc_fq_codel ( self
):
5553 copy_network_unit ( '25-qdisc-fq_codel.network' , '12-dummy.netdev' )
5555 self
. wait_online ( 'dummy98:routable' )
5557 output
= check_output ( 'tc qdisc show dev dummy98' )
5559 self
. assertRegex ( output
, 'qdisc fq_codel 34: root' )
5560 self
. assertRegex ( output
, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn' )
5562 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_pie' )
5563 def test_qdisc_fq_pie ( self
):
5564 copy_network_unit ( '25-qdisc-fq_pie.network' , '12-dummy.netdev' )
5566 self
. wait_online ( 'dummy98:routable' )
5568 output
= check_output ( 'tc qdisc show dev dummy98' )
5571 self
. assertRegex ( output
, 'qdisc fq_pie 3a: root' )
5572 self
. assertRegex ( output
, 'limit 200000p' )
5574 @expectedFailureIfModuleIsNotAvailable ( 'sch_gred' )
5575 def test_qdisc_gred ( self
):
5576 copy_network_unit ( '25-qdisc-gred.network' , '12-dummy.netdev' )
5578 self
. wait_online ( 'dummy98:routable' )
5580 output
= check_output ( 'tc qdisc show dev dummy98' )
5582 self
. assertRegex ( output
, 'qdisc gred 38: root' )
5583 self
. assertRegex ( output
, 'vqs 12 default 10 grio' )
5585 @expectedFailureIfModuleIsNotAvailable ( 'sch_hhf' )
5586 def test_qdisc_hhf ( self
):
5587 copy_network_unit ( '25-qdisc-hhf.network' , '12-dummy.netdev' )
5589 self
. wait_online ( 'dummy98:routable' )
5591 output
= check_output ( 'tc qdisc show dev dummy98' )
5593 self
. assertRegex ( output
, 'qdisc hhf 3a: root' )
5594 self
. assertRegex ( output
, 'limit 1022p' )
5596 @expectedFailureIfModuleIsNotAvailable ( 'sch_htb' )
5597 def test_qdisc_htb_fifo ( self
):
5598 copy_network_unit ( '25-qdisc-htb-fifo.network' , '12-dummy.netdev' )
5600 self
. wait_online ( 'dummy98:routable' )
5602 output
= check_output ( 'tc qdisc show dev dummy98' )
5604 self
. assertRegex ( output
, 'qdisc htb 2: root' )
5605 self
. assertRegex ( output
, r
'default (0x30|30)' )
5607 self
. assertRegex ( output
, 'qdisc pfifo 37: parent 2:37' )
5608 self
. assertRegex ( output
, 'limit 100000p' )
5610 self
. assertRegex ( output
, 'qdisc bfifo 3a: parent 2:3a' )
5611 self
. assertRegex ( output
, 'limit 1000000' )
5613 self
. assertRegex ( output
, 'qdisc pfifo_head_drop 3b: parent 2:3b' )
5614 self
. assertRegex ( output
, 'limit 1023p' )
5616 self
. assertRegex ( output
, 'qdisc pfifo_fast 3c: parent 2:3c' )
5618 output
= check_output ( 'tc -d class show dev dummy98' )
5620 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
5621 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
5622 # which is fixed in v6.3.0 by
5623 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
5624 self
. assertRegex ( output
, 'class htb 2:37 root leaf 37(:|prio) ' )
5625 self
. assertRegex ( output
, 'class htb 2:3a root leaf 3a(:|prio) ' )
5626 self
. assertRegex ( output
, 'class htb 2:3b root leaf 3b(:|prio) ' )
5627 self
. assertRegex ( output
, 'class htb 2:3c root leaf 3c(:|prio) ' )
5628 self
. assertRegex ( output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit' )
5629 self
. assertRegex ( output
, 'burst 123456' )
5630 self
. assertRegex ( output
, 'cburst 123457' )
5632 @expectedFailureIfModuleIsNotAvailable ( 'sch_ingress' )
5633 def test_qdisc_ingress ( self
):
5634 copy_network_unit ( '25-qdisc-clsact.network' , '12-dummy.netdev' ,
5635 '25-qdisc-ingress.network' , '11-dummy.netdev' )
5637 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
5639 output
= check_output ( 'tc qdisc show dev dummy98' )
5641 self
. assertRegex ( output
, 'qdisc clsact' )
5643 output
= check_output ( 'tc qdisc show dev test1' )
5645 self
. assertRegex ( output
, 'qdisc ingress' )
5647 def test_qdisc_mq ( self
):
5648 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '25-qdisc-mq.network' )
5650 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
5652 output
= check_output ( 'tc qdisc show dev testtun99' )
5654 self
. assertIn ( 'qdisc mq 2: root' , output
)
5656 @expectedFailureIfModuleIsNotAvailable ( 'sch_multiq' )
5657 def test_qdisc_multiq ( self
):
5658 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '25-qdisc-multiq.network' )
5660 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
5662 output
= check_output ( 'tc qdisc show dev testtun99' )
5664 self
. assertIn ( 'qdisc multiq 2: root' , output
)
5666 @expectedFailureIfModuleIsNotAvailable ( 'sch_netem' )
5667 def test_qdisc_netem ( self
):
5668 copy_network_unit ( '25-qdisc-netem.network' , '12-dummy.netdev' ,
5669 '25-qdisc-netem-compat.network' , '11-dummy.netdev' )
5671 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
5673 output
= check_output ( 'tc qdisc show dev dummy98' )
5675 self
. assertRegex ( output
, 'qdisc netem 30: root' )
5676 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
5678 output
= check_output ( 'tc qdisc show dev test1' )
5680 self
. assertRegex ( output
, 'qdisc netem [0-9a-f]*: root' )
5681 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
5683 @expectedFailureIfModuleIsNotAvailable ( 'sch_pie' )
5684 def test_qdisc_pie ( self
):
5685 copy_network_unit ( '25-qdisc-pie.network' , '12-dummy.netdev' )
5687 self
. wait_online ( 'dummy98:routable' )
5689 output
= check_output ( 'tc qdisc show dev dummy98' )
5691 self
. assertRegex ( output
, 'qdisc pie 3a: root' )
5692 self
. assertRegex ( output
, 'limit 200000' )
5694 @expectedFailureIfModuleIsNotAvailable ( 'sch_qfq' )
5695 def test_qdisc_qfq ( self
):
5696 copy_network_unit ( '25-qdisc-qfq.network' , '12-dummy.netdev' )
5698 self
. wait_online ( 'dummy98:routable' )
5700 output
= check_output ( 'tc qdisc show dev dummy98' )
5702 self
. assertRegex ( output
, 'qdisc qfq 2: root' )
5703 output
= check_output ( 'tc class show dev dummy98' )
5705 self
. assertRegex ( output
, 'class qfq 2:30 root weight 2 maxpkt 16000' )
5706 self
. assertRegex ( output
, 'class qfq 2:31 root weight 10 maxpkt 8000' )
5708 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfb' )
5709 def test_qdisc_sfb ( self
):
5710 copy_network_unit ( '25-qdisc-sfb.network' , '12-dummy.netdev' )
5712 self
. wait_online ( 'dummy98:routable' )
5714 output
= check_output ( 'tc qdisc show dev dummy98' )
5716 self
. assertRegex ( output
, 'qdisc sfb 39: root' )
5717 self
. assertRegex ( output
, 'limit 200000' )
5719 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfq' )
5720 def test_qdisc_sfq ( self
):
5721 copy_network_unit ( '25-qdisc-sfq.network' , '12-dummy.netdev' )
5723 self
. wait_online ( 'dummy98:routable' )
5725 output
= check_output ( 'tc qdisc show dev dummy98' )
5727 self
. assertRegex ( output
, 'qdisc sfq 36: root' )
5728 self
. assertRegex ( output
, 'perturb 5sec' )
5730 @expectedFailureIfModuleIsNotAvailable ( 'sch_tbf' )
5731 def test_qdisc_tbf ( self
):
5732 copy_network_unit ( '25-qdisc-tbf.network' , '12-dummy.netdev' )
5734 self
. wait_online ( 'dummy98:routable' )
5736 output
= check_output ( 'tc qdisc show dev dummy98' )
5738 self
. assertRegex ( output
, 'qdisc tbf 35: root' )
5739 self
. assertRegex ( output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms' )
5741 @expectedFailureIfModuleIsNotAvailable ( 'sch_teql' )
5742 def test_qdisc_teql ( self
):
5743 call_quiet ( 'rmmod sch_teql' )
5745 copy_network_unit ( '25-qdisc-teql.network' , '12-dummy.netdev' )
5747 self
. wait_links ( 'dummy98' )
5748 check_output ( 'modprobe sch_teql max_equalizers=2' )
5749 self
. wait_online ( 'dummy98:routable' )
5751 output
= check_output ( 'tc qdisc show dev dummy98' )
5753 self
. assertRegex ( output
, 'qdisc teql1 31: root' )
5755 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq' , 'sch_sfq' , 'sch_tbf' )
5756 def test_qdisc_drop ( self
):
5757 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' )
5759 self
. wait_online ( 'dummy98:routable' )
5761 # Test case for issue #32247 and #32254.
5763 check_output ( 'tc qdisc replace dev dummy98 root fq' )
5764 self
. assertFalse ( networkd_is_failed ())
5765 check_output ( 'tc qdisc replace dev dummy98 root fq pacing' )
5766 self
. assertFalse ( networkd_is_failed ())
5767 check_output ( 'tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540' )
5768 self
. assertFalse ( networkd_is_failed ())
5769 check_output ( 'tc qdisc add dev dummy98 parent 10:1 handle 100: sfq' )
5770 self
. assertFalse ( networkd_is_failed ())
5772 class NetworkdStateFileTests ( unittest
. TestCase
, Utilities
):
5780 def test_state_file ( self
):
5781 copy_network_unit ( '12-dummy.netdev' , '25-state-file-tests.network' )
5783 self
. wait_online ( 'dummy98:routable' )
5785 # make link state file updated
5786 resolvectl ( 'revert' , 'dummy98' )
5788 check_json ( networkctl_json ())
5790 output
= read_link_state_file ( 'dummy98' )
5792 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
5793 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
5794 self
. assertIn ( 'ADMIN_STATE=configured' , output
)
5795 self
. assertIn ( 'OPER_STATE=routable' , output
)
5796 self
. assertIn ( 'REQUIRED_FOR_ONLINE=yes' , output
)
5797 self
. assertIn ( 'REQUIRED_OPER_STATE_FOR_ONLINE=routable' , output
)
5798 self
. assertIn ( 'REQUIRED_FAMILY_FOR_ONLINE=both' , output
)
5799 self
. assertIn ( 'ACTIVATION_POLICY=up' , output
)
5800 self
. assertIn ( 'NETWORK_FILE=/run/systemd/network/25-state-file-tests.network' , output
)
5801 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
5802 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5803 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
5804 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
5805 self
. assertIn ( 'LLMNR=no' , output
)
5806 self
. assertIn ( 'MDNS=yes' , output
)
5807 self
. assertIn ( 'DNSSEC=no' , output
)
5809 resolvectl ( 'dns' , 'dummy98' , '10.10.10.12#ccc.com' , '10.10.10.13' , '1111:2222::3333' )
5810 resolvectl ( 'domain' , 'dummy98' , 'hogehogehoge' , '~foofoofoo' )
5811 resolvectl ( 'llmnr' , 'dummy98' , 'yes' )
5812 resolvectl ( 'mdns' , 'dummy98' , 'no' )
5813 resolvectl ( 'dnssec' , 'dummy98' , 'yes' )
5814 timedatectl ( 'ntp-servers' , 'dummy98' , '2.fedora.pool.ntp.org' , '3.fedora.pool.ntp.org' )
5816 check_json ( networkctl_json ())
5818 output
= read_link_state_file ( 'dummy98' )
5820 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
5821 self
. assertIn ( 'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org' , output
)
5822 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
5823 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
5824 self
. assertIn ( 'LLMNR=yes' , output
)
5825 self
. assertIn ( 'MDNS=no' , output
)
5826 self
. assertIn ( 'DNSSEC=yes' , output
)
5828 timedatectl ( 'revert' , 'dummy98' )
5830 check_json ( networkctl_json ())
5832 output
= read_link_state_file ( 'dummy98' )
5834 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
5835 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5836 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
5837 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
5838 self
. assertIn ( 'LLMNR=yes' , output
)
5839 self
. assertIn ( 'MDNS=no' , output
)
5840 self
. assertIn ( 'DNSSEC=yes' , output
)
5842 resolvectl ( 'revert' , 'dummy98' )
5844 check_json ( networkctl_json ())
5846 output
= read_link_state_file ( 'dummy98' )
5848 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
5849 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5850 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
5851 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
5852 self
. assertIn ( 'LLMNR=no' , output
)
5853 self
. assertIn ( 'MDNS=yes' , output
)
5854 self
. assertIn ( 'DNSSEC=no' , output
)
5856 def test_address_state ( self
):
5857 copy_network_unit ( '12-dummy.netdev' , '12-dummy-no-address.network' )
5860 self
. wait_online ( 'dummy98:degraded' )
5862 output
= read_link_state_file ( 'dummy98' )
5863 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
5864 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
5866 # with a routable IPv4 address
5867 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
5868 self
. wait_online ( 'dummy98:routable' , ipv4
= True )
5869 self
. wait_online ( 'dummy98:routable' )
5871 output
= read_link_state_file ( 'dummy98' )
5872 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
5873 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
5875 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
5877 # with a routable IPv6 address
5878 check_output ( 'ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98' )
5879 self
. wait_online ( 'dummy98:routable' , ipv6
= True )
5880 self
. wait_online ( 'dummy98:routable' )
5882 output
= read_link_state_file ( 'dummy98' )
5883 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
5884 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
5886 class NetworkdBondTests ( unittest
. TestCase
, Utilities
):
5894 def test_bond_keep_master ( self
):
5895 check_output ( 'ip link add bond199 type bond mode active-backup' )
5896 check_output ( 'ip link add dummy98 type dummy' )
5897 check_output ( 'ip link set dummy98 master bond199' )
5899 copy_network_unit ( '23-keep-master.network' )
5901 self
. wait_online ( 'dummy98:enslaved' )
5903 output
= check_output ( 'ip -d link show bond199' )
5905 self
. assertRegex ( output
, 'active_slave dummy98' )
5907 output
= check_output ( 'ip -d link show dummy98' )
5909 self
. assertRegex ( output
, 'master bond199' )
5911 # Test case for #37629
5913 # When a slave leaved from its master bonding interface, the kernel brings down the slave.
5914 check_output ( 'ip link set dummy98 nomaster' )
5915 self
. wait_online ( 'dummy98:off' )
5917 # Bring up the interface to check if networkd recognizes the interface has no master now.
5918 check_output ( 'ip link set dummy98 up' )
5919 self
. wait_online ( 'dummy98:carrier' )
5921 # We need to first bring down the interface to make it join a bonding interface.
5922 check_output ( 'ip link set dummy98 down' )
5923 check_output ( 'ip link set dummy98 master bond199' )
5924 self
. wait_online ( 'dummy98:enslaved' )
5926 def test_bond_active_slave ( self
):
5927 copy_network_unit ( '23-active-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
5929 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5931 output
= check_output ( 'ip -d link show bond199' )
5933 self
. assertIn ( 'active_slave dummy98' , output
)
5935 # test case for issue #31165.
5936 since
= datetime
. datetime
. now ()
5937 networkctl_reconfigure ( 'dummy98' )
5938 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5939 self
. assertNotIn ( 'dummy98: Bringing link down' , read_networkd_log ( since
= since
))
5941 # test for reloading.
5943 '23-active-slave.network' ,
5944 '23-bond199.network' ,
5945 '25-bond-active-backup-slave.netdev' ,
5948 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5950 def test_bond_primary_slave ( self
):
5951 copy_network_unit ( '23-primary-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
5953 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5955 output
= check_output ( 'ip -d link show bond199' )
5957 self
. assertIn ( 'primary dummy98' , output
)
5960 mkdir_p ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d' ))
5961 for mac
in [ '00:11:22:33:44:55' , '00:11:22:33:44:56' ]:
5962 with
open ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d/mac.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
5963 f
. write ( f
'[Link] \n MACAddress= {mac} \n ' )
5966 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5968 output
= check_output ( 'ip -d link show bond199' )
5970 self
. assertIn ( f
'link/ether {mac} ' , output
)
5972 def test_bond_operstate ( self
):
5973 copy_network_unit ( '25-bond.netdev' , '11-dummy.netdev' , '12-dummy.netdev' ,
5974 '25-bond99.network' , '25-bond-slave.network' )
5976 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bond99:routable' )
5978 output
= check_output ( 'ip -d link show dummy98' )
5980 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
5982 output
= check_output ( 'ip -d link show test1' )
5984 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
5986 output
= check_output ( 'ip -d link show bond99' )
5988 self
. assertRegex ( output
, 'MASTER,UP,LOWER_UP' )
5990 # test case for issue #32186
5992 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bond99:routable' )
5994 self
. wait_operstate ( 'dummy98' , 'enslaved' )
5995 self
. wait_operstate ( 'test1' , 'enslaved' )
5996 self
. wait_operstate ( 'bond99' , 'routable' )
5998 check_output ( 'ip link set dummy98 down' )
6000 self
. wait_operstate ( 'dummy98' , 'off' )
6001 self
. wait_operstate ( 'test1' , 'enslaved' )
6002 self
. wait_operstate ( 'bond99' , 'routable' )
6004 check_output ( 'ip link set dummy98 up' )
6006 self
. wait_operstate ( 'dummy98' , 'enslaved' )
6007 self
. wait_operstate ( 'test1' , 'enslaved' )
6008 self
. wait_operstate ( 'bond99' , 'routable' )
6010 check_output ( 'ip link set dummy98 down' )
6011 check_output ( 'ip link set test1 down' )
6013 self
. wait_operstate ( 'dummy98' , 'off' )
6014 self
. wait_operstate ( 'test1' , 'off' )
6016 if not self
. wait_operstate ( 'bond99' , 'no-carrier' , setup_timeout
= 30 , fail_assert
= False ):
6017 # Huh? Kernel does not recognize that all slave interfaces are down?
6018 # Let's confirm that networkd's operstate is consistent with ip's result.
6019 output
= check_output ( 'ip -d link show bond99' )
6021 self
. assertNotRegex ( output
, 'NO-CARRIER' )
6023 class NetworkdBridgeTests ( unittest
. TestCase
, Utilities
):
6031 def test_bridge_mac_none ( self
):
6032 copy_network_unit ( '12-dummy-mac.netdev' , '26-bridge-mac-slave.network' ,
6033 '26-bridge-mac.netdev' , '26-bridge-mac-master.network' , '26-bridge-mac.link' )
6035 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:degraded' )
6037 output
= check_output ( 'ip link show dev dummy98' )
6039 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
6041 output
= check_output ( 'ip link show dev bridge99' )
6043 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
6045 def test_bridge_vlan ( self
):
6046 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave.network' ,
6047 '26-bridge.netdev' , '26-bridge-vlan-master.network' ,
6050 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6052 output
= check_output ( 'bridge vlan show dev test1' )
6054 # check if the default VID is removed
6055 self
. assertNotIn ( '1 Egress Untagged' , output
)
6056 for i
in range ( 1000 , 3000 ):
6058 self
. assertIn ( f
' {i} PVID' , output
)
6059 elif i
in range ( 1012 , 1016 ) or i
in range ( 1103 , 1109 ):
6060 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6061 elif i
in range ( 1008 , 1014 ) or i
in range ( 1100 , 1111 ):
6062 self
. assertIn ( f
' {i} ' , output
)
6064 self
. assertNotIn ( f
' {i} ' , output
)
6066 output
= check_output ( 'bridge vlan show dev bridge99' )
6068 # check if the default VID is removed
6069 self
. assertNotIn ( '1 Egress Untagged' , output
)
6070 for i
in range ( 1000 , 3000 ):
6072 self
. assertIn ( f
' {i} PVID' , output
)
6073 elif i
in range ( 1022 , 1026 ) or i
in range ( 1203 , 1209 ):
6074 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6075 elif i
in range ( 1018 , 1024 ) or i
in range ( 1200 , 1211 ):
6076 self
. assertIn ( f
' {i} ' , output
)
6078 self
. assertNotIn ( f
' {i} ' , output
)
6081 copy_network_unit ( '26-bridge-vlan-slave.network.d/10-override.conf' ,
6082 '26-bridge-vlan-master.network.d/10-override.conf' )
6084 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6086 output
= check_output ( 'bridge vlan show dev test1' )
6088 for i
in range ( 1000 , 3000 ):
6090 self
. assertIn ( f
' {i} PVID' , output
)
6091 elif i
in range ( 2012 , 2016 ) or i
in range ( 2103 , 2109 ):
6092 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6093 elif i
in range ( 2008 , 2014 ) or i
in range ( 2100 , 2111 ):
6094 self
. assertIn ( f
' {i} ' , output
)
6096 self
. assertNotIn ( f
' {i} ' , output
)
6098 output
= check_output ( 'bridge vlan show dev bridge99' )
6100 for i
in range ( 1000 , 3000 ):
6102 self
. assertIn ( f
' {i} PVID' , output
)
6103 elif i
in range ( 2022 , 2026 ) or i
in range ( 2203 , 2209 ):
6104 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6105 elif i
in range ( 2018 , 2024 ) or i
in range ( 2200 , 2211 ):
6106 self
. assertIn ( f
' {i} ' , output
)
6108 self
. assertNotIn ( f
' {i} ' , output
)
6110 # Remove several vlan IDs
6111 copy_network_unit ( '26-bridge-vlan-slave.network.d/20-override.conf' ,
6112 '26-bridge-vlan-master.network.d/20-override.conf' )
6114 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6116 output
= check_output ( 'bridge vlan show dev test1' )
6118 for i
in range ( 1000 , 3000 ):
6120 self
. assertIn ( f
' {i} PVID' , output
)
6121 elif i
in range ( 2012 , 2016 ):
6122 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6123 elif i
in range ( 2008 , 2014 ):
6124 self
. assertIn ( f
' {i} ' , output
)
6126 self
. assertNotIn ( f
' {i} ' , output
)
6128 output
= check_output ( 'bridge vlan show dev bridge99' )
6130 for i
in range ( 1000 , 3000 ):
6132 self
. assertIn ( f
' {i} PVID' , output
)
6133 elif i
in range ( 2022 , 2026 ):
6134 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6135 elif i
in range ( 2018 , 2024 ):
6136 self
. assertIn ( f
' {i} ' , output
)
6138 self
. assertNotIn ( f
' {i} ' , output
)
6140 # Remove all vlan IDs
6141 copy_network_unit ( '26-bridge-vlan-slave.network.d/30-override.conf' ,
6142 '26-bridge-vlan-master.network.d/30-override.conf' )
6144 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6146 output
= check_output ( 'bridge vlan show dev test1' )
6148 self
. assertNotIn ( 'PVID' , output
)
6149 for i
in range ( 1000 , 3000 ):
6150 self
. assertNotIn ( f
' {i} ' , output
)
6152 output
= check_output ( 'bridge vlan show dev bridge99' )
6154 self
. assertNotIn ( 'PVID' , output
)
6155 for i
in range ( 1000 , 3000 ):
6156 self
. assertNotIn ( f
' {i} ' , output
)
6158 def test_bridge_vlan_issue_20373 ( self
):
6159 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave-issue-20373.network' ,
6160 '26-bridge-issue-20373.netdev' , '26-bridge-vlan-master-issue-20373.network' ,
6161 '21-vlan.netdev' , '21-vlan.network' )
6163 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' , 'vlan99:routable' )
6165 output
= check_output ( 'bridge vlan show dev test1' )
6167 self
. assertIn ( '100 PVID Egress Untagged' , output
)
6168 self
. assertIn ( '560' , output
)
6169 self
. assertIn ( '600' , output
)
6171 output
= check_output ( 'bridge vlan show dev bridge99' )
6173 self
. assertIn ( '1 PVID Egress Untagged' , output
)
6174 self
. assertIn ( '100' , output
)
6175 self
. assertIn ( '600' , output
)
6177 def test_bridge_mdb ( self
):
6178 copy_network_unit ( '11-dummy.netdev' , '26-bridge-mdb-slave.network' ,
6179 '26-bridge.netdev' , '26-bridge-mdb-master.network' )
6181 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6183 output
= check_output ( 'bridge mdb show dev bridge99' )
6185 self
. assertRegex ( output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064' )
6186 self
. assertRegex ( output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065' )
6188 # Old kernel may not support bridge MDB entries on bridge master
6189 if call_quiet ( 'bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068' ) == 0 :
6190 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066' )
6191 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067' )
6193 # Old kernel may not support L2 bridge MDB entries
6194 if call_quiet ( 'bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070' ) == 0 :
6195 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069' )
6197 def test_bridge_keep_master ( self
):
6198 check_output ( 'ip link add bridge99 type bridge' )
6199 check_output ( 'ip link set bridge99 up' )
6200 check_output ( 'ip link add dummy98 type dummy' )
6201 check_output ( 'ip link set dummy98 master bridge99' )
6203 copy_network_unit ( '23-keep-master.network' )
6205 self
. wait_online ( 'dummy98:enslaved' )
6207 output
= check_output ( 'ip -d link show dummy98' )
6209 self
. assertRegex ( output
, 'master bridge99' )
6210 self
. assertRegex ( output
, 'bridge' )
6212 output
= check_output ( 'bridge -d link show dummy98' )
6214 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
6215 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
6216 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
6217 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
6218 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
6219 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6220 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
6221 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
6222 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
6223 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
6224 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
6225 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
6227 def check_bridge_property ( self
):
6228 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
6230 output
= check_output ( 'ip -d link show bridge99' )
6232 self
. assertIn ( 'mtu 9000 ' , output
)
6234 output
= check_output ( 'ip -d link show test1' )
6236 self
. assertIn ( 'master bridge99 ' , output
)
6237 self
. assertIn ( 'bridge_slave' , output
)
6238 self
. assertIn ( 'mtu 9000 ' , output
)
6240 output
= check_output ( 'ip -d link show dummy98' )
6242 self
. assertIn ( 'master bridge99 ' , output
)
6243 self
. assertIn ( 'bridge_slave' , output
)
6244 self
. assertIn ( 'mtu 9000 ' , output
)
6246 output
= check_output ( 'ip addr show bridge99' )
6248 self
. assertIn ( '192.168.0.15/24' , output
)
6250 output
= check_output ( 'bridge -d link show dummy98' )
6252 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
6253 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
6254 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'isolated' , '1' )
6255 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
6256 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
6257 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
6258 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6259 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
6260 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
6261 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
6262 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
6263 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
6264 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
6266 output
= check_output ( 'bridge -d link show test1' )
6268 self
. check_bridge_port_attr ( 'bridge99' , 'test1' , 'priority' , '0' )
6269 self
. assertIn ( 'locked on' , output
)
6270 if ' mab ' in output
: # This is new in kernel and iproute2 v6.2
6271 self
. assertIn ( 'mab on' , output
)
6273 def test_bridge_property ( self
):
6274 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
6275 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
6276 '25-bridge99.network' , '14-dummy.netdev' , '26-bridge-vlan-tunnel.network' )
6278 self
. check_bridge_property ()
6285 '26-bridge-slave-interface-1.network' ,
6286 '26-bridge-slave-interface-2.network' ,
6287 '26-bridge-vlan-tunnel.network' ,
6288 '25-bridge99.network' )
6290 self
. check_bridge_property ()
6292 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
6293 output
= check_output ( 'ip addr show bridge99' )
6295 self
. assertIn ( '192.168.0.16/24' , output
)
6298 print ( '### ip -6 route list table all dev bridge99' )
6299 output
= check_output ( 'ip -6 route list table all dev bridge99' )
6301 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
6303 remove_link ( 'test1' )
6304 self
. wait_operstate ( 'bridge99' , 'routable' )
6306 output
= check_output ( 'ip -d link show bridge99' )
6308 self
. assertIn ( 'mtu 9000 ' , output
)
6310 output
= check_output ( 'ip -d link show dummy98' )
6312 self
. assertIn ( 'master bridge99 ' , output
)
6313 self
. assertIn ( 'bridge_slave' , output
)
6314 self
. assertIn ( 'mtu 9000 ' , output
)
6316 output
= check_output ( 'ip -d link show dummy97' )
6317 self
. assertIn ( 'vlan_tunnel on ' , output
)
6319 remove_link ( 'dummy98' )
6320 remove_link ( 'dummy97' )
6321 self
. wait_operstate ( 'bridge99' , 'no-carrier' )
6323 output
= check_output ( 'ip -d link show bridge99' )
6325 # When no carrier, the kernel may reset the MTU
6326 self
. assertIn ( 'NO-CARRIER' , output
)
6328 output
= check_output ( 'ip address show bridge99' )
6330 self
. assertNotIn ( '192.168.0.15/24' , output
)
6331 self
. assertIn ( '192.168.0.16/24' , output
) # foreign address is kept
6333 print ( '### ip -6 route list table all dev bridge99' )
6334 output
= check_output ( 'ip -6 route list table all dev bridge99' )
6336 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
6338 check_output ( 'ip link add dummy98 type dummy' )
6339 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:routable' )
6341 output
= check_output ( 'ip -d link show bridge99' )
6343 self
. assertIn ( 'mtu 9000 ' , output
)
6345 output
= check_output ( 'ip -d link show dummy98' )
6347 self
. assertIn ( 'master bridge99 ' , output
)
6348 self
. assertIn ( 'bridge_slave' , output
)
6349 self
. assertIn ( 'mtu 9000 ' , output
)
6351 def test_bridge_configure_without_carrier ( self
):
6352 copy_network_unit ( '26-bridge.netdev' , '26-bridge-configure-without-carrier.network' ,
6356 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
6357 for test
in [ 'no-slave' , 'add-slave' , 'slave-up' , 'slave-no-carrier' , 'slave-carrier' , 'slave-down' ]:
6358 with self
. subTest ( test
= test
):
6359 if test
== 'no-slave' :
6360 # bridge has no slaves; it's up but *might* not have carrier
6361 self
. wait_operstate ( 'bridge99' , operstate
= r
'(no-carrier|routable)' , setup_state
= None , setup_timeout
= 30 )
6362 # due to a bug in the kernel, newly-created bridges are brought up
6363 # *with* carrier, unless they have had any setting changed; e.g.
6364 # their mac set, priority set, etc. Then, they will lose carrier
6365 # as soon as a (down) slave interface is added, and regain carrier
6366 # again once the slave interface is brought up.
6367 #self.check_link_attr('bridge99', 'carrier', '0')
6368 elif test
== 'add-slave' :
6369 # add slave to bridge, but leave it down; bridge is definitely no-carrier
6370 self
. check_link_attr ( 'test1' , 'operstate' , 'down' )
6371 check_output ( 'ip link set dev test1 master bridge99' )
6372 self
. wait_operstate ( 'bridge99' , operstate
= 'no-carrier' , setup_state
= None )
6373 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6374 elif test
== 'slave-up' :
6375 # bring up slave, which will have carrier; bridge gains carrier
6376 check_output ( 'ip link set dev test1 up' )
6377 self
. wait_online ( 'bridge99:routable' )
6378 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
6379 elif test
== 'slave-no-carrier' :
6380 # drop slave carrier; bridge loses carrier
6381 check_output ( 'ip link set dev test1 carrier off' )
6382 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
6383 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6384 elif test
== 'slave-carrier' :
6385 # restore slave carrier; bridge gains carrier
6386 check_output ( 'ip link set dev test1 carrier on' )
6387 self
. wait_online ( 'bridge99:routable' )
6388 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
6389 elif test
== 'slave-down' :
6390 # bring down slave; bridge loses carrier
6391 check_output ( 'ip link set dev test1 down' )
6392 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
6393 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6395 output
= networkctl_status ( 'bridge99' )
6396 self
. assertRegex ( output
, '10.1.2.3' )
6397 self
. assertRegex ( output
, '10.1.2.1' )
6399 def test_bridge_ignore_carrier_loss ( self
):
6400 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
6401 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
6402 '25-bridge99-ignore-carrier-loss.network' )
6404 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
6406 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
6407 remove_link ( 'test1' , 'dummy98' )
6410 output
= check_output ( 'ip address show bridge99' )
6412 self
. assertRegex ( output
, 'NO-CARRIER' )
6413 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
6414 self
. assertRegex ( output
, 'inet 192.168.0.16/24 scope global secondary bridge99' )
6416 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain ( self
):
6417 copy_network_unit ( '26-bridge.netdev' , '26-bridge-slave-interface-1.network' ,
6418 '25-bridge99-ignore-carrier-loss.network' )
6420 self
. wait_online ( 'bridge99:no-carrier' )
6422 for trial
in range ( 4 ):
6423 check_output ( 'ip link add dummy98 type dummy' )
6424 check_output ( 'ip link set dummy98 up' )
6426 remove_link ( 'dummy98' )
6428 self
. wait_online ( 'bridge99:routable' , 'dummy98:enslaved' )
6430 output
= check_output ( 'ip address show bridge99' )
6432 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
6434 output
= check_output ( 'ip rule list table 100' )
6436 self
. assertIn ( 'from all to 8.8.8.8 lookup 100' , output
)
6438 class NetworkdSRIOVTests ( unittest
. TestCase
, Utilities
):
6446 def setup_netdevsim ( self
, id = 99 , num_ports
= 1 , num_vfs
= 0 ):
6447 call ( 'modprobe netdevsim' )
6449 # Create netdevsim device.
6450 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
6451 f
. write ( f
' {id} {num_ports} ' )
6455 with
open ( f
'/sys/bus/netdevsim/devices/netdevsim {id} /sriov_numvfs' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
6456 f
. write ( f
' {num_vfs} ' )
6458 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
6459 def test_sriov ( self
):
6460 copy_network_unit ( '25-netdevsim.link' , '25-sriov.network' )
6462 self
. setup_netdevsim ( num_vfs
= 3 )
6465 self
. wait_online ( 'sim99:routable' )
6467 output
= check_output ( 'ip link show dev sim99' )
6469 self
. assertRegex ( output
,
6470 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6471 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6472 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6475 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
6476 def test_sriov_udev ( self
):
6477 copy_network_unit ( '25-sriov.link' , '25-sriov-udev.network' )
6479 self
. setup_netdevsim ()
6482 self
. wait_online ( 'sim99:routable' )
6484 # The name sim99 is an alternative name, and cannot be used by udevadm below.
6485 ifname
= link_resolve ( 'sim99' )
6487 output
= check_output ( 'ip link show dev sim99' )
6489 self
. assertRegex ( output
,
6490 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6491 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6492 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6494 self
. assertNotIn ( 'vf 3' , output
)
6495 self
. assertNotIn ( 'vf 4' , output
)
6497 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6498 f
. write ( '[Link] \n SR-IOVVirtualFunctions=4 \n ' )
6501 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6503 output
= check_output ( 'ip link show dev sim99' )
6505 self
. assertRegex ( output
,
6506 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6507 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6508 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
6511 self
. assertNotIn ( 'vf 4' , output
)
6513 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6514 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
6517 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6519 output
= check_output ( 'ip link show dev sim99' )
6521 self
. assertRegex ( output
,
6522 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6523 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6524 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
6527 self
. assertNotIn ( 'vf 4' , output
)
6529 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6530 f
. write ( '[Link] \n SR-IOVVirtualFunctions=2 \n ' )
6533 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6535 output
= check_output ( 'ip link show dev sim99' )
6537 self
. assertRegex ( output
,
6538 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6539 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
6541 self
. assertNotIn ( 'vf 2' , output
)
6542 self
. assertNotIn ( 'vf 3' , output
)
6543 self
. assertNotIn ( 'vf 4' , output
)
6545 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6546 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
6549 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6551 output
= check_output ( 'ip link show dev sim99' )
6553 self
. assertRegex ( output
,
6554 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on \n *'
6555 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6556 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6558 self
. assertNotIn ( 'vf 3' , output
)
6559 self
. assertNotIn ( 'vf 4' , output
)
6561 class NetworkdLLDPTests ( unittest
. TestCase
, Utilities
):
6569 def test_lldp ( self
):
6570 copy_network_unit ( '23-emit-lldp.network' , '24-lldp.network' , '25-veth.netdev' )
6572 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' )
6575 output
= networkctl ( 'lldp' )
6577 if re
. search ( r
'veth99 .* veth-peer .* .......a...' , output
):
6583 # With interface name
6584 output
= networkctl ( 'lldp' , 'veth99' );
6586 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
6588 # With interface name pattern
6589 output
= networkctl ( 'lldp' , 've*9' );
6591 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
6594 output
= networkctl ( '--json=short' , 'lldp' )
6596 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6597 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6598 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6600 # json format with interface name
6601 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
6603 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6604 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6605 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6607 # json format with interface name pattern
6608 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
6610 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6611 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6612 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6614 # LLDP neighbors in status
6615 output
= networkctl_status ( 'veth99' )
6617 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
6619 # enable forwarding, to enable the Router flag
6620 with
open ( os
. path
. join ( network_unit_dir
, '23-emit-lldp.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6621 f
. write ( '[Network] \n IPv4Forwarding=yes \n ' )
6624 self
. wait_online ( 'veth-peer:degraded' )
6627 output
= networkctl ( 'lldp' )
6629 if re
. search ( r
'veth99 .* veth-peer .* ....r......' , output
):
6635 # With interface name
6636 output
= networkctl ( 'lldp' , 'veth99' );
6638 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
6640 # With interface name pattern
6641 output
= networkctl ( 'lldp' , 've*9' );
6643 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
6646 output
= networkctl ( '--json=short' , 'lldp' )
6648 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6649 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6650 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6652 # json format with interface name
6653 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
6655 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6656 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6657 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6659 # json format with interface name pattern
6660 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
6662 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6663 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6664 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6666 # LLDP neighbors in status
6667 output
= networkctl_status ( 'veth99' )
6669 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
6671 # Compare the json output from sender and receiver
6672 sender_json
= get_link_description ( 'veth-peer' )[ 'LLDP' ]
6673 receiver_json
= json
. loads ( networkctl ( '--json=short' , 'lldp' , 'veth99' ))[ 'Neighbors' ][ 0 ][ 'Neighbors' ][ 0 ]
6675 print ( receiver_json
)
6676 self
. assertEqual ( sender_json
, receiver_json
)
6678 class NetworkdRATests ( unittest
. TestCase
, Utilities
):
6686 def test_ipv6_prefix_delegation ( self
):
6687 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth.network' )
6688 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
6689 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
6690 self
. setup_nftset ( 'ifindex' , 'iface_index' )
6692 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6694 # IPv6SendRA=yes implies IPv6Forwarding.
6695 self
. check_ipv6_sysctl_attr ( 'veth-peer' , 'forwarding' , '1' )
6697 output
= resolvectl ( 'dns' , 'veth99' )
6699 self
. assertRegex ( output
, 'fe80::' )
6700 self
. assertRegex ( output
, '2002:da8:1::1' )
6702 output
= resolvectl ( 'domain' , 'veth99' )
6704 self
. assertIn ( 'hogehoge.test' , output
)
6706 output
= networkctl_status ( 'veth99' )
6708 self
. assertRegex ( output
, '2002:da8:1:0' )
6710 self
. check_ipv6_neigh_sysctl_attr ( 'veth99' , 'base_reachable_time_ms' , '42000' )
6711 self
. check_ipv6_neigh_sysctl_attr ( 'veth99' , 'retrans_time_ms' , '500' )
6713 self
. check_netlabel ( 'veth99' , '2002:da8:1::/64' )
6714 self
. check_netlabel ( 'veth99' , '2002:da8:2::/64' )
6716 self
. check_nftset ( 'addr6' , '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
6717 self
. check_nftset ( 'addr6' , '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
6718 self
. check_nftset ( 'network6' , '2002:da8:1::/64' )
6719 self
. check_nftset ( 'network6' , '2002:da8:2::/64' )
6720 self
. check_nftset ( 'ifindex' , 'veth99' )
6722 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
6724 def check_ipv6_token_static ( self
):
6725 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6727 output
= networkctl_status ( 'veth99' )
6729 self
. assertRegex ( output
, '2002:da8:1:0:1a:2b:3c:4d' )
6730 self
. assertRegex ( output
, '2002:da8:1:0:fa:de:ca:fe' )
6731 self
. assertRegex ( output
, '2002:da8:2:0:1a:2b:3c:4d' )
6732 self
. assertRegex ( output
, '2002:da8:2:0:fa:de:ca:fe' )
6734 def test_ipv6_token_static ( self
):
6735 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
6738 self
. check_ipv6_token_static ()
6741 check_output ( 'ip link set veth99 down' )
6742 check_output ( 'ip link set veth99 up' )
6744 self
. check_ipv6_token_static ()
6747 check_output ( 'ip link set veth99 down' )
6748 time
. sleep ( random
. uniform ( 0 , 0.1 ))
6749 check_output ( 'ip link set veth99 up' )
6750 time
. sleep ( random
. uniform ( 0 , 0.1 ))
6752 self
. check_ipv6_token_static ()
6754 def test_ndisc_redirect ( self
):
6755 if not os
. path
. exists ( test_ndisc_send
):
6756 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
6758 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
6761 self
. check_ipv6_token_static ()
6763 # Introduce three redirect routes.
6764 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d' )
6765 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d' )
6766 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d' )
6767 self
. wait_route ( 'veth99' , '2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6768 self
. wait_route ( 'veth99' , '2002:da8:1:2:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6769 self
. wait_route ( 'veth99' , '2002:da8:1:3:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6771 # Change the target address of the redirects.
6772 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d' )
6773 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d' )
6774 self
. wait_route_dropped ( 'veth99' , '2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6775 self
. wait_route_dropped ( 'veth99' , '2002:da8:1:2:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6776 self
. wait_route ( 'veth99' , r
'2002:da8:1:1:1a:2b:3c:4d nhid [0-9]* via fe80::1 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6777 self
. wait_route ( 'veth99' , r
'2002:da8:1:2:1a:2b:3c:4d nhid [0-9]* via fe80::2 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6779 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
6780 # Then, verify that all redirect routes and the default route are dropped.
6781 output
= check_output ( 'ip -6 address show dev veth-peer scope link' )
6782 veth_peer_ipv6ll
= re
. search ( 'fe80:[:0-9a-f]*' , output
). group ()
6783 print ( f
'veth-peer IPv6LL address: {veth_peer_ipv6ll} ' )
6784 check_output ( f
' {test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no' )
6785 self
. wait_route_dropped ( 'veth99' , 'proto ra' , ipv
= '-6' , timeout_sec
= 10 )
6786 self
. wait_route_dropped ( 'veth99' , 'proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6788 # Check if sd-radv refuses RS from the same interface.
6789 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
6790 since
= datetime
. datetime
. now ()
6791 check_output ( f
' {test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll} ' )
6792 self
. check_networkd_log ( 'veth-peer: RADV: Received RS from the same interface, ignoring.' , since
= since
)
6794 def check_ndisc_mtu ( self
, mtu
):
6796 output
= read_ipv6_sysctl_attr ( 'veth99' , 'mtu' )
6797 if output
== f
' {mtu} ' :
6801 self
. fail ( f
'IPv6 MTU does not matches: value= {output} , expected= {mtu} ' )
6803 def test_ndisc_mtu ( self
):
6804 if not os
. path
. exists ( test_ndisc_send
):
6805 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
6807 copy_network_unit ( '25-veth.netdev' ,
6808 '25-veth-peer-no-address.network' ,
6809 '25-ipv6-prefix-veth-token-static.network' )
6811 self
. wait_online ( 'veth-peer:degraded' )
6813 self
. check_networkd_log ( 'veth99: NDISC: Started IPv6 Router Solicitation client' )
6815 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400' )
6816 self
. check_ndisc_mtu ( 1400 )
6818 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410' )
6819 self
. check_ndisc_mtu ( 1410 )
6821 check_output ( 'ip link set dev veth99 mtu 1600' )
6822 check_output ( 'ip link set dev veth-peer mtu 1600' )
6823 self
. check_ndisc_mtu ( 1410 )
6825 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700' )
6826 self
. check_ndisc_mtu ( 1600 )
6828 check_output ( 'ip link set dev veth99 mtu 1800' )
6829 check_output ( 'ip link set dev veth-peer mtu 1800' )
6830 self
. check_ndisc_mtu ( 1700 )
6832 def test_ipv6_token_prefixstable ( self
):
6833 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable.network' )
6835 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6837 output
= check_output ( 'ip -6 address show dev veth99' )
6839 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
6840 self
. assertIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
6842 with
open ( os
. path
. join ( network_unit_dir
, '25-ipv6-prefix-veth-token-prefixstable.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6843 f
. write ( ' \n [IPv6AcceptRA] \n PrefixAllowList=2002:da8:1:0::/64 \n ' )
6846 self
. wait_online ( 'veth99:routable' )
6848 output
= check_output ( 'ip -6 address show dev veth99' )
6850 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
6851 self
. assertNotIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
6853 check_output ( 'ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99' )
6854 check_output ( 'ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad' )
6856 networkctl_reconfigure ( 'veth99' )
6857 self
. wait_online ( 'veth99:routable' )
6858 self
. wait_address ( 'veth99' , '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 2nd prefixstable
6859 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 1st prefixstable
6861 check_output ( 'ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99' )
6862 check_output ( 'ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad' )
6864 networkctl_reconfigure ( 'veth99' )
6865 self
. wait_online ( 'veth99:routable' )
6866 self
. wait_address ( 'veth99' , '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 3rd prefixstable
6867 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 2nd prefixstable
6868 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 1st prefixstable
6870 def test_ipv6_token_prefixstable_without_address ( self
):
6871 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable-without-address.network' )
6873 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6875 output
= networkctl_status ( 'veth99' )
6877 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e' , output
)
6878 self
. assertIn ( '2002:da8:2:0:f689:561a:8eda:7443' , output
)
6880 def test_router_hop_limit ( self
):
6881 copy_network_unit ( '25-veth-client.netdev' ,
6882 '25-veth-router.netdev' ,
6884 '25-veth-bridge.network' ,
6885 '25-veth-client.network' ,
6886 '25-veth-router-hop-limit.network' ,
6887 '25-bridge99.network' )
6889 self
. wait_online ( 'client:routable' , 'client-p:enslaved' ,
6890 'router:degraded' , 'router-p:enslaved' ,
6891 'bridge99:routable' )
6893 self
. check_ipv6_sysctl_attr ( 'client' , 'hop_limit' , '42' )
6895 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-hop-limit.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6896 f
. write ( ' \n [IPv6SendRA] \n HopLimit=43 \n ' )
6901 output
= read_ipv6_sysctl_attr ( 'client' , 'hop_limit' )
6906 self
. check_ipv6_sysctl_attr ( 'client' , 'hop_limit' , '43' )
6908 def check_router_preference ( self
, suffix
, metric_1
, preference_1
, metric_2
, preference_2
):
6909 self
. wait_online ( 'client:routable' )
6910 self
. wait_address ( 'client' , f
'2002:da8:1:99:1034:56ff:fe78:9a {suffix} /64' , ipv
= '-6' , timeout_sec
= 10 )
6911 self
. wait_address ( 'client' , f
'2002:da8:1:98:1034:56ff:fe78:9a {suffix} /64' , ipv
= '-6' , timeout_sec
= 10 )
6912 self
. wait_route ( 'client' , rf
'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1} ' , ipv
= '-6' , timeout_sec
= 10 )
6913 self
. wait_route ( 'client' , rf
'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2} ' , ipv
= '-6' , timeout_sec
= 10 )
6915 print ( '### ip -6 route show dev client default' )
6916 output
= check_output ( 'ip -6 route show dev client default' )
6918 self
. assertRegex ( output
, rf
'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1} expires [0-9]*sec pref {preference_1} ' )
6919 self
. assertRegex ( output
, rf
'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2} expires [0-9]*sec pref {preference_2} ' )
6921 for i
in [ 100 , 200 , 300 , 512 , 1024 , 2048 ]:
6922 if i
not in [ metric_1
, metric_2
]:
6923 self
. assertNotIn ( f
'metric {i} ' , output
)
6925 for i
in [ 'low' , 'medium' , 'high' ]:
6926 if i
not in [ preference_1
, preference_2
]:
6927 self
. assertNotIn ( f
'pref {i} ' , output
)
6929 def test_router_preference ( self
):
6930 copy_network_unit ( '25-veth-client.netdev' ,
6931 '25-veth-router-high.netdev' ,
6932 '25-veth-router-low.netdev' ,
6934 '25-veth-bridge.network' ,
6935 '25-veth-client.network' ,
6936 '25-veth-router-high.network' ,
6937 '25-veth-router-low.network' ,
6938 '25-bridge99.network' )
6940 self
. wait_online ( 'client-p:enslaved' ,
6941 'router-high:degraded' , 'router-high-p:enslaved' ,
6942 'router-low:degraded' , 'router-low-p:enslaved' ,
6943 'bridge99:routable' )
6945 networkctl_reconfigure ( 'client' )
6946 self
. wait_online ( 'client:routable' )
6947 self
. check_router_preference ( '00' , 512 , 'high' , 2048 , 'low' )
6949 # change the map from preference to metric.
6950 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6951 f
. write ( ' \n [Link] \n MACAddress=12:34:56:78:9a:01 \n [IPv6AcceptRA] \n RouteMetric=100:200:300 \n ' )
6953 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
6955 # swap the preference (for issue #28439)
6956 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6957 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=low \n ' )
6958 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6959 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=high \n ' )
6961 self
. check_router_preference ( '01' , 300 , 'low' , 100 , 'high' )
6963 # Use the same preference, and check if the two routes are not coalesced. See issue #33470.
6964 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6965 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=medium \n ' )
6966 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6967 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=medium \n ' )
6969 self
. check_router_preference ( '01' , 200 , 'medium' , 200 , 'medium' )
6971 # Use route options to configure default routes.
6972 # The preference specified in the RA header should be ignored. See issue #33468.
6973 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6974 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=high \n [IPv6RoutePrefix] \n Route=::/0 \n LifetimeSec=1200 \n ' )
6975 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6976 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=low \n [IPv6RoutePrefix] \n Route=::/0 \n LifetimeSec=1200 \n ' )
6978 self
. check_router_preference ( '01' , 200 , 'medium' , 200 , 'medium' )
6980 # Set zero lifetime to the route options.
6981 # The preference specified in the RA header should be used.
6982 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6983 f
. write ( 'LifetimeSec=0 \n ' )
6984 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6985 f
. write ( 'LifetimeSec=0 \n ' )
6987 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
6989 # Use route options with preference to configure default routes.
6990 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6991 f
. write ( 'LifetimeSec=1200 \n Preference=low \n ' )
6992 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6993 f
. write ( 'LifetimeSec=1200 \n Preference=high \n ' )
6995 self
. check_router_preference ( '01' , 300 , 'low' , 100 , 'high' )
6997 # Set zero lifetime again to the route options.
6998 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6999 f
. write ( 'LifetimeSec=0 \n ' )
7000 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7001 f
. write ( 'LifetimeSec=0 \n ' )
7003 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
7005 def _test_ndisc_vs_static_route ( self
, manage_foreign_nexthops
):
7006 if not manage_foreign_nexthops
:
7007 copy_networkd_conf_dropin ( 'networkd-manage-foreign-nexthops-no.conf' )
7008 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-static-route.network' )
7010 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
7012 # If a conflicting static route is already configured, do not override the static route.
7013 print ( '### ip -6 route show dev veth99 default' )
7014 output
= check_output ( 'ip -6 route show dev veth99 default' )
7016 self
. assertIn ( 'via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium' , output
)
7017 if manage_foreign_nexthops
:
7018 self
. assertRegex ( output
, r
'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium' )
7020 self
. assertNotIn ( 'proto ra' , output
)
7022 print ( '### ip -6 nexthop show dev veth99' )
7023 output
= check_output ( 'ip -6 nexthop show dev veth99' )
7025 if manage_foreign_nexthops
:
7026 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra' )
7028 self
. assertEqual ( output
, '' )
7030 # Also check if the static route is protected from RA with zero lifetime
7031 with
open ( os
. path
. join ( network_unit_dir
, '25-ipv6-prefix.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7032 f
. write ( ' \n [Network] \n IPv6SendRA=no \n ' )
7033 networkctl_reload () # This makes veth-peer being reconfigured, and send RA with zero lifetime
7034 self
. wait_route_dropped ( 'veth99' , r
'default (nhid [0-9]* |)via fe80::1034:56ff:fe78:9abd proto ra metric 256' , ipv
= '-6' , timeout_sec
= 10 )
7036 print ( '### ip -6 route show dev veth99 default' )
7037 output
= check_output ( 'ip -6 route show dev veth99 default' )
7039 self
. assertIn ( 'via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium' , output
)
7040 self
. assertNotIn ( 'proto ra' , output
)
7042 # Check if nexthop is removed.
7043 print ( '### ip -6 nexthop show dev veth99' )
7044 output
= check_output ( 'ip -6 nexthop show dev veth99' )
7046 self
. assertEqual ( output
, '' )
7048 def test_ndisc_vs_static_route ( self
):
7050 for manage_foreign_nexthops
in [ True , False ]:
7056 print ( f
'### test_ndisc_vs_static_route(manage_foreign_nexthops= {manage_foreign_nexthops} )' )
7057 with self
. subTest ( manage_foreign_nexthops
= manage_foreign_nexthops
):
7058 self
._ test
_ ndisc
_ vs
_ static
_ route
( manage_foreign_nexthops
)
7060 # radvd supports captive portal since v2.20.
7061 # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc
7062 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
7063 def test_captive_portal ( self
):
7064 copy_network_unit ( '25-veth-client.netdev' ,
7065 '25-veth-router-captive.netdev' ,
7067 '25-veth-client-captive.network' ,
7068 '25-veth-router-captive.network' ,
7069 '25-veth-bridge-captive.network' ,
7070 '25-bridge99.network' )
7072 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
7073 'router-captive:degraded' , 'router-captivep:enslaved' )
7075 start_radvd ( config_file
= 'captive-portal.conf' )
7076 networkctl_reconfigure ( 'client' )
7077 self
. wait_online ( 'client:routable' )
7079 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
7080 output
= networkctl_status ( 'client' )
7082 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
7084 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
7085 def test_invalid_captive_portal ( self
):
7086 def radvd_write_config ( captive_portal_uri
):
7087 with
open ( os
. path
. join ( networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
7088 f
. write ( f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI " {captive_portal_uri} "; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};' )
7090 captive_portal_uris
= [
7091 "42ěščěškd ěšč ě s" ,
7096 copy_network_unit ( '25-veth-client.netdev' ,
7097 '25-veth-router-captive.netdev' ,
7099 '25-veth-client-captive.network' ,
7100 '25-veth-router-captive.network' ,
7101 '25-veth-bridge-captive.network' ,
7102 '25-bridge99.network' )
7104 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
7105 'router-captive:degraded' , 'router-captivep:enslaved' )
7107 for uri
in captive_portal_uris
:
7108 print ( f
"Captive portal: {uri} " )
7109 radvd_write_config ( uri
)
7111 start_radvd ( config_file
= 'bogus-captive-portal.conf' )
7112 networkctl_reconfigure ( 'client' )
7113 self
. wait_online ( 'client:routable' )
7115 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
7116 output
= networkctl_status ( 'client' )
7118 self
. assertNotIn ( 'Captive Portal:' , output
)
7120 class NetworkdDHCPServerTests ( unittest
. TestCase
, Utilities
):
7128 def check_dhcp_server ( self
, persist_leases
= 'yes' ):
7129 output
= networkctl_status ( 'veth99' )
7131 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
7132 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
7133 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
7134 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
7136 output
= networkctl_status ( 'veth-peer' )
7138 self
. assertRegex ( output
, "Offered DHCP leases: 192.168.5.[0-9]*" )
7140 if persist_leases
== 'yes' :
7141 path
= '/var/lib/systemd/network/dhcp-server-lease/veth-peer'
7142 elif persist_leases
== 'runtime' :
7143 path
= '/run/systemd/netif/dhcp-server-lease/veth-peer'
7148 with
open ( path
, encoding
= 'utf-8' ) as f
:
7149 check_json ( f
. read ())
7151 def test_dhcp_server ( self
):
7152 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7154 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7156 self
. check_dhcp_server ()
7158 networkctl_reconfigure ( 'veth-peer' )
7159 self
. wait_online ( 'veth-peer:routable' )
7162 output
= check_output (* networkctl_cmd
, '-n' , '0' , 'status' , 'veth-peer' , env
= env
)
7163 if 'Offered DHCP leases: 192.168.5.' in output
:
7169 def test_dhcp_server_persist_leases_no ( self
):
7170 copy_networkd_conf_dropin ( 'persist-leases-no.conf' )
7171 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7173 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7175 self
. check_dhcp_server ( persist_leases
= 'no' )
7177 remove_networkd_conf_dropin ( 'persist-leases-no.conf' )
7178 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-server.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7179 f
. write ( '[DHCPServer] \n PersistLeases=no' )
7181 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7183 self
. check_dhcp_server ( persist_leases
= 'no' )
7185 def test_dhcp_server_persist_leases_runtime ( self
):
7186 copy_networkd_conf_dropin ( 'persist-leases-runtime.conf' )
7187 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7189 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7191 self
. check_dhcp_server ( persist_leases
= 'runtime' )
7193 remove_networkd_conf_dropin ( 'persist-leases-runtime.conf' )
7194 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-server.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7195 f
. write ( '[DHCPServer] \n PersistLeases=runtime' )
7197 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7199 self
. check_dhcp_server ( persist_leases
= 'runtime' )
7201 def test_dhcp_server_null_server_address ( self
):
7202 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-null-server-address.network' )
7204 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7206 output
= check_output ( 'ip --json address show dev veth-peer' )
7207 server_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
7208 print ( server_address
)
7210 output
= check_output ( 'ip --json address show dev veth99' )
7211 client_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
7212 print ( client_address
)
7214 output
= networkctl_status ( 'veth99' )
7216 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCPv4 via {server_address} \)' )
7217 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
7218 self
. assertIn ( f
'DNS: {server_address} ' , output
)
7219 self
. assertIn ( f
'NTP: {server_address} ' , output
)
7221 output
= networkctl_status ( 'veth-peer' )
7222 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
7224 # Check if the same addresses are used even if the service is restarted.
7226 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7228 output
= check_output ( 'ip -4 address show dev veth-peer' )
7230 self
. assertIn ( f
' {server_address} ' , output
)
7232 output
= check_output ( 'ip -4 address show dev veth99' )
7234 self
. assertIn ( f
' {client_address} ' , output
)
7236 output
= networkctl_status ( 'veth99' )
7238 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCPv4 via {server_address} \)' )
7239 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
7240 self
. assertIn ( f
'DNS: {server_address} ' , output
)
7241 self
. assertIn ( f
'NTP: {server_address} ' , output
)
7243 output
= networkctl_status ( 'veth-peer' )
7244 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
7246 def test_dhcp_server_with_uplink ( self
):
7247 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-downstream.network' ,
7248 '12-dummy.netdev' , '25-dhcp-server-uplink.network' )
7250 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7252 output
= networkctl_status ( 'veth99' )
7254 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
7255 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
7256 self
. assertIn ( 'DNS: 192.168.5.1' , output
)
7257 self
. assertIn ( 'NTP: 192.168.5.1' , output
)
7259 def test_emit_router_timezone ( self
):
7260 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-timezone-router.network' , '25-dhcp-server-timezone-router.network' )
7262 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7264 output
= networkctl_status ( 'veth99' )
7266 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
7267 self
. assertIn ( 'Gateway: 192.168.5.1' , output
)
7268 self
. assertIn ( 'Time Zone: Europe/Berlin' , output
)
7270 def test_dhcp_server_static_lease_mac_by_network ( self
):
7271 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-static-lease.network' , '25-dhcp-server-static-lease.network' )
7272 copy_networkd_conf_dropin ( '10-dhcp-client-id-duid.conf' )
7274 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7276 output
= networkctl_status ( 'veth99' )
7278 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7279 self
. assertIn ( 'DHCPv4 Client ID: 12:34:56:78:9a:bc' , output
)
7281 def test_dhcp_server_static_lease_mac_by_global ( self
):
7282 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-static-lease.network' )
7283 copy_networkd_conf_dropin ( '10-dhcp-client-id-mac.conf' )
7285 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7287 output
= networkctl_status ( 'veth99' )
7289 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7290 self
. assertIn ( 'DHCPv4 Client ID: 12:34:56:78:9a:bc' , output
)
7292 def test_dhcp_server_static_lease_duid ( self
):
7293 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-static-lease.network' )
7295 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7297 output
= networkctl_status ( 'veth99' )
7299 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7300 self
. assertRegex ( output
, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID' )
7302 class NetworkdDHCPServerRelayAgentTests ( unittest
. TestCase
, Utilities
):
7310 def test_relay_agent ( self
):
7311 copy_network_unit ( '25-agent-veth-client.netdev' ,
7312 '25-agent-veth-server.netdev' ,
7313 '25-agent-client.network' ,
7314 '25-agent-server.network' ,
7315 '25-agent-client-peer.network' ,
7316 '25-agent-server-peer.network' )
7319 self
. wait_online ( 'client:routable' )
7321 output
= networkctl_status ( 'client' )
7323 self
. assertRegex ( output
, r
'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)' )
7325 def test_relay_agent_on_bridge ( self
):
7326 copy_network_unit ( '25-agent-bridge.netdev' ,
7327 '25-agent-veth-client.netdev' ,
7328 '25-agent-bridge.network' ,
7329 '25-agent-bridge-port.network' ,
7330 '25-agent-client.network' )
7332 self
. wait_online ( 'bridge-relay:routable' , 'client-peer:enslaved' )
7335 self
. check_networkd_log ( 'bridge-relay: DHCPv4 server: STARTED' )
7337 class NetworkdDHCPClientTests ( unittest
. TestCase
, Utilities
):
7345 def test_dhcp_client_ipv6_only ( self
):
7346 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
7349 self
. wait_online ( 'veth-peer:carrier' )
7351 # information request mode
7352 # The name ipv6-only option may not be supported by older dnsmasq
7353 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
7354 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7355 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7356 '--dhcp-option=option6:ntp-server,[2600::ff]' ,
7357 ra_mode
= 'ra-stateless' )
7358 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7360 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
7361 # Let's wait for the expected DNS server being listed in the state file.
7362 for _
in range ( 100 ):
7363 output
= read_link_state_file ( 'veth99' )
7364 if 'DNS=2600::ee' in output
:
7368 # Check link state file
7369 print ( '## link state file' )
7370 output
= read_link_state_file ( 'veth99' )
7372 self
. assertIn ( 'DNS=2600::ee' , output
)
7373 self
. assertIn ( 'NTP=2600::ff' , output
)
7375 # Check manager state file
7376 print ( '## manager state file' )
7377 output
= read_manager_state_file ()
7379 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7380 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7382 print ( '## dnsmasq log' )
7383 output
= read_dnsmasq_log_file ()
7385 self
. assertIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7386 self
. assertNotIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7387 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7388 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7389 self
. assertNotIn ( 'DHCPREPLY(veth-peer)' , output
)
7392 check_json ( networkctl_json ( 'veth99' ))
7396 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7397 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7398 '--dhcp-option=option6:ntp-server,[2600::ff]' )
7399 networkctl_reconfigure ( 'veth99' )
7400 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7403 output
= check_output ( 'ip address show dev veth99 scope global' )
7405 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7406 self
. assertNotIn ( '192.168.5' , output
)
7408 # checking semi-static route
7409 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
7411 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
7413 # Confirm that ipv6 token is not set in the kernel
7414 output
= check_output ( 'ip token show dev veth99' )
7416 self
. assertRegex ( output
, 'token :: dev veth99' )
7418 # Make manager and link state file updated
7419 resolvectl ( 'revert' , 'veth99' )
7421 # Check link state file
7422 print ( '## link state file' )
7423 output
= read_link_state_file ( 'veth99' )
7425 self
. assertIn ( 'DNS=2600::ee' , output
)
7426 self
. assertIn ( 'NTP=2600::ff' , output
)
7428 # Check manager state file
7429 print ( '## manager state file' )
7430 output
= read_manager_state_file ()
7432 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7433 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7435 print ( '## dnsmasq log' )
7436 output
= read_dnsmasq_log_file ()
7438 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7439 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7440 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7441 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7442 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7443 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
7446 check_json ( networkctl_json ( 'veth99' ))
7448 # Testing without rapid commit support
7449 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-ipv6-only.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7450 f
. write ( ' \n [DHCPv6] \n RapidCommit=no \n ' )
7453 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7454 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7455 '--dhcp-option=option6:ntp-server,[2600::ff]' )
7458 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7461 output
= check_output ( 'ip address show dev veth99 scope global' )
7463 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7464 self
. assertNotIn ( '192.168.5' , output
)
7466 # checking semi-static route
7467 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
7469 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
7471 # Make manager and link state file updated
7472 resolvectl ( 'revert' , 'veth99' )
7474 # Check link state file
7475 print ( '## link state file' )
7476 output
= read_link_state_file ( 'veth99' )
7478 self
. assertIn ( 'DNS=2600::ee' , output
)
7479 self
. assertIn ( 'NTP=2600::ff' , output
)
7481 # Check manager state file
7482 print ( '## manager state file' )
7483 output
= read_manager_state_file ()
7485 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7486 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7488 print ( '## dnsmasq log' )
7489 output
= read_dnsmasq_log_file ()
7491 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7492 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7493 self
. assertIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7494 self
. assertIn ( 'DHCPREQUEST(veth-peer)' , output
)
7495 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7496 self
. assertNotIn ( 'rapid-commit' , output
)
7499 check_json ( networkctl_json ( 'veth99' ))
7501 def test_dhcp_client_ipv6_dbus_status ( self
):
7502 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
7504 self
. wait_online ( 'veth-peer:carrier' )
7506 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
7507 # bit set) has yet been received and the configuration does not include WithoutRA=true
7508 state
= get_dhcp6_client_state ( 'veth99' )
7509 print ( f
"DHCPv6 client state = {state} " )
7510 self
. assertEqual ( state
, 'stopped' )
7512 state
= get_dhcp4_client_state ( 'veth99' )
7513 print ( f
"DHCPv4 client state = {state} " )
7514 self
. assertEqual ( state
, 'selecting' )
7516 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
7517 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7519 state
= get_dhcp6_client_state ( 'veth99' )
7520 print ( f
"DHCPv6 client state = {state} " )
7521 self
. assertEqual ( state
, 'bound' )
7523 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
7524 for _
in range ( 100 ):
7525 state
= get_dhcp4_client_state ( 'veth99' )
7526 if state
== 'stopped' :
7530 print ( f
"DHCPv4 client state = {state} " )
7531 self
. assertEqual ( state
, 'stopped' )
7533 # restart dnsmasq to clear log
7535 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
7537 # Test renew command
7538 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
7539 networkctl ( 'renew' , 'veth99' )
7541 for _
in range ( 100 ):
7542 state
= get_dhcp4_client_state ( 'veth99' )
7543 if state
== 'stopped' :
7547 print ( f
"DHCPv4 client state = {state} " )
7548 self
. assertEqual ( state
, 'stopped' )
7550 print ( '## dnsmasq log' )
7551 output
= read_dnsmasq_log_file ()
7553 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc' , output
)
7554 self
. assertIn ( 'DHCPOFFER(veth-peer)' , output
)
7555 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7556 self
. assertNotIn ( 'DHCPACK(veth-peer)' , output
)
7558 def test_dhcp_client_ipv6_only_with_custom_client_identifier ( self
):
7559 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only-custom-client-identifier.network' )
7562 self
. wait_online ( 'veth-peer:carrier' )
7564 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7567 output
= check_output ( 'ip address show dev veth99 scope global' )
7569 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7570 self
. assertNotIn ( '192.168.5' , output
)
7572 print ( '## dnsmasq log' )
7573 output
= read_dnsmasq_log_file ()
7575 self
. assertIn ( 'DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00' , output
)
7576 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7577 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7578 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7579 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
7581 @expectedFailureIfKernelReturnsInvalidFlags ()
7582 def test_dhcp_client_ipv4_only ( self
):
7583 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' ,
7584 '25-sit-dhcp4.netdev' , '25-sit-dhcp4.network' )
7586 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
7587 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
7588 self
. setup_nftset ( 'ifindex' , 'iface_index' )
7591 self
. wait_online ( 'veth-peer:carrier' )
7592 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
7593 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22' ,
7594 '--dhcp-option=option:domain-search,example.com' ,
7595 '--dhcp-alternate-port=67,5555' ,
7596 ipv4_range
= '192.168.5.110,192.168.5.119' )
7597 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'sit-dhcp4:carrier' )
7598 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
7600 print ( '## ip address show dev veth99 scope global' )
7601 output
= check_output ( 'ip address show dev veth99 scope global' )
7603 self
. assertIn ( 'mtu 1492' , output
)
7604 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
7605 self
. assertRegex ( output
, r
'inet 192.168.5.11[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label' )
7606 self
. assertNotIn ( '2600::' , output
)
7608 output
= check_output ( 'ip -4 --json address show dev veth99' )
7609 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
7610 if i
[ 'label' ] == 'test-label' :
7611 address1
= i
[ 'local' ]
7614 self
. assertFalse ( True )
7616 self
. assertRegex ( address1
, r
'^192.168.5.11[0-9]$' )
7618 print ( '## ip route show table main dev veth99' )
7619 output
= check_output ( 'ip route show table main dev veth99' )
7621 # no DHCP routes assigned to the main table
7622 self
. assertNotIn ( 'proto dhcp' , output
)
7624 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
7625 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
7626 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
7627 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
7629 print ( '## ip route show table 211 dev veth99' )
7630 output
= check_output ( 'ip route show table 211 dev veth99' )
7632 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24' )
7633 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24' )
7634 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24' )
7635 self
. assertRegex ( output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24' )
7636 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24' )
7637 self
. assertRegex ( output
, f
'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1} ' )
7639 print ( '## ip route show table 212 dev veth99' )
7640 output
= check_output ( 'ip route show table 212 dev veth99' )
7642 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24' )
7643 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24' )
7644 self
. assertRegex ( output
, f
'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1} ' )
7646 print ( '## link state file' )
7647 output
= read_link_state_file ( 'veth99' )
7649 # checking DNS server, SIP server, and Domains
7650 self
. assertIn ( 'DNS=192.168.5.6 192.168.5.7' , output
)
7651 self
. assertIn ( 'SIP=192.168.5.21 192.168.5.22' , output
)
7652 self
. assertIn ( 'DOMAINS=example.com' , output
)
7655 j
= json
. loads ( networkctl_json ( 'veth99' ))
7657 self
. assertEqual ( len ( j
[ 'DNS' ]), 2 )
7660 self
. assertEqual ( i
[ 'Family' ], 2 )
7661 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7662 self
. assertRegex ( a
, '^192.168.5.[67]$' )
7663 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7664 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7665 self
. assertEqual ( '192.168.5.1' , a
)
7667 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
7670 self
. assertEqual ( i
[ 'Family' ], 2 )
7671 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7672 self
. assertRegex ( a
, '^192.168.5.2[12]$' )
7673 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7674 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7675 self
. assertEqual ( '192.168.5.1' , a
)
7678 output
= check_output ( 'ip -d link show sit-dhcp4' )
7680 self
. assertRegex ( output
, fr
'sit (ip6ip )?remote any local {address1} dev veth99' )
7682 print ( '## dnsmasq log' )
7683 output
= read_dnsmasq_log_file ()
7685 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
7686 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc' , output
)
7687 self
. assertIn ( 'client provides name: test-hostname' , output
)
7688 self
. assertIn ( '26:mtu' , output
)
7690 # change address range, DNS servers, and Domains
7692 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8' ,
7693 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24' ,
7694 '--dhcp-option=option:domain-search,foo.example.com' ,
7695 '--dhcp-alternate-port=67,5555' ,
7696 ipv4_range
= '192.168.5.120,192.168.5.129' ,)
7698 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
7699 print ( 'Wait for the DHCP lease to be expired' )
7700 self
. wait_address_dropped ( 'veth99' , f
'inet {address1} /24' , ipv
= '-4' , timeout_sec
= 120 )
7701 self
. wait_address ( 'veth99' , r
'inet 192.168.5.12[0-9]*/24' , ipv
= '-4' )
7703 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7705 print ( '## ip address show dev veth99 scope global' )
7706 output
= check_output ( 'ip address show dev veth99 scope global' )
7708 self
. assertIn ( 'mtu 1492' , output
)
7709 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
7710 self
. assertNotIn ( f
' {address1} ' , output
)
7711 self
. assertRegex ( output
, r
'inet 192.168.5.12[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label' )
7712 self
. assertNotIn ( '2600::' , output
)
7714 output
= check_output ( 'ip -4 --json address show dev veth99' )
7715 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
7716 if i
[ 'label' ] == 'test-label' :
7717 address2
= i
[ 'local' ]
7720 self
. assertFalse ( True )
7722 self
. assertRegex ( address2
, r
'^192.168.5.12[0-9]$' )
7724 print ( '## ip route show table main dev veth99' )
7725 output
= check_output ( 'ip route show table main dev veth99' )
7727 # no DHCP routes assigned to the main table
7728 self
. assertNotIn ( 'proto dhcp' , output
)
7730 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
7731 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
7732 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
7733 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
7735 print ( '## ip route show table 211 dev veth99' )
7736 output
= check_output ( 'ip route show table 211 dev veth99' )
7738 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24' )
7739 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24' )
7740 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24' )
7741 self
. assertNotIn ( '192.168.5.6' , output
)
7742 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24' )
7743 self
. assertRegex ( output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24' )
7744 self
. assertRegex ( output
, f
'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2} ' )
7746 print ( '## ip route show table 212 dev veth99' )
7747 output
= check_output ( 'ip route show table 212 dev veth99' )
7749 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24' )
7750 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24' )
7751 self
. assertRegex ( output
, f
'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2} ' )
7753 print ( '## link state file' )
7754 output
= read_link_state_file ( 'veth99' )
7756 # checking DNS server, SIP server, and Domains
7757 self
. assertIn ( 'DNS=192.168.5.1 192.168.5.7 192.168.5.8' , output
)
7758 self
. assertIn ( 'SIP=192.168.5.23 192.168.5.24' , output
)
7759 self
. assertIn ( 'DOMAINS=foo.example.com' , output
)
7762 j
= json
. loads ( networkctl_json ( 'veth99' ))
7764 self
. assertEqual ( len ( j
[ 'DNS' ]), 3 )
7767 self
. assertEqual ( i
[ 'Family' ], 2 )
7768 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7769 self
. assertRegex ( a
, '^192.168.5.[178]$' )
7770 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7771 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7772 self
. assertEqual ( '192.168.5.1' , a
)
7774 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
7777 self
. assertEqual ( i
[ 'Family' ], 2 )
7778 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7779 self
. assertRegex ( a
, '^192.168.5.2[34]$' )
7780 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7781 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7782 self
. assertEqual ( '192.168.5.1' , a
)
7785 output
= check_output ( 'ip -d link show sit-dhcp4' )
7787 self
. assertRegex ( output
, fr
'sit (ip6ip )?remote any local {address2} dev veth99' )
7789 print ( '## dnsmasq log' )
7790 output
= read_dnsmasq_log_file ()
7792 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
7793 self
. assertIn ( f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc' , output
)
7794 self
. assertIn ( 'client provides name: test-hostname' , output
)
7795 self
. assertIn ( '26:mtu' , output
)
7797 self
. check_netlabel ( 'veth99' , r
'192\.168\.5\.0/24' )
7799 self
. check_nftset ( 'addr4' , r
'192\.168\.5\.1' )
7800 self
. check_nftset ( 'network4' , r
'192\.168\.5\.0/24' )
7801 self
. check_nftset ( 'ifindex' , 'veth99' )
7803 # Check if DHCPv4 address and routes are removed on stop. For issue #34837.
7804 stop_networkd ( show_logs
= False )
7805 self
. wait_address_dropped ( 'veth99' , f
'inet {address2} /24' , ipv
= '-4' , timeout_sec
= 120 )
7807 print ( '## ip address show dev veth99 scope global' )
7808 output
= check_output ( 'ip address show dev veth99 scope global' )
7810 self
. assertNotIn ( f
' {address2} ' , output
)
7812 print ( '## ip route show table main dev veth99' )
7813 output
= check_output ( 'ip route show table main dev veth99' )
7815 self
. assertNotIn ( f
' {address2} ' , output
)
7817 print ( '## ip route show table 211 dev veth99' )
7818 output
= check_output ( 'ip route show table 211 dev veth99' )
7820 self
. assertNotIn ( f
' {address2} ' , output
)
7822 print ( '## ip route show table 212 dev veth99' )
7823 output
= check_output ( 'ip route show table 212 dev veth99' )
7825 self
. assertNotIn ( f
' {address2} ' , output
)
7827 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
7829 def test_dhcp_client_ipv4_dbus_status ( self
):
7830 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' )
7832 self
. wait_online ( 'veth-peer:carrier' )
7834 state
= get_dhcp4_client_state ( 'veth99' )
7835 print ( f
"State = {state} " )
7836 self
. assertEqual ( state
, 'rebooting' )
7838 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
7839 '--dhcp-option=option:domain-search,example.com' ,
7840 '--dhcp-alternate-port=67,5555' ,
7841 ipv4_range
= '192.168.5.110,192.168.5.119' )
7842 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7843 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
7845 state
= get_dhcp4_client_state ( 'veth99' )
7846 print ( f
"State = {state} " )
7847 self
. assertEqual ( state
, 'bound' )
7849 def test_dhcp_client_allow_list ( self
):
7850 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-allow-list.network' , copy_dropins
= False )
7853 self
. wait_online ( 'veth-peer:carrier' )
7854 since
= datetime
. datetime
. now ()
7857 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' , since
= since
)
7859 copy_network_unit ( '25-dhcp-client-allow-list.network.d/00-allow-list.conf' )
7860 since
= datetime
. datetime
. now ()
7863 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' , since
= since
)
7865 copy_network_unit ( '25-dhcp-client-allow-list.network.d/10-deny-list.conf' )
7866 since
= datetime
. datetime
. now ()
7869 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.' , since
= since
)
7871 @unittest . skipUnless ( "--dhcp-rapid-commit" in run ( "dnsmasq --help" ). stdout
, reason
= "dnsmasq is missing dhcp-rapid-commit support" )
7872 def test_dhcp_client_rapid_commit ( self
):
7873 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
7875 self
. wait_online ( 'veth-peer:carrier' )
7877 start_dnsmasq ( '--dhcp-rapid-commit' )
7878 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7879 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
7881 state
= get_dhcp4_client_state ( 'veth99' )
7882 print ( f
"DHCPv4 client state = {state} " )
7883 self
. assertEqual ( state
, 'bound' )
7885 output
= read_dnsmasq_log_file ()
7886 self
. assertIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7887 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7888 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7889 self
. assertIn ( 'DHCPACK(veth-peer)' , output
)
7891 def check_bootp_client ( self
, check_log
):
7892 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7893 output
= check_output ( 'ip -4 address show dev veth99' )
7895 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24' )
7897 state
= get_dhcp4_client_state ( 'veth99' )
7898 print ( f
"DHCPv4 client state = {state} " )
7899 self
. assertEqual ( state
, 'bound' )
7902 output
= read_dnsmasq_log_file ()
7904 self
. assertIn ( 'BOOTP(veth-peer)' , output
)
7905 self
. assertNotIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7906 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7907 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7908 self
. assertNotIn ( 'DHCPACK(veth-peer)' , output
)
7910 def test_bootp_client ( self
):
7911 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-bootp-client.network' )
7913 self
. wait_online ( 'veth-peer:carrier' )
7914 start_dnsmasq ( '--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule' )
7915 self
. check_bootp_client ( check_log
= True )
7917 touch_network_unit ( '25-bootp-client.network' )
7919 self
. check_bootp_client ( check_log
= True )
7921 with
open ( os
. path
. join ( network_unit_dir
, '25-bootp-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7922 f
. write ( '[DHCPv4] \n BOOTP=no \n ' )
7925 self
. check_bootp_client ( check_log
= False )
7927 output
= read_dnsmasq_log_file ()
7929 # Note, on reload, the DHCP client will be started from INIT-REBOOT state,
7930 # hence DISCOVER and OFFER message will not be sent/received.
7931 self
. assertNotIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7932 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7933 self
. assertIn ( 'DHCPREQUEST(veth-peer)' , output
)
7934 self
. assertIn ( 'DHCPACK(veth-peer)' , output
)
7936 with
open ( os
. path
. join ( network_unit_dir
, '25-bootp-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7937 f
. write ( '[DHCPv4] \n BOOTP=yes \n ' )
7939 since
= datetime
. datetime
. now ()
7942 self
. check_bootp_client ( check_log
= False )
7944 # Check if the client send RELEASE message of the previous lease
7945 self
. check_networkd_log ( 'veth99: DHCPv4 client: RELEASE' , since
= since
)
7947 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity ( self
):
7948 copy_network_unit ( '25-veth.netdev' ,
7949 '25-dhcp-server-ipv6-only-mode.network' ,
7950 '25-dhcp-client-ipv6-only-mode.network' )
7952 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , timeout
= '40s' )
7953 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
7955 state
= get_dhcp4_client_state ( 'veth99' )
7956 print ( f
"State = {state} " )
7957 self
. assertEqual ( state
, 'bound' )
7959 def test_dhcp_client_ipv4_use_routes_gateway ( self
):
7961 for ( routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
. product ([ True , False ], repeat
= 4 ):
7967 print ( f
'### test_dhcp_client_ipv4_use_routes_gateway(routes= {routes} , gateway= {gateway} , dns_and_ntp_routes= {dns_and_ntp_routes} , classless= {classless} )' )
7968 with self
. subTest ( routes
= routes
, gateway
= gateway
, dns_and_ntp_routes
= dns_and_ntp_routes
, classless
= classless
):
7969 self
._ test
_ dhcp
_ client
_ ipv
4_u se
_ routes
_ gateway
( routes
, gateway
, dns_and_ntp_routes
, classless
)
7971 def _test_dhcp_client_ipv4_use_routes_gateway ( self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
7972 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
7973 testunits
= [ '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , testunit
]
7974 testunits
. append ( f
' {testunit} .d/use-routes- {use_routes} .conf' )
7975 testunits
. append ( f
' {testunit} .d/use-gateway- {use_gateway} .conf' )
7976 testunits
. append ( f
' {testunit} .d/use-dns-and-ntp-routes- {dns_and_ntp_routes} .conf' )
7977 copy_network_unit (* testunits
, copy_dropins
= False )
7980 self
. wait_online ( 'veth-peer:carrier' )
7981 additional_options
= [
7982 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8' ,
7983 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9' ,
7984 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
7987 additional_options
+= [
7988 '--dhcp-option=option:classless-static-route,0.0.0.0/0,192.168.5.4,8.0.0.0/8,192.168.5.5,192.168.5.64/26,192.168.5.5'
7990 start_dnsmasq (* additional_options
)
7991 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7993 output
= check_output ( 'ip -4 route show dev veth99' )
7999 self
. assertRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8000 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8001 self
. assertRegex ( output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8002 self
. assertRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8003 self
. assertRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8005 self
. assertRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8006 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8007 self
. assertRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8008 self
. assertRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8010 self
. assertNotRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8011 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8012 self
. assertNotRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8013 self
. assertNotRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8014 self
. assertNotRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8015 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8016 self
. assertNotRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8017 self
. assertNotRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8020 if use_gateway
and ( not classless
or not use_routes
):
8021 self
. assertRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8023 self
. assertNotRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8025 # Check route to gateway
8026 if ( use_gateway
or dns_and_ntp_routes
) and ( not classless
or not use_routes
):
8027 self
. assertRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8029 self
. assertNotRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8031 # Check RoutesToDNS= and RoutesToNTP=
8032 if dns_and_ntp_routes
:
8033 self
. assertRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8034 self
. assertRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8037 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8038 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8040 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8041 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8043 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8044 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8046 self
. assertNotRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8047 self
. assertNotRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8048 self
. assertNotRegex ( output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
8049 self
. assertNotRegex ( output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
8051 check_json ( networkctl_json ())
8053 def test_dhcp_client_settings_anonymize ( self
):
8054 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-anonymize.network' )
8056 self
. wait_online ( 'veth-peer:carrier' )
8058 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8060 print ( '## dnsmasq log' )
8061 output
= read_dnsmasq_log_file ()
8063 self
. assertNotIn ( 'VendorClassIdentifier=SusantVendorTest' , output
)
8064 self
. assertNotIn ( 'test-hostname' , output
)
8065 self
. assertNotIn ( '26:mtu' , output
)
8067 def test_dhcp_keep_configuration_dynamic ( self
):
8068 copy_network_unit ( '25-veth.netdev' ,
8069 '25-dhcp-server-veth-peer.network' ,
8070 '25-dhcp-client-keep-configuration-dynamic.network' )
8072 self
. wait_online ( 'veth-peer:carrier' )
8074 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8076 output
= check_output ( 'ip address show dev veth99 scope global' )
8078 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8079 'valid_lft forever preferred_lft forever' )
8081 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
8084 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
8085 print ( 'Wait for the DHCP lease to be expired' )
8088 # The lease address should be kept after the lease expired
8089 output
= check_output ( 'ip address show dev veth99 scope global' )
8091 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8092 'valid_lft forever preferred_lft forever' )
8096 # The lease address should be kept after networkd stopped
8097 output
= check_output ( 'ip address show dev veth99 scope global' )
8099 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8100 'valid_lft forever preferred_lft forever' )
8102 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-keep-configuration-dynamic.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
8103 f
. write ( '[Network] \n DHCP=no \n ' )
8106 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8108 # Still the lease address should be kept after networkd restarted
8109 output
= check_output ( 'ip address show dev veth99 scope global' )
8111 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8112 'valid_lft forever preferred_lft forever' )
8114 def test_dhcp_keep_configuration_dynamic_on_stop ( self
):
8115 copy_network_unit ( '25-veth.netdev' ,
8116 '25-dhcp-server-veth-peer.network' ,
8117 '25-dhcp-client-keep-configuration-dynamic-on-stop.network' )
8119 self
. wait_online ( 'veth-peer:carrier' )
8121 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8123 output
= check_output ( 'ip address show dev veth99 scope global' )
8125 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8130 output
= check_output ( 'ip address show dev veth99 scope global' )
8132 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8135 self
. wait_online ( 'veth-peer:routable' )
8137 output
= check_output ( 'ip address show dev veth99 scope global' )
8139 self
. assertNotIn ( '192.168.5.' , output
)
8141 def test_dhcp_client_reuse_address_as_static ( self
):
8142 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
8144 self
. wait_online ( 'veth-peer:carrier' )
8146 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8148 # link become 'routable' when at least one protocol provide an valid address.
8149 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8150 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8152 output
= check_output ( 'ip address show dev veth99 scope global' )
8153 ipv4_address
= re
. search ( r
'192.168.5.[0-9]*/24' , output
). group ()
8154 ipv6_address
= re
. search ( r
'2600::[0-9a-f:]*/128' , output
). group ()
8155 static_network
= ' \n ' . join ([ '[Match]' , 'Name=veth99' , '[Network]' , 'IPv6AcceptRA=no' , 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
8156 print ( static_network
)
8158 remove_network_unit ( '25-dhcp-client.network' )
8160 with
open ( os
. path
. join ( network_unit_dir
, '25-static.network' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8161 f
. write ( static_network
)
8164 self
. wait_online ( 'veth99:routable' )
8166 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
8168 self
. assertRegex ( output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99 \n *'
8169 'valid_lft forever preferred_lft forever' )
8171 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8173 self
. assertRegex ( output
, f
'inet6 {ipv6_address} scope global * \n *'
8174 'valid_lft forever preferred_lft forever' )
8176 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
8177 def test_dhcp_client_vrf ( self
):
8178 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-vrf.network' ,
8179 '25-vrf.netdev' , '25-vrf.network' )
8181 self
. wait_online ( 'veth-peer:carrier' )
8183 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'vrf99:carrier' )
8185 # link become 'routable' when at least one protocol provide an valid address.
8186 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8187 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8189 print ( '## ip -d link show dev vrf99' )
8190 output
= check_output ( 'ip -d link show dev vrf99' )
8192 self
. assertRegex ( output
, 'vrf table 42' )
8194 print ( '## ip address show vrf vrf99' )
8195 output
= check_output ( 'ip address show vrf vrf99' )
8197 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8198 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8199 self
. assertRegex ( output
, 'inet6 .* scope link' )
8201 print ( '## ip address show dev veth99' )
8202 output
= check_output ( 'ip address show dev veth99' )
8204 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8205 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8206 self
. assertRegex ( output
, 'inet6 .* scope link' )
8208 print ( '## ip route show vrf vrf99' )
8209 output
= check_output ( 'ip route show vrf vrf99' )
8211 self
. assertRegex ( output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.' )
8212 self
. assertRegex ( output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5' )
8213 self
. assertRegex ( output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5' )
8215 print ( '## ip route show table main dev veth99' )
8216 output
= check_output ( 'ip route show table main dev veth99' )
8218 self
. assertEqual ( output
, '' )
8220 def test_dhcp_client_gateway_onlink_implicit ( self
):
8221 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
8222 '25-dhcp-client-gateway-onlink-implicit.network' )
8224 self
. wait_online ( 'veth-peer:carrier' )
8226 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8228 output
= networkctl_status ( 'veth99' )
8230 self
. assertRegex ( output
, '192.168.5' )
8232 output
= check_output ( 'ip route list dev veth99 10.0.0.0/8' )
8234 self
. assertRegex ( output
, 'onlink' )
8235 output
= check_output ( 'ip route list dev veth99 192.168.100.0/24' )
8237 self
. assertRegex ( output
, 'onlink' )
8239 def test_dhcp_client_with_ipv4ll ( self
):
8240 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
8241 '25-dhcp-client-with-ipv4ll.network' )
8243 # we need to increase timeout above default, as this will need to wait for
8244 # systemd-networkd to get the dhcpv4 transient failure event
8245 self
. wait_online ( 'veth99:degraded' , 'veth-peer:routable' , timeout
= '60s' )
8247 output
= check_output ( 'ip -4 address show dev veth99' )
8249 self
. assertNotIn ( '192.168.5.' , output
)
8250 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
8253 print ( 'Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped' )
8254 self
. wait_address ( 'veth99' , r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic' , ipv
= '-4' )
8255 self
. wait_address_dropped ( 'veth99' , r
'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link' , scope
= 'link' , ipv
= '-4' )
8256 self
. wait_online ( 'veth99:routable' )
8258 output
= check_output ( 'ip -4 address show dev veth99' )
8260 self
. assertRegex ( output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99' )
8261 self
. assertNotIn ( '169.254.' , output
)
8262 self
. assertNotIn ( 'scope link' , output
)
8265 print ( 'Wait for the DHCP lease to be expired and an IPv4LL address to be acquired' )
8266 self
. wait_address_dropped ( 'veth99' , r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic' , ipv
= '-4' , timeout_sec
= 130 )
8267 self
. wait_address ( 'veth99' , r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link' , scope
= 'link' , ipv
= '-4' )
8269 output
= check_output ( 'ip -4 address show dev veth99' )
8271 self
. assertNotIn ( '192.168.5.' , output
)
8272 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
8274 def test_dhcp_client_use_dns ( self
):
8275 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8276 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8277 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8278 f
. write ( '[DHCPv4] \n UseDNS=' )
8279 f
. write ( 'yes' if ipv4
else 'no' )
8280 f
. write ( ' \n [DHCPv6] \n UseDNS=' )
8281 f
. write ( 'yes' if ipv6
else 'no' )
8282 f
. write ( ' \n [IPv6AcceptRA] \n UseDNS=no' )
8285 if needs_reconfigure
:
8286 networkctl_reconfigure ( 'veth99' )
8287 self
. wait_online ( 'veth99:routable' )
8289 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8290 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8291 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8293 # make resolved re-read the link state file
8294 resolvectl ( 'revert' , 'veth99' )
8296 output
= resolvectl ( 'dns' , 'veth99' )
8299 self
. assertIn ( '192.168.5.1' , output
)
8301 self
. assertNotIn ( '192.168.5.1' , output
)
8303 self
. assertIn ( '2600::1' , output
)
8305 self
. assertNotIn ( '2600::1' , output
)
8307 check_json ( networkctl_json ())
8309 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8312 self
. wait_online ( 'veth-peer:carrier' )
8313 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1' ,
8314 '--dhcp-option=option6:dns-server,[2600::1]' )
8316 check ( self
, True , True )
8317 check ( self
, True , False )
8318 check ( self
, False , True , needs_reconfigure
= True )
8319 check ( self
, False , False )
8321 def test_dhcp_client_default_use_domains ( self
):
8322 def check ( self
, common
, ipv4
, ipv6
):
8323 mkdir_p ( networkd_conf_dropin_dir
)
8324 with
open ( os
. path
. join ( networkd_conf_dropin_dir
, 'default_use_domains.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8325 f
. write ( '[Network] \n UseDomains=' )
8326 f
. write ( 'yes \n ' if common
else 'no \n ' )
8327 f
. write ( '[DHCPv4] \n UseDomains=' )
8328 f
. write ( 'yes \n ' if ipv4
else 'no \n ' )
8329 f
. write ( '[DHCPv6] \n UseDomains=' )
8330 f
. write ( 'yes \n ' if ipv6
else 'no \n ' )
8333 self
. wait_online ( 'veth-peer:carrier' )
8334 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1' ,
8335 '--dhcp-option=option6:dns-server,[2600::1]' ,
8336 '--dhcp-option=option:domain-search,example.com' ,
8337 '--dhcp-option=option6:domain-search,example.com' )
8339 self
. wait_online ( 'veth99:routable' )
8341 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8342 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8343 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8346 output
= resolvectl ( 'domain' , 'veth99' )
8347 if common
or ipv4
or ipv6
:
8348 if 'example.com' in output
:
8351 if 'example.com' not in output
:
8356 print ( read_link_state_file ( 'veth99' ))
8357 self
. fail ( 'unexpected domain setting in resolved...' )
8360 remove_networkd_conf_dropin ( 'default_use_domains.conf' )
8362 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8363 check ( self
, True , False , False )
8364 check ( self
, False , True , True )
8365 check ( self
, False , True , False )
8366 check ( self
, False , False , True )
8367 check ( self
, False , False , False )
8369 def test_dhcp_client_use_dnr ( self
):
8370 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8371 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8372 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8373 f
. write ( '[DHCPv4] \n UseDNS=' )
8374 f
. write ( 'yes' if ipv4
else 'no' )
8375 f
. write ( ' \n [DHCPv6] \n UseDNS=' )
8376 f
. write ( 'yes' if ipv6
else 'no' )
8377 f
. write ( ' \n [IPv6AcceptRA] \n UseDNS=no' )
8380 if needs_reconfigure
:
8381 networkctl_reconfigure ( 'veth99' )
8382 self
. wait_online ( 'veth99:routable' )
8384 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8385 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8386 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8388 # make resolved re-read the link state file
8389 resolvectl ( 'revert' , 'veth99' )
8391 output
= resolvectl ( 'dns' , 'veth99' )
8394 self
. assertIn ( '8.8.8.8#dns.google' , output
)
8395 self
. assertIn ( '0.7.4.2#homer.simpson' , output
)
8397 self
. assertNotIn ( '8.8.8.8#dns.google' , output
)
8399 self
. assertIn ( '2001:4860:4860::8888#dns.google' , output
)
8401 self
. assertNotIn ( '2001:4860:4860::8888#dns.google' , output
)
8403 check_json ( networkctl_json ())
8405 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8408 self
. wait_online ( 'veth-peer:carrier' )
8409 dnr_v4
= dnr_v4_instance_data ( adn
= "dns.google" , addrs
= [ "8.8.8.8" , "8.8.4.4" ])
8410 dnr_v4
+= dnr_v4_instance_data ( adn
= "homer.simpson" , addrs
= [ "0.7.4.2" ], alpns
= ( "dot" , "h2" , "h3" ), dohpath
= "/springfield{?dns}" )
8411 dnr_v6
= dnr_v6_instance_data ( adn
= "dns.google" , addrs
= [ "2001:4860:4860::8888" , "2001:4860:4860::8844" ])
8412 masq
= lambda bs
: ':' . join ( f
"{b:02x}" for b
in bs
)
8413 start_dnsmasq ( f
'--dhcp-option=162,{masq(dnr_v4)}' ,
8414 f
'--dhcp-option=option6:144,{masq(dnr_v6)}' )
8416 check ( self
, True , True )
8417 check ( self
, True , False )
8418 check ( self
, False , True , needs_reconfigure
= True )
8419 check ( self
, False , False )
8421 def test_dhcp_client_use_captive_portal ( self
):
8422 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8423 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8424 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8425 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
8426 f
. write ( 'yes' if ipv4
else 'no' )
8427 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
8428 f
. write ( 'yes' if ipv6
else 'no' )
8429 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
8432 if needs_reconfigure
:
8433 networkctl_reconfigure ( 'veth99' )
8434 self
. wait_online ( 'veth99:routable' )
8436 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8437 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8438 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8440 output
= networkctl_status ( 'veth99' )
8443 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
8445 self
. assertNotIn ( 'Captive Portal: http://systemd.io' , output
)
8447 check_json ( networkctl_json ())
8449 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8452 self
. wait_online ( 'veth-peer:carrier' )
8453 start_dnsmasq ( '--dhcp-option=114,http://systemd.io' ,
8454 '--dhcp-option=option6:103,http://systemd.io' )
8456 check ( self
, True , True )
8457 check ( self
, True , False )
8458 check ( self
, False , True , needs_reconfigure
= True )
8459 check ( self
, False , False )
8461 def test_dhcp_client_reject_captive_portal ( self
):
8462 def check ( self
, ipv4
, ipv6
):
8463 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8464 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8465 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
8466 f
. write ( 'yes' if ipv4
else 'no' )
8467 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
8468 f
. write ( 'yes' if ipv6
else 'no' )
8469 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
8472 self
. wait_online ( 'veth99:routable' )
8474 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8475 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8476 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8478 output
= networkctl_status ( 'veth99' )
8480 self
. assertNotIn ( 'Captive Portal: ' , output
)
8481 self
. assertNotIn ( 'invalid/url' , output
)
8483 check_json ( networkctl_json ())
8485 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8488 self
. wait_online ( 'veth-peer:carrier' )
8489 masq
= lambda bs
: ':' . join ( f
'{b:02x}' for b
in bs
)
8490 start_dnsmasq ( '--dhcp-option=114,' + masq ( b
'http:// \x00 invalid/url' ),
8491 '--dhcp-option=option6:103,' + masq ( b
'http:// \x00 /invalid/url' ))
8493 check ( self
, True , True )
8494 check ( self
, True , False )
8495 check ( self
, False , True )
8496 check ( self
, False , False )
8498 class NetworkdDHCPPDTests ( unittest
. TestCase
, Utilities
):
8506 def check_dhcp6_prefix ( self
, link
):
8507 description
= get_link_description ( link
)
8509 self
. assertIn ( 'DHCPv6Client' , description
. keys ())
8510 self
. assertIn ( 'Prefixes' , description
[ 'DHCPv6Client' ])
8512 prefixInfo
= description
[ 'DHCPv6Client' ][ 'Prefixes' ]
8514 self
. assertEqual ( len ( prefixInfo
), 1 )
8516 self
. assertIn ( 'Prefix' , prefixInfo
[ 0 ]. keys ())
8517 self
. assertIn ( 'PrefixLength' , prefixInfo
[ 0 ]. keys ())
8518 self
. assertIn ( 'PreferredLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
8519 self
. assertIn ( 'ValidLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
8521 self
. assertEqual ( prefixInfo
[ 0 ][ 'Prefix' ][ 0 : 6 ], [ 63 , 254 , 5 , 1 , 255 , 255 ])
8522 self
. assertEqual ( prefixInfo
[ 0 ][ 'PrefixLength' ], 56 )
8523 self
. assertGreater ( prefixInfo
[ 0 ][ 'PreferredLifetimeUSec' ], 0 )
8524 self
. assertGreater ( prefixInfo
[ 0 ][ 'ValidLifetimeUSec' ], 0 )
8526 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8527 def test_dhcp6pd_no_address ( self
):
8529 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-address.network' )
8532 self
. wait_online ( 'veth-peer:routable' )
8533 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
8534 self
. wait_online ( 'veth99:degraded' )
8536 print ( '### ip -6 address show dev veth99 scope global' )
8537 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8539 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
8541 print ( '### ip -6 route show dev lo' )
8542 output
= check_output ( 'ip -6 route show dev lo' )
8544 self
. assertNotRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/56' )
8546 self
. check_dhcp6_prefix ( 'veth99' )
8548 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8549 def test_dhcp6pd_no_assign ( self
):
8550 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
8551 # However, the server does not provide IA_NA. For issue #31349.
8552 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-assign.network' )
8555 self
. wait_online ( 'veth-peer:routable' )
8556 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd-no-range.conf' , ipv
= '-6' )
8557 self
. wait_online ( 'veth99:degraded' )
8559 print ( '### ip -6 address show dev veth99 scope global' )
8560 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8562 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
8564 print ( '### ip -6 route show type blackhole' )
8565 output
= check_output ( 'ip -6 route show type blackhole' )
8567 self
. assertRegex ( output
, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp' )
8569 self
. check_dhcp6_prefix ( 'veth99' )
8571 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8572 def test_dhcp6pd ( self
):
8573 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream.network' ,
8574 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
8575 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
8576 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
8577 '25-dhcp-pd-downstream-dummy97.network' ,
8578 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
8579 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' )
8582 self
. wait_online ( 'veth-peer:routable' )
8583 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
8584 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
8585 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
8587 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
8588 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
8589 self
. setup_nftset ( 'ifindex' , 'iface_index' )
8591 # Check DBus assigned prefix information to veth99
8592 self
. check_dhcp6_prefix ( 'veth99' )
8594 print ( '### ip -6 address show dev veth-peer scope global' )
8595 output
= check_output ( 'ip -6 address show dev veth-peer scope global' )
8597 self
. assertIn ( 'inet6 3ffe:501:ffff:100::1/64 scope global' , output
)
8601 # dummy97: 0x01 (The link will appear later)
8603 # dummy99: auto -> 0x02 (No address assignment)
8608 print ( '### ip -6 address show dev veth99 scope global' )
8609 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8612 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8613 # address in IA_PD (Token=static)
8614 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic' )
8615 # address in IA_PD (Token=eui64)
8616 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic' )
8617 # address in IA_PD (temporary)
8618 # Note that the temporary addresses may appear after the link enters configured state
8619 self
. wait_address ( 'veth99' , 'inet6 3ffe:501:ffff:[2-9a-f]10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8621 print ( '### ip -6 address show dev test1 scope global' )
8622 output
= check_output ( 'ip -6 address show dev test1 scope global' )
8624 # address in IA_PD (Token=static)
8625 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8626 # address in IA_PD (temporary)
8627 self
. wait_address ( 'test1' , 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8629 print ( '### ip -6 address show dev dummy98 scope global' )
8630 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8632 # address in IA_PD (Token=static)
8633 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8634 # address in IA_PD (temporary)
8635 self
. wait_address ( 'dummy98' , 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8637 print ( '### ip -6 address show dev dummy99 scope global' )
8638 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8641 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
8643 print ( '### ip -6 address show dev veth97 scope global' )
8644 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
8646 # address in IA_PD (Token=static)
8647 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8648 # address in IA_PD (Token=eui64)
8649 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8650 # address in IA_PD (temporary)
8651 self
. wait_address ( 'veth97' , 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8653 print ( '### ip -6 address show dev veth97-peer scope global' )
8654 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
8656 # NDisc address (Token=static)
8657 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8658 # NDisc address (Token=eui64)
8659 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8660 # NDisc address (temporary)
8661 self
. wait_address ( 'veth97-peer' , 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8663 print ( '### ip -6 address show dev veth98 scope global' )
8664 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
8666 # address in IA_PD (Token=static)
8667 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8668 # address in IA_PD (Token=eui64)
8669 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8670 # address in IA_PD (temporary)
8671 self
. wait_address ( 'veth98' , 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8673 print ( '### ip -6 address show dev veth98-peer scope global' )
8674 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
8676 # NDisc address (Token=static)
8677 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8678 # NDisc address (Token=eui64)
8679 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8680 # NDisc address (temporary)
8681 self
. wait_address ( 'veth98-peer' , 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8683 print ( '### ip -6 route show type unreachable' )
8684 output
= check_output ( 'ip -6 route show type unreachable' )
8686 self
. assertRegex ( output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp' )
8688 print ( '### ip -6 route show dev veth99' )
8689 output
= check_output ( 'ip -6 route show dev veth99' )
8691 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires' )
8693 print ( '### ip -6 route show dev test1' )
8694 output
= check_output ( 'ip -6 route show dev test1' )
8696 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8698 print ( '### ip -6 route show dev dummy98' )
8699 output
= check_output ( 'ip -6 route show dev dummy98' )
8701 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8703 print ( '### ip -6 route show dev dummy99' )
8704 output
= check_output ( 'ip -6 route show dev dummy99' )
8706 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
8708 print ( '### ip -6 route show dev veth97' )
8709 output
= check_output ( 'ip -6 route show dev veth97' )
8711 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires' )
8713 print ( '### ip -6 route show dev veth97-peer' )
8714 output
= check_output ( 'ip -6 route show dev veth97-peer' )
8716 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires' )
8718 print ( '### ip -6 route show dev veth98' )
8719 output
= check_output ( 'ip -6 route show dev veth98' )
8721 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires' )
8723 print ( '### ip -6 route show dev veth98-peer' )
8724 output
= check_output ( 'ip -6 route show dev veth98-peer' )
8726 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires' )
8728 # Test case for a downstream which appears later
8729 check_output ( 'ip link add dummy97 type dummy' )
8730 self
. wait_online ( 'dummy97:routable' )
8732 print ( '### ip -6 address show dev dummy97 scope global' )
8733 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
8735 # address in IA_PD (Token=static)
8736 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8737 # address in IA_PD (temporary)
8738 self
. wait_address ( 'dummy97' , 'inet6 3ffe:501:ffff:[2-9a-f]01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8740 print ( '### ip -6 route show dev dummy97' )
8741 output
= check_output ( 'ip -6 route show dev dummy97' )
8743 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires' )
8745 # Test case for reconfigure
8746 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
8747 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
8749 print ( '### ip -6 address show dev dummy98 scope global' )
8750 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8752 # address in IA_PD (Token=static)
8753 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8754 # address in IA_PD (temporary)
8755 self
. wait_address ( 'dummy98' , 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8757 print ( '### ip -6 address show dev dummy99 scope global' )
8758 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8761 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
8763 print ( '### ip -6 route show dev dummy98' )
8764 output
= check_output ( 'ip -6 route show dev dummy98' )
8766 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8768 print ( '### ip -6 route show dev dummy99' )
8769 output
= check_output ( 'ip -6 route show dev dummy99' )
8771 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
8773 self
. check_netlabel ( 'dummy98' , '3ffe:501:ffff:[2-9a-f]00::/64' )
8775 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d' )
8776 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
8777 self
. check_nftset ( 'network6' , '3ffe:501:ffff:[2-9a-f]00::/64' )
8778 self
. check_nftset ( 'ifindex' , 'dummy98' )
8780 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
8782 def verify_dhcp4_6rd ( self
, tunnel_name
, address_prefix
, border_router
):
8783 print ( '### ip -4 address show dev veth-peer scope global' )
8784 output
= check_output ( 'ip -4 address show dev veth-peer scope global' )
8786 self
. assertIn ( 'inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer' , output
)
8790 # dummy97: 0x01 (The link will appear later)
8792 # dummy99: auto -> 0x0[23] (No address assignment)
8793 # 6rd-XXX: auto -> 0x0[23]
8798 print ( '### ip -4 address show dev veth99 scope global' )
8799 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
8801 self
. assertRegex ( output
, fr
'inet {address_prefix} [0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99' )
8803 print ( '### ip -6 address show dev veth99 scope global' )
8804 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8806 # address in IA_PD (Token=static)
8807 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8808 # address in IA_PD (Token=eui64)
8809 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8810 # address in IA_PD (temporary)
8811 # Note that the temporary addresses may appear after the link enters configured state
8812 self
. wait_address ( 'veth99' , 'inet6 2001:db8:6464:[0-9a-f]+10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8814 print ( '### ip -6 address show dev test1 scope global' )
8815 output
= check_output ( 'ip -6 address show dev test1 scope global' )
8817 # address in IA_PD (Token=static)
8818 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8819 # address in IA_PD (temporary)
8820 self
. wait_address ( 'test1' , 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8822 print ( '### ip -6 address show dev dummy98 scope global' )
8823 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8825 # address in IA_PD (Token=static)
8826 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8827 # address in IA_PD (temporary)
8828 self
. wait_address ( 'dummy98' , 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8830 print ( '### ip -6 address show dev dummy99 scope global' )
8831 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8834 self
. assertNotRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]' )
8836 print ( '### ip -6 address show dev veth97 scope global' )
8837 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
8839 # address in IA_PD (Token=static)
8840 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8841 # address in IA_PD (Token=eui64)
8842 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8843 # address in IA_PD (temporary)
8844 self
. wait_address ( 'veth97' , 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8846 print ( '### ip -6 address show dev veth97-peer scope global' )
8847 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
8849 # NDisc address (Token=static)
8850 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8851 # NDisc address (Token=eui64)
8852 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8853 # NDisc address (temporary)
8854 self
. wait_address ( 'veth97-peer' , 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8856 print ( '### ip -6 address show dev veth98 scope global' )
8857 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
8859 # address in IA_PD (Token=static)
8860 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8861 # address in IA_PD (Token=eui64)
8862 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8863 # address in IA_PD (temporary)
8864 self
. wait_address ( 'veth98' , 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8866 print ( '### ip -6 address show dev veth98-peer scope global' )
8867 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
8869 # NDisc address (Token=static)
8870 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8871 # NDisc address (Token=eui64)
8872 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8873 # NDisc address (temporary)
8874 self
. wait_address ( 'veth98-peer' , 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8876 print ( '### ip -6 route show type unreachable' )
8877 output
= check_output ( 'ip -6 route show type unreachable' )
8879 self
. assertRegex ( output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp' )
8881 print ( '### ip -6 route show dev veth99' )
8882 output
= check_output ( 'ip -6 route show dev veth99' )
8884 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires' )
8886 print ( '### ip -6 route show dev test1' )
8887 output
= check_output ( 'ip -6 route show dev test1' )
8889 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
8891 print ( '### ip -6 route show dev dummy98' )
8892 output
= check_output ( 'ip -6 route show dev dummy98' )
8894 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
8896 print ( '### ip -6 route show dev dummy99' )
8897 output
= check_output ( 'ip -6 route show dev dummy99' )
8899 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires' )
8901 print ( '### ip -6 route show dev veth97' )
8902 output
= check_output ( 'ip -6 route show dev veth97' )
8904 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires' )
8906 print ( '### ip -6 route show dev veth97-peer' )
8907 output
= check_output ( 'ip -6 route show dev veth97-peer' )
8909 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires' )
8911 print ( '### ip -6 route show dev veth98' )
8912 output
= check_output ( 'ip -6 route show dev veth98' )
8914 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires' )
8916 print ( '### ip -6 route show dev veth98-peer' )
8917 output
= check_output ( 'ip -6 route show dev veth98-peer' )
8919 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires' )
8921 print ( '### ip -6 address show dev dummy97 scope global' )
8922 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
8924 # address in IA_PD (Token=static)
8925 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8926 # address in IA_PD (temporary)
8927 self
. wait_address ( 'dummy97' , 'inet6 2001:db8:6464:[0-9a-f]+01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic' , ipv
= '-6' )
8929 print ( '### ip -6 route show dev dummy97' )
8930 output
= check_output ( 'ip -6 route show dev dummy97' )
8932 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires' )
8934 print ( f
'### ip -d link show dev {tunnel_name} ' )
8935 output
= check_output ( f
'ip -d link show dev {tunnel_name} ' )
8937 self
. assertIn ( f
'link/sit {address_prefix} ' , output
)
8938 self
. assertIn ( f
'local {address_prefix} ' , output
)
8939 self
. assertIn ( 'ttl 64' , output
)
8940 self
. assertIn ( '6rd-prefix 2001:db8::/32' , output
)
8941 self
. assertIn ( '6rd-relay_prefix 10.0.0.0/8' , output
)
8943 print ( f
'### ip -6 address show dev {tunnel_name} ' )
8944 output
= check_output ( f
'ip -6 address show dev {tunnel_name} ' )
8946 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic' )
8947 self
. assertRegex ( output
, fr
'inet6 :: {address_prefix} [0-9]+/96 scope global' )
8949 print ( f
'### ip -6 route show dev {tunnel_name} ' )
8950 output
= check_output ( f
'ip -6 route show dev {tunnel_name} ' )
8952 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires' )
8953 self
. assertRegex ( output
, '::/96 proto kernel metric [0-9]*' )
8955 print ( '### ip -6 route show default' )
8956 output
= check_output ( 'ip -6 route show default' )
8958 self
. assertIn ( 'default' , output
)
8959 self
. assertIn ( f
'via :: {border_router} dev {tunnel_name} ' , output
)
8961 def test_dhcp4_6rd ( self
):
8962 def get_dhcp_6rd_prefix ( link
):
8963 description
= get_link_description ( link
)
8965 self
. assertIn ( 'DHCPv4Client' , description
. keys ())
8966 self
. assertIn ( '6rdPrefix' , description
[ 'DHCPv4Client' ]. keys ())
8968 prefixInfo
= description
[ 'DHCPv4Client' ][ '6rdPrefix' ]
8969 self
. assertIn ( 'Prefix' , prefixInfo
. keys ())
8970 self
. assertIn ( 'PrefixLength' , prefixInfo
. keys ())
8971 self
. assertIn ( 'IPv4MaskLength' , prefixInfo
. keys ())
8972 self
. assertIn ( 'BorderRouters' , prefixInfo
. keys ())
8976 copy_network_unit ( '25-veth.netdev' , '25-dhcp4-6rd-server.network' , '25-dhcp4-6rd-upstream.network' ,
8977 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
8978 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
8979 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
8980 '25-dhcp-pd-downstream-dummy97.network' ,
8981 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
8982 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' ,
8983 '80-6rd-tunnel.network' )
8986 self
. wait_online ( 'veth-peer:routable' )
8989 # 6rd-prefix: 2001:db8::/32
8990 # br-addresss: 10.0.0.1
8992 start_dnsmasq ( '--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01' ,
8993 ipv4_range
= '10.100.100.100,10.100.100.200' ,
8994 ipv4_router
= '10.0.0.1' )
8995 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
8996 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
8998 # Check the DBus interface for assigned prefix information
8999 prefixInfo
= get_dhcp_6rd_prefix ( 'veth99' )
9001 self
. assertEqual ( prefixInfo
[ 'Prefix' ], [ 32 , 1 , 13 , 184 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]) # 2001:db8::
9002 self
. assertEqual ( prefixInfo
[ 'PrefixLength' ], 32 )
9003 self
. assertEqual ( prefixInfo
[ 'IPv4MaskLength' ], 8 )
9004 self
. assertEqual ( prefixInfo
[ 'BorderRouters' ], [[ 10 , 0 , 0 , 1 ]])
9006 # Test case for a downstream which appears later
9007 check_output ( 'ip link add dummy97 type dummy' )
9008 self
. wait_online ( 'dummy97:routable' )
9012 for name
in os
. listdir ( '/sys/class/net/' ):
9013 if name
. startswith ( '6rd-' ):
9017 self
. wait_online ( f
' {tunnel_name} :routable' )
9019 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.1' , '10.0.0.1' )
9021 # Test case for reconfigure
9022 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
9023 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
9025 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.1' , '10.0.0.1' )
9027 # Change the address range and (border) router, then if check the same tunnel is reused.
9029 start_dnsmasq ( '--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:02' ,
9030 ipv4_range
= '10.100.100.200,10.100.100.250' ,
9031 ipv4_router
= '10.0.0.2' )
9033 print ( 'Wait for the DHCP lease to be renewed/rebind' )
9036 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy97:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
9037 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
9039 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.2' , '10.0.0.2' )
9041 class NetworkdIPv6PrefixTests ( unittest
. TestCase
, Utilities
):
9049 def test_ipv6_route_prefix ( self
):
9050 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client.network' , '25-ipv6ra-prefix.network' ,
9051 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
9054 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
9056 print ( '### ip -6 address show dev veth-peer' )
9057 output
= check_output ( 'ip -6 address show dev veth-peer' )
9059 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
9060 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
9061 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
9063 print ( '### ip -6 route show dev veth-peer' )
9064 output
= check_output ( 'ip -6 route show dev veth-peer' )
9066 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
9067 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
9068 self
. assertNotIn ( '2001:db8:0:3::/64 proto ra' , output
)
9069 self
. assertRegex ( output
, r
'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc' )
9070 self
. assertNotIn ( '2001:db1:fff::/64' , output
)
9071 self
. assertNotIn ( '2001:db2:fff::/64' , output
)
9073 print ( '### ip -6 nexthop show dev veth-peer' )
9074 output
= check_output ( 'ip -6 nexthop show dev veth-peer' )
9076 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra' )
9078 print ( '### ip -6 address show dev veth99' )
9079 output
= check_output ( 'ip -6 address show dev veth99' )
9081 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
9082 self
. assertIn ( 'inet6 2001:db8:0:2:1a:2b:3c:4d' , output
)
9083 self
. assertIn ( 'inet6 2001:db8:0:2:fa:de:ca:fe' , output
)
9084 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
9086 output
= resolvectl ( 'dns' , 'veth-peer' )
9088 self
. assertRegex ( output
, '2001:db8:1:1::2' )
9090 output
= resolvectl ( 'domain' , 'veth-peer' )
9092 self
. assertIn ( 'example.com' , output
)
9094 check_json ( networkctl_json ())
9096 output
= networkctl_json ( 'veth-peer' )
9100 pref64
= json
. loads ( output
)[ 'NDisc' ][ 'PREF64' ][ 0 ]
9102 prefix
= socket
. inet_ntop ( socket
. AF_INET6
, bytearray ( pref64
[ 'Prefix' ]))
9103 self
. assertEqual ( prefix
, '64:ff9b::' )
9105 prefix_length
= pref64
[ 'PrefixLength' ]
9106 self
. assertEqual ( prefix_length
, 96 )
9108 def test_ipv6_route_prefix_deny_list ( self
):
9109 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client-deny-list.network' , '25-ipv6ra-prefix.network' ,
9110 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
9113 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
9115 print ( '### ip -6 address show dev veth-peer' )
9116 output
= check_output ( 'ip -6 address show dev veth-peer' )
9118 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
9119 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
9121 print ( '### ip -6 route show dev veth-peer' )
9122 output
= check_output ( 'ip -6 route show dev veth-peer' )
9124 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
9125 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
9126 self
. assertRegex ( output
, r
'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc' )
9127 self
. assertNotIn ( '2001:db1:fff::/64' , output
)
9129 print ( '### ip -6 nexthop show dev veth-peer' )
9130 output
= check_output ( 'ip -6 nexthop show dev veth-peer' )
9132 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra' )
9134 print ( '### ip -6 address show dev veth99' )
9135 output
= check_output ( 'ip -6 address show dev veth99' )
9137 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
9138 self
. assertIn ( 'inet6 2001:db8:0:2:' , output
)
9140 output
= resolvectl ( 'dns' , 'veth-peer' )
9142 self
. assertRegex ( output
, '2001:db8:1:1::2' )
9144 output
= resolvectl ( 'domain' , 'veth-peer' )
9146 self
. assertIn ( 'example.com' , output
)
9148 class NetworkdMTUTests ( unittest
. TestCase
, Utilities
):
9156 def check_mtu ( self
, mtu
, ipv6_mtu
= None , reset
= True ):
9162 self
. wait_online ( 'dummy98:routable' )
9163 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
9164 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
9166 # test normal restart
9168 self
. wait_online ( 'dummy98:routable' )
9169 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
9170 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
9173 self
. reset_check_mtu ( mtu
, ipv6_mtu
)
9175 def reset_check_mtu ( self
, mtu
, ipv6_mtu
= None ):
9176 ''' test setting mtu/ipv6_mtu with interface already up '''
9179 # note - changing the device mtu resets the ipv6 mtu
9180 check_output ( 'ip link set up mtu 1501 dev dummy98' )
9181 check_output ( 'ip link set up mtu 1500 dev dummy98' )
9182 self
. check_link_attr ( 'dummy98' , 'mtu' , '1500' )
9183 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , '1500' )
9185 self
. check_mtu ( mtu
, ipv6_mtu
, reset
= False )
9187 def test_mtu_network ( self
):
9188 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' )
9189 self
. check_mtu ( '1600' )
9191 def test_mtu_netdev ( self
):
9192 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network' , copy_dropins
= False )
9193 # note - MTU set by .netdev happens ONLY at device creation!
9194 self
. check_mtu ( '1600' , reset
= False )
9196 def test_mtu_link ( self
):
9197 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network' , copy_dropins
= False )
9198 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
9199 self
. check_mtu ( '1600' , reset
= False )
9201 def test_ipv6_mtu ( self
):
9202 ''' set ipv6 mtu without setting device mtu '''
9203 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1400.conf' )
9204 self
. check_mtu ( '1500' , '1400' )
9206 def test_ipv6_mtu_toolarge ( self
):
9207 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
9208 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9209 self
. check_mtu ( '1500' , '1500' )
9211 def test_mtu_network_ipv6_mtu ( self
):
9212 ''' set ipv6 mtu and set device mtu via network file '''
9213 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9214 self
. check_mtu ( '1600' , '1550' )
9216 def test_mtu_netdev_ipv6_mtu ( self
):
9217 ''' set ipv6 mtu and set device mtu via netdev file '''
9218 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9219 self
. check_mtu ( '1600' , '1550' , reset
= False )
9221 def test_mtu_link_ipv6_mtu ( self
):
9222 ''' set ipv6 mtu and set device mtu via link file '''
9223 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9224 self
. check_mtu ( '1600' , '1550' , reset
= False )
9226 class NetworkdSysctlTest ( unittest
. TestCase
, Utilities
):
9234 @unittest . skipUnless ( compare_kernel_version ( "6.12" ), reason
= "On kernels <= 6.12, bpf_current_task_under_cgroup() isn't available for program types BPF_PROG_TYPE_CGROUP_SYSCTL" )
9235 def check_sysctl_watch ( self
):
9236 copy_network_unit ( '12-dummy.network' , '12-dummy.netdev' , '12-dummy.link' )
9239 self
. wait_online ( 'dummy98:routable' )
9241 # Change managed sysctls
9242 call ( 'sysctl -w net.ipv6.conf.dummy98.accept_ra=1' )
9243 call ( 'sysctl -w net.ipv6.conf.dummy98.mtu=1360' )
9244 call ( 'sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0' )
9245 call ( 'sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1' )
9247 # And unmanaged ones
9248 call ( 'sysctl -w net.ipv6.conf.dummy98.hop_limit=4' )
9249 call ( 'sysctl -w net.ipv6.conf.dummy98.max_addresses=10' )
9251 log
= read_networkd_log ()
9252 self
. assertRegex ( log
, r
"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/accept_ra' from '0' to '1', conflicting with our setting to '0'" )
9253 self
. assertRegex ( log
, r
"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/mtu' from '1550' to '1360', conflicting with our setting to '1550'" )
9254 self
. assertRegex ( log
, r
"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv4/conf/dummy98/promote_secondaries' from '1' to '0', conflicting with our setting to '1'" )
9255 self
. assertRegex ( log
, r
"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/proxy_ndp' from '0' to '1', conflicting with our setting to '0'" )
9256 self
. assertNotIn ( "changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'" , log
)
9257 self
. assertNotIn ( "changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'" , log
)
9259 if __name__
== '__main__' :
9260 parser
= argparse
. ArgumentParser ()
9261 parser
. add_argument ( '--build-dir' , help = 'Path to build dir' , dest
= 'build_dir' )
9262 parser
. add_argument ( '--source-dir' , help = 'Path to source dir/git tree' , dest
= 'source_dir' )
9263 parser
. add_argument ( '--valgrind' , help = 'Enable valgrind' , dest
= 'use_valgrind' , type = bool , nargs
= '?' , const
= True , default
= use_valgrind
)
9264 parser
. add_argument ( '--debug' , help = 'Generate debugging logs' , dest
= 'enable_debug' , type = bool , nargs
= '?' , const
= True , default
= enable_debug
)
9265 parser
. add_argument ( '--asan-options' , help = 'ASAN options' , dest
= 'asan_options' )
9266 parser
. add_argument ( '--lsan-options' , help = 'LSAN options' , dest
= 'lsan_options' )
9267 parser
. add_argument ( '--ubsan-options' , help = 'UBSAN options' , dest
= 'ubsan_options' )
9268 parser
. add_argument ( '--with-coverage' , help = 'Loosen certain sandbox restrictions to make gcov happy' , dest
= 'with_coverage' , type = bool , nargs
= '?' , const
= True , default
= with_coverage
)
9269 parser
. add_argument ( '--no-journal' , help = 'Do not show journal of systemd-networkd on stop' , dest
= 'show_journal' , action
= 'store_false' )
9270 ns
, unknown_args
= parser
. parse_known_args ( namespace
= unittest
)
9273 networkd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd' )
9274 resolved_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-resolved' )
9275 timesyncd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-timesyncd' )
9276 wait_online_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd-wait-online' )
9277 networkctl_bin
= os
. path
. join ( ns
. build_dir
, 'networkctl' )
9278 resolvectl_bin
= os
. path
. join ( ns
. build_dir
, 'resolvectl' )
9279 timedatectl_bin
= os
. path
. join ( ns
. build_dir
, 'timedatectl' )
9280 udevadm_bin
= os
. path
. join ( ns
. build_dir
, 'udevadm' )
9281 build_dir
= ns
. build_dir
9284 source_dir
= ns
. source_dir
9285 assert os
. path
. exists ( os
. path
. join ( source_dir
, "meson_options.txt" )), f
" {source_dir} doesn't appear to be a systemd source tree."
9286 elif os
. path
. exists ( os
. path
. normpath ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), "../../meson_options.txt" ))):
9287 source_dir
= os
. path
. normpath ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), "../../" ))
9291 if networkd_bin
is None or resolved_bin
is None or timesyncd_bin
is None :
9292 print ( "networkd tests require networkd/resolved/timesyncd to be enabled" )
9295 use_valgrind
= ns
. use_valgrind
9296 enable_debug
= ns
. enable_debug
9297 asan_options
= ns
. asan_options
9298 lsan_options
= ns
. lsan_options
9299 ubsan_options
= ns
. ubsan_options
9300 with_coverage
= ns
. with_coverage
or "COVERAGE_BUILD_DIR" in os
. environ
9301 show_journal
= ns
. show_journal
9304 # Do not forget the trailing space.
9305 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
9307 networkctl_cmd
= valgrind_cmd
. split () + [ networkctl_bin
]
9308 resolvectl_cmd
= valgrind_cmd
. split () + [ resolvectl_bin
]
9309 timedatectl_cmd
= valgrind_cmd
. split () + [ timedatectl_bin
]
9310 udevadm_cmd
= valgrind_cmd
. split () + [ udevadm_bin
]
9311 wait_online_cmd
= valgrind_cmd
. split () + [ wait_online_bin
]
9314 test_ndisc_send
= os
. path
. normpath ( os
. path
. join ( build_dir
, 'test-ndisc-send' ))
9316 test_ndisc_send
= '/usr/lib/tests/test-ndisc-send'
9319 env
. update ({ 'ASAN_OPTIONS' : asan_options
})
9321 env
. update ({ 'LSAN_OPTIONS' : lsan_options
})
9323 env
. update ({ 'UBSAN_OPTIONS' : ubsan_options
})
9325 env
. update ({ 'SYSTEMD_MEMPOOL' : '0' })
9327 wait_online_env
= env
. copy ()
9329 wait_online_env
. update ({ 'SYSTEMD_LOG_LEVEL' : 'debug' })
9336 *([ "-k" , match
] if ( match
:= os
. getenv ( "TEST_MATCH_TESTCASE" )) else [])