]>
git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
dc867cd1d47da9e026177da9a6988a64f01b9618
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 expectedFailureIfAlternativeNameIsNotAvailable ():
244 call_quiet ( 'ip link add dummy98 type dummy' )
246 call_quiet ( 'ip link prop add dev dummy98 altname hogehogehogehogehoge' ) == 0 and \
247 call_quiet ( 'ip link show dev hogehogehogehogehoge' ) == 0
248 remove_link ( 'dummy98' )
249 return func
if supported
else unittest
. expectedFailure ( func
)
253 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ():
255 def finalize ( func
, supported
):
256 call_quiet ( 'rmmod netdevsim' )
257 return func
if supported
else unittest
. expectedFailure ( func
)
259 call_quiet ( 'rmmod netdevsim' )
260 if call_quiet ( 'modprobe netdevsim' ) != 0 :
261 return finalize ( func
, False )
264 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
267 return finalize ( func
, False )
269 return finalize ( func
, os
. path
. exists ( '/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs' ))
273 def expectedFailureIfKernelReturnsInvalidFlags ():
275 This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
276 fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
279 call_quiet ( 'ip link add dummy98 type dummy' )
280 call_quiet ( 'ip link set up dev dummy98' )
281 call_quiet ( 'ip address add 192.0.2.1/24 dev dummy98 noprefixroute' )
282 output
= check_output ( 'ip address show dev dummy98' )
283 remove_link ( 'dummy98' )
284 return func
if 'noprefixroute' in output
else unittest
. expectedFailure ( func
)
288 # pylint: disable=C0415
289 def compare_kernel_version ( min_kernel_version
):
292 from packaging
import version
294 print ( 'Failed to import either platform or packaging module, assuming the comparison failed' )
297 # Get only the actual kernel version without any build/distro/arch stuff
298 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
299 kver
= platform
. release (). split ( '-' )[ 0 ]
300 # Get also rid of '+'
301 kver
= kver
. split ( '+' )[ 0 ]
303 return version
. parse ( kver
) >= version
. parse ( min_kernel_version
)
305 def copy_network_unit (* units
, copy_dropins
= True ):
307 Copy networkd unit files into the testbed.
309 Any networkd unit file type can be specified, as well as drop-in files.
311 By default, all drop-ins for a specified unit file are copied in;
312 to avoid that specify dropins=False.
314 When a drop-in file is specified, its unit file is also copied in automatically.
317 mkdir_p ( network_unit_dir
)
319 if copy_dropins
and os
. path
. exists ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' )):
320 cp_r ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' ), os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
322 if unit
. endswith ( '.conf' ):
324 unit
= os
. path
. dirname ( dropin
). rstrip ( '.d' )
325 dropindir
= os
. path
. join ( network_unit_dir
, unit
+ '.d' )
327 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), dropindir
)
329 cp ( os
. path
. join ( networkd_ci_temp_dir
, unit
), network_unit_dir
)
331 if unit
. endswith ( '.link' ):
337 def copy_credential ( src
, target
):
338 mkdir_p ( credstore_dir
)
339 cp ( os
. path
. join ( networkd_ci_temp_dir
, src
),
340 os
. path
. join ( credstore_dir
, target
))
342 def remove_network_unit (* units
):
344 Remove previously copied unit files from the testbed.
346 Drop-ins will be removed automatically.
350 rm_f ( os
. path
. join ( network_unit_dir
, unit
))
351 rm_rf ( os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
353 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
359 def touch_network_unit (* units
):
361 touch ( os
. path
. join ( network_unit_dir
, unit
))
363 def clear_network_units ():
365 if os
. path
. exists ( network_unit_dir
):
366 units
= os
. listdir ( network_unit_dir
)
368 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
371 rm_rf ( network_unit_dir
)
376 def copy_networkd_conf_dropin (* dropins
):
377 """Copy networkd.conf dropin files into the testbed."""
378 mkdir_p ( networkd_conf_dropin_dir
)
379 for dropin
in dropins
:
380 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
382 def remove_networkd_conf_dropin (* dropins
):
383 """Remove previously copied networkd.conf dropin files from the testbed."""
384 for dropin
in dropins
:
385 rm_f ( os
. path
. join ( networkd_conf_dropin_dir
, dropin
))
387 def clear_networkd_conf_dropins ():
388 rm_rf ( networkd_conf_dropin_dir
)
390 def setup_systemd_udev_rules ():
391 if not build_dir
and not source_dir
:
394 mkdir_p ( udev_rules_dir
)
396 for path
in [ build_dir
, source_dir
]:
400 path
= os
. path
. join ( path
, "rules.d" )
401 print ( f
"Copying udev rules from {path} to {udev_rules_dir} " )
403 for rule
in os
. listdir ( path
):
404 if not rule
. endswith ( ".rules" ):
406 cp ( os
. path
. join ( path
, rule
), udev_rules_dir
)
408 def clear_networkd_state_files ():
409 rm_rf ( '/var/lib/systemd/network/' )
411 def copy_udev_rule (* rules
):
412 """Copy udev rules"""
413 mkdir_p ( udev_rules_dir
)
415 cp ( os
. path
. join ( networkd_ci_temp_dir
, rule
), udev_rules_dir
)
417 def remove_udev_rule (* rules
):
418 """Remove previously copied udev rules"""
420 rm_f ( os
. path
. join ( udev_rules_dir
, rule
))
422 def clear_udev_rules ():
423 rm_rf ( udev_rules_dir
)
425 def save_active_units ():
426 for u
in [ 'systemd-networkd.socket' , 'systemd-networkd.service' ,
427 'systemd-resolved.service' , 'systemd-timesyncd.service' ,
428 'firewalld.service' ]:
429 if call ( f
'systemctl is-active --quiet {u} ' ) == 0 :
430 call ( f
'systemctl stop {u} ' )
431 active_units
. append ( u
)
433 def restore_active_units ():
434 if 'systemd-networkd.socket' in active_units
:
435 call ( 'systemctl stop systemd-networkd.socket systemd-networkd.service' )
436 for u
in active_units
:
437 call ( f
'systemctl restart {u} ' )
439 def create_unit_dropin ( unit
, contents
):
440 mkdir_p ( f
'/run/systemd/system/ {unit} .d' )
441 with
open ( f
'/run/systemd/system/ {unit} .d/00-override.conf' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
442 f
. write ( ' \n ' . join ( contents
))
444 def create_service_dropin ( service
, command
, additional_settings
= None ):
445 drop_in
= [ '[Service]' ]
449 f
'ExecStart= {valgrind_cmd}{command} ' ,
452 drop_in
+= [ 'Environment=SYSTEMD_LOG_LEVEL=debug' ]
454 drop_in
+= [ f
'Environment=ASAN_OPTIONS=" {asan_options} "' ]
456 drop_in
+= [ f
'Environment=LSAN_OPTIONS=" {lsan_options} "' ]
458 drop_in
+= [ f
'Environment=UBSAN_OPTIONS=" {ubsan_options} "' ]
459 if asan_options
or lsan_options
or ubsan_options
:
460 drop_in
+= [ 'SystemCallFilter=' ]
461 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
462 drop_in
+= [ 'MemoryDenyWriteExecute=no' ]
465 'Environment=SYSTEMD_MEMPOOL=0' ,
473 if additional_settings
:
474 drop_in
+= additional_settings
476 create_unit_dropin ( f
' {service} .service' , drop_in
)
478 def setup_system_units ():
479 if build_dir
or source_dir
:
480 mkdir_p ( '/run/systemd/system/' )
483 'systemd-networkd.service' ,
484 'systemd-networkd.socket' ,
485 'systemd-networkd-persistent-storage.service' ,
486 'systemd-resolved.service' ,
487 'systemd-timesyncd.service' ,
488 'systemd-udevd.service' ,
490 for path
in [ build_dir
, source_dir
]:
494 fullpath
= os
. path
. join ( os
. path
. join ( path
, "units" ), unit
)
495 if os
. path
. exists ( fullpath
):
496 print ( f
"Copying unit file from {fullpath} to /run/systemd/system/" )
497 cp ( fullpath
, '/run/systemd/system/' )
500 create_service_dropin ( 'systemd-networkd' , networkd_bin
,
503 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes' ,
505 'StartLimitIntervalSec=0' ])
506 create_service_dropin ( 'systemd-resolved' , resolved_bin
)
507 create_service_dropin ( 'systemd-timesyncd' , timesyncd_bin
)
509 # TODO: also run udevd with sanitizers, valgrind, or coverage
511 'systemd-udevd.service' ,
515 f
'ExecStart=@ {udevadm_bin} systemd-udevd' ,
519 'systemd-networkd.socket' ,
522 'StartLimitIntervalSec=0' ,
526 'systemd-networkd-persistent-storage.service' ,
529 'StartLimitIntervalSec=0' ,
532 f
'ExecStart= {networkctl_bin} persistent-storage yes' ,
534 f
'ExecStop= {networkctl_bin} persistent-storage no' ,
535 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug
else '' ,
539 check_output ( 'systemctl daemon-reload' )
540 print ( check_output ( 'systemctl cat systemd-networkd.service' ))
541 print ( check_output ( 'systemctl cat systemd-networkd-persistent-storage.service' ))
542 print ( check_output ( 'systemctl cat systemd-resolved.service' ))
543 print ( check_output ( 'systemctl cat systemd-timesyncd.service' ))
544 print ( check_output ( 'systemctl cat systemd-udevd.service' ))
545 check_output ( 'systemctl restart systemd-resolved.service' )
546 check_output ( 'systemctl restart systemd-timesyncd.service' )
547 check_output ( 'systemctl restart systemd-udevd.service' )
549 def clear_system_units ():
551 rm_f ( f
'/run/systemd/system/ {name} ' )
552 rm_rf ( f
'/run/systemd/system/ {name} .d' )
554 rm_unit ( 'systemd-networkd.service' )
555 rm_unit ( 'systemd-networkd.socket' )
556 rm_unit ( 'systemd-networkd-persistent-storage.service' )
557 rm_unit ( 'systemd-resolved.service' )
558 rm_unit ( 'systemd-timesyncd.service' )
559 rm_unit ( 'systemd-udevd.service' )
560 check_output ( 'systemctl daemon-reload' )
561 check_output ( 'systemctl restart systemd-udevd.service' )
563 def link_exists (* links
):
565 if call_quiet ( f
'ip link show {link} ' ) != 0 :
569 def link_resolve ( link
):
570 return check_output ( f
'ip link show {link} ' ). split ( ':' )[ 1 ]. strip (). split ( '@' )[ 0 ]
572 def remove_link (* links
, protect
= False ):
574 if protect
and link
in protected_links
:
576 if link_exists ( link
):
577 call ( f
'ip link del dev {link} ' )
579 def save_existing_links ():
580 links
= os
. listdir ( '/sys/class/net' )
582 if link_exists ( link
):
583 protected_links
. add ( link
)
585 print ( '### The following links will be protected:' )
586 print ( ', ' . join ( sorted ( list ( protected_links
))))
588 def unmanage_existing_links ():
589 mkdir_p ( network_unit_dir
)
591 with
open ( os
. path
. join ( network_unit_dir
, '00-unmanaged.network' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
593 for link
in protected_links
:
594 f
. write ( f
'Name= {link} \n ' )
595 f
. write ( ' \n [Link] \n Unmanaged=yes \n ' )
598 links
= os
. listdir ( '/sys/class/net' )
599 remove_link (* links
, protect
= True )
601 def flush_nexthops ():
602 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
603 # Hence, we cannot restore nexthops in a simple way.
604 # Let's assume there is no nexthop used in the system
605 call_quiet ( 'ip nexthop flush' )
608 # pylint: disable=global-statement
610 saved_routes
= check_output ( 'ip route show table all' )
611 print ( '### The following routes will be protected:' )
616 output
= check_output ( 'ip route show table all' )
617 for line
in output
. splitlines ():
618 if line
in saved_routes
:
620 if 'proto kernel' in line
:
622 if ' dev ' in line
and not ' dev lo ' in line
:
626 print ( '### Removing routes that did not exist when the test started.' )
628 call ( f
'ip route del {line} ' )
630 def save_routing_policy_rules ():
631 # pylint: disable=global-statement
632 global saved_ipv4_rules
, saved_ipv6_rules
634 output
= check_output ( f
'ip - {ipv} rule show' )
635 print ( f
'### The following IPv {ipv} routing policy rules will be protected:' )
639 saved_ipv4_rules
= save ( 4 )
640 saved_ipv6_rules
= save ( 6 )
642 def flush_routing_policy_rules ():
643 def flush ( ipv
, saved_rules
):
645 output
= check_output ( f
'ip - {ipv} rule show' )
646 for line
in output
. splitlines ():
647 if line
in saved_rules
:
651 print ( f
'### Removing IPv {ipv} routing policy rules that did not exist when the test started.' )
653 words
= line
. replace ( 'lookup [l3mdev-table]' , 'l3mdev' ). replace ( '[detached]' , '' ). split ()
654 priority
= words
[ 0 ]. rstrip ( ':' )
655 call ( f
'ip - {ipv} rule del priority {priority} ' + ' ' . join ( words
[ 1 :]))
657 flush ( 4 , saved_ipv4_rules
)
658 flush ( 6 , saved_ipv6_rules
)
660 def flush_fou_ports ():
661 ret
= run ( 'ip fou show' )
662 if ret
. returncode
!= 0 :
663 return # fou may not be supported
664 for line
in ret
. stdout
. splitlines ():
665 port
= line
. split ()[ 1 ]
666 call ( f
'ip fou del port {port} ' )
668 def flush_l2tp_tunnels ():
670 ret
= run ( 'ip l2tp show tunnel' )
671 if ret
. returncode
!= 0 :
672 return # l2tp may not be supported
673 for line
in ret
. stdout
. splitlines ():
675 if words
[ 0 ] == 'Tunnel' :
676 tid
= words
[ 1 ]. rstrip ( ',' )
677 call ( f
'ip l2tp del tunnel tunnel_id {tid} ' )
680 # Removing L2TP tunnel is asynchronous and slightly takes a time.
683 r
= run ( f
'ip l2tp show tunnel tunnel_id {tid} ' )
684 if r
. returncode
!= 0 or len ( r
. stdout
. rstrip ()) == 0 :
688 print ( f
'Cannot remove L2TP tunnel {tid} , ignoring.' )
691 # pylint: disable=global-statement
692 global saved_timezone
693 r
= run (* timedatectl_cmd
, 'show' , '--value' , '--property' , 'Timezone' , env
= env
)
694 if r
. returncode
== 0 :
695 saved_timezone
= r
. stdout
. rstrip ()
696 print ( f
'### Saved timezone: {saved_timezone} ' )
698 def restore_timezone ():
700 call (* timedatectl_cmd
, 'set-timezone' , f
' {saved_timezone} ' , env
= env
)
702 def read_link_attr (* args
):
703 with
open ( os
. path
. join ( '/sys/class/net' , * args
), encoding
= 'utf-8' ) as f
:
704 return f
. readline (). strip ()
706 def read_manager_state_file ():
707 with
open ( '/run/systemd/netif/state' , encoding
= 'utf-8' ) as f
:
710 def read_link_state_file ( link
):
711 ifindex
= read_link_attr ( link
, 'ifindex' )
712 path
= os
. path
. join ( '/run/systemd/netif/links' , ifindex
)
713 with
open ( path
, encoding
= 'utf-8' ) as f
:
716 def read_ip_sysctl_attr ( link
, attribute
, ipv
):
717 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'conf' , link
, attribute
), encoding
= 'utf-8' ) as f
:
718 return f
. readline (). strip ()
720 def read_ip_neigh_sysctl_attr ( link
, attribute
, ipv
):
721 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'neigh' , link
, attribute
), encoding
= 'utf-8' ) as f
:
722 return f
. readline (). strip ()
724 def read_ipv6_sysctl_attr ( link
, attribute
):
725 return read_ip_sysctl_attr ( link
, attribute
, 'ipv6' )
727 def read_ipv6_neigh_sysctl_attr ( link
, attribute
):
728 return read_ip_neigh_sysctl_attr ( link
, attribute
, 'ipv6' )
730 def read_ipv4_sysctl_attr ( link
, attribute
):
731 return read_ip_sysctl_attr ( link
, attribute
, 'ipv4' )
733 def read_mpls_sysctl_attr ( link
, attribute
):
734 return read_ip_sysctl_attr ( link
, attribute
, 'mpls' )
736 def stop_by_pid_file ( pid_file
):
737 if not os
. path
. exists ( pid_file
):
739 with
open ( pid_file
, 'r' , encoding
= 'utf-8' ) as f
:
740 pid
= f
. read (). rstrip ( ' \t\r\n \0' )
741 os
. kill ( int ( pid
), signal
. SIGTERM
)
745 print ( f
"PID {pid} is still alive, waiting..." )
748 if e
. errno
== errno
. ESRCH
:
750 print ( f
"Unexpected exception when waiting for {pid} to die: {e.errno}" )
753 def dnr_v4_instance_data ( adn
, addrs
= None , prio
= 1 , alpns
=( "dot" ,), dohpath
= None ):
755 pack
= lambda c
, w
= 1 : struct
. pack ( '>' + "_BH_I" [ w
], len ( c
)) + c
756 pyton
= lambda n
, w
= 2 : struct
. pack ( '>' + "_BH_I" [ w
], n
)
757 ipv4
= ipaddress
. IPv4Address
758 class SvcParam ( enum
. Enum
):
764 adn
= adn
. rstrip ( '.' ) + '.'
765 data
+= pack ( b
. join ( pack ( label
. encode ( 'ascii' )) for label
in adn
. split ( '.' )))
767 if not addrs
: # adn-only mode
770 data
+= pack ( b
. join ( ipv4 ( addr
). packed
for addr
in addrs
))
771 data
+= pyton ( SvcParam
. ALPN
. value
) + pack ( b
. join ( pack ( alpn
. encode ( 'ascii' )) for alpn
in alpns
), 2 )
772 if dohpath
is not None :
773 data
+= pyton ( SvcParam
. DOHPATH
. value
) + pack ( dohpath
. encode ( 'utf-8' ), 2 )
777 def dnr_v6_instance_data ( adn
, addrs
= None , prio
= 1 , alpns
=( "dot" ,), dohpath
= None ):
779 pack
= lambda c
, w
= 1 : struct
. pack ( '>' + "_BH_I" [ w
], len ( c
)) + c
780 pyton
= lambda n
, w
= 2 : struct
. pack ( '>' + "_BH_I" [ w
], n
)
781 ipv6
= ipaddress
. IPv6Address
782 class SvcParam ( enum
. Enum
):
788 adn
= adn
. rstrip ( '.' ) + '.'
789 data
+= pack ( b
. join ( pack ( label
. encode ( 'ascii' )) for label
in adn
. split ( '.' )), 2 )
791 if not addrs
: # adn-only mode
794 data
+= pack ( b
. join ( ipv6 ( addr
). packed
for addr
in addrs
), 2 )
795 data
+= pyton ( SvcParam
. ALPN
. value
) + pack ( b
. join ( pack ( alpn
. encode ( 'ascii' )) for alpn
in alpns
), 2 )
796 if dohpath
is not None :
797 data
+= pyton ( SvcParam
. DOHPATH
. value
) + pack ( dohpath
. encode ( 'utf-8' ), 2 )
801 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' ):
803 ra_mode
= f
', {ra_mode} '
809 f
'--log-facility= {dnsmasq_log_file} ' ,
810 '--log-queries=extra' ,
812 f
'--pid-file= {dnsmasq_pid_file} ' ,
813 '--conf-file=/dev/null' ,
815 f
'--interface= {interface} ' ,
816 f
'--dhcp-leasefile= {dnsmasq_lease_file} ' ,
818 f
'--dhcp-range= {ipv6_range}{ra_mode} ,2m' ,
819 f
'--dhcp-range= {ipv4_range} ,2m' ,
820 '--dhcp-option=option:mtu,1492' ,
821 f
'--dhcp-option=option:router, {ipv4_router} ' ,
824 ) + additional_options
825 check_output (* command
)
828 stop_by_pid_file ( dnsmasq_pid_file
)
829 rm_f ( dnsmasq_lease_file
)
830 rm_f ( dnsmasq_log_file
)
832 def read_dnsmasq_log_file ():
833 with
open ( dnsmasq_log_file
, encoding
= 'utf-8' ) as f
:
836 def start_isc_dhcpd ( conf_file
, ipv
, interface
= 'veth-peer' ):
837 conf_file_path
= os
. path
. join ( networkd_ci_temp_dir
, conf_file
)
838 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface} '
839 touch ( isc_dhcpd_lease_file
)
840 check_output ( isc_dhcpd_command
)
842 def stop_isc_dhcpd ():
843 stop_by_pid_file ( isc_dhcpd_pid_file
)
844 rm_f ( isc_dhcpd_lease_file
)
846 def get_dbus_link_path ( link
):
847 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
848 '/org/freedesktop/network1' , 'org.freedesktop.network1.Manager' ,
849 'GetLinkByName' , 's' , link
])
851 assert out
. startswith ( b
'io ' )
853 assert out
. endswith ( b
'"' )
855 return out
[:- 1 ]. split ( '"' )[ 1 ]
857 def get_dhcp_client_state ( link
, family
):
858 link_path
= get_dbus_link_path ( link
)
860 out
= subprocess
. check_output ([ 'busctl' , 'get-property' , 'org.freedesktop.network1' ,
861 link_path
, f
'org.freedesktop.network1.DHCPv {family} Client' , 'State' ])
862 assert out
. startswith ( b
's "' )
864 assert out
. endswith ( b
'"' )
865 return out
[ 3 :- 1 ]. decode ()
867 def get_dhcp4_client_state ( link
):
868 return get_dhcp_client_state ( link
, '4' )
870 def get_dhcp6_client_state ( link
):
871 return get_dhcp_client_state ( link
, '6' )
873 def get_link_description ( link
):
874 link_path
= get_dbus_link_path ( link
)
876 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
877 link_path
, 'org.freedesktop.network1.Link' , 'Describe' ])
878 assert out
. startswith ( b
's "' )
880 assert out
. endswith ( b
'"' )
881 json_raw
= out
[ 2 :]. decode ()
883 description
= json
. loads ( json_raw
) # Convert from escaped sequences to json
884 check_json ( description
)
885 return json
. loads ( description
) # Now parse the json
887 def start_radvd (* additional_options
, config_file
):
888 config_file_path
= os
. path
. join ( networkd_ci_temp_dir
, 'radvd' , config_file
)
891 f
'--pidfile= {radvd_pid_file} ' ,
892 f
'--config= {config_file_path} ' ,
893 '--logmethod=stderr' ,
894 ) + additional_options
895 check_output (* command
)
898 stop_by_pid_file ( radvd_pid_file
)
900 def radvd_check_config ( config_file
):
901 if not shutil
. which ( 'radvd' ):
902 print ( 'radvd is not installed, assuming the config check failed' )
905 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
906 # set up (one instance is @unittest.skipX())
907 config_file_path
= os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf/radvd' , config_file
)
908 return call ( f
'radvd --config= {config_file_path} --configtest' ) == 0
910 def networkd_invocation_id ():
911 return check_output ( 'systemctl show --value -p InvocationID systemd-networkd.service' )
914 return check_output ( 'systemctl show --value -p MainPID systemd-networkd.service' )
916 def read_networkd_log ( invocation_id
= None , since
= None ):
917 if not invocation_id
:
918 invocation_id
= networkd_invocation_id ()
922 '--output=short-monotonic' ,
923 f
'_SYSTEMD_INVOCATION_ID= {invocation_id} ' ,
926 command
. append ( f
'--since= {since} ' )
927 check_output ( 'journalctl --sync' )
928 return check_output (* command
)
930 def networkd_is_failed ():
931 return call_quiet ( 'systemctl is-failed -q systemd-networkd.service' ) != 1
933 def stop_networkd ( show_logs
= True , check_failed
= True ):
935 show_logs
= show_logs
and show_journal
937 invocation_id
= networkd_invocation_id ()
940 check_output ( 'systemctl stop systemd-networkd.socket' )
941 check_output ( 'systemctl stop systemd-networkd.service' )
943 call ( 'systemctl stop systemd-networkd.socket' )
944 call ( 'systemctl stop systemd-networkd.service' )
947 print ( read_networkd_log ( invocation_id
))
949 # Check if networkd exits cleanly.
951 assert not networkd_is_failed ()
953 def start_networkd ():
954 check_output ( 'systemctl start systemd-networkd' )
955 invocation_id
= networkd_invocation_id ()
957 print ( f
'Started systemd-networkd.service: PID= {pid} , Invocation ID= {invocation_id} ' )
959 def restart_networkd ( show_logs
= True ):
961 show_logs
= show_logs
and show_journal
963 invocation_id
= networkd_invocation_id ()
964 check_output ( 'systemctl restart systemd-networkd.service' )
966 print ( read_networkd_log ( invocation_id
))
968 invocation_id
= networkd_invocation_id ()
970 print ( f
'Restarted systemd-networkd.service: PID= {pid} , Invocation ID= {invocation_id} ' )
973 return int ( check_output ( 'systemctl show --value -p MainPID systemd-networkd.service' ))
975 def networkctl (* args
):
976 # Do not call networkctl if networkd is in failed state.
977 # Otherwise, networkd may be restarted and we may get wrong results.
978 assert not networkd_is_failed ()
979 return check_output (*( networkctl_cmd
+ list ( args
)), env
= env
)
981 def networkctl_status (* args
):
982 return networkctl ( '-n' , '0' , 'status' , * args
)
984 def networkctl_json (* args
):
985 return networkctl ( '--json=short' , 'status' , * args
)
987 def networkctl_reconfigure (* links
):
988 networkctl ( 'reconfigure' , * links
)
990 def networkctl_reload ():
993 def resolvectl (* args
):
994 return check_output (*( resolvectl_cmd
+ list ( args
)), env
= env
)
996 def timedatectl (* args
):
997 return check_output (*( timedatectl_cmd
+ list ( args
)), env
= env
)
1000 return check_output (*( udevadm_cmd
+ list ( args
)))
1002 def udevadm_reload ():
1003 udevadm ( 'control' , '--reload' )
1005 def udevadm_trigger (* args
, action
= 'add' ):
1006 udevadm ( 'trigger' , '--settle' , f
'--action= {action} ' , * args
)
1009 # Protect existing links
1010 unmanage_existing_links ()
1012 # We usually show something in each test. So, let's break line to make the title of a test and output
1013 # from the test mixed. Then, flush stream buffer and journals.
1016 check_output ( 'journalctl --sync' )
1018 def tear_down_common ():
1019 # 1. stop DHCP/RA servers
1025 call_quiet ( 'rmmod netdevsim' )
1026 call_quiet ( 'rmmod sch_teql' )
1028 # 3. remove network namespace
1029 call_quiet ( 'ip netns del ns99' )
1032 flush_l2tp_tunnels ()
1036 stop_networkd ( check_failed
= False )
1039 clear_network_units ()
1040 clear_networkd_conf_dropins ()
1041 clear_networkd_state_files ()
1046 flush_routing_policy_rules ()
1049 # 8. flush stream buffer and journals to make not any output from the test with the next one
1051 check_output ( 'journalctl --sync' )
1053 # 9. check the status of networkd
1054 assert not networkd_is_failed ()
1057 rm_rf ( networkd_ci_temp_dir
)
1058 cp_r ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf' ), networkd_ci_temp_dir
)
1060 clear_network_units ()
1061 clear_networkd_conf_dropins ()
1062 clear_networkd_state_files ()
1065 setup_systemd_udev_rules ()
1066 copy_udev_rule ( '00-debug-net.rules' )
1068 # Save current state
1070 save_existing_links ()
1072 save_routing_policy_rules ()
1075 setup_system_units ()
1077 def tearDownModule ():
1078 rm_rf ( networkd_ci_temp_dir
)
1080 clear_network_units ()
1081 clear_networkd_conf_dropins ()
1082 clear_networkd_state_files ()
1086 clear_system_units ()
1087 restore_active_units ()
1089 # Flush stream buffer and journals before showing the test summary.
1091 check_output ( 'journalctl --sync' )
1094 # pylint: disable=no-member
1096 def check_link_exists ( self
, * link
, expected
= True ):
1098 self
. assertTrue ( link_exists (* link
))
1100 self
. assertFalse ( link_exists (* link
))
1102 def check_link_attr ( self
, * args
):
1103 self
. assertEqual ( read_link_attr (* args
[:- 1 ]), args
[- 1 ])
1105 def check_bridge_port_attr ( self
, master
, port
, attribute
, expected
, allow_enoent
= False ):
1106 path
= os
. path
. join ( '/sys/devices/virtual/net' , master
, 'lower_' + port
, 'brport' , attribute
)
1107 if allow_enoent
and not os
. path
. exists ( path
):
1109 with
open ( path
, encoding
= 'utf-8' ) as f
:
1110 self
. assertEqual ( f
. readline (). strip (), expected
)
1112 def check_ipv4_sysctl_attr ( self
, link
, attribute
, expected
):
1113 self
. assertEqual ( read_ipv4_sysctl_attr ( link
, attribute
), expected
)
1115 def check_ipv6_sysctl_attr ( self
, link
, attribute
, expected
):
1116 self
. assertEqual ( read_ipv6_sysctl_attr ( link
, attribute
), expected
)
1118 def check_ipv6_neigh_sysctl_attr ( self
, link
, attribute
, expected
):
1119 self
. assertEqual ( read_ipv6_neigh_sysctl_attr ( link
, attribute
), expected
)
1121 def check_mpls_sysctl_attr ( self
, link
, attribute
, expected
):
1122 self
. assertEqual ( read_mpls_sysctl_attr ( link
, attribute
), expected
)
1124 def wait_links ( self
, * links
, trial
= 40 ):
1125 for _
in range ( trial
):
1126 if link_exists (* links
):
1130 self
. fail ( 'Timed out waiting for all links to be created: ' + ', ' . join ( list ( links
)))
1132 def wait_activated ( self
, link
, state
= 'down' , trial
= 40 ):
1133 # wait for the interface is activated.
1134 needle
= f
' {link} : Bringing link {state} '
1135 flag
= state
. upper ()
1136 self
. wait_links ( link
, trial
= trial
)
1137 self
. check_networkd_log ( needle
, trial
= trial
)
1138 for _
in range ( trial
):
1139 if flag
in check_output ( f
'ip link show {link} ' ):
1143 self
. fail ( f
'Timed out waiting for {link} activated.' )
1145 def wait_operstate ( self
, link
, operstate
= 'degraded' , setup_state
= 'configured' , setup_timeout
= 5 , fail_assert
= True ):
1146 """Wait for the link to reach the specified operstate and/or setup state.
1148 Specify None or '' for either operstate or setup_state to ignore that state.
1149 This will recheck until the state conditions are met or the timeout expires.
1151 If the link successfully matches the requested state, this returns True.
1152 If this times out waiting for the link to match, the behavior depends on the
1153 'fail_assert' parameter; if True, this causes a test assertion failure,
1154 otherwise this returns False. The default is to cause assertion failure.
1156 Note that this function matches on *exactly* the given operstate and setup_state.
1157 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1162 setup_state
= r
'\S+'
1164 for _
in range ( setup_timeout
* 2 ):
1165 if not link_exists ( link
):
1168 output
= networkctl_status ( link
)
1169 if re
. search ( rf
'(?m)^\s*State:\s+ {operstate} \s+\( {setup_state} \)\s*$' , output
):
1174 self
. fail ( f
'Timed out waiting for {link} to reach state {operstate} / {setup_state} ' )
1177 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 ):
1178 """Wait for the links to reach the specified operstate and/or setup state.
1180 This is similar to wait_operstate() but can be used for multiple links,
1181 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1182 The operstate should be specified in the link name, like 'eth0:degraded'.
1183 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1185 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1186 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1188 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1189 If this is set, no setup_state checks are done.
1191 Set 'bool_dns' to True to wait for DNS servers to be accessible.
1193 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1194 This is applied only for the operational state 'degraded' or above.
1196 Note that this function waits for the links to reach *or exceed* the given operstate.
1197 However, the setup_state, if specified, must be matched *exactly*.
1199 This returns if the links reached the requested operstate/setup_state; otherwise it
1200 raises CalledProcessError or fails test assertion.
1202 args
= wait_online_cmd
+ [ f
'--timeout= {timeout} ' ] + [ f
'--interface= {link} ' for link
in links_with_operstate
] + [ f
'--ignore= {link} ' for link
in protected_links
]
1212 check_output (* args
, env
= wait_online_env
)
1213 except subprocess
. CalledProcessError
:
1214 if networkd_is_failed ():
1215 print ( '!!!!! systemd-networkd.service is failed !!!!!' )
1216 call ( 'systemctl status systemd-networkd.service' )
1218 # show detailed status on failure
1219 for link
in links_with_operstate
:
1220 name
= link
. split ( ':' )[ 0 ]
1221 if link_exists ( name
):
1222 print ( networkctl_status ( name
))
1224 print ( f
'Interface {name} not found.' )
1226 if not bool_any
and setup_state
:
1227 for link
in links_with_operstate
:
1228 self
. wait_operstate ( link
. split ( ':' )[ 0 ], None , setup_state
, setup_timeout
)
1230 def wait_address ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1231 for _
in range ( timeout_sec
* 2 ):
1232 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1233 if re
. search ( address_regex
, output
) and 'tentative' not in output
:
1237 self
. assertRegex ( output
, address_regex
)
1239 def wait_address_dropped ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1240 for _
in range ( timeout_sec
* 2 ):
1241 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1242 if not re
. search ( address_regex
, output
):
1246 self
. assertNotRegex ( output
, address_regex
)
1248 def wait_route ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1249 for _
in range ( timeout_sec
* 2 ):
1250 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1251 if re
. search ( route_regex
, output
):
1255 self
. assertRegex ( output
, route_regex
)
1257 def wait_route_dropped ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1258 for _
in range ( timeout_sec
* 2 ):
1259 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1260 if not re
. search ( route_regex
, output
):
1264 self
. assertNotRegex ( output
, route_regex
)
1266 def check_netlabel ( self
, interface
, address
, label
= 'system_u:object_r:root_t:s0' ):
1267 if not shutil
. which ( 'selinuxenabled' ):
1268 print ( '## Checking NetLabel skipped: selinuxenabled command not found.' )
1269 elif call_quiet ( 'selinuxenabled' ) != 0 :
1270 print ( '## Checking NetLabel skipped: SELinux disabled.' )
1271 elif not shutil
. which ( 'netlabelctl' ): # not packaged by all distros
1272 print ( '## Checking NetLabel skipped: netlabelctl command not found.' )
1274 output
= check_output ( 'netlabelctl unlbl list' )
1276 self
. assertRegex ( output
, f
'interface: {interface} ,address: {address} ,label:" {label} "' )
1278 def setup_nftset ( self
, filter_name
, filter_type
, flags
= '' ):
1279 if not shutil
. which ( 'nft' ):
1280 print ( '## Setting up NFT sets skipped: nft command not found.' )
1282 if call ( f
'nft add table inet sd_test' ) != 0 :
1283 print ( '## Setting up NFT table failed.' )
1285 if call ( f
'nft add set inet sd_test {filter_name} {{ type {filter_type} ; {flags} }}' ) != 0 :
1286 print ( '## Setting up NFT sets failed.' )
1289 def teardown_nftset ( self
, * filters
):
1290 if not shutil
. which ( 'nft' ):
1291 print ( '## Tearing down NFT sets skipped: nft command not found.' )
1293 for filter_name
in filters
:
1294 if call ( f
'nft delete set inet sd_test {filter_name} ' ) != 0 :
1295 print ( '## Tearing down NFT sets failed.' )
1297 if call ( f
'nft delete table inet sd_test' ) != 0 :
1298 print ( '## Tearing down NFT table failed.' )
1301 def check_nftset ( self
, filter_name
, contents
):
1302 if not shutil
. which ( 'nft' ):
1303 print ( '## Checking NFT sets skipped: nft command not found.' )
1305 output
= check_output ( f
'nft list set inet sd_test {filter_name} ' )
1307 self
. assertRegex ( output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*' )
1309 def check_networkd_log ( self
, contents
, since
= None , trial
= 20 ):
1310 for _
in range ( trial
):
1311 if contents
in read_networkd_log ( since
= since
):
1315 self
. fail ( f
'" {contents} " not found in journal.' )
1317 def networkctl_check_unit ( self
, ifname
, netdev_file
= None , network_file
= None , link_file
= None ):
1318 output
= networkctl_status ( ifname
)
1321 self
. assertRegex ( output
, rf
'NetDev File: .*/ {netdev_file} \.netdev' )
1323 self
. assertNotIn ( 'NetDev File:' , output
)
1325 self
. assertRegex ( output
, rf
'Network File: .*/ {network_file} \.network' )
1327 self
. assertIn ( 'Network File: n/a' , output
)
1329 self
. assertRegex ( output
, rf
'Link File: .*/ {link_file} \.link' )
1331 class NetworkctlTests ( unittest
. TestCase
, Utilities
):
1339 @expectedFailureIfAlternativeNameIsNotAvailable ()
1340 def test_altname ( self
):
1341 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' , '12-dummy.netdev' , '12-dummy.link' )
1343 self
. wait_online ( 'dummy98:degraded' )
1345 output
= networkctl_status ( 'dummy98' )
1346 self
. assertRegex ( output
, 'hogehogehogehogehogehoge' )
1348 @expectedFailureIfAlternativeNameIsNotAvailable ()
1349 def test_rename_to_altname ( self
):
1350 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' ,
1351 '12-dummy.netdev' , '12-dummy-rename-to-altname.link' )
1353 self
. wait_online ( 'dummyalt:degraded' )
1355 output
= networkctl_status ( 'dummyalt' )
1356 self
. assertIn ( 'hogehogehogehogehogehoge' , output
)
1357 self
. assertNotIn ( 'dummy98' , output
)
1359 def test_reconfigure ( self
):
1360 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1362 self
. wait_online ( 'dummy98:routable' )
1364 output
= check_output ( 'ip -4 address show dev dummy98' )
1366 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1367 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1368 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1370 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
1371 check_output ( 'ip address del 10.1.2.4/16 dev dummy98' )
1372 check_output ( 'ip address del 10.2.2.4/16 dev dummy98' )
1374 networkctl_reconfigure ( 'dummy98' )
1375 self
. wait_online ( 'dummy98:routable' )
1377 output
= check_output ( 'ip -4 address show dev dummy98' )
1379 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1380 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1381 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1383 remove_network_unit ( '25-address-static.network' )
1386 self
. wait_operstate ( 'dummy98' , 'degraded' , setup_state
= 'unmanaged' )
1388 output
= check_output ( 'ip -4 address show dev dummy98' )
1390 self
. assertNotIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1391 self
. assertNotIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1392 self
. assertNotIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1394 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
1396 self
. wait_online ( 'dummy98:routable' )
1398 output
= check_output ( 'ip -4 address show dev dummy98' )
1400 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1401 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1402 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1404 def test_renew ( self
):
1406 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
1407 output
= networkctl_status ( 'veth99' )
1409 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
1410 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
1411 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
1412 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
1414 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
1417 check_json ( networkctl_json ( '--lines=0' , '--stats' , '--all' , '--full' ))
1419 for verb
in [ 'renew' , 'forcerenew' ]:
1420 networkctl ( verb
, 'veth99' )
1422 networkctl ( verb
, 'veth99' , 'veth99' , 'veth99' )
1425 def test_up_down ( self
):
1426 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1428 self
. wait_online ( 'dummy98:routable' )
1430 networkctl ( 'down' , 'dummy98' )
1431 self
. wait_online ( 'dummy98:off' )
1432 networkctl ( 'up' , 'dummy98' )
1433 self
. wait_online ( 'dummy98:routable' )
1434 networkctl ( 'down' , 'dummy98' , 'dummy98' , 'dummy98' )
1435 self
. wait_online ( 'dummy98:off' )
1436 networkctl ( 'up' , 'dummy98' , 'dummy98' , 'dummy98' )
1437 self
. wait_online ( 'dummy98:routable' )
1439 def test_reload ( self
):
1442 copy_network_unit ( '11-dummy.netdev' )
1444 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'unmanaged' )
1446 copy_network_unit ( '11-dummy.network' )
1448 self
. wait_online ( 'test1:degraded' )
1450 remove_network_unit ( '11-dummy.network' )
1452 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1454 remove_network_unit ( '11-dummy.netdev' )
1456 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1458 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1460 self
. wait_operstate ( 'test1' , 'degraded' )
1462 def test_glob ( self
):
1463 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1466 self
. wait_online ( 'test1:degraded' )
1468 output
= networkctl ( 'list' )
1469 self
. assertRegex ( output
, '1 lo ' )
1470 self
. assertRegex ( output
, 'test1' )
1472 output
= networkctl ( 'list' , 'test1' )
1473 self
. assertNotRegex ( output
, '1 lo ' )
1474 self
. assertRegex ( output
, 'test1' )
1476 output
= networkctl ( 'list' , 'te*' )
1477 self
. assertNotRegex ( output
, '1 lo ' )
1478 self
. assertRegex ( output
, 'test1' )
1480 output
= networkctl_status ( 'te*' )
1481 self
. assertNotRegex ( output
, '1: lo ' )
1482 self
. assertRegex ( output
, 'test1' )
1484 output
= networkctl_status ( 'tes[a-z][0-9]' )
1485 self
. assertNotRegex ( output
, '1: lo ' )
1486 self
. assertRegex ( output
, 'test1' )
1489 copy_network_unit ( '11-dummy-mtu.netdev' , '11-dummy.network' )
1492 self
. wait_online ( 'test1:degraded' )
1494 output
= networkctl_status ( 'test1' )
1495 self
. assertRegex ( output
, 'MTU: 1600' )
1497 def test_type ( self
):
1498 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1500 self
. wait_online ( 'test1:degraded' )
1502 output
= networkctl_status ( 'test1' )
1504 self
. assertRegex ( output
, 'Type: ether' )
1506 output
= networkctl_status ( 'lo' )
1508 self
. assertRegex ( output
, 'Type: loopback' )
1510 def test_unit_file ( self
):
1511 copy_network_unit ( '11-test-unit-file.netdev' , '11-test-unit-file.network' , '11-test-unit-file.link' )
1513 self
. wait_online ( 'test1:degraded' )
1515 output
= networkctl_status ( 'test1' )
1517 self
. assertIn ( 'Link File: /run/systemd/network/11-test-unit-file.link' , output
)
1518 self
. assertIn ( '/run/systemd/network/11-test-unit-file.link.d/dropin.conf' , output
)
1519 self
. assertIn ( 'Network File: /run/systemd/network/11-test-unit-file.network' , output
)
1520 self
. assertIn ( '/run/systemd/network/11-test-unit-file.network.d/dropin.conf' , output
)
1522 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).' )
1524 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1525 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1526 # Let's reprocess the interface and drop the property.
1527 udevadm_trigger ( '/sys/class/net/lo' )
1528 output
= networkctl_status ( 'lo' )
1530 self
. assertIn ( 'Link File: n/a' , output
)
1531 self
. assertIn ( 'Network File: n/a' , output
)
1533 def test_delete_links ( self
):
1534 copy_network_unit ( '11-dummy.netdev' , '25-veth.netdev' )
1536 self
. wait_links ( 'test1' , 'veth99' , 'veth-peer' )
1537 networkctl ( 'delete' , 'test1' , 'veth99' )
1538 self
. check_link_exists ( 'test1' , 'veth99' , 'veth-peer' , expected
= False )
1540 def test_label ( self
):
1543 class NetworkdMatchTests ( unittest
. TestCase
, Utilities
):
1551 @expectedFailureIfAlternativeNameIsNotAvailable ()
1552 def test_match ( self
):
1553 copy_network_unit ( '12-dummy-mac.netdev' ,
1554 '12-dummy-match-mac-01.network' ,
1555 '12-dummy-match-mac-02.network' ,
1556 '12-dummy-match-renamed.network' ,
1557 '12-dummy-match-altname.network' ,
1558 '12-dummy-altname.link' )
1561 self
. wait_online ( 'dummy98:routable' )
1562 output
= networkctl_status ( 'dummy98' )
1563 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-01.network' , output
)
1564 output
= check_output ( 'ip -4 address show dev dummy98' )
1565 self
. assertIn ( '10.0.0.1/16' , output
)
1567 check_output ( 'ip link set dev dummy98 down' )
1568 check_output ( 'ip link set dev dummy98 address 12:34:56:78:9a:02' )
1570 self
. wait_address ( 'dummy98' , '10.0.0.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1571 self
. wait_online ( 'dummy98:routable' )
1572 output
= networkctl_status ( 'dummy98' )
1573 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-02.network' , output
)
1575 check_output ( 'ip link set dev dummy98 down' )
1576 check_output ( 'ip link set dev dummy98 name dummy98-1' )
1578 self
. wait_address ( 'dummy98-1' , '10.0.1.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1579 self
. wait_online ( 'dummy98-1:routable' )
1580 output
= networkctl_status ( 'dummy98-1' )
1581 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-renamed.network' , output
)
1583 check_output ( 'ip link set dev dummy98-1 down' )
1584 check_output ( 'ip link set dev dummy98-1 name dummy98-2' )
1585 udevadm_trigger ( '/sys/class/net/dummy98-2' )
1587 self
. wait_address ( 'dummy98-2' , '10.0.2.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1588 self
. wait_online ( 'dummy98-2:routable' )
1589 output
= networkctl_status ( 'dummy98-2' )
1590 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-altname.network' , output
)
1592 def test_match_udev_property ( self
):
1593 copy_network_unit ( '12-dummy.netdev' , '13-not-match-udev-property.network' , '14-match-udev-property.network' )
1595 self
. wait_online ( 'dummy98:routable' )
1597 output
= networkctl_status ( 'dummy98' )
1599 self
. assertRegex ( output
, 'Network File: /run/systemd/network/14-match-udev-property' )
1601 class WaitOnlineTests ( unittest
. TestCase
, Utilities
):
1609 def test_wait_online_any ( self
):
1610 copy_network_unit ( '25-bridge.netdev' , '25-bridge.network' , '11-dummy.netdev' , '11-dummy.network' )
1613 self
. wait_online ( 'bridge99' , 'test1:degraded' , bool_any
= True )
1615 self
. wait_operstate ( 'bridge99' , '(off|no-carrier)' , setup_state
= 'configuring' )
1616 self
. wait_operstate ( 'test1' , 'degraded' )
1618 def do_test_wait_online_dns (
1622 expect_timeout
= False ,
1623 network_dropin
= None ,
1625 global wait_online_env
1627 if network_dropin
is not None :
1628 network_dropin_path
= os
. path
. join (
1630 '25-dhcp-client-use-dns-ipv4.network.d/test.conf'
1632 mkdir_p ( os
. path
. dirname ( network_dropin_path
))
1633 with
open ( network_dropin_path
, 'w' ) as f
:
1634 f
. write ( network_dropin
)
1638 '25-dhcp-client-use-dns-ipv4.network' ,
1639 '25-dhcp-server.network'
1642 self
. wait_online ( 'veth-peer:routable' )
1644 # Unless given, clear global DNS configuration
1645 resolved_dropin
= '/run/systemd/resolved.conf.d/global-dns.conf'
1646 mkdir_p ( os
. path
. dirname ( resolved_dropin
))
1647 with
open ( resolved_dropin
, 'w' ) as f
:
1650 f
'DNS= {global_dns} \n '
1651 f
'FallbackDNS= {fallback_dns} \n '
1653 self
. addCleanup ( os
. remove
, resolved_dropin
)
1654 check_output ( 'systemctl reload systemd-resolved' )
1657 wait_online_env_copy
= wait_online_env
. copy ()
1659 wait_online_env
[ 'SYSTEMD_LOG_LEVEL' ] = 'debug'
1660 wait_online_env
[ 'SYSTEMD_LOG_TARGET' ] = 'console'
1662 self
. wait_online ( 'veth99:routable' , bool_dns
= True )
1665 # The above should have thrown an exception.
1667 'Expected systemd-networkd-wait-online to time out'
1670 except subprocess
. CalledProcessError
as e
:
1674 f
'veth99: No link-specific DNS server is accessible' ,
1675 f
'Missing expected log message: \n {e.output}'
1679 f
'Command timed out: \n {e.output}'
1682 wait_online_env
= wait_online_env_copy
1684 def test_wait_online_dns ( self
):
1685 ''' test systemd-networkd-wait-online with --dns '''
1686 self
. do_test_wait_online_dns ()
1688 def test_wait_online_dns_global ( self
):
1690 test systemd-networkd-wait-online with --dns, expect pass due to global DNS
1693 # Set UseDNS=no, and allow global DNS to be used.
1694 self
. do_test_wait_online_dns (
1695 global_dns
= '192.168.5.1' ,
1702 def test_wait_online_dns_expect_timeout ( self
):
1703 ''' test systemd-networkd-wait-online with --dns, and expect timeout '''
1705 # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
1706 self
. do_test_wait_online_dns (
1707 expect_timeout
= True ,
1710 'DNSDefaultRoute=yes \n '
1716 def test_wait_online_dns_expect_timeout_global ( self
):
1718 test systemd-networkd-wait-online with --dns, and expect timeout
1722 # Configure Domains=~., and expect timeout despite global DNS servers
1724 self
. do_test_wait_online_dns (
1725 expect_timeout
= True ,
1726 global_dns
= '192.168.5.1' ,
1736 class NetworkdNetDevTests ( unittest
. TestCase
, Utilities
):
1744 def test_dropin_and_name_conflict ( self
):
1745 copy_network_unit ( '10-dropin-test.netdev' , '15-name-conflict-test.netdev' )
1748 self
. wait_online ( 'dropin-test:off' , setup_state
= 'unmanaged' )
1750 output
= check_output ( 'ip link show dropin-test' )
1752 self
. assertRegex ( output
, '00:50:56:c0:00:28' )
1754 @expectedFailureIfModuleIsNotAvailable ( 'bareudp' )
1755 def test_bareudp ( self
):
1756 copy_network_unit ( '25-bareudp.netdev' , '26-netdev-link-local-addressing-yes.network' )
1759 self
. wait_online ( 'bareudp99:degraded' )
1760 self
. networkctl_check_unit ( 'bareudp99' , '25-bareudp' , '26-netdev-link-local-addressing-yes' )
1762 output
= check_output ( 'ip -d link show bareudp99' )
1764 self
. assertRegex ( output
, 'dstport 1000 ' )
1765 self
. assertRegex ( output
, 'ethertype ip ' )
1766 self
. assertRegex ( output
, 'srcportmin 1001 ' )
1768 touch_network_unit ( '25-bareudp.netdev' , '26-netdev-link-local-addressing-yes.network' )
1770 self
. wait_online ( 'bareudp99:degraded' )
1772 @expectedFailureIfModuleIsNotAvailable ( 'batman-adv' )
1773 def test_batadv ( self
):
1774 copy_network_unit ( '25-batadv.netdev' , '26-netdev-link-local-addressing-yes.network' )
1777 self
. wait_online ( 'batadv99:degraded' )
1778 self
. networkctl_check_unit ( 'batadv99' , '25-batadv' , '26-netdev-link-local-addressing-yes' )
1780 output
= check_output ( 'ip -d link show batadv99' )
1782 self
. assertRegex ( output
, 'batadv' )
1784 touch_network_unit ( '25-batadv.netdev' , '26-netdev-link-local-addressing-yes.network' )
1786 self
. wait_online ( 'batadv99:degraded' )
1788 def test_bridge ( self
):
1789 copy_network_unit ( '25-bridge.netdev' , '25-bridge-configure-without-carrier.network' )
1792 self
. wait_online ( 'bridge99:no-carrier' )
1793 self
. networkctl_check_unit ( 'bridge99' , '25-bridge' , '25-bridge-configure-without-carrier' )
1795 tick
= os
. sysconf ( 'SC_CLK_TCK' )
1796 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'hello_time' )) / tick
))
1797 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'max_age' )) / tick
))
1798 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'forward_delay' )) / tick
))
1799 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'ageing_time' )) / tick
))
1800 self
. assertEqual ( 9 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'priority' )))
1801 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_querier' )))
1802 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_snooping' )))
1803 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'stp_state' )))
1804 self
. assertEqual ( 3 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_igmp_version' )))
1805 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'no_linklocal_learn' )))
1807 output
= networkctl_status ( 'bridge99' )
1809 self
. assertRegex ( output
, 'Priority: 9' )
1810 self
. assertRegex ( output
, 'STP: yes' )
1811 self
. assertRegex ( output
, 'Multicast IGMP Version: 3' )
1812 if 'FDB Max Learned' in output
:
1813 self
. assertRegex ( output
, 'FDB Max Learned: 4' )
1815 output
= check_output ( 'ip -d link show bridge99' )
1817 self
. assertIn ( 'vlan_filtering 1 ' , output
)
1818 self
. assertIn ( 'vlan_protocol 802.1ad ' , output
)
1819 self
. assertIn ( 'vlan_default_pvid 9 ' , output
)
1820 if 'fdb_max_learned' in output
:
1821 self
. assertIn ( 'fdb_max_learned 4 ' , output
)
1823 def test_bond ( self
):
1824 copy_network_unit ( '25-bond.netdev' , '25-bond-balanced-tlb.netdev' , '25-bond-property.netdev' )
1827 self
. wait_online ( 'bond99:off' , 'bond98:off' , 'bond97:off' , setup_state
= 'unmanaged' )
1828 self
. networkctl_check_unit ( 'bond99' , '25-bond' )
1829 self
. networkctl_check_unit ( 'bond98' , '25-bond-balanced-tlb' )
1830 self
. networkctl_check_unit ( 'bond97' , '25-bond-property' )
1832 self
. check_link_attr ( 'bond99' , 'bonding' , 'mode' , '802.3ad 4' )
1833 self
. check_link_attr ( 'bond99' , 'bonding' , 'xmit_hash_policy' , 'layer3+4 1' )
1834 self
. check_link_attr ( 'bond99' , 'bonding' , 'miimon' , '1000' )
1835 self
. check_link_attr ( 'bond99' , 'bonding' , 'lacp_rate' , 'fast 1' )
1836 self
. check_link_attr ( 'bond99' , 'bonding' , 'updelay' , '2000' )
1837 self
. check_link_attr ( 'bond99' , 'bonding' , 'downdelay' , '2000' )
1838 self
. check_link_attr ( 'bond99' , 'bonding' , 'resend_igmp' , '4' )
1839 self
. check_link_attr ( 'bond99' , 'bonding' , 'min_links' , '1' )
1840 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_sys_prio' , '1218' )
1841 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_user_port_key' , '811' )
1842 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_system' , '00:11:22:33:44:55' )
1844 self
. check_link_attr ( 'bond98' , 'bonding' , 'mode' , 'balance-tlb 5' )
1845 self
. check_link_attr ( 'bond98' , 'bonding' , 'tlb_dynamic_lb' , '1' )
1847 output
= networkctl_status ( 'bond99' )
1849 self
. assertIn ( 'Mode: 802.3ad' , output
)
1850 self
. assertIn ( 'Miimon: 1s' , output
)
1851 self
. assertIn ( 'Updelay: 2s' , output
)
1852 self
. assertIn ( 'Downdelay: 2s' , output
)
1854 output
= networkctl_status ( 'bond98' )
1856 self
. assertIn ( 'Mode: balance-tlb' , output
)
1858 output
= networkctl_status ( 'bond97' )
1861 self
. check_link_attr ( 'bond97' , 'bonding' , 'arp_missed_max' , '10' )
1862 self
. check_link_attr ( 'bond97' , 'bonding' , 'peer_notif_delay' , '300000' )
1864 def check_vlan ( self
, id , flags
):
1865 self
. wait_online ( 'test1:degraded' , 'vlan99:routable' )
1866 self
. networkctl_check_unit ( 'vlan99' , '21-vlan' , '21-vlan' )
1867 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '21-vlan-test1' )
1869 output
= check_output ( 'ip -d link show test1' )
1871 self
. assertRegex ( output
, ' mtu 2000 ' )
1873 output
= check_output ( 'ip -d link show vlan99' )
1875 self
. assertIn ( ' mtu 2000 ' , output
)
1877 self
. assertIn ( 'REORDER_HDR' , output
)
1878 self
. assertIn ( 'LOOSE_BINDING' , output
)
1879 self
. assertIn ( 'GVRP' , output
)
1880 self
. assertIn ( 'MVRP' , output
)
1882 self
. assertNotIn ( 'REORDER_HDR' , output
)
1883 self
. assertNotIn ( 'LOOSE_BINDING' , output
)
1884 self
. assertNotIn ( 'GVRP' , output
)
1885 self
. assertNotIn ( 'MVRP' , output
)
1886 self
. assertIn ( f
' id {id} ' , output
)
1887 self
. assertIn ( 'ingress-qos-map { 4:100 7:13 }' , output
)
1888 self
. assertIn ( 'egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }' , output
)
1890 output
= check_output ( 'ip -4 address show dev test1' )
1892 self
. assertRegex ( output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1' )
1893 self
. assertRegex ( output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1' )
1895 output
= check_output ( 'ip -4 address show dev vlan99' )
1897 self
. assertRegex ( output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99' )
1899 def test_vlan ( self
):
1900 copy_network_unit ( '21-vlan.netdev' , '11-dummy.netdev' ,
1901 '21-vlan.network' , '21-vlan-test1.network' )
1903 self
. check_vlan ( id = 99 , flags
= True )
1905 # Test for reloading .netdev file. See issue #34907.
1906 with
open ( os
. path
. join ( network_unit_dir
, '21-vlan.netdev.d/override.conf' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1907 f
. write ( '[VLAN] \n Id=42 \n ' )
1909 # VLAN ID cannot be changed, so we need to remove the existing netdev.
1910 check_output ( "ip link del vlan99" )
1912 self
. check_vlan ( id = 42 , flags
= True )
1914 with
open ( os
. path
. join ( network_unit_dir
, '21-vlan.netdev.d/override.conf' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1919 'ReorderHeader=no \n ' )
1921 # flags can be changed, hence it is not necessary to remove the existing netdev.
1923 self
. check_vlan ( id = 42 , flags
= False )
1925 def test_vlan_on_bond ( self
):
1926 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1927 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1929 copy_network_unit ( '21-bond-802.3ad.netdev' , '21-bond-802.3ad.network' ,
1930 '21-vlan-on-bond.netdev' , '21-vlan-on-bond.network' )
1932 self
. wait_online ( 'bond99:off' )
1933 self
. wait_operstate ( 'vlan99' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 10 )
1934 self
. networkctl_check_unit ( 'vlan99' , '21-vlan-on-bond' , '21-vlan-on-bond' )
1935 self
. networkctl_check_unit ( 'bond99' , '21-bond-802.3ad' , '21-bond-802.3ad' )
1937 self
. check_networkd_log ( 'vlan99: Could not bring up interface, ignoring: Network is down' )
1939 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '21-dummy-bond-slave.network' )
1941 self
. wait_online ( 'test1:enslaved' , 'dummy98:enslaved' , 'bond99:carrier' , 'vlan99:routable' )
1943 def test_macvtap ( self
):
1945 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1951 print ( f
'### test_macvtap(mode= {mode} )' )
1952 with self
. subTest ( mode
= mode
):
1953 copy_network_unit ( '21-macvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1954 '11-dummy.netdev' , '25-macvtap.network' )
1955 with
open ( os
. path
. join ( network_unit_dir
, '21-macvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1956 f
. write ( '[MACVTAP] \n Mode=' + mode
)
1959 self
. wait_online ( 'macvtap99:degraded' ,
1960 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1961 self
. networkctl_check_unit ( 'macvtap99' , '21-macvtap' , '26-netdev-link-local-addressing-yes' )
1962 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-macvtap' )
1964 output
= check_output ( 'ip -d link show macvtap99' )
1966 self
. assertRegex ( output
, 'macvtap mode ' + mode
+ ' ' )
1968 touch_network_unit ( '21-macvtap.netdev' )
1970 self
. wait_online ( 'macvtap99:degraded' ,
1971 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1973 @expectedFailureIfModuleIsNotAvailable ( 'macvlan' )
1974 def test_macvlan ( self
):
1976 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1982 print ( f
'### test_macvlan(mode= {mode} )' )
1983 with self
. subTest ( mode
= mode
):
1984 copy_network_unit ( '21-macvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1985 '11-dummy.netdev' , '25-macvlan.network' )
1986 with
open ( os
. path
. join ( network_unit_dir
, '21-macvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1987 f
. write ( '[MACVLAN] \n Mode=' + mode
)
1990 self
. wait_online ( 'macvlan99:degraded' ,
1991 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1992 self
. networkctl_check_unit ( 'macvlan99' , '21-macvlan' , '26-netdev-link-local-addressing-yes' )
1993 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-macvlan' )
1995 output
= check_output ( 'ip -d link show test1' )
1997 self
. assertIn ( ' mtu 2000 ' , output
)
1999 output
= check_output ( 'ip -d link show macvlan99' )
2001 self
. assertIn ( ' mtu 2000 ' , output
)
2002 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
2004 remove_link ( 'test1' )
2007 check_output ( "ip link add test1 type dummy" )
2008 self
. wait_online ( 'macvlan99:degraded' ,
2009 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
2011 output
= check_output ( 'ip -d link show test1' )
2013 self
. assertIn ( ' mtu 2000 ' , output
)
2015 output
= check_output ( 'ip -d link show macvlan99' )
2017 self
. assertIn ( ' mtu 2000 ' , output
)
2018 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
2019 self
. assertIn ( ' bcqueuelen 1234 ' , output
)
2020 if ' bclim ' in output
: # This is new in kernel and iproute2 v6.4
2021 self
. assertIn ( ' bclim 2147483647 ' , output
)
2023 touch_network_unit ( '21-macvlan.netdev' )
2025 self
. wait_online ( 'macvlan99:degraded' ,
2026 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
2028 @expectedFailureIfModuleIsNotAvailable ( 'ipvlan' )
2029 def test_ipvlan ( self
):
2031 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
2037 print ( f
'### test_ipvlan(mode= {mode} , flag= {flag} )' )
2038 with self
. subTest ( mode
= mode
, flag
= flag
):
2039 copy_network_unit ( '25-ipvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2040 '11-dummy.netdev' , '25-ipvlan.network' )
2041 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2042 f
. write ( '[IPVLAN] \n Mode=' + mode
+ ' \n Flags=' + flag
)
2045 self
. wait_online ( 'ipvlan99:degraded' , 'test1:degraded' )
2046 self
. networkctl_check_unit ( 'ipvlan99' , '25-ipvlan' , '26-netdev-link-local-addressing-yes' )
2047 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-ipvlan' )
2049 output
= check_output ( 'ip -d link show ipvlan99' )
2051 self
. assertRegex ( output
, 'ipvlan *mode ' + mode
. lower () + ' ' + flag
)
2053 touch_network_unit ( '25-ipvlan.netdev' )
2055 self
. wait_online ( 'ipvlan99:degraded' , 'test1:degraded' )
2057 @expectedFailureIfModuleIsNotAvailable ( 'hsr' )
2060 for proto
, supervision
in [[ 'hsr' , 9 ], [ 'prp' , 127 ]]:
2066 print ( f
'### test_hsr(proto= {proto} , supervision= {supervision} )' )
2067 with self
. subTest ( proto
= proto
, supervision
= supervision
):
2068 copy_network_unit ( '25-hsr.netdev' , '25-hsr.network' ,
2069 '11-dummy.netdev' , '11-dummy.network' ,
2070 '12-dummy.netdev' , '12-dummy-no-address.network' )
2071 with
open ( os
. path
. join ( network_unit_dir
, '25-hsr.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2072 f
. write ( 'Protocol=' + proto
+ ' \n Supervision=' + str ( supervision
))
2075 self
. wait_online ( 'hsr99:degraded' )
2076 self
. networkctl_check_unit ( 'hsr99' , '25-hsr' , '25-hsr' )
2077 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '11-dummy' )
2078 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy-no-address' )
2080 output
= check_output ( 'ip -d link show hsr99' )
2082 self
. assertRegex ( output
, 'hsr slave1 test1 slave2 dummy98' )
2083 self
. assertRegex ( output
, f
'supervision 01:15:4e:00:01:{supervision:02x}' )
2084 self
. assertRegex ( output
, 'proto ' + ( '0' if proto
== 'hsr' else '1' ) + ' ' )
2086 touch_network_unit ( '25-hsr.netdev' )
2088 self
. wait_online ( 'hsr99:degraded' )
2090 @expectedFailureIfModuleIsNotAvailable ( 'ipvtap' )
2091 def test_ipvtap ( self
):
2093 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
2099 print ( f
'### test_ipvtap(mode= {mode} , flag= {flag} )' )
2100 with self
. subTest ( mode
= mode
, flag
= flag
):
2101 copy_network_unit ( '25-ipvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2102 '11-dummy.netdev' , '25-ipvtap.network' )
2103 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2104 f
. write ( '[IPVTAP] \n Mode=' + mode
+ ' \n Flags=' + flag
)
2107 self
. wait_online ( 'ipvtap99:degraded' , 'test1:degraded' )
2108 self
. networkctl_check_unit ( 'ipvtap99' , '25-ipvtap' , '26-netdev-link-local-addressing-yes' )
2109 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-ipvtap' )
2111 output
= check_output ( 'ip -d link show ipvtap99' )
2113 self
. assertRegex ( output
, 'ipvtap *mode ' + mode
. lower () + ' ' + flag
)
2115 touch_network_unit ( '25-ipvtap.netdev' )
2117 self
. wait_online ( 'ipvtap99:degraded' , 'test1:degraded' )
2119 def test_veth ( self
):
2120 copy_network_unit ( '25-veth.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2121 '25-veth-mtu.netdev' )
2124 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' , 'veth-mtu:degraded' , 'veth-mtu-peer:degraded' )
2125 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '26-netdev-link-local-addressing-yes' )
2126 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '26-netdev-link-local-addressing-yes' )
2127 self
. networkctl_check_unit ( 'veth-mtu' , '25-veth-mtu' , '26-netdev-link-local-addressing-yes' )
2128 self
. networkctl_check_unit ( 'veth-mtu-peer' , '25-veth-mtu' , '26-netdev-link-local-addressing-yes' )
2130 output
= check_output ( 'ip -d link show veth99' )
2132 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bc' )
2133 output
= check_output ( 'ip -d link show veth-peer' )
2135 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bd' )
2137 output
= check_output ( 'ip -d link show veth-mtu' )
2139 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:be' )
2140 self
. assertRegex ( output
, 'mtu 1800' )
2141 output
= check_output ( 'ip -d link show veth-mtu-peer' )
2143 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bf' )
2144 self
. assertRegex ( output
, 'mtu 1800' )
2148 '26-netdev-link-local-addressing-yes.network' ,
2149 '25-veth-mtu.netdev' )
2153 'veth-peer:degraded' ,
2154 'veth-mtu:degraded' ,
2155 'veth-mtu-peer:degraded' )
2157 def check_tuntap ( self
, attached
):
2158 pid
= networkd_pid ()
2159 name
= psutil
. Process ( pid
). name ()[: 15 ]
2161 output
= check_output ( 'ip -d -oneline tuntap show' )
2163 self
. assertRegex ( output
, r
'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:' )
2164 self
. assertRegex ( output
, r
'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:' )
2167 self
. assertRegex ( output
, fr
'testtap99: .* {name} \( {pid} \)' )
2168 self
. assertRegex ( output
, fr
'testtun99: .* {name} \( {pid} \)' )
2169 self
. assertRegex ( output
, r
'testtap99: .*systemd\(1\)' )
2170 self
. assertRegex ( output
, r
'testtun99: .*systemd\(1\)' )
2172 output
= check_output ( 'ip -d link show testtun99' )
2174 # Old ip command does not support IFF_ flags
2175 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
2176 self
. assertIn ( 'UP,LOWER_UP' , output
)
2178 output
= check_output ( 'ip -d link show testtap99' )
2180 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
2181 self
. assertIn ( 'UP,LOWER_UP' , output
)
2184 self
. assertNotIn ( f
' {name} ( {pid} )' , output
)
2185 self
. assertNotIn ( 'systemd(1)' , output
)
2188 output
= check_output ( 'ip -d link show testtun99' )
2190 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
2191 if 'NO-CARRIER' in output
:
2198 output
= check_output ( 'ip -d link show testtap99' )
2200 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
2201 if 'NO-CARRIER' in output
:
2207 def test_tuntap ( self
):
2208 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '26-netdev-link-local-addressing-yes.network' )
2210 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
2211 self
. networkctl_check_unit ( 'testtap99' , '25-tap' , '26-netdev-link-local-addressing-yes' )
2212 self
. networkctl_check_unit ( 'testtun99' , '25-tun' , '26-netdev-link-local-addressing-yes' )
2214 self
. check_tuntap ( True )
2216 touch_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '26-netdev-link-local-addressing-yes.network' )
2218 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
2220 self
. check_tuntap ( True )
2222 remove_network_unit ( '26-netdev-link-local-addressing-yes.network' )
2224 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' , setup_state
= 'unmanaged' )
2225 self
. networkctl_check_unit ( 'testtap99' , '25-tap' )
2226 self
. networkctl_check_unit ( 'testtun99' , '25-tun' )
2228 self
. check_tuntap ( True )
2230 clear_network_units ()
2231 unmanage_existing_links ()
2233 self
. wait_online ( 'testtun99:off' , 'testtap99:off' , setup_state
= 'unmanaged' )
2234 self
. networkctl_check_unit ( 'testtap99' )
2235 self
. networkctl_check_unit ( 'testtun99' )
2237 self
. check_tuntap ( False )
2239 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
2241 copy_network_unit ( '25-vrf.netdev' , '26-netdev-link-local-addressing-yes.network' )
2244 self
. wait_online ( 'vrf99:carrier' )
2245 self
. networkctl_check_unit ( 'vrf99' , '25-vrf' , '26-netdev-link-local-addressing-yes' )
2247 touch_network_unit ( '25-vrf.netdev' , '26-netdev-link-local-addressing-yes.network' )
2249 self
. wait_online ( 'vrf99:carrier' )
2251 @expectedFailureIfModuleIsNotAvailable ( 'vcan' )
2252 def test_vcan ( self
):
2253 copy_network_unit ( '25-vcan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2254 '25-vcan98.netdev' , '25-vcan98.network' )
2257 self
. wait_online ( 'vcan99:carrier' , 'vcan98:carrier' )
2258 # For can devices, 'carrier' is the default required operational state.
2259 self
. wait_online ( 'vcan99' , 'vcan98' )
2260 self
. networkctl_check_unit ( 'vcan99' , '25-vcan' , '26-netdev-link-local-addressing-yes' )
2261 self
. networkctl_check_unit ( 'vcan98' , '25-vcan98' , '25-vcan98' )
2263 # https://github.com/systemd/systemd/issues/30140
2264 output
= check_output ( 'ip -d link show vcan99' )
2266 self
. assertIn ( 'mtu 16 ' , output
)
2268 output
= check_output ( 'ip -d link show vcan98' )
2270 self
. assertIn ( 'mtu 16 ' , output
)
2274 '26-netdev-link-local-addressing-yes.network' ,
2276 '25-vcan98.network' )
2278 self
. wait_online ( 'vcan99:carrier' , 'vcan98:carrier' )
2280 @expectedFailureIfModuleIsNotAvailable ( 'vxcan' )
2281 def test_vxcan ( self
):
2282 copy_network_unit ( '25-vxcan.netdev' , '26-netdev-link-local-addressing-yes.network' )
2285 self
. wait_online ( 'vxcan99:carrier' , 'vxcan-peer:carrier' )
2286 # For can devices, 'carrier' is the default required operational state.
2287 self
. wait_online ( 'vxcan99' , 'vxcan-peer' )
2288 self
. networkctl_check_unit ( 'vxcan99' , '25-vxcan' , '26-netdev-link-local-addressing-yes' )
2289 self
. networkctl_check_unit ( 'vxcan-peer' , '25-vxcan' , '26-netdev-link-local-addressing-yes' )
2291 touch_network_unit ( '25-vxcan.netdev' , '26-netdev-link-local-addressing-yes.network' )
2293 self
. wait_online ( 'vxcan99:carrier' , 'vxcan-peer:carrier' )
2295 @expectedFailureIfModuleIsNotAvailable ( 'wireguard' )
2296 def test_wireguard ( self
):
2297 copy_credential ( '25-wireguard-endpoint-peer0-cred.txt' , 'network.wireguard.peer0.endpoint' )
2298 copy_credential ( '25-wireguard-preshared-key-peer2-cred.txt' , 'network.wireguard.peer2.psk' )
2299 copy_credential ( '25-wireguard-no-peer-private-key-cred.txt' , 'network.wireguard.private.25-wireguard-no-peer' )
2301 copy_network_unit ( '25-wireguard.netdev' , '25-wireguard.network' ,
2302 '25-wireguard-23-peers.netdev' , '25-wireguard-23-peers.network' ,
2303 '25-wireguard-public-key.txt' , '25-wireguard-preshared-key.txt' , '25-wireguard-private-key.txt' ,
2304 '25-wireguard-no-peer.netdev' , '25-wireguard-no-peer.network' )
2306 self
. wait_online ( 'wg99:routable' , 'wg98:routable' , 'wg97:carrier' )
2307 self
. networkctl_check_unit ( 'wg99' , '25-wireguard' , '25-wireguard' )
2308 self
. networkctl_check_unit ( 'wg98' , '25-wireguard-23-peers' , '25-wireguard-23-peers' )
2309 self
. networkctl_check_unit ( 'wg97' , '25-wireguard-no-peer' , '25-wireguard-no-peer' )
2311 output
= check_output ( 'ip -4 address show dev wg99' )
2313 self
. assertIn ( 'inet 192.168.124.1/24 scope global wg99' , output
)
2315 output
= check_output ( 'ip -4 address show dev wg99' )
2317 self
. assertIn ( 'inet 169.254.11.1/24 scope link wg99' , output
)
2319 output
= check_output ( 'ip -6 address show dev wg99' )
2321 self
. assertIn ( 'inet6 fe80::1/64 scope link' , output
)
2323 output
= check_output ( 'ip -4 address show dev wg98' )
2325 self
. assertIn ( 'inet 192.168.123.123/24 scope global wg98' , output
)
2327 output
= check_output ( 'ip -6 address show dev wg98' )
2329 self
. assertIn ( 'inet6 fd8d:4d6d:3ccb:500::1/64 scope global' , output
)
2331 output
= check_output ( 'ip -4 route show dev wg99 table 1234' )
2333 self
. assertIn ( '192.168.26.0/24 proto static scope link metric 123' , output
)
2335 output
= check_output ( 'ip -6 route show dev wg99 table 1234' )
2337 self
. assertIn ( 'fd31:bf08:57cb::/48 proto static metric 123 pref medium' , output
)
2339 output
= check_output ( 'ip -6 route show dev wg98 table 1234' )
2341 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium' , output
)
2342 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium' , output
)
2343 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium' , output
)
2344 self
. assertIn ( 'fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium' , output
)
2345 self
. assertIn ( 'fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium' , output
)
2346 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium' , output
)
2347 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium' , output
)
2348 self
. assertIn ( 'fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium' , output
)
2349 self
. assertIn ( 'fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium' , output
)
2350 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium' , output
)
2351 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium' , output
)
2352 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium' , output
)
2353 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium' , output
)
2354 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium' , output
)
2355 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium' , output
)
2356 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium' , output
)
2357 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium' , output
)
2358 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium' , output
)
2359 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium' , output
)
2360 self
. assertIn ( 'fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium' , output
)
2361 self
. assertIn ( 'fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium' , output
)
2362 self
. assertIn ( 'fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium' , output
)
2363 self
. assertIn ( 'fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium' , output
)
2364 self
. assertIn ( 'fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium' , output
)
2365 self
. assertIn ( 'fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium' , output
)
2366 self
. assertIn ( 'fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium' , output
)
2367 self
. assertIn ( 'fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium' , output
)
2368 self
. assertIn ( 'fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium' , output
)
2369 self
. assertIn ( 'fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium' , output
)
2370 self
. assertIn ( 'fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium' , output
)
2371 self
. assertIn ( 'fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium' , output
)
2372 self
. assertIn ( 'fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium' , output
)
2373 self
. assertIn ( 'fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium' , output
)
2374 self
. assertIn ( 'fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium' , output
)
2375 self
. assertIn ( 'fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium' , output
)
2376 self
. assertIn ( 'fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium' , output
)
2377 self
. assertIn ( 'fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium' , output
)
2378 self
. assertIn ( 'fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium' , output
)
2379 self
. assertIn ( 'fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium' , output
)
2380 self
. assertIn ( 'fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium' , output
)
2381 self
. assertIn ( 'fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium' , output
)
2382 self
. assertIn ( 'fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium' , output
)
2383 self
. assertIn ( 'fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium' , output
)
2384 self
. assertIn ( 'fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium' , output
)
2385 self
. assertIn ( 'fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium' , output
)
2386 self
. assertIn ( 'fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium' , output
)
2388 if shutil
. which ( 'wg' ):
2391 output
= check_output ( 'wg show wg99 listen-port' )
2392 self
. assertEqual ( output
, '51820' )
2393 output
= check_output ( 'wg show wg99 fwmark' )
2394 self
. assertEqual ( output
, '0x4d2' )
2395 output
= check_output ( 'wg show wg99 private-key' )
2396 self
. assertEqual ( output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=' )
2397 output
= check_output ( 'wg show wg99 allowed-ips' )
2398 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 192.168.124.3/32' , output
)
2399 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t 192.168.124.2/32' , output
)
2400 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t fdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128' , output
)
2401 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.26.0/24 fd31:bf08:57cb::/48' , output
)
2402 output
= check_output ( 'wg show wg99 persistent-keepalive' )
2403 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t off' , output
)
2404 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t off' , output
)
2405 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t off' , output
)
2406 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 20' , output
)
2407 output
= check_output ( 'wg show wg99 endpoints' )
2408 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t (none)' , output
)
2409 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t (none)' , output
)
2410 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t (none)' , output
)
2411 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.27.3:51820' , output
)
2412 output
= check_output ( 'wg show wg99 preshared-keys' )
2413 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=' , output
)
2414 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t it7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=' , output
)
2415 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=' , output
)
2416 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=' , output
)
2418 output
= check_output ( 'wg show wg98 private-key' )
2419 self
. assertEqual ( output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=' )
2421 output
= check_output ( 'wg show wg97 listen-port' )
2422 self
. assertEqual ( output
, '51821' )
2423 output
= check_output ( 'wg show wg97 fwmark' )
2424 self
. assertEqual ( output
, '0x4d3' )
2427 '25-wireguard.netdev' , '25-wireguard.network' ,
2428 '25-wireguard-23-peers.netdev' , '25-wireguard-23-peers.network' ,
2429 '25-wireguard-public-key.txt' , '25-wireguard-preshared-key.txt' , '25-wireguard-private-key.txt' ,
2430 '25-wireguard-no-peer.netdev' , '25-wireguard-no-peer.network' )
2432 self
. wait_online ( 'wg99:routable' , 'wg98:routable' , 'wg97:carrier' )
2434 def test_geneve ( self
):
2435 copy_network_unit ( '25-geneve.netdev' , '26-netdev-link-local-addressing-yes.network' )
2438 self
. wait_online ( 'geneve99:degraded' )
2439 self
. networkctl_check_unit ( 'geneve99' , '25-geneve' , '26-netdev-link-local-addressing-yes' )
2441 output
= check_output ( 'ip -d link show geneve99' )
2443 self
. assertRegex ( output
, '192.168.22.1' )
2444 self
. assertRegex ( output
, '6082' )
2445 self
. assertRegex ( output
, 'udpcsum' )
2446 self
. assertRegex ( output
, 'udp6zerocsumrx' )
2448 touch_network_unit ( '25-geneve.netdev' , '26-netdev-link-local-addressing-yes.network' )
2450 self
. wait_online ( 'geneve99:degraded' )
2452 def _test_ipip_tunnel ( self
, mode
):
2453 copy_network_unit ( '12-dummy.netdev' , '25-ipip.network' ,
2454 '25-ipip-tunnel.netdev' , '25-tunnel.network' ,
2455 '25-ipip-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2456 '25-ipip-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2457 '25-ipip-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2460 for netdev
in [ '25-ipip-tunnel.netdev' ,
2461 '25-ipip-tunnel-local-any.netdev' ,
2462 '25-ipip-tunnel-remote-any.netdev' ,
2463 '25-ipip-tunnel-any-any.netdev' ]:
2464 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2465 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2467 mode
= 'ipip' # kernel default
2470 self
. wait_online ( 'ipiptun99:routable' , 'ipiptun98:routable' , 'ipiptun97:routable' , 'ipiptun96:routable' , 'dummy98:degraded' )
2472 output
= check_output ( 'ip -d link show ipiptun99' )
2474 self
. assertIn ( f
'ipip {mode} remote 192.169.224.239 local 192.168.223.238 dev dummy98' , output
)
2475 output
= check_output ( 'ip -d link show ipiptun98' )
2477 self
. assertIn ( f
'ipip {mode} remote 192.169.224.239 local any dev dummy98' , output
)
2478 output
= check_output ( 'ip -d link show ipiptun97' )
2480 self
. assertIn ( f
'ipip {mode} remote any local 192.168.223.238 dev dummy98' , output
)
2481 output
= check_output ( 'ip -d link show ipiptun96' )
2483 self
. assertIn ( f
'ipip {mode} remote any local any dev dummy98' , output
)
2486 '25-ipip-tunnel.netdev' ,
2487 '25-ipip-tunnel-local-any.netdev' ,
2488 '25-ipip-tunnel-remote-any.netdev' ,
2489 '25-ipip-tunnel-any-any.netdev' )
2492 'ipiptun99:routable' ,
2493 'ipiptun98:routable' ,
2494 'ipiptun97:routable' ,
2495 'ipiptun96:routable' ,
2498 def test_ipip_tunnel ( self
):
2500 for mode
in [ None , 'ipip' , 'any' ]:
2506 print ( f
'### test_ipip_tunnel(mode= {mode} )' )
2507 with self
. subTest ( mode
= mode
):
2508 self
._ test
_ ipip
_ tunnel
( mode
)
2510 def test_gre_tunnel ( self
):
2511 copy_network_unit ( '12-dummy.netdev' , '25-gretun.network' ,
2512 '25-gre-tunnel.netdev' , '25-tunnel.network' ,
2513 '25-gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2514 '25-gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2515 '25-gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2517 self
. wait_online ( 'gretun99:routable' , 'gretun98:routable' , 'gretun97:routable' , 'gretun96:routable' , 'dummy98:degraded' )
2518 self
. networkctl_check_unit ( 'gretun99' , '25-gre-tunnel' , '25-tunnel' )
2519 self
. networkctl_check_unit ( 'gretun98' , '25-gre-tunnel-local-any' , '25-tunnel-local-any' )
2520 self
. networkctl_check_unit ( 'gretun97' , '25-gre-tunnel-remote-any' , '25-tunnel-remote-any' )
2521 self
. networkctl_check_unit ( 'gretun96' , '25-gre-tunnel-any-any' , '25-tunnel-any-any' )
2522 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-gretun' )
2524 output
= check_output ( 'ip -d link show gretun99' )
2526 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2527 self
. assertRegex ( output
, 'ikey 1.2.3.103' )
2528 self
. assertRegex ( output
, 'okey 1.2.4.103' )
2529 self
. assertRegex ( output
, 'iseq' )
2530 self
. assertRegex ( output
, 'oseq' )
2531 output
= check_output ( 'ip -d link show gretun98' )
2533 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local any dev dummy98' )
2534 self
. assertRegex ( output
, 'ikey 0.0.0.104' )
2535 self
. assertRegex ( output
, 'okey 0.0.0.104' )
2536 self
. assertNotRegex ( output
, 'iseq' )
2537 self
. assertNotRegex ( output
, 'oseq' )
2538 output
= check_output ( 'ip -d link show gretun97' )
2540 self
. assertRegex ( output
, 'gre remote any local 10.65.223.238 dev dummy98' )
2541 self
. assertRegex ( output
, 'ikey 0.0.0.105' )
2542 self
. assertRegex ( output
, 'okey 0.0.0.105' )
2543 self
. assertNotRegex ( output
, 'iseq' )
2544 self
. assertNotRegex ( output
, 'oseq' )
2545 output
= check_output ( 'ip -d link show gretun96' )
2547 self
. assertRegex ( output
, 'gre remote any local any dev dummy98' )
2548 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2549 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2550 self
. assertNotRegex ( output
, 'iseq' )
2551 self
. assertNotRegex ( output
, 'oseq' )
2554 '25-gre-tunnel.netdev' ,
2555 '25-gre-tunnel-local-any.netdev' ,
2556 '25-gre-tunnel-remote-any.netdev' ,
2557 '25-gre-tunnel-any-any.netdev' )
2560 'gretun99:routable' ,
2561 'gretun98:routable' ,
2562 'gretun97:routable' ,
2563 'gretun96:routable' ,
2566 def test_ip6gre_tunnel ( self
):
2567 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretun.network' ,
2568 '25-ip6gre-tunnel.netdev' , '25-tunnel.network' ,
2569 '25-ip6gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2570 '25-ip6gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2571 '25-ip6gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2574 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2576 self
. wait_links ( 'dummy98' , 'ip6gretun99' , 'ip6gretun98' , 'ip6gretun97' , 'ip6gretun96' )
2578 output
= check_output ( 'ip -d link show ip6gretun99' )
2580 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2581 output
= check_output ( 'ip -d link show ip6gretun98' )
2583 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2584 output
= check_output ( 'ip -d link show ip6gretun97' )
2586 self
. assertRegex ( output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98' )
2587 output
= check_output ( 'ip -d link show ip6gretun96' )
2589 self
. assertRegex ( output
, 'ip6gre remote any local any dev dummy98' )
2592 '25-ip6gre-tunnel.netdev' ,
2593 '25-ip6gre-tunnel-local-any.netdev' ,
2594 '25-ip6gre-tunnel-remote-any.netdev' ,
2595 '25-ip6gre-tunnel-any-any.netdev' )
2604 def test_gretap_tunnel ( self
):
2605 copy_network_unit ( '12-dummy.netdev' , '25-gretap.network' ,
2606 '25-gretap-tunnel.netdev' , '25-tunnel.network' ,
2607 '25-gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2609 self
. wait_online ( 'gretap99:routable' , 'gretap98:routable' , 'dummy98:degraded' )
2610 self
. networkctl_check_unit ( 'gretap99' , '25-gretap-tunnel' , '25-tunnel' )
2611 self
. networkctl_check_unit ( 'gretap98' , '25-gretap-tunnel-local-any' , '25-tunnel-local-any' )
2612 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-gretap' )
2614 output
= check_output ( 'ip -d link show gretap99' )
2616 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2617 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2618 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2619 self
. assertRegex ( output
, 'iseq' )
2620 self
. assertRegex ( output
, 'oseq' )
2621 self
. assertIn ( 'nopmtudisc' , output
)
2622 self
. assertIn ( 'ignore-df' , output
)
2623 output
= check_output ( 'ip -d link show gretap98' )
2625 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local any dev dummy98' )
2626 self
. assertRegex ( output
, 'ikey 0.0.0.107' )
2627 self
. assertRegex ( output
, 'okey 0.0.0.107' )
2628 self
. assertRegex ( output
, 'iseq' )
2629 self
. assertRegex ( output
, 'oseq' )
2632 '25-gretap-tunnel.netdev' ,
2633 '25-gretap-tunnel-local-any.netdev' )
2636 'gretap99:routable' ,
2637 'gretap98:routable' ,
2640 def test_ip6gretap_tunnel ( self
):
2641 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretap.network' ,
2642 '25-ip6gretap-tunnel.netdev' , '25-tunnel.network' ,
2643 '25-ip6gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2645 self
. wait_online ( 'ip6gretap99:routable' , 'ip6gretap98:routable' , 'dummy98:degraded' )
2646 self
. networkctl_check_unit ( 'ip6gretap99' , '25-ip6gretap-tunnel' , '25-tunnel' )
2647 self
. networkctl_check_unit ( 'ip6gretap98' , '25-ip6gretap-tunnel-local-any' , '25-tunnel-local-any' )
2648 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-ip6gretap' )
2650 output
= check_output ( 'ip -d link show ip6gretap99' )
2652 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2653 output
= check_output ( 'ip -d link show ip6gretap98' )
2655 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2658 '25-ip6gretap-tunnel.netdev' ,
2659 '25-ip6gretap-tunnel-local-any.netdev' )
2662 'ip6gretap99:routable' ,
2663 'ip6gretap98:routable' ,
2666 def test_vti_tunnel ( self
):
2667 copy_network_unit ( '12-dummy.netdev' , '25-vti.network' ,
2668 '25-vti-tunnel.netdev' , '25-tunnel.network' ,
2669 '25-vti-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2670 '25-vti-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2671 '25-vti-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2673 self
. wait_online ( 'vtitun99:routable' , 'vtitun98:routable' , 'vtitun97:routable' , 'vtitun96:routable' , 'dummy98:degraded' )
2674 self
. networkctl_check_unit ( 'vtitun99' , '25-vti-tunnel' , '25-tunnel' )
2675 self
. networkctl_check_unit ( 'vtitun98' , '25-vti-tunnel-local-any' , '25-tunnel-local-any' )
2676 self
. networkctl_check_unit ( 'vtitun97' , '25-vti-tunnel-remote-any' , '25-tunnel-remote-any' )
2677 self
. networkctl_check_unit ( 'vtitun96' , '25-vti-tunnel-any-any' , '25-tunnel-any-any' )
2678 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-vti' )
2680 output
= check_output ( 'ip -d link show vtitun99' )
2682 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2683 output
= check_output ( 'ip -d link show vtitun98' )
2685 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local any dev dummy98' )
2686 output
= check_output ( 'ip -d link show vtitun97' )
2688 self
. assertRegex ( output
, 'vti remote any local 10.65.223.238 dev dummy98' )
2689 output
= check_output ( 'ip -d link show vtitun96' )
2691 self
. assertRegex ( output
, 'vti remote any local any dev dummy98' )
2694 '25-vti-tunnel.netdev' ,
2695 '25-vti-tunnel-local-any.netdev' ,
2696 '25-vti-tunnel-remote-any.netdev' ,
2697 '25-vti-tunnel-any-any.netdev' )
2700 'vtitun99:routable' ,
2701 'vtitun98:routable' ,
2702 'vtitun97:routable' ,
2703 'vtitun96:routable' ,
2706 def test_vti6_tunnel ( self
):
2707 copy_network_unit ( '12-dummy.netdev' , '25-vti6.network' ,
2708 '25-vti6-tunnel.netdev' , '25-tunnel.network' ,
2709 '25-vti6-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2710 '25-vti6-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' )
2712 self
. wait_online ( 'vti6tun99:routable' , 'vti6tun98:routable' , 'vti6tun97:routable' , 'dummy98:degraded' )
2713 self
. networkctl_check_unit ( 'vti6tun99' , '25-vti6-tunnel' , '25-tunnel' )
2714 self
. networkctl_check_unit ( 'vti6tun98' , '25-vti6-tunnel-local-any' , '25-tunnel-local-any' )
2715 self
. networkctl_check_unit ( 'vti6tun97' , '25-vti6-tunnel-remote-any' , '25-tunnel-remote-any' )
2716 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-vti6' )
2718 output
= check_output ( 'ip -d link show vti6tun99' )
2720 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2721 output
= check_output ( 'ip -d link show vti6tun98' )
2723 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98' )
2724 output
= check_output ( 'ip -d link show vti6tun97' )
2726 self
. assertRegex ( output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98' )
2729 '25-vti6-tunnel.netdev' ,
2730 '25-vti6-tunnel-local-any.netdev' ,
2731 '25-vti6-tunnel-remote-any.netdev' )
2734 'vti6tun99:routable' ,
2735 'vti6tun98:routable' ,
2736 'vti6tun97:routable' ,
2739 def _test_ip6tnl_tunnel ( self
, mode
):
2740 copy_network_unit ( '12-dummy.netdev' , '25-ip6tnl.network' ,
2741 '25-ip6tnl-tunnel.netdev' , '25-tunnel.network' ,
2742 '25-ip6tnl-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2743 '25-ip6tnl-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2744 '25-veth.netdev' , '25-ip6tnl-slaac.network' , '25-ipv6-prefix.network' ,
2745 '25-ip6tnl-tunnel-local-slaac.netdev' , '25-ip6tnl-tunnel-local-slaac.network' ,
2746 '25-ip6tnl-tunnel-external.netdev' , '26-netdev-link-local-addressing-yes.network' )
2749 for netdev
in [ '25-ip6tnl-tunnel.netdev' ,
2750 '25-ip6tnl-tunnel-local-any.netdev' ,
2751 '25-ip6tnl-tunnel-remote-any.netdev' ,
2752 '25-ip6tnl-tunnel-local-slaac.netdev' ,
2753 '25-ip6tnl-tunnel-external.netdev' ]:
2754 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2755 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2757 mode
= 'any' # kernel default
2760 self
. wait_online ( 'ip6tnl99:routable' , 'ip6tnl98:routable' , 'ip6tnl97:routable' ,
2761 'ip6tnl-slaac:degraded' , 'ip6tnl-external:degraded' ,
2762 'dummy98:degraded' , 'veth99:routable' , 'veth-peer:degraded' )
2763 self
. networkctl_check_unit ( 'ip6tnl99' , '25-ip6tnl-tunnel' , '25-tunnel' )
2764 self
. networkctl_check_unit ( 'ip6tnl98' , '25-ip6tnl-tunnel-local-any' , '25-tunnel-local-any' )
2765 self
. networkctl_check_unit ( 'ip6tnl97' , '25-ip6tnl-tunnel-remote-any' , '25-tunnel-remote-any' )
2766 self
. networkctl_check_unit ( 'ip6tnl-slaac' , '25-ip6tnl-tunnel-local-slaac' , '25-ip6tnl-tunnel-local-slaac' )
2767 self
. networkctl_check_unit ( 'ip6tnl-external' , '25-ip6tnl-tunnel-external' , '26-netdev-link-local-addressing-yes' )
2768 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-ip6tnl' )
2769 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '25-ip6tnl-slaac' )
2770 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '25-ipv6-prefix' )
2772 output
= check_output ( 'ip -d link show ip6tnl99' )
2774 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' , output
)
2775 output
= check_output ( 'ip -d link show ip6tnl98' )
2777 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98' , output
)
2778 output
= check_output ( 'ip -d link show ip6tnl97' )
2780 self
. assertIn ( f
'ip6tnl {mode} remote any local 2a00:ffde:4567:edde::4987 dev dummy98' , output
)
2781 output
= check_output ( 'ip -d link show ip6tnl-external' )
2783 self
. assertIn ( 'ip6tnl-external@NONE:' , output
)
2784 self
. assertIn ( 'ip6tnl external ' , output
)
2785 output
= check_output ( 'ip -d link show ip6tnl-slaac' )
2787 self
. assertIn ( f
'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
2789 output
= check_output ( 'ip -6 address show veth99' )
2791 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
2793 output
= check_output ( 'ip -4 route show default' )
2795 self
. assertIn ( 'default dev ip6tnl-slaac proto static' , output
)
2798 '25-ip6tnl-tunnel.netdev' ,
2799 '25-ip6tnl-tunnel-local-any.netdev' ,
2800 '25-ip6tnl-tunnel-remote-any.netdev' ,
2801 '25-ip6tnl-tunnel-local-slaac.netdev' ,
2802 '25-ip6tnl-tunnel-external.netdev' )
2805 'ip6tnl99:routable' ,
2806 'ip6tnl98:routable' ,
2807 'ip6tnl97:routable' ,
2808 'ip6tnl-slaac:degraded' ,
2809 'ip6tnl-external:degraded' ,
2812 'veth-peer:degraded' )
2814 def test_ip6tnl_tunnel ( self
):
2816 for mode
in [ None , 'ipip6' , 'ip6ip6' , 'any' ]:
2822 print ( f
'### test_ip6tnl_tunnel(mode= {mode} )' )
2823 with self
. subTest ( mode
= mode
):
2824 self
._ test
_ ip
6 tnl
_ tunnel
( mode
)
2826 def _test_sit_tunnel ( self
, mode
):
2827 copy_network_unit ( '12-dummy.netdev' , '25-sit.network' ,
2828 '25-sit-tunnel.netdev' , '25-tunnel.network' ,
2829 '25-sit-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2830 '25-sit-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2831 '25-sit-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2834 for netdev
in [ '25-sit-tunnel.netdev' ,
2835 '25-sit-tunnel-local-any.netdev' ,
2836 '25-sit-tunnel-remote-any.netdev' ,
2837 '25-sit-tunnel-any-any.netdev' ]:
2838 with
open ( os
. path
. join ( network_unit_dir
, netdev
), mode
= 'a' , encoding
= 'utf-8' ) as f
:
2839 f
. write ( f
'[Tunnel] \n Mode= {mode} \n ' )
2841 mode
= 'ip6ip' # kernel default
2844 self
. wait_online ( 'sittun99:routable' , 'sittun98:routable' , 'sittun97:routable' , 'sittun96:routable' , 'dummy98:degraded' )
2845 self
. networkctl_check_unit ( 'sittun99' , '25-sit-tunnel' , '25-tunnel' )
2846 self
. networkctl_check_unit ( 'sittun98' , '25-sit-tunnel-local-any' , '25-tunnel-local-any' )
2847 self
. networkctl_check_unit ( 'sittun97' , '25-sit-tunnel-remote-any' , '25-tunnel-remote-any' )
2848 self
. networkctl_check_unit ( 'sittun96' , '25-sit-tunnel-any-any' , '25-tunnel-any-any' )
2849 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-sit' )
2851 output
= check_output ( 'ip -d link show sittun99' )
2853 self
. assertIn ( f
'sit {mode} remote 10.65.223.239 local 10.65.223.238 dev dummy98' , output
)
2854 output
= check_output ( 'ip -d link show sittun98' )
2856 self
. assertIn ( f
'sit {mode} remote 10.65.223.239 local any dev dummy98' , output
)
2857 output
= check_output ( 'ip -d link show sittun97' )
2859 self
. assertIn ( f
'sit {mode} remote any local 10.65.223.238 dev dummy98' , output
)
2860 output
= check_output ( 'ip -d link show sittun96' )
2862 self
. assertIn ( f
'sit {mode} remote any local any dev dummy98' , output
)
2865 '25-sit-tunnel.netdev' ,
2866 '25-sit-tunnel-local-any.netdev' ,
2867 '25-sit-tunnel-remote-any.netdev' ,
2868 '25-sit-tunnel-any-any.netdev' )
2871 'sittun99:routable' ,
2872 'sittun98:routable' ,
2873 'sittun97:routable' ,
2874 'sittun96:routable' ,
2877 def test_sit_tunnel ( self
):
2879 for mode
in [ None , 'ipip' , 'ip6ip' , 'any' ]:
2885 print ( f
'### test_sit_tunnel(mode= {mode} )' )
2886 with self
. subTest ( mode
= mode
):
2887 self
._ test
_ sit
_ tunnel
( mode
)
2889 def test_isatap_tunnel ( self
):
2890 copy_network_unit ( '12-dummy.netdev' , '25-isatap.network' ,
2891 '25-isatap-tunnel.netdev' , '25-tunnel.network' )
2893 self
. wait_online ( 'isataptun99:routable' , 'dummy98:degraded' )
2894 self
. networkctl_check_unit ( 'isataptun99' , '25-isatap-tunnel' , '25-tunnel' )
2895 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-isatap' )
2897 output
= check_output ( 'ip -d link show isataptun99' )
2899 self
. assertRegex ( output
, "isatap " )
2901 touch_network_unit ( '25-isatap-tunnel.netdev' )
2903 self
. wait_online ( 'isataptun99:routable' , 'dummy98:degraded' )
2905 def test_6rd_tunnel ( self
):
2906 copy_network_unit ( '12-dummy.netdev' , '25-6rd.network' ,
2907 '25-6rd-tunnel.netdev' , '25-tunnel.network' )
2909 self
. wait_online ( 'sittun99:routable' , 'dummy98:degraded' )
2910 self
. networkctl_check_unit ( 'sittun99' , '25-6rd-tunnel' , '25-tunnel' )
2911 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-6rd' )
2913 output
= check_output ( 'ip -d link show sittun99' )
2915 self
. assertRegex ( output
, '6rd-prefix 2602::/24' )
2917 touch_network_unit ( '25-6rd-tunnel.netdev' )
2919 self
. wait_online ( 'sittun99:routable' , 'dummy98:degraded' )
2921 @expectedFailureIfERSPANv0IsNotSupported ()
2922 def test_erspan_tunnel_v0 ( self
):
2923 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2924 '25-erspan0-tunnel.netdev' , '25-tunnel.network' ,
2925 '25-erspan0-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2927 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2928 self
. networkctl_check_unit ( 'erspan99' , '25-erspan0-tunnel' , '25-tunnel' )
2929 self
. networkctl_check_unit ( 'erspan98' , '25-erspan0-tunnel-local-any' , '25-tunnel-local-any' )
2930 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
2932 output
= check_output ( 'ip -d link show erspan99' )
2934 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2935 self
. assertIn ( 'erspan_ver 0' , output
)
2936 self
. assertNotIn ( 'erspan_index 123' , output
)
2937 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2938 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2939 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2940 self
. assertIn ( 'iseq' , output
)
2941 self
. assertIn ( 'nopmtudisc' , output
)
2942 self
. assertIn ( 'ignore-df' , output
)
2943 output
= check_output ( 'ip -d link show erspan98' )
2945 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2946 self
. assertIn ( 'erspan_ver 0' , output
)
2947 self
. assertNotIn ( 'erspan_index 124' , output
)
2948 self
. assertNotIn ( 'erspan_dir egress' , output
)
2949 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
2950 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2951 self
. assertIn ( 'iseq' , output
)
2954 '25-erspan0-tunnel.netdev' ,
2955 '25-erspan0-tunnel-local-any.netdev' )
2958 'erspan99:routable' ,
2959 'erspan98:routable' ,
2962 def test_erspan_tunnel_v1 ( self
):
2963 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2964 '25-erspan1-tunnel.netdev' , '25-tunnel.network' ,
2965 '25-erspan1-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2967 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2968 self
. networkctl_check_unit ( 'erspan99' , '25-erspan1-tunnel' , '25-tunnel' )
2969 self
. networkctl_check_unit ( 'erspan98' , '25-erspan1-tunnel-local-any' , '25-tunnel-local-any' )
2970 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
2972 output
= check_output ( 'ip -d link show erspan99' )
2974 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2975 self
. assertIn ( 'erspan_ver 1' , output
)
2976 self
. assertIn ( 'erspan_index 123' , output
)
2977 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2978 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2979 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2980 self
. assertIn ( 'okey 0.0.0.101' , output
)
2981 self
. assertIn ( 'iseq' , output
)
2982 self
. assertIn ( 'oseq' , output
)
2983 output
= check_output ( 'ip -d link show erspan98' )
2985 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2986 self
. assertIn ( 'erspan_ver 1' , output
)
2987 self
. assertIn ( 'erspan_index 124' , output
)
2988 self
. assertNotIn ( 'erspan_dir egress' , output
)
2989 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
2990 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2991 self
. assertIn ( 'okey 0.0.0.102' , output
)
2992 self
. assertIn ( 'iseq' , output
)
2993 self
. assertIn ( 'oseq' , output
)
2996 '25-erspan1-tunnel.netdev' ,
2997 '25-erspan1-tunnel-local-any.netdev' )
3000 'erspan99:routable' ,
3001 'erspan98:routable' ,
3004 @expectedFailureIfERSPANv2IsNotSupported ()
3005 def test_erspan_tunnel_v2 ( self
):
3006 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
3007 '25-erspan2-tunnel.netdev' , '25-tunnel.network' ,
3008 '25-erspan2-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
3010 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
3011 self
. networkctl_check_unit ( 'erspan99' , '25-erspan2-tunnel' , '25-tunnel' )
3012 self
. networkctl_check_unit ( 'erspan98' , '25-erspan2-tunnel-local-any' , '25-tunnel-local-any' )
3013 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-erspan' )
3015 output
= check_output ( 'ip -d link show erspan99' )
3017 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
3018 self
. assertIn ( 'erspan_ver 2' , output
)
3019 self
. assertNotIn ( 'erspan_index 123' , output
)
3020 self
. assertIn ( 'erspan_dir ingress' , output
)
3021 self
. assertIn ( 'erspan_hwid 0x1f' , output
)
3022 self
. assertIn ( 'ikey 0.0.0.101' , output
)
3023 self
. assertIn ( 'okey 0.0.0.101' , output
)
3024 self
. assertIn ( 'iseq' , output
)
3025 self
. assertIn ( 'oseq' , output
)
3026 output
= check_output ( 'ip -d link show erspan98' )
3028 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
3029 self
. assertIn ( 'erspan_ver 2' , output
)
3030 self
. assertNotIn ( 'erspan_index 124' , output
)
3031 self
. assertIn ( 'erspan_dir egress' , output
)
3032 self
. assertIn ( 'erspan_hwid 0x2f' , output
)
3033 self
. assertIn ( 'ikey 0.0.0.102' , output
)
3034 self
. assertIn ( 'okey 0.0.0.102' , output
)
3035 self
. assertIn ( 'iseq' , output
)
3036 self
. assertIn ( 'oseq' , output
)
3039 '25-erspan2-tunnel.netdev' ,
3040 '25-erspan2-tunnel-local-any.netdev' )
3043 'erspan99:routable' ,
3044 'erspan98:routable' ,
3047 def test_tunnel_independent ( self
):
3048 copy_network_unit ( '25-ipip-tunnel-independent.netdev' , '26-netdev-link-local-addressing-yes.network' )
3051 self
. wait_online ( 'ipiptun99:carrier' )
3052 self
. networkctl_check_unit ( 'ipiptun99' , '25-ipip-tunnel-independent' , '26-netdev-link-local-addressing-yes' )
3054 def test_tunnel_independent_loopback ( self
):
3055 copy_network_unit ( '25-ipip-tunnel-independent-loopback.netdev' , '26-netdev-link-local-addressing-yes.network' )
3058 self
. wait_online ( 'ipiptun99:carrier' )
3059 self
. networkctl_check_unit ( 'ipiptun99' , '25-ipip-tunnel-independent-loopback' , '26-netdev-link-local-addressing-yes' )
3061 @expectedFailureIfModuleIsNotAvailable ( 'xfrm_interface' )
3062 def test_xfrm ( self
):
3063 copy_network_unit ( '12-dummy.netdev' , '25-xfrm.network' ,
3064 '25-xfrm.netdev' , '25-xfrm-independent.netdev' ,
3065 '26-netdev-link-local-addressing-yes.network' )
3068 self
. wait_online ( 'dummy98:degraded' , 'xfrm98:degraded' , 'xfrm99:degraded' )
3069 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '25-xfrm' )
3070 self
. networkctl_check_unit ( 'xfrm98' , '25-xfrm' , '26-netdev-link-local-addressing-yes' )
3071 self
. networkctl_check_unit ( 'xfrm99' , '25-xfrm-independent' , '26-netdev-link-local-addressing-yes' )
3073 output
= check_output ( 'ip -d link show dev xfrm98' )
3075 self
. assertIn ( 'xfrm98@dummy98:' , output
)
3076 self
. assertIn ( 'xfrm if_id 0x98 ' , output
)
3078 output
= check_output ( 'ip -d link show dev xfrm99' )
3080 self
. assertIn ( 'xfrm99@lo:' , output
)
3081 self
. assertIn ( 'xfrm if_id 0x99 ' , output
)
3083 touch_network_unit ( '25-xfrm.netdev' )
3085 self
. wait_online ( 'dummy98:degraded' , 'xfrm98:degraded' , 'xfrm99:degraded' )
3087 @expectedFailureIfModuleIsNotAvailable ( 'fou' )
3089 copy_network_unit ( '25-fou-ipproto-ipip.netdev' , '25-fou-ipproto-gre.netdev' ,
3090 '25-fou-ipip.netdev' , '25-fou-sit.netdev' ,
3091 '25-fou-gre.netdev' , '25-fou-gretap.netdev' )
3094 self
. wait_online ( 'ipiptun96:off' , 'sittun96:off' , 'gretun96:off' , 'gretap96:off' , setup_state
= 'unmanaged' )
3095 self
. networkctl_check_unit ( 'ipiptun96' , '25-fou-ipip' )
3096 self
. networkctl_check_unit ( 'sittun96' , '25-fou-sit' )
3097 self
. networkctl_check_unit ( 'gretun96' , '25-fou-gre' )
3098 self
. networkctl_check_unit ( 'gretap96' , '25-fou-gretap' )
3100 output
= check_output ( 'ip fou show' )
3102 self
. assertRegex ( output
, 'port 55555 ipproto 4' )
3103 self
. assertRegex ( output
, 'port 55556 ipproto 47' )
3105 output
= check_output ( 'ip -d link show ipiptun96' )
3107 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
3108 output
= check_output ( 'ip -d link show sittun96' )
3110 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
3111 output
= check_output ( 'ip -d link show gretun96' )
3113 self
. assertRegex ( output
, 'encap fou encap-sport 1001 encap-dport 55556' )
3114 output
= check_output ( 'ip -d link show gretap96' )
3116 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55556' )
3119 '25-fou-ipproto-ipip.netdev' , '25-fou-ipproto-gre.netdev' ,
3120 '25-fou-ipip.netdev' , '25-fou-sit.netdev' ,
3121 '25-fou-gre.netdev' , '25-fou-gretap.netdev' )
3123 self
. wait_online ( 'ipiptun96:off' , 'sittun96:off' , 'gretun96:off' , 'gretap96:off' , setup_state
= 'unmanaged' )
3125 def test_vxlan ( self
):
3126 copy_network_unit ( '11-dummy.netdev' , '25-vxlan-test1.network' ,
3127 '25-vxlan.netdev' , '25-vxlan.network' ,
3128 '25-vxlan-ipv6.netdev' , '25-vxlan-ipv6.network' ,
3129 '25-vxlan-independent.netdev' , '26-netdev-link-local-addressing-yes.network' ,
3130 '25-veth.netdev' , '25-vxlan-veth99.network' , '25-ipv6-prefix.network' ,
3131 '25-vxlan-local-slaac.netdev' , '25-vxlan-local-slaac.network' ,
3132 '25-vxlan-external.netdev' , '25-vxlan-external.network' )
3135 self
. wait_online ( 'test1:degraded' , 'veth99:routable' , 'veth-peer:degraded' ,
3136 'vxlan99:degraded' , 'vxlan98:degraded' , 'vxlan97:degraded' , 'vxlan-slaac:degraded' ,
3137 'vxlan-external:degraded' )
3138 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-vxlan-test1' )
3139 self
. networkctl_check_unit ( 'veth99' , '25-veth' , '25-vxlan-veth99' )
3140 self
. networkctl_check_unit ( 'veth-peer' , '25-veth' , '25-ipv6-prefix' )
3141 self
. networkctl_check_unit ( 'vxlan99' , '25-vxlan' , '25-vxlan' )
3142 self
. networkctl_check_unit ( 'vxlan98' , '25-vxlan-independent' , '26-netdev-link-local-addressing-yes' )
3143 self
. networkctl_check_unit ( 'vxlan97' , '25-vxlan-ipv6' , '25-vxlan-ipv6' )
3144 self
. networkctl_check_unit ( 'vxlan-slaac' , '25-vxlan-local-slaac' , '25-vxlan-local-slaac' )
3145 self
. networkctl_check_unit ( 'vxlan-external' , '25-vxlan-external' , '25-vxlan-external' )
3147 output
= check_output ( 'ip -d -d link show vxlan99' )
3149 self
. assertIn ( '999' , output
)
3150 self
. assertIn ( '5555' , output
)
3151 self
. assertIn ( 'l2miss' , output
)
3152 self
. assertIn ( 'l3miss' , output
)
3153 self
. assertIn ( 'gbp' , output
)
3154 # Since [0] some of the options use slightly different names and some
3155 # options with default values are shown only if the -d(etails) setting
3157 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
3158 self
. assertRegex ( output
, '(udpcsum|udp_csum)' )
3159 self
. assertRegex ( output
, '(udp6zerocsumtx|udp_zero_csum6_tx)' )
3160 self
. assertRegex ( output
, '(udp6zerocsumrx|udp_zero_csum6_rx)' )
3161 self
. assertRegex ( output
, '(remcsumtx|remcsum_tx)' )
3162 self
. assertRegex ( output
, '(remcsumrx|remcsum_rx)' )
3164 output
= check_output ( 'bridge fdb show dev vxlan99' )
3166 self
. assertIn ( '00:11:22:33:44:55 dst 10.0.0.5 self permanent' , output
)
3167 self
. assertIn ( '00:11:22:33:44:66 dst 10.0.0.6 self permanent' , output
)
3168 self
. assertIn ( '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent' , output
)
3170 output
= networkctl_status ( 'vxlan99' )
3172 self
. assertIn ( 'VNI: 999' , output
)
3173 self
. assertIn ( 'Destination Port: 5555' , output
)
3174 self
. assertIn ( 'Underlying Device: test1' , output
)
3176 output
= check_output ( 'bridge fdb show dev vxlan97' )
3178 self
. assertIn ( '00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent' , output
)
3179 self
. assertIn ( '00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent' , output
)
3180 self
. assertIn ( '00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent' , output
)
3182 output
= check_output ( 'ip -d link show vxlan-slaac' )
3184 self
. assertIn ( 'vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
3186 output
= check_output ( 'ip -6 address show veth99' )
3188 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
3190 output
= check_output ( 'ip -d link show vxlan-external' )
3192 self
. assertIn ( 'id 0 ' , output
)
3193 self
. assertIn ( 'external' , output
)
3194 self
. assertIn ( 'vnifilter' , output
)
3196 @unittest . skipUnless ( compare_kernel_version ( "6" ), reason
= "Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315" )
3197 def test_macsec ( self
):
3198 copy_network_unit ( '25-macsec.netdev' , '25-macsec.network' , '25-macsec.key' ,
3199 '26-macsec.network' , '12-dummy.netdev' )
3202 self
. wait_online ( 'dummy98:degraded' , 'macsec99:routable' )
3203 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '26-macsec' )
3204 self
. networkctl_check_unit ( 'macsec99' , '25-macsec' , '25-macsec' )
3206 output
= check_output ( 'ip -d link show macsec99' )
3208 self
. assertRegex ( output
, 'macsec99@dummy98' )
3209 self
. assertRegex ( output
, 'macsec sci [0-9a-f]*000b' )
3210 self
. assertRegex ( output
, 'encrypt on' )
3212 output
= check_output ( 'ip macsec show macsec99' )
3214 self
. assertRegex ( output
, 'encrypt on' )
3215 self
. assertRegex ( output
, 'TXSC: [0-9a-f]*000b on SA 1' )
3216 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000' )
3217 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000' )
3218 self
. assertRegex ( output
, 'RXSC: c619528fe6a00100, state on' )
3219 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000' )
3220 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000' )
3221 self
. assertRegex ( output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000' )
3222 self
. assertRegex ( output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000' )
3223 self
. assertNotRegex ( output
, 'key 02030405067080900000000000000000' )
3224 self
. assertRegex ( output
, 'RXSC: 8c16456c83a90002, state on' )
3225 self
. assertRegex ( output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000' )
3227 touch_network_unit ( '25-macsec.netdev' )
3229 self
. wait_online ( 'dummy98:degraded' , 'macsec99:routable' )
3231 def test_nlmon ( self
):
3232 copy_network_unit ( '25-nlmon.netdev' , '26-netdev-link-local-addressing-yes.network' )
3235 self
. wait_online ( 'nlmon99:carrier' )
3236 self
. networkctl_check_unit ( 'nlmon99' , '25-nlmon' , '26-netdev-link-local-addressing-yes' )
3238 touch_network_unit ( '25-nlmon.netdev' , '26-netdev-link-local-addressing-yes.network' )
3240 self
. wait_online ( 'nlmon99:carrier' )
3242 @expectedFailureIfModuleIsNotAvailable ( 'ifb' )
3244 copy_network_unit ( '25-ifb.netdev' , '26-netdev-link-local-addressing-yes.network' )
3247 self
. wait_online ( 'ifb99:degraded' )
3248 self
. networkctl_check_unit ( 'ifb99' , '25-ifb' , '26-netdev-link-local-addressing-yes' )
3250 touch_network_unit ( '25-ifb.netdev' , '26-netdev-link-local-addressing-yes.network' )
3252 self
. wait_online ( 'ifb99:degraded' )
3254 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
3255 def test_rps_cpu_1 ( self
):
3256 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-1.link' )
3259 self
. wait_online ( 'dummy98:carrier' )
3260 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-1' )
3262 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3264 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 2 )
3266 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
3267 def test_rps_cpu_0_1 ( self
):
3268 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-0-1.link' )
3271 self
. wait_online ( 'dummy98:carrier' )
3272 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-1' )
3274 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3276 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 3 )
3278 @unittest . skipUnless ( os
. cpu_count () >= 4 , reason
= "CPU count should be >= 4 to pass this test" )
3279 def test_rps_cpu_multi ( self
):
3280 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-multi.link' )
3283 self
. wait_online ( 'dummy98:carrier' )
3284 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-multi' )
3286 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3288 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 15 )
3290 def test_rps_cpu ( self
):
3291 cpu_count
= os
. cpu_count ()
3293 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' )
3296 self
. wait_online ( 'dummy98:carrier' )
3297 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' )
3300 copy_network_unit ( '25-rps-cpu-0.link' )
3301 udevadm_trigger ( '/sys/class/net/dummy98' )
3302 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0' )
3303 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3305 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3306 remove_network_unit ( '25-rps-cpu-0.link' )
3309 copy_network_unit ( '25-rps-cpu-all.link' )
3310 udevadm_trigger ( '/sys/class/net/dummy98' )
3311 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-all' )
3312 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3314 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3315 remove_network_unit ( '25-rps-cpu-all.link' )
3318 copy_network_unit ( '24-rps-cpu-disable.link' )
3319 udevadm_trigger ( '/sys/class/net/dummy98' )
3320 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-disable' )
3321 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3323 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 0 )
3324 remove_network_unit ( '24-rps-cpu-disable.link' )
3327 copy_network_unit ( '25-rps-cpu-all.link' )
3328 udevadm_trigger ( '/sys/class/net/dummy98' )
3329 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-all' )
3330 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3332 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3333 remove_network_unit ( '25-rps-cpu-all.link' )
3335 # empty -> unchanged
3336 copy_network_unit ( '24-rps-cpu-empty.link' )
3337 udevadm_trigger ( '/sys/class/net/dummy98' )
3338 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-empty' )
3339 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3341 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3342 remove_network_unit ( '24-rps-cpu-empty.link' )
3344 # 0, then empty -> unchanged
3345 copy_network_unit ( '25-rps-cpu-0-empty.link' )
3346 udevadm_trigger ( '/sys/class/net/dummy98' )
3347 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-empty' )
3348 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3350 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
3351 remove_network_unit ( '25-rps-cpu-0-empty.link' )
3353 # 0, then invalid -> 0
3354 copy_network_unit ( '25-rps-cpu-0-invalid.link' )
3355 udevadm_trigger ( '/sys/class/net/dummy98' )
3356 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '25-rps-cpu-0-invalid' )
3357 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3359 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3360 remove_network_unit ( '25-rps-cpu-0-invalid.link' )
3362 # invalid -> unchanged
3363 copy_network_unit ( '24-rps-cpu-invalid.link' )
3364 udevadm_trigger ( '/sys/class/net/dummy98' )
3365 self
. networkctl_check_unit ( 'dummy98' , '12-dummy' , '12-dummy' , '24-rps-cpu-invalid' )
3366 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
3368 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
3369 remove_network_unit ( '24-rps-cpu-invalid.link' )
3371 class NetworkdL2TPTests ( unittest
. TestCase
, Utilities
):
3379 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_netlink' )
3380 def test_l2tp_udp ( self
):
3381 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3382 '25-l2tp-udp.netdev' , '25-l2tp.network' )
3385 self
. wait_online ( 'test1:routable' , 'l2tp-ses1:degraded' , 'l2tp-ses2:degraded' )
3386 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-l2tp-dummy' )
3387 self
. networkctl_check_unit ( 'l2tp-ses1' , '25-l2tp-udp' , '25-l2tp' )
3388 self
. networkctl_check_unit ( 'l2tp-ses2' , '25-l2tp-udp' , '25-l2tp' )
3390 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
3392 self
. assertRegex ( output
, "Tunnel 10, encap UDP" )
3393 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
3394 self
. assertRegex ( output
, "Peer tunnel 11" )
3395 self
. assertRegex ( output
, "UDP source / dest ports: 3000/4000" )
3396 self
. assertRegex ( output
, "UDP checksum: enabled" )
3398 output
= check_output ( 'ip l2tp show session tid 10 session_id 15' )
3400 self
. assertRegex ( output
, "Session 15 in tunnel 10" )
3401 self
. assertRegex ( output
, "Peer session 16, tunnel 11" )
3402 self
. assertRegex ( output
, "interface name: l2tp-ses1" )
3404 output
= check_output ( 'ip l2tp show session tid 10 session_id 17' )
3406 self
. assertRegex ( output
, "Session 17 in tunnel 10" )
3407 self
. assertRegex ( output
, "Peer session 18, tunnel 11" )
3408 self
. assertRegex ( output
, "interface name: l2tp-ses2" )
3411 '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3412 '25-l2tp-udp.netdev' , '25-l2tp.network' )
3414 self
. wait_online ( 'test1:routable' , 'l2tp-ses1:degraded' , 'l2tp-ses2:degraded' )
3416 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_ip' , 'l2tp_netlink' )
3417 def test_l2tp_ip ( self
):
3418 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3419 '25-l2tp-ip.netdev' , '25-l2tp.network' )
3422 self
. wait_online ( 'test1:routable' , 'l2tp-ses3:degraded' , 'l2tp-ses4:degraded' )
3423 self
. networkctl_check_unit ( 'test1' , '11-dummy' , '25-l2tp-dummy' )
3424 self
. networkctl_check_unit ( 'l2tp-ses3' , '25-l2tp-ip' , '25-l2tp' )
3425 self
. networkctl_check_unit ( 'l2tp-ses4' , '25-l2tp-ip' , '25-l2tp' )
3427 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
3429 self
. assertRegex ( output
, "Tunnel 10, encap IP" )
3430 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
3431 self
. assertRegex ( output
, "Peer tunnel 12" )
3433 output
= check_output ( 'ip l2tp show session tid 10 session_id 25' )
3435 self
. assertRegex ( output
, "Session 25 in tunnel 10" )
3436 self
. assertRegex ( output
, "Peer session 26, tunnel 12" )
3437 self
. assertRegex ( output
, "interface name: l2tp-ses3" )
3439 output
= check_output ( 'ip l2tp show session tid 10 session_id 27' )
3441 self
. assertRegex ( output
, "Session 27 in tunnel 10" )
3442 self
. assertRegex ( output
, "Peer session 28, tunnel 12" )
3443 self
. assertRegex ( output
, "interface name: l2tp-ses4" )
3446 '11-dummy.netdev' , '25-l2tp-dummy.network' ,
3447 '25-l2tp-ip.netdev' , '25-l2tp.network' )
3449 self
. wait_online ( 'test1:routable' , 'l2tp-ses3:degraded' , 'l2tp-ses4:degraded' )
3451 class NetworkdNetworkTests ( unittest
. TestCase
, Utilities
):
3459 def test_ID_NET_MANAGED_BY ( self
):
3460 copy_network_unit ( '11-dummy.netdev' , '11-dummy-unmanaged.link' , '11-dummy.network' )
3462 self
. wait_online ( 'test1:off' , setup_state
= 'unmanaged' )
3464 check_output ( 'ip link set dev test1 up' )
3465 self
. wait_online ( 'test1:degraded' , setup_state
= 'unmanaged' )
3467 check_output ( 'ip link set dev test1 down' )
3468 self
. wait_online ( 'test1:off' , setup_state
= 'unmanaged' )
3470 def verify_address_static (
3500 output
= check_output ( 'ip address show dev dummy98' )
3504 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
3505 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
3506 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
3507 self
. assertIn ( 'inet6 2001:db8:0:f101::15/64 scope global' , output
)
3508 self
. assertIn ( 'inet6 2001:db8:0:f101::16/64 scope global' , output
)
3509 self
. assertIn ( 'inet6 2001:db8:0:f102::15/64 scope global' , output
)
3512 self
. assertIn ( f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1} ' , output
)
3513 self
. assertIn ( f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2} ' , output
)
3514 self
. assertIn ( f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3} ' , output
)
3517 self
. assertIn ( f
'inet 10.4.1.1/24 {broadcast1} scope global dummy98' , output
)
3518 self
. assertIn ( f
'inet 10.4.2.1/24 {broadcast2} scope global dummy98' , output
)
3519 self
. assertIn ( f
'inet 10.4.3.1/24 {broadcast3} scope global dummy98' , output
)
3522 self
. assertIn ( f
'inet 10.5.1.1 {peer1} scope global dummy98' , output
)
3523 self
. assertIn ( f
'inet 10.5.2.1 {peer2} scope global dummy98' , output
)
3524 self
. assertIn ( f
'inet 10.5.3.1 {peer3} scope global dummy98' , output
)
3525 self
. assertIn ( f
'inet6 2001:db8:0:f103::1 {peer4} scope global' , output
)
3526 self
. assertIn ( f
'inet6 2001:db8:0:f103::2 {peer5} scope global' , output
)
3527 self
. assertIn ( f
'inet6 2001:db8:0:f103::3 {peer6} scope global' , output
)
3530 self
. assertIn ( f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98' , output
)
3531 self
. assertIn ( f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98' , output
)
3534 self
. assertIn ( f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global {deprecated1} dummy98' , output
)
3535 self
. assertIn ( f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global {deprecated2} dummy98' , output
)
3536 self
. assertIn ( f
'inet6 2001:db8:0:f104::1/64 scope global {deprecated3} ' , output
)
3537 self
. assertIn ( f
'inet6 2001:db8:0:f104::2/64 scope global {deprecated4} ' , output
)
3540 self
. assertRegex ( output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98' )
3541 self
. assertRegex ( output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global' )
3543 output_route
= check_output ( 'ip -4 route show dev dummy98 10.8.1.0/24' )
3545 self
. assertIn ( f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric} ' , output_route
)
3547 output_route
= check_output ( 'ip -6 route show dev dummy98 2001:db8:0:f105::/64' )
3549 self
. assertIn ( f
'2001:db8:0:f105::/64 proto kernel metric {route_metric} ' , output_route
)
3552 self
. assertIn ( f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global {flag1} dummy98' , output
)
3553 self
. assertIn ( f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global {flag2} dummy98' , output
)
3554 self
. assertIn ( f
'inet6 2001:db8:0:f106::1/64 scope global {flag3} ' , output
)
3555 self
. assertIn ( f
'inet6 2001:db8:0:f106::2/64 scope global {flag4} ' , output
)
3558 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
3559 prefix16
= ip4_null_16
[:- len ( '.0.1' )]
3560 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
3561 prefix24
= ip4_null_24
[:- len ( '.1' )]
3562 self
. assertIn ( f
'inet {ip4_null_16} /16 brd {prefix16} .255.255 scope global subnet16' , output
)
3563 self
. assertIn ( f
'inet {ip4_null_24} /24 brd {prefix24} .255 scope global subnet24' , output
)
3564 self
. assertIn ( f
'inet6 {ip6_null_73} /73 scope global' , output
)
3565 self
. assertIn ( f
'inet6 {ip6_null_74} /74 scope global' , output
)
3568 self
. assertNotIn ( '10.4.4.1' , output
)
3569 self
. assertNotIn ( '10.5.4.1' , output
)
3570 self
. assertNotIn ( '10.5.5.1' , output
)
3571 self
. assertNotIn ( '10.8.2.1' , output
)
3572 self
. assertNotIn ( '10.9.3.1' , output
)
3573 self
. assertNotIn ( '2001:db8:0:f101::2' , output
)
3574 self
. assertNotIn ( '2001:db8:0:f103::4' , output
)
3577 self
. check_netlabel ( 'dummy98' , r
'10\.10\.1\.0/24' )
3579 check_json ( networkctl_json ())
3581 @expectedFailureIfKernelReturnsInvalidFlags ()
3582 def test_address_static ( self
):
3583 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
3584 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
3585 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
3586 self
. setup_nftset ( 'ifindex' , 'iface_index' )
3589 self
. wait_online ( 'dummy98:routable' )
3593 output
= check_output ( 'ip -4 --json address show dev dummy98' )
3594 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
3595 if i
[ 'label' ] == 'subnet16' :
3596 ip4_null_16
= i
[ 'local' ]
3597 elif i
[ 'label' ] == 'subnet24' :
3598 ip4_null_24
= i
[ 'local' ]
3599 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
3600 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
3604 output
= check_output ( 'ip -6 --json address show dev dummy98' )
3605 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
3606 if i
[ 'prefixlen' ] == 73 :
3607 ip6_null_73
= i
[ 'local' ]
3608 elif i
[ 'prefixlen' ] == 74 :
3609 ip6_null_74
= i
[ 'local' ]
3610 self
. assertTrue ( ip6_null_73
. endswith ( ':1' ))
3611 self
. assertTrue ( ip6_null_74
. endswith ( ':1' ))
3613 self
. verify_address_static (
3618 broadcast2
= ' brd 10.4.2.255' ,
3619 broadcast3
= ' brd 10.4.3.63' ,
3620 peer1
= ' peer 10.5.1.101/24' ,
3621 peer2
= ' peer 10.5.2.101/24' ,
3622 peer3
= '/24 brd 10.5.3.255' ,
3623 peer4
= ' peer 2001:db8:0:f103::101/128' ,
3624 peer5
= ' peer 2001:db8:0:f103::102/128' ,
3629 deprecated2
= ' deprecated' ,
3631 deprecated4
= ' deprecated' ,
3633 flag1
= ' noprefixroute' ,
3635 flag3
= ' noprefixroute' ,
3636 flag4
= ' home mngtmpaddr' ,
3637 ip4_null_16
= ip4_null_16
,
3638 ip4_null_24
= ip4_null_24
,
3639 ip6_null_73
= ip6_null_73
,
3640 ip6_null_74
= ip6_null_74
,
3643 self
. check_nftset ( 'addr4' , r
'10\.10\.1\.1' )
3644 self
. check_nftset ( 'network4' , r
'10\.10\.1\.0/24' )
3645 self
. check_nftset ( 'ifindex' , 'dummy98' )
3647 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
3649 copy_network_unit ( '25-address-static.network.d/10-override.conf' )
3651 self
. wait_online ( 'dummy98:routable' )
3652 self
. verify_address_static (
3653 label1
= 'new-label1' ,
3655 label3
= 'new-label3' ,
3656 broadcast1
= ' brd 10.4.1.255' ,
3658 broadcast3
= ' brd 10.4.3.31' ,
3659 peer1
= ' peer 10.5.1.102/24' ,
3660 peer2
= '/24 brd 10.5.2.255' ,
3661 peer3
= ' peer 10.5.3.102/24' ,
3662 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3664 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3667 deprecated1
= ' deprecated' ,
3669 deprecated3
= ' deprecated' ,
3673 flag2
= ' noprefixroute' ,
3674 flag3
= ' home mngtmpaddr' ,
3675 flag4
= ' noprefixroute' ,
3676 ip4_null_16
= ip4_null_16
,
3677 ip4_null_24
= ip4_null_24
,
3678 ip6_null_73
= ip6_null_73
,
3679 ip6_null_74
= ip6_null_74
,
3682 networkctl_reconfigure ( 'dummy98' )
3683 self
. wait_online ( 'dummy98:routable' )
3684 self
. verify_address_static (
3685 label1
= 'new-label1' ,
3687 label3
= 'new-label3' ,
3688 broadcast1
= ' brd 10.4.1.255' ,
3690 broadcast3
= ' brd 10.4.3.31' ,
3691 peer1
= ' peer 10.5.1.102/24' ,
3692 peer2
= '/24 brd 10.5.2.255' ,
3693 peer3
= ' peer 10.5.3.102/24' ,
3694 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3696 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3699 deprecated1
= ' deprecated' ,
3701 deprecated3
= ' deprecated' ,
3705 flag2
= ' noprefixroute' ,
3706 flag3
= ' home mngtmpaddr' ,
3707 flag4
= ' noprefixroute' ,
3708 ip4_null_16
= ip4_null_16
,
3709 ip4_null_24
= ip4_null_24
,
3710 ip6_null_73
= ip6_null_73
,
3711 ip6_null_74
= ip6_null_74
,
3715 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
3716 check_output ( 'ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever' )
3717 check_output ( 'ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever' )
3718 output
= check_output ( 'ip address show dev dummy98' )
3720 self
. assertNotRegex ( output
, '10.7.1.1/24 .* deprecated' )
3721 self
. assertNotRegex ( output
, '2001:db8:0:f104::1/64 .* deprecated' )
3723 # 2. reconfigure the interface, and check the deprecated flag is set again
3724 networkctl_reconfigure ( 'dummy98' )
3725 self
. wait_online ( 'dummy98:routable' )
3726 self
. verify_address_static (
3727 label1
= 'new-label1' ,
3729 label3
= 'new-label3' ,
3730 broadcast1
= ' brd 10.4.1.255' ,
3732 broadcast3
= ' brd 10.4.3.31' ,
3733 peer1
= ' peer 10.5.1.102/24' ,
3734 peer2
= '/24 brd 10.5.2.255' ,
3735 peer3
= ' peer 10.5.3.102/24' ,
3736 peer4
= ' peer 2001:db8:0:f103::201/128' ,
3738 peer6
= ' peer 2001:db8:0:f103::203/128' ,
3741 deprecated1
= ' deprecated' ,
3743 deprecated3
= ' deprecated' ,
3747 flag2
= ' noprefixroute' ,
3748 flag3
= ' home mngtmpaddr' ,
3749 flag4
= ' noprefixroute' ,
3750 ip4_null_16
= ip4_null_16
,
3751 ip4_null_24
= ip4_null_24
,
3752 ip6_null_73
= ip6_null_73
,
3753 ip6_null_74
= ip6_null_74
,
3756 # test for ENOBUFS issue #17012 (with reload)
3757 copy_network_unit ( '25-address-static.network.d/10-many-address.conf' )
3759 self
. wait_online ( 'dummy98:routable' )
3760 output
= check_output ( 'ip -4 address show dev dummy98' )
3761 for i
in range ( 1 , 254 ):
3762 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3764 # (with reconfigure)
3765 networkctl_reconfigure ( 'dummy98' )
3766 self
. wait_online ( 'dummy98:routable' )
3767 output
= check_output ( 'ip -4 address show dev dummy98' )
3768 for i
in range ( 1 , 254 ):
3769 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3771 # test for an empty string assignment for Address= in [Network]
3772 copy_network_unit ( '25-address-static.network.d/20-clear-addresses.conf' )
3774 self
. wait_online ( 'dummy98:routable' )
3775 output
= check_output ( 'ip -4 address show dev dummy98' )
3776 for i
in range ( 1 , 254 ):
3777 self
. assertNotIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3778 self
. assertIn ( 'inet 10.4.0.1/16 brd 10.4.255.255' , output
)
3780 def test_address_ipv4acd ( self
):
3781 check_output ( 'ip netns add ns99' )
3782 check_output ( 'ip link add veth99 type veth peer veth-peer' )
3783 check_output ( 'ip link set veth-peer netns ns99' )
3784 check_output ( 'ip link set veth99 up' )
3785 check_output ( 'ip netns exec ns99 ip link set veth-peer up' )
3786 check_output ( 'ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer' )
3788 copy_network_unit ( '25-address-ipv4acd-veth99.network' , copy_dropins
= False )
3790 self
. wait_online ( 'veth99:routable' )
3792 output
= check_output ( 'ip -4 address show dev veth99' )
3794 self
. assertNotIn ( '192.168.100.10/24' , output
)
3795 self
. assertIn ( '192.168.100.11/24' , output
)
3797 copy_network_unit ( '25-address-ipv4acd-veth99.network.d/conflict-address.conf' )
3799 self
. wait_operstate ( 'veth99' , operstate
= 'routable' , setup_state
= 'configuring' , setup_timeout
= 10 )
3801 output
= check_output ( 'ip -4 address show dev veth99' )
3803 self
. assertNotIn ( '192.168.100.10/24' , output
)
3804 self
. assertIn ( '192.168.100.11/24' , output
)
3806 def test_address_peer_ipv4 ( self
):
3807 # test for issue #17304
3808 copy_network_unit ( '25-address-peer-ipv4.network' , '12-dummy.netdev' )
3810 for trial
in range ( 2 ):
3816 self
. wait_online ( 'dummy98:routable' )
3818 output
= check_output ( 'ip -4 address show dev dummy98' )
3819 self
. assertIn ( 'inet 100.64.0.1 peer 100.64.0.2/32 scope global' , output
)
3821 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
3822 def test_prefix_route ( self
):
3823 copy_network_unit ( '25-prefix-route-with-vrf.network' , '12-dummy.netdev' ,
3824 '25-prefix-route-without-vrf.network' , '11-dummy.netdev' ,
3825 '25-vrf.netdev' , '25-vrf.network' )
3826 for trial
in range ( 2 ):
3832 self
. wait_online ( 'dummy98:routable' , 'test1:routable' , 'vrf99:carrier' )
3834 output
= check_output ( 'ip route show table 42 dev dummy98' )
3835 print ( '### ip route show table 42 dev dummy98' )
3837 self
. assertRegex ( output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1' )
3838 self
. assertRegex ( output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1' )
3839 self
. assertRegex ( output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1' )
3840 self
. assertRegex ( output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1' )
3841 self
. assertRegex ( output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1' )
3842 self
. assertRegex ( output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1' )
3843 self
. assertRegex ( output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1' )
3844 output
= check_output ( 'ip -6 route show table 42 dev dummy98' )
3845 print ( '### ip -6 route show table 42 dev dummy98' )
3849 self
. assertRegex ( output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium' )
3850 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3851 self
. assertRegex ( output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium' )
3852 self
. assertRegex ( output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium' )
3853 self
. assertRegex ( output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium' )
3854 self
. assertRegex ( output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium' )
3855 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3856 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3860 output
= check_output ( 'ip route show dev test1' )
3861 print ( '### ip route show dev test1' )
3863 self
. assertRegex ( output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1' )
3864 output
= check_output ( 'ip route show table local dev test1' )
3865 print ( '### ip route show table local dev test1' )
3867 self
. assertRegex ( output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1' )
3868 self
. assertRegex ( output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1' )
3869 self
. assertRegex ( output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1' )
3870 self
. assertRegex ( output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1' )
3871 self
. assertRegex ( output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1' )
3872 self
. assertRegex ( output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1' )
3873 output
= check_output ( 'ip -6 route show dev test1' )
3874 print ( '### ip -6 route show dev test1' )
3876 self
. assertRegex ( output
, 'fdde:12:22::1 proto kernel metric 256 pref medium' )
3877 self
. assertRegex ( output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium' )
3878 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3879 output
= check_output ( 'ip -6 route show table local dev test1' )
3880 print ( '### ip -6 route show table local dev test1' )
3882 self
. assertRegex ( output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium' )
3883 self
. assertRegex ( output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium' )
3884 self
. assertRegex ( output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium' )
3885 self
. assertRegex ( output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium' )
3886 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3888 def test_configure_without_carrier ( self
):
3889 copy_network_unit ( '11-dummy.netdev' )
3891 self
. wait_operstate ( 'test1' , 'off' , '' )
3892 check_output ( 'ip link set dev test1 up carrier off' )
3894 copy_network_unit ( '25-test1.network.d/configure-without-carrier.conf' , copy_dropins
= False )
3896 self
. wait_online ( 'test1:no-carrier' )
3898 carrier_map
= { 'on' : '1' , 'off' : '0' }
3899 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3900 for carrier
in [ 'off' , 'on' , 'off' ]:
3901 with self
. subTest ( carrier
= carrier
):
3902 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3903 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3904 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3906 output
= networkctl_status ( 'test1' )
3908 self
. assertRegex ( output
, '192.168.0.15' )
3909 self
. assertRegex ( output
, '192.168.0.1' )
3910 self
. assertRegex ( output
, routable_map
[ carrier
])
3912 def test_configure_without_carrier_yes_ignore_carrier_loss_no ( self
):
3913 copy_network_unit ( '11-dummy.netdev' )
3915 self
. wait_operstate ( 'test1' , 'off' , '' )
3916 check_output ( 'ip link set dev test1 up carrier off' )
3918 copy_network_unit ( '25-test1.network' )
3920 self
. wait_online ( 'test1:no-carrier' )
3922 carrier_map
= { 'on' : '1' , 'off' : '0' }
3923 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3924 for ( carrier
, have_config
) in [( 'off' , True ), ( 'on' , True ), ( 'off' , False )]:
3925 with self
. subTest ( carrier
= carrier
, have_config
= have_config
):
3926 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3927 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3928 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3930 output
= networkctl_status ( 'test1' )
3933 self
. assertRegex ( output
, '192.168.0.15' )
3934 self
. assertRegex ( output
, '192.168.0.1' )
3936 self
. assertNotRegex ( output
, '192.168.0.15' )
3937 self
. assertNotRegex ( output
, '192.168.0.1' )
3938 self
. assertRegex ( output
, routable_map
[ carrier
])
3940 def check_routing_policy_rule_test1 ( self
):
3941 print ( '### Checking routing policy rules requested by test1' )
3943 output
= check_output ( 'ip rule list iif test1 priority 111' )
3945 self
. assertRegex ( output
, r
'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7' )
3947 output
= check_output ( 'ip -6 rule list iif test1 priority 100' )
3949 self
. assertIn ( '100: from all iif test1 lookup 8' , output
)
3951 output
= check_output ( 'ip rule list iif test1 priority 101' )
3953 self
. assertIn ( '101: from all iif test1 lookup 9' , output
)
3955 output
= check_output ( 'ip -6 rule list iif test1 priority 101' )
3957 self
. assertIn ( '101: from all iif test1 lookup 9' , output
)
3959 output
= check_output ( 'ip rule list iif test1 priority 102' )
3961 self
. assertIn ( '102: from 0.0.0.0/8 iif test1 lookup 10' , output
)
3963 output
= check_output ( 'ip rule list iif test1 priority 103' )
3965 self
. assertIn ( '103: from 10.0.0.0/16 iif test1 lookup 11 goto 111' , output
)
3967 output
= check_output ( 'ip rule list iif test1 priority 104' )
3969 self
. assertIn ( '104: from 10.1.0.0/16 iif test1 lookup 12 nop' , output
)
3971 output
= check_output ( 'ip rule list iif test1 priority 200' )
3973 self
. assertIn ( '200: from all fwmark 0/0x1 iif test1 lookup 20' , output
)
3975 output
= check_output ( 'ip rule list iif test1 priority 201' )
3977 self
. assertIn ( '201: from all fwmark 0x7/0xff iif test1 lookup 21' , output
)
3979 output
= check_output ( 'ip rule list iif test1 priority 202' )
3981 self
. assertIn ( '202: from all fwmark 0x270f iif test1 lookup 22' , output
)
3983 output
= check_output ( 'ip rule list to 192.0.2.0/26' )
3985 self
. assertIn ( 'to 192.0.2.0/26 lookup 1001' , output
)
3987 output
= check_output ( 'ip rule list to 192.0.2.64/26' )
3989 self
. assertIn ( 'to 192.0.2.64/26 lookup 1001' , output
)
3991 output
= check_output ( 'ip rule list to 192.0.2.128/26' )
3993 self
. assertIn ( 'to 192.0.2.128/26 lookup 1001' , output
)
3995 output
= check_output ( 'ip rule list to 192.0.2.192/26' )
3997 self
. assertIn ( 'to 192.0.2.192/26 lookup 1001' , output
)
3999 def check_routing_policy_rule_dummy98 ( self
):
4000 print ( '### Checking routing policy rules requested by dummy98' )
4002 output
= check_output ( 'ip rule list priority 112' )
4004 self
. assertRegex ( output
, r
'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8' )
4006 def _test_routing_policy_rule ( self
, manage_foreign_routes
):
4007 if not manage_foreign_routes
:
4008 copy_networkd_conf_dropin ( 'networkd-manage-foreign-rules-no.conf' )
4009 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' )
4013 check_output ( 'ip -4 rule add priority 20001 table 9999 from 10.10.0.0/16' )
4014 check_output ( 'ip -6 rule add priority 20001 table 9999 from 2001:db8:0:1::/64' )
4017 self
. wait_online ( 'test1:degraded' )
4019 self
. check_routing_policy_rule_test1 ()
4020 check_json ( networkctl_json ())
4022 output
= check_output ( 'ip -4 rule list priority 20001 table 9999 from 10.10.0.0/16' )
4024 if manage_foreign_routes
:
4025 self
. assertEqual ( output
, '' )
4027 self
. assertIn ( output
, '20001: from 10.10.0.0/16 lookup 9999' )
4028 check_output ( 'ip -4 rule del priority 20001 table 9999 from 10.10.0.0/16' )
4030 output
= check_output ( 'ip -6 rule list priority 20001 table 9999 from 2001:db8:0:1::/64' )
4032 if manage_foreign_routes
:
4033 self
. assertEqual ( output
, '' )
4035 self
. assertIn ( output
, '20001: from 2001:db8:0:1::/64 lookup 9999' )
4036 check_output ( 'ip -6 rule del priority 20001 table 9999 from 2001:db8:0:1::/64' )
4038 def test_routing_policy_rule ( self
):
4040 for manage_foreign_routes
in [ True , False ]:
4046 print ( f
'### test_routing_policy_rule(manage_foreign_routes= {manage_foreign_routes} )' )
4047 with self
. subTest ( manage_foreign_routes
= manage_foreign_routes
):
4048 self
._ test
_ routing
_ policy
_ rule
( manage_foreign_routes
)
4050 def test_routing_policy_rule_restart_and_reconfigure ( self
):
4051 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' ,
4052 '25-routing-policy-rule-dummy98.network' , '12-dummy.netdev' )
4054 # For #11280 and #34068.
4056 for trial
in range ( 3 ):
4057 restart_networkd ( show_logs
=( trial
> 0 ))
4058 self
. wait_online ( 'test1:degraded' , 'dummy98:degraded' )
4060 self
. check_routing_policy_rule_test1 ()
4061 self
. check_routing_policy_rule_dummy98 ()
4063 networkctl_reconfigure ( 'test1' )
4064 self
. wait_online ( 'test1:degraded' )
4066 self
. check_routing_policy_rule_test1 ()
4067 self
. check_routing_policy_rule_dummy98 ()
4069 networkctl_reconfigure ( 'dummy98' )
4070 self
. wait_online ( 'dummy98:degraded' )
4072 self
. check_routing_policy_rule_test1 ()
4073 self
. check_routing_policy_rule_dummy98 ()
4075 def test_routing_policy_rule_reconfigure ( self
):
4076 copy_network_unit ( '25-routing-policy-rule-reconfigure2.network' , '11-dummy.netdev' )
4078 self
. wait_online ( 'test1:degraded' )
4080 output
= check_output ( 'ip rule list table 1011' )
4082 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4083 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4084 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4085 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4087 output
= check_output ( 'ip -6 rule list table 1011' )
4089 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4091 copy_network_unit ( '25-routing-policy-rule-reconfigure1.network' , '11-dummy.netdev' )
4093 self
. wait_online ( 'test1:degraded' )
4095 output
= check_output ( 'ip rule list table 1011' )
4097 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4098 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4099 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4100 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4102 output
= check_output ( 'ip -6 rule list table 1011' )
4104 self
. assertNotIn ( '10112: from all oif test1 lookup 1011' , output
)
4105 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4107 call ( 'ip rule delete priority 10111' )
4108 call ( 'ip rule delete priority 10112' )
4109 call ( 'ip rule delete priority 10113' )
4110 call ( 'ip rule delete priority 10114' )
4111 call ( 'ip -6 rule delete priority 10113' )
4113 output
= check_output ( 'ip rule list table 1011' )
4115 self
. assertEqual ( output
, '' )
4117 output
= check_output ( 'ip -6 rule list table 1011' )
4119 self
. assertEqual ( output
, '' )
4121 networkctl_reconfigure ( 'test1' )
4122 self
. wait_online ( 'test1:degraded' )
4124 output
= check_output ( 'ip rule list table 1011' )
4126 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
4127 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
4128 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4129 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
4131 output
= check_output ( 'ip -6 rule list table 1011' )
4133 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
4135 def test_routing_policy_rule_manual ( self
):
4139 '25-routing-policy-rule-manual.network' )
4141 self
. wait_operstate ( 'test1' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 20 )
4143 check_output ( 'ip link add test2 type dummy' )
4144 self
. wait_operstate ( 'test2' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 20 )
4146 networkctl ( 'up' , 'test2' )
4147 self
. wait_online ( 'test2:degraded' )
4149 # The request for the routing policy rules are bound to test1. Hence, we need to wait for the rules
4150 # being configured explicitly.
4154 output
= check_output ( 'ip -4 rule list table 51819' )
4155 if output
!= '10: from all lookup 51819 suppress_prefixlength 0 proto static' :
4158 output
= check_output ( 'ip -6 rule list table 51819' )
4159 if output
!= '10: from all lookup 51819 suppress_prefixlength 0 proto static' :
4162 output
= check_output ( 'ip -4 rule list table 51820' )
4163 if output
!= '11: not from all fwmark 0x38f lookup 51820 proto static' :
4166 output
= check_output ( 'ip -6 rule list table 51820' )
4167 if output
!= '11: not from all fwmark 0x38f lookup 51820 proto static' :
4172 self
. assertFalse ( True )
4174 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable ()
4175 def test_routing_policy_rule_port_range ( self
):
4176 copy_network_unit ( '25-fibrule-port-range.network' , '11-dummy.netdev' )
4178 self
. wait_online ( 'test1:degraded' )
4180 output
= check_output ( 'ip rule' )
4182 self
. assertIn ( '111:' , output
)
4183 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4184 self
. assertIn ( 'sport 1123-1150 ' , output
)
4185 self
. assertIn ( 'dport 3224-3290 ' , output
)
4186 self
. assertRegex ( output
, 'ipproto (tcp|ipproto-6) ' )
4187 self
. assertIn ( 'lookup 7 ' , output
)
4189 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable ()
4190 def test_routing_policy_rule_invert ( self
):
4191 copy_network_unit ( '25-fibrule-invert.network' , '11-dummy.netdev' )
4193 self
. wait_online ( 'test1:degraded' )
4195 output
= check_output ( 'ip rule' )
4197 self
. assertIn ( '111:' , output
)
4198 self
. assertIn ( 'not ' , output
)
4199 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4200 self
. assertRegex ( output
, 'ipproto (tcp|ipproto-6) ' )
4201 self
. assertIn ( 'lookup 7 ' , output
)
4203 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable ()
4204 def test_routing_policy_rule_l3mdev ( self
):
4205 copy_network_unit ( '25-fibrule-l3mdev.network' , '11-dummy.netdev' )
4207 self
. wait_online ( 'test1:degraded' )
4209 output
= check_output ( 'ip rule' )
4211 self
. assertIn ( '1500: from all lookup [l3mdev-table]' , output
)
4212 self
. assertIn ( '2000: from all lookup [l3mdev-table] unreachable' , output
)
4214 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable ()
4215 def test_routing_policy_rule_uidrange ( self
):
4216 copy_network_unit ( '25-fibrule-uidrange.network' , '11-dummy.netdev' )
4218 self
. wait_online ( 'test1:degraded' )
4220 output
= check_output ( 'ip rule' )
4222 self
. assertIn ( '111:' , output
)
4223 self
. assertIn ( 'from 192.168.100.18 ' , output
)
4224 self
. assertIn ( 'lookup 7 ' , output
)
4225 self
. assertIn ( 'uidrange 100-200 ' , output
)
4227 def _check_route_static ( self
, test1_is_managed
: bool ):
4228 output
= networkctl_status ( 'dummy98' )
4230 output
= networkctl_status ( 'test1' )
4233 print ( '### ip -6 route show dev dummy98' )
4234 output
= check_output ( 'ip -6 route show dev dummy98' )
4236 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
4237 self
. assertIn ( '2001:1234:5:8f63::1 proto kernel' , output
)
4238 self
. assertIn ( '2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static' , output
)
4240 print ( '### ip -6 route show default' )
4241 output
= check_output ( 'ip -6 route show default' )
4243 self
. assertIn ( 'default' , output
)
4244 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff' , output
)
4246 print ( '### ip -4 route show dev dummy98' )
4247 output
= check_output ( 'ip -4 route show dev dummy98' )
4249 self
. assertIn ( '149.10.124.48/28 proto kernel scope link src 149.10.124.58' , output
)
4250 self
. assertIn ( '149.10.124.64 proto static scope link' , output
)
4251 self
. assertIn ( '169.254.0.0/16 proto static scope link metric 2048' , output
)
4252 self
. assertIn ( '192.168.1.1 proto static scope link initcwnd 20' , output
)
4253 self
. assertIn ( '192.168.1.2 proto static scope link initrwnd 30' , output
)
4254 self
. assertIn ( '192.168.1.3 proto static scope link advmss 30' , output
)
4255 self
. assertIn ( '192.168.1.4 proto static scope link hoplimit 122' , output
)
4256 self
. assertIn ( 'multicast 149.10.123.4 proto static' , output
)
4258 print ( '### ip -4 route show dev dummy98 default' )
4259 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4261 self
. assertIn ( 'default via 149.10.125.65 proto static onlink' , output
)
4262 self
. assertIn ( 'default via 149.10.124.64 proto static' , output
)
4263 self
. assertIn ( 'default proto static' , output
)
4264 self
. assertIn ( 'default via 1.1.8.104 proto static' , output
)
4266 print ( '### ip -4 route show table local dev dummy98' )
4267 output
= check_output ( 'ip -4 route show table local dev dummy98' )
4269 self
. assertIn ( 'local 149.10.123.1 proto static scope host' , output
)
4270 self
. assertIn ( 'anycast 149.10.123.2 proto static scope link' , output
)
4271 self
. assertIn ( 'broadcast 149.10.123.3 proto static scope link' , output
)
4273 print ( '### ip -4 route show type blackhole' )
4274 output
= check_output ( 'ip -4 route show type blackhole' )
4276 self
. assertIn ( 'blackhole 202.54.1.2 proto static' , output
)
4278 print ( '### ip -4 route show type unreachable' )
4279 output
= check_output ( 'ip -4 route show type unreachable' )
4281 self
. assertIn ( 'unreachable 202.54.1.3 proto static' , output
)
4283 print ( '### ip -4 route show type prohibit' )
4284 output
= check_output ( 'ip -4 route show type prohibit' )
4286 self
. assertIn ( 'prohibit 202.54.1.4 proto static' , output
)
4288 print ( '### ip -6 route show type blackhole' )
4289 output
= check_output ( 'ip -6 route show type blackhole' )
4291 self
. assertIn ( 'blackhole 2001:1234:5678::2 dev lo proto static' , output
)
4293 print ( '### ip -6 route show type unreachable' )
4294 output
= check_output ( 'ip -6 route show type unreachable' )
4296 self
. assertIn ( 'unreachable 2001:1234:5678::3 dev lo proto static' , output
)
4298 print ( '### ip -6 route show type prohibit' )
4299 output
= check_output ( 'ip -6 route show type prohibit' )
4301 self
. assertIn ( 'prohibit 2001:1234:5678::4 dev lo proto static' , output
)
4303 print ( '### ip route show 192.168.10.1' )
4304 output
= check_output ( 'ip route show 192.168.10.1' )
4306 self
. assertIn ( '192.168.10.1 proto static' , output
)
4307 self
. assertIn ( 'nexthop via 149.10.123.59 dev test1 weight 20' , output
)
4308 self
. assertIn ( 'nexthop via 149.10.123.60 dev test1 weight 30' , output
)
4309 self
. assertIn ( 'nexthop via 149.10.124.59 dev dummy98 weight 10' , output
)
4310 self
. assertIn ( 'nexthop via 149.10.124.60 dev dummy98 weight 5' , output
)
4312 print ( '### ip route show 192.168.10.2' )
4313 output
= check_output ( 'ip route show 192.168.10.2' )
4315 # old ip command does not show IPv6 gateways...
4316 self
. assertIn ( '192.168.10.2 proto static' , output
)
4317 self
. assertIn ( 'nexthop' , output
)
4318 self
. assertIn ( 'dev test1 weight 20' , output
)
4319 self
. assertIn ( 'dev test1 weight 30' , output
)
4320 self
. assertIn ( 'dev dummy98 weight 10' , output
)
4321 self
. assertIn ( 'dev dummy98 weight 5' , output
)
4323 print ( '### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff' )
4324 output
= check_output ( 'ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff' )
4326 # old ip command does not show 'nexthop' keyword and weight...
4327 self
. assertIn ( '2001:1234:5:bfff:ff:ff:ff:ff' , output
)
4328 if test1_is_managed
:
4329 self
. assertIn ( 'via 2001:1234:5:6fff:ff:ff:ff:ff dev test1' , output
)
4330 self
. assertIn ( 'via 2001:1234:5:7fff:ff:ff:ff:ff dev test1' , output
)
4331 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98' , output
)
4332 self
. assertIn ( 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98' , output
)
4334 check_json ( networkctl_json ())
4336 def _check_unreachable_routes_removed ( self
):
4337 print ( '### ip -4 route show type blackhole' )
4338 output
= check_output ( 'ip -4 route show type blackhole' )
4340 self
. assertEqual ( output
, '' )
4342 print ( '### ip -4 route show type unreachable' )
4343 output
= check_output ( 'ip -4 route show type unreachable' )
4345 self
. assertEqual ( output
, '' )
4347 print ( '### ip -4 route show type prohibit' )
4348 output
= check_output ( 'ip -4 route show type prohibit' )
4350 self
. assertEqual ( output
, '' )
4352 print ( '### ip -6 route show type blackhole' )
4353 output
= check_output ( 'ip -6 route show type blackhole' )
4355 self
. assertEqual ( output
, '' )
4357 print ( '### ip -6 route show type unreachable' )
4358 output
= check_output ( 'ip -6 route show type unreachable' )
4360 self
. assertEqual ( output
, '' )
4362 print ( '### ip -6 route show type prohibit' )
4363 output
= check_output ( 'ip -6 route show type prohibit' )
4365 self
. assertEqual ( output
, '' )
4367 check_json ( networkctl_json ())
4369 def _test_route_static ( self
, manage_foreign_routes
):
4370 if not manage_foreign_routes
:
4371 copy_networkd_conf_dropin ( 'networkd-manage-foreign-routes-no.conf' )
4373 copy_network_unit ( '25-route-static.network' , '12-dummy.netdev' ,
4374 '25-route-static-test1.network' , '11-dummy.netdev' )
4376 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4377 self
._ check
_ route
_ static
( test1_is_managed
= True )
4380 remove_network_unit ( '25-route-static-test1.network' )
4382 self
. wait_online ( 'dummy98:routable' )
4383 self
. wait_operstate ( 'test1' , setup_state
= 'unmanaged' )
4384 self
._ check
_ route
_ static
( test1_is_managed
= False )
4386 # managing test1 again
4387 copy_network_unit ( '25-route-static-test1.network' )
4389 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4390 self
._ check
_ route
_ static
( test1_is_managed
= True )
4392 # adding an unmanaged interface
4393 check_output ( 'ip link add test2 type dummy' )
4394 self
. wait_operstate ( 'test2' , operstate
= 'off' , setup_state
= 'unmanaged' )
4395 self
._ check
_ route
_ static
( test1_is_managed
= True )
4397 # reconfiguring with another config, and check all routes managed by Manager are removed
4398 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
4400 self
. wait_online ( 'dummy98:routable' )
4401 self
._ check
_u nreachable
_ routes
_ removed
()
4403 # reconfiguring the original config again
4404 remove_network_unit ( '25-address-static.network' )
4406 self
. wait_online ( 'dummy98:routable' )
4407 self
._ check
_ route
_ static
( test1_is_managed
= True )
4409 # removing interface, and check all routes managed by Manager are removed
4410 remove_link ( 'dummy98' )
4412 self
._ check
_u nreachable
_ routes
_ removed
()
4414 def test_route_static ( self
):
4416 for manage_foreign_routes
in [ True , False ]:
4422 print ( f
'### test_route_static(manage_foreign_routes= {manage_foreign_routes} )' )
4423 with self
. subTest ( manage_foreign_routes
= manage_foreign_routes
):
4424 self
._ test
_ route
_ static
( manage_foreign_routes
)
4426 def test_route_static_issue_35047 ( self
):
4428 '25-route-static-issue-35047.network' ,
4429 '25-route-static-issue-35047.network.d/step1.conf' ,
4433 self
. wait_online ( 'dummy98:routable' )
4435 print ( '### ip -4 route show table all dev dummy98' )
4436 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4438 self
. assertIn ( '192.0.2.2 proto kernel scope link src 192.0.2.1' , output
)
4439 self
. assertIn ( 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1' , output
)
4440 self
. assertIn ( '198.51.100.0/24 via 192.0.2.2 proto static' , output
)
4442 check_output ( 'ip link set dev dummy98 down' )
4443 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 )
4444 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 )
4445 self
. wait_route_dropped ( 'dummy98' , '198.51.100.0/24 via 192.0.2.2 proto static' , ipv
= '-4' , table
= 'all' , timeout_sec
= 10 )
4447 print ( '### ip -4 route show table all dev dummy98' )
4448 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4450 self
. assertNotIn ( '192.0.2.2' , output
)
4451 self
. assertNotIn ( '192.0.2.1' , output
)
4452 self
. assertNotIn ( '198.51.100.0/24' , output
)
4454 remove_network_unit ( '25-route-static-issue-35047.network.d/step1.conf' )
4455 copy_network_unit ( '25-route-static-issue-35047.network.d/step2.conf' )
4457 self
. wait_online ( 'dummy98:routable' )
4459 print ( '### ip -4 route show table all dev dummy98' )
4460 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4462 self
. assertIn ( '192.0.2.2 proto static scope link' , output
)
4463 self
. assertIn ( 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1' , output
)
4464 self
. assertIn ( '198.51.100.0/24 via 192.0.2.2 proto static' , output
)
4466 def test_route_static_issue_37714 ( self
):
4467 copy_network_unit ( '12-dummy.netdev' , '25-route-static-issue-37714.network' )
4469 self
. wait_online ( 'dummy98:routable' )
4471 print ( '### ip -4 rule list table 249' )
4472 output
= check_output ( 'ip -4 rule list table 249' )
4474 self
. assertIn ( '32765: from 192.168.0.227 lookup 249 proto static' , output
)
4476 print ( '### ip -6 rule list table 249' )
4477 output
= check_output ( 'ip -6 rule list table 249' )
4479 self
. assertIn ( '32765: from 2000:f00::227 lookup 249 proto static' , output
)
4481 print ( '### ip -4 route show table all dev dummy98' )
4482 output
= check_output ( 'ip -4 route show table all dev dummy98' )
4484 self
. assertIn ( 'default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink' , output
)
4485 self
. assertIn ( '192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128' , output
)
4486 self
. assertIn ( '10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink' , output
)
4487 self
. assertIn ( '192.168.0.72 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink' , output
)
4488 self
. assertIn ( '192.168.0.193 proto static scope link src 192.168.0.227 metric 128' , output
)
4489 self
. assertIn ( 'local 192.168.0.227 table local proto kernel scope host src 192.168.0.227' , output
)
4490 self
. assertIn ( 'broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227' , output
)
4492 print ( '### ip -6 route show table all dev dummy98' )
4493 output
= check_output ( 'ip -6 route show table all dev dummy98' )
4495 self
. assertIn ( '2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium' , output
)
4496 self
. assertIn ( 'default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium' , output
)
4497 self
. assertIn ( 'fe80::/64 proto kernel metric 256 pref medium' , output
)
4498 self
. assertIn ( 'local 2000:f00::227 table local proto kernel metric 0 pref medium' , output
)
4499 self
. assertRegex ( output
, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium' , output
)
4500 self
. assertIn ( 'multicast ff00::/8 table local proto kernel metric 256 pref medium' , output
)
4502 def test_route_via_ipv6 ( self
):
4503 copy_network_unit ( '25-route-via-ipv6.network' , '12-dummy.netdev' )
4505 self
. wait_online ( 'dummy98:routable' )
4507 output
= networkctl_status ( 'dummy98' )
4510 print ( '### ip -6 route show dev dummy98' )
4511 output
= check_output ( 'ip -6 route show dev dummy98' )
4513 self
. assertRegex ( output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static' )
4514 self
. assertRegex ( output
, '2001:1234:5:8f63::1 proto kernel' )
4516 print ( '### ip -4 route show dev dummy98' )
4517 output
= check_output ( 'ip -4 route show dev dummy98' )
4519 self
. assertRegex ( output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58' )
4520 self
. assertRegex ( output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static' )
4522 @expectedFailureIfModuleIsNotAvailable ( 'tcp_dctcp' )
4523 def test_route_congctl ( self
):
4524 copy_network_unit ( '25-route-congctl.network' , '12-dummy.netdev' )
4526 self
. wait_online ( 'dummy98:routable' )
4528 print ( '### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
4529 output
= check_output ( 'ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
4531 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
4532 self
. assertIn ( 'congctl dctcp' , output
)
4534 print ( '### ip -4 route show dev dummy98 149.10.124.66' )
4535 output
= check_output ( 'ip -4 route show dev dummy98 149.10.124.66' )
4537 self
. assertIn ( '149.10.124.66 proto static' , output
)
4538 self
. assertIn ( 'congctl dctcp' , output
)
4539 self
. assertIn ( 'rto_min 300s' , output
)
4541 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
4542 def test_route_vrf ( self
):
4543 copy_network_unit ( '25-route-vrf.network' , '12-dummy.netdev' ,
4544 '25-vrf.netdev' , '25-vrf.network' )
4546 self
. wait_online ( 'dummy98:routable' , 'vrf99:carrier' )
4548 output
= check_output ( 'ip route show vrf vrf99' )
4550 self
. assertRegex ( output
, 'default via 192.168.100.1' )
4552 output
= check_output ( 'ip route show' )
4554 self
. assertNotRegex ( output
, 'default via 192.168.100.1' )
4556 def test_gateway_reconfigure ( self
):
4557 copy_network_unit ( '25-gateway-static.network' , '12-dummy.netdev' )
4559 self
. wait_online ( 'dummy98:routable' )
4560 print ( '### ip -4 route show dev dummy98 default' )
4561 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4563 self
. assertIn ( 'default via 149.10.124.59 proto static' , output
)
4564 self
. assertNotIn ( '149.10.124.60' , output
)
4566 remove_network_unit ( '25-gateway-static.network' )
4567 copy_network_unit ( '25-gateway-next-static.network' )
4569 self
. wait_online ( 'dummy98:routable' )
4570 print ( '### ip -4 route show dev dummy98 default' )
4571 output
= check_output ( 'ip -4 route show dev dummy98 default' )
4573 self
. assertNotIn ( '149.10.124.59' , output
)
4574 self
. assertIn ( 'default via 149.10.124.60 proto static' , output
)
4576 def test_ip_route_ipv6_src_route ( self
):
4577 # a dummy device does not make the addresses go through tentative state, so we
4578 # reuse a bond from an earlier test, which does make the addresses go through
4579 # tentative state, and do our test on that
4580 copy_network_unit ( '23-active-slave.network' , '25-route-ipv6-src.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
4582 self
. wait_online ( 'dummy98:enslaved' , 'bond199:routable' )
4584 output
= check_output ( 'ip -6 route list dev bond199' )
4586 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2' , output
)
4588 def test_route_preferred_source_with_existing_address ( self
):
4590 copy_network_unit ( '25-route-preferred-source.network' , '12-dummy.netdev' )
4595 networkctl_reconfigure ( 'dummy98' )
4597 self
. wait_online ( 'dummy98:routable' )
4599 output
= check_output ( 'ip -6 route list dev dummy98' )
4601 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1' , output
)
4603 output
= check_output ( 'ip -4 route list dev dummy98' )
4605 self
. assertIn ( '10.123.0.0/16 via 192.168.30.1 proto static src 10.10.10.1' , output
)
4607 def test_ip_link_mac_address ( self
):
4608 copy_network_unit ( '25-address-link-section.network' , '12-dummy.netdev' )
4610 self
. wait_online ( 'dummy98:degraded' )
4612 output
= check_output ( 'ip link show dummy98' )
4614 self
. assertRegex ( output
, '00:01:02:aa:bb:cc' )
4616 def test_ip_link_unmanaged ( self
):
4617 copy_network_unit ( '25-link-section-unmanaged.network' , '12-dummy.netdev' )
4620 self
. wait_operstate ( 'dummy98' , 'off' , setup_state
= 'unmanaged' )
4622 def test_ipv6_address_label ( self
):
4623 copy_network_unit ( '25-ipv6-address-label-section.network' , '12-dummy.netdev' )
4624 copy_networkd_conf_dropin ( 'networkd-address-label.conf' )
4626 self
. wait_online ( 'dummy98:degraded' )
4628 output
= check_output ( 'ip addrlabel list' )
4630 self
. assertRegex ( output
, '2004:da8:1::/64 dev dummy98 label 4444' )
4631 self
. assertRegex ( output
, '2004:da8:2::/64 label 5555' )
4633 def test_ipv6_proxy_ndp ( self
):
4634 copy_network_unit ( '25-ipv6-proxy-ndp.network' , '12-dummy.netdev' )
4637 self
. wait_online ( 'dummy98:routable' )
4639 output
= check_output ( 'ip neighbor show proxy dev dummy98' )
4641 for i
in range ( 1 , 5 ):
4642 self
. assertRegex ( output
, f
'2607:5300:203:5215: {i} ::1 *proxy' )
4644 def test_ipv6_neigh_retrans_time ( self
):
4646 copy_network_unit ( '25-dummy.netdev' , '25-dummy.network' )
4649 self
. wait_online ( f
' {link} :degraded' )
4650 remove_network_unit ( '25-dummy.network' )
4652 # expect retrans_time_ms updated
4653 copy_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
4655 self
. wait_online ( f
' {link} :degraded' )
4656 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4657 remove_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
4659 # expect retrans_time_ms unchanged
4660 copy_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
4662 self
. wait_online ( f
' {link} :degraded' )
4663 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4664 remove_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
4666 # expect retrans_time_ms unchanged
4667 copy_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
4669 self
. wait_online ( f
' {link} :degraded' )
4670 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4671 remove_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
4673 # expect retrans_time_ms unchanged
4674 copy_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
4676 self
. wait_online ( f
' {link} :degraded' )
4677 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4678 remove_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
4680 # expect retrans_time_ms unchanged
4681 copy_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
4683 self
. wait_online ( f
' {link} :degraded' )
4684 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
4685 remove_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
4687 # expect retrans_time_ms updated
4688 copy_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
4690 self
. wait_online ( f
' {link} :degraded' )
4691 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '4000' )
4692 remove_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
4694 def test_neighbor ( self
):
4695 copy_network_unit ( '12-dummy.netdev' , '25-neighbor-dummy.network' , '25-neighbor-dummy.network.d/10-step1.conf' ,
4696 '25-gre-tunnel-remote-any.netdev' , '25-neighbor-ip.network' ,
4697 '25-ip6gre-tunnel-remote-any.netdev' , '25-neighbor-ipv6.network' ,
4700 self
. wait_online ( 'dummy98:degraded' , 'gretun97:routable' , 'ip6gretun97:routable' )
4702 print ( '### ip neigh list dev gretun97' )
4703 output
= check_output ( 'ip neigh list dev gretun97' )
4705 self
. assertIn ( '10.0.0.22 lladdr 10.65.223.239 PERMANENT' , output
)
4706 self
. assertNotIn ( '10.0.0.23' , output
)
4708 print ( '### ip neigh list dev ip6gretun97' )
4709 output
= check_output ( 'ip neigh list dev ip6gretun97' )
4711 self
. assertRegex ( output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT' )
4712 self
. assertNotIn ( '2001:db8:0:f102::18' , output
)
4714 print ( '### ip neigh list dev dummy98' )
4715 output
= check_output ( 'ip neigh list dev dummy98' )
4717 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT' , output
)
4718 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT' , output
)
4719 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
4720 self
. assertNotIn ( '192.168.10.2' , output
)
4721 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
4723 check_json ( networkctl_json ())
4725 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
4726 # the valid configurations in 10-step1.conf.
4727 copy_network_unit ( '25-neighbor-dummy.network.d/10-step2.conf' )
4729 self
. wait_online ( 'dummy98:degraded' )
4731 print ( '### ip neigh list dev dummy98' )
4732 output
= check_output ( 'ip neigh list dev dummy98' )
4734 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT' , output
)
4735 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
4736 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
4737 self
. assertNotIn ( '192.168.10.2' , output
)
4738 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
4740 check_json ( networkctl_json ())
4742 remove_network_unit ( '25-neighbor-dummy.network.d/10-step1.conf' ,
4743 '25-neighbor-dummy.network.d/10-step2.conf' )
4744 copy_network_unit ( '25-neighbor-dummy.network.d/10-step3.conf' )
4746 self
. wait_online ( 'dummy98:degraded' )
4748 print ( '### ip neigh list dev dummy98' )
4749 output
= check_output ( 'ip neigh list dev dummy98' )
4751 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
4752 self
. assertNotIn ( '00:00:5e:00:02:65' , output
)
4753 self
. assertNotIn ( '00:00:5e:00:02:66' , output
)
4754 self
. assertNotIn ( '00:00:5e:00:03:65' , output
)
4755 self
. assertNotIn ( '2004:da8:1::1' , output
)
4757 def test_link_local_addressing ( self
):
4758 copy_network_unit ( '25-link-local-addressing-yes.network' , '11-dummy.netdev' ,
4759 '25-link-local-addressing-no.network' , '12-dummy.netdev' )
4761 self
. wait_online ( 'test1:degraded' , 'dummy98:carrier' )
4763 output
= check_output ( 'ip address show dev test1' )
4765 self
. assertRegex ( output
, 'inet .* scope link' )
4766 self
. assertRegex ( output
, 'inet6 .* scope link' )
4768 output
= check_output ( 'ip address show dev dummy98' )
4770 self
. assertNotRegex ( output
, 'inet6* .* scope link' )
4772 # Documentation/networking/ip-sysctl.txt
4774 # addr_gen_mode - INTEGER
4775 # Defines how link-local and autoconf addresses are generated.
4777 # 0: generate address based on EUI64 (default)
4778 # 1: do no generate a link-local address, use EUI64 for addresses generated
4780 # 2: generate stable privacy addresses, using the secret from
4781 # stable_secret (RFC7217)
4782 # 3: generate stable privacy addresses, using a random secret if unset
4784 self
. check_ipv6_sysctl_attr ( 'test1' , 'stable_secret' , '0123:4567:89ab:cdef:0123:4567:89ab:cdef' )
4785 self
. check_ipv6_sysctl_attr ( 'test1' , 'addr_gen_mode' , '2' )
4786 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'addr_gen_mode' , '1' )
4788 def test_link_local_addressing_ipv6ll ( self
):
4789 copy_network_unit ( '26-link-local-addressing-ipv6.network' , '12-dummy.netdev' )
4791 self
. wait_online ( 'dummy98:degraded' )
4793 # An IPv6LL address exists by default.
4794 output
= check_output ( 'ip address show dev dummy98' )
4796 self
. assertRegex ( output
, 'inet6 .* scope link' )
4798 copy_network_unit ( '25-link-local-addressing-no.network' )
4800 self
. wait_online ( 'dummy98:carrier' )
4802 # Check if the IPv6LL address is removed.
4803 output
= check_output ( 'ip address show dev dummy98' )
4805 self
. assertNotRegex ( output
, 'inet6 .* scope link' )
4807 remove_network_unit ( '25-link-local-addressing-no.network' )
4809 self
. wait_online ( 'dummy98:degraded' )
4811 # Check if a new IPv6LL address is assigned.
4812 output
= check_output ( 'ip address show dev dummy98' )
4814 self
. assertRegex ( output
, 'inet6 .* scope link' )
4816 def test_sysctl ( self
):
4817 copy_networkd_conf_dropin ( '25-global-ipv6-privacy-extensions.conf' )
4818 copy_network_unit ( '25-sysctl.network' , '12-dummy.netdev' , copy_dropins
= False )
4820 self
. wait_online ( 'dummy98:degraded' )
4822 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
4823 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '1' )
4824 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'dad_transmits' , '3' )
4825 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'hop_limit' , '5' )
4826 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'proxy_ndp' , '1' )
4827 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
4828 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp' , '1' )
4829 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp_pvlan' , '1' )
4830 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'accept_local' , '1' )
4831 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'rp_filter' , '0' )
4832 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'force_igmp_version' , '1' )
4834 copy_network_unit ( '25-sysctl.network.d/25-ipv6-privacy-extensions.conf' )
4836 self
. wait_online ( 'dummy98:degraded' )
4838 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '2' )
4840 def test_sysctl_disable_ipv6 ( self
):
4841 copy_network_unit ( '25-sysctl-disable-ipv6.network' , '12-dummy.netdev' )
4843 print ( '## Disable ipv6' )
4844 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=1' )
4845 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=1' )
4848 self
. wait_online ( 'dummy98:routable' )
4850 output
= check_output ( 'ip -4 address show dummy98' )
4852 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
4853 output
= check_output ( 'ip -6 address show dummy98' )
4855 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
4856 self
. assertRegex ( output
, 'inet6 .* scope link' )
4857 output
= check_output ( 'ip -4 route show dev dummy98' )
4859 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
4860 output
= check_output ( 'ip -6 route show default' )
4862 self
. assertRegex ( output
, 'default' )
4863 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
4865 remove_link ( 'dummy98' )
4867 print ( '## Enable ipv6' )
4868 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=0' )
4869 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=0' )
4872 self
. wait_online ( 'dummy98:routable' )
4874 output
= check_output ( 'ip -4 address show dummy98' )
4876 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
4877 output
= check_output ( 'ip -6 address show dummy98' )
4879 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
4880 self
. assertRegex ( output
, 'inet6 .* scope link' )
4881 output
= check_output ( 'ip -4 route show dev dummy98' )
4883 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
4884 output
= check_output ( 'ip -6 route show default' )
4886 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
4888 @expectedFailureIfModuleIsNotAvailable ( 'mpls_router' )
4889 def test_sysctl_mpls ( self
):
4890 check_output ( 'modprobe mpls_router' )
4891 copy_network_unit ( '25-sysctl-mpls.network' , '12-dummy.netdev' )
4893 self
. wait_online ( 'dummy98:degraded' )
4895 self
. check_mpls_sysctl_attr ( 'dummy98' , 'input' , '1' )
4897 def test_bind_carrier ( self
):
4898 copy_network_unit ( '25-bind-carrier.network' , '11-dummy.netdev' )
4901 # no bound interface.
4902 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
4903 output
= check_output ( 'ip address show test1' )
4905 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4906 self
. assertIn ( 'DOWN' , output
)
4907 self
. assertNotIn ( '192.168.10' , output
)
4909 # add one bound interface. The interface will be up.
4910 check_output ( 'ip link add dummy98 type dummy' )
4911 check_output ( 'ip link set dummy98 up' )
4912 self
. wait_online ( 'test1:routable' )
4913 output
= check_output ( 'ip address show test1' )
4915 self
. assertIn ( 'UP,LOWER_UP' , output
)
4916 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4918 # add another bound interface. The interface is still up.
4919 check_output ( 'ip link add dummy99 type dummy' )
4920 check_output ( 'ip link set dummy99 up' )
4921 self
. wait_operstate ( 'dummy99' , 'degraded' , setup_state
= 'unmanaged' )
4922 output
= check_output ( 'ip address show test1' )
4924 self
. assertIn ( 'UP,LOWER_UP' , output
)
4925 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4927 # remove one of the bound interfaces. The interface is still up
4928 remove_link ( 'dummy98' )
4929 output
= check_output ( 'ip address show test1' )
4931 self
. assertIn ( 'UP,LOWER_UP' , output
)
4932 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4934 # bring down the remaining bound interface. The interface will be down.
4935 check_output ( 'ip link set dummy99 down' )
4936 self
. wait_operstate ( 'test1' , 'off' )
4937 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
4938 output
= check_output ( 'ip address show test1' )
4940 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4941 self
. assertIn ( 'DOWN' , output
)
4942 self
. assertNotIn ( '192.168.10' , output
)
4944 # bring up the bound interface. The interface will be up.
4945 check_output ( 'ip link set dummy99 up' )
4946 self
. wait_online ( 'test1:routable' )
4947 output
= check_output ( 'ip address show test1' )
4949 self
. assertIn ( 'UP,LOWER_UP' , output
)
4950 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4952 # remove the remaining bound interface. The interface will be down.
4953 remove_link ( 'dummy99' )
4954 self
. wait_operstate ( 'test1' , 'off' )
4955 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
4956 output
= check_output ( 'ip address show test1' )
4958 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4959 self
. assertIn ( 'DOWN' , output
)
4960 self
. assertNotIn ( '192.168.10' , output
)
4962 # re-add one bound interface. The interface will be up.
4963 check_output ( 'ip link add dummy98 type dummy' )
4964 check_output ( 'ip link set dummy98 up' )
4965 self
. wait_online ( 'test1:routable' )
4966 output
= check_output ( 'ip address show test1' )
4968 self
. assertIn ( 'UP,LOWER_UP' , output
)
4969 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4971 def _test_activation_policy ( self
, interface
, test
):
4972 conffile
= '25-activation-policy.network'
4974 conffile
= f
' {conffile} .d/ {test} .conf'
4975 if interface
== 'vlan99' :
4976 copy_network_unit ( '21-vlan.netdev' , '21-vlan-test1.network' )
4977 copy_network_unit ( '11-dummy.netdev' , conffile
, copy_dropins
= False )
4980 always
= test
. startswith ( 'always' )
4981 initial_up
= test
!= 'manual' and not test
. endswith ( 'down' ) # note: default is up
4982 expect_up
= initial_up
4983 next_up
= not expect_up
4985 if test
. endswith ( 'down' ):
4986 self
. wait_activated ( interface
)
4988 for iteration
in range ( 4 ):
4989 with self
. subTest ( iteration
= iteration
, expect_up
= expect_up
):
4990 operstate
= 'routable' if expect_up
else 'off'
4991 setup_state
= 'configured' if expect_up
else ( 'configuring' if iteration
== 0 else None )
4992 self
. wait_operstate ( interface
, operstate
, setup_state
= setup_state
, setup_timeout
= 20 )
4995 self
. assertIn ( 'UP' , check_output ( f
'ip link show {interface} ' ))
4996 self
. assertIn ( '192.168.10.30/24' , check_output ( f
'ip address show {interface} ' ))
4997 self
. assertIn ( 'default via 192.168.10.1' , check_output ( f
'ip route show dev {interface} ' ))
4999 self
. assertIn ( 'DOWN' , check_output ( f
'ip link show {interface} ' ))
5002 check_output ( f
'ip link set dev {interface} up' )
5004 check_output ( f
'ip link set dev {interface} down' )
5005 expect_up
= initial_up
if always
else next_up
5006 next_up
= not next_up
5010 def test_activation_policy ( self
):
5012 for interface
in [ 'test1' , 'vlan99' ]:
5013 for test
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , '' ]:
5019 print ( f
'### test_activation_policy(interface= {interface} , test= {test} )' )
5020 with self
. subTest ( interface
= interface
, test
= test
):
5021 self
._ test
_ activation
_ policy
( interface
, test
)
5023 def _test_activation_policy_required_for_online ( self
, policy
, required
):
5024 conffile
= '25-activation-policy.network'
5025 units
= [ '11-dummy.netdev' , '12-dummy.netdev' , '12-dummy.network' , conffile
]
5027 units
+= [ f
' {conffile} .d/ {policy} .conf' ]
5029 units
+= [ f
' {conffile} .d/required- {required} .conf' ]
5030 copy_network_unit (* units
, copy_dropins
= False )
5033 if policy
. endswith ( 'down' ):
5034 self
. wait_activated ( 'test1' )
5036 if policy
. endswith ( 'down' ) or policy
== 'manual' :
5037 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
5039 self
. wait_online ( 'test1' )
5041 if policy
== 'always-down' :
5042 # if always-down, required for online is forced to no
5045 # otherwise if required for online is specified, it should match that
5046 expected
= required
== 'yes'
5048 # otherwise if only policy specified, required for online defaults to
5049 # true if policy is up, always-up, or bound
5050 expected
= policy
. endswith ( 'up' ) or policy
== 'bound'
5052 # default is true, if neither are specified
5055 output
= networkctl_status ( 'test1' )
5058 yesno
= 'yes' if expected
else 'no'
5059 self
. assertRegex ( output
, f
'Required For Online: {yesno} ' )
5061 def test_activation_policy_required_for_online ( self
):
5063 for policy
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , 'bound' , '' ]:
5064 for required
in [ 'yes' , 'no' , '' ]:
5070 print ( f
'### test_activation_policy_required_for_online(policy= {policy} , required= {required} )' )
5071 with self
. subTest ( policy
= policy
, required
= required
):
5072 self
._ test
_ activation
_ policy
_ required
_ for
_ online
( policy
, required
)
5074 def test_domain ( self
):
5075 copy_network_unit ( '12-dummy.netdev' , '24-search-domain.network' )
5077 self
. wait_online ( 'dummy98:routable' )
5079 output
= networkctl_status ( 'dummy98' )
5081 self
. assertRegex ( output
, 'Address: 192.168.42.100' )
5082 self
. assertRegex ( output
, 'DNS: 192.168.42.1' )
5083 self
. assertRegex ( output
, 'Search Domains: one' )
5085 def test_keep_configuration_yes ( self
):
5086 check_output ( 'ip link add dummy98 type dummy' )
5087 check_output ( 'ip link set dev dummy98 up' )
5088 check_output ( 'ip address add 198.51.100.1/24 brd 198.51.100.255 dev dummy98' )
5089 check_output ( 'ip route add 203.0.113.0/24 via 198.51.100.10 dev dummy98 proto boot' )
5091 copy_network_unit ( '24-keep-configuration-yes.network' )
5093 self
. wait_online ( 'dummy98:routable' )
5095 output
= check_output ( 'ip address show dummy98' )
5097 self
. assertIn ( 'inet 198.51.100.1/24 brd 198.51.100.255 scope global dummy98' , output
)
5099 output
= check_output ( 'ip -d -4 route show dev dummy98' )
5101 self
. assertIn ( '203.0.113.0/24 via 198.51.100.10 proto boot' , output
)
5103 def test_keep_configuration_static ( self
):
5104 check_output ( 'ip link add name dummy98 type dummy' )
5105 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
5106 check_output ( 'ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500' )
5107 output
= check_output ( 'ip address show dummy98' )
5109 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
5110 self
. assertRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
5111 output
= check_output ( 'ip route show dev dummy98' )
5114 copy_network_unit ( '24-keep-configuration-static.network' )
5116 self
. wait_online ( 'dummy98:routable' )
5118 output
= check_output ( 'ip address show dummy98' )
5120 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
5121 self
. assertNotRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
5123 def check_keep_configuration_on_restart ( self
):
5124 self
. wait_online ( 'dummy98:routable' )
5125 self
. wait_online ( 'unmanaged0:routable' , setup_state
= 'unmanaged' )
5127 print ( '### ip -6 address show dev dummy98' )
5128 output
= check_output ( 'ip -6 address show dev dummy98' )
5130 self
. assertIn ( 'inet6 2001:db8:0:f101::15/64 scope global' , output
)
5131 self
. assertIn ( 'inet6 2001:db8:1:f101::15/64 scope global deprecated' , output
)
5133 print ( '### ip -6 address show dev unmanaged0' )
5134 output
= check_output ( 'ip -6 address show dev unmanaged0' )
5136 self
. assertIn ( 'inet6 2001:db8:9999:f101::15/64 scope global' , output
)
5138 print ( '### ip -6 route show default dev dummy98' )
5139 output
= check_output ( 'ip -6 route show default dev dummy98' )
5141 self
. assertIn ( 'default via fe80::f0ca:cc1a proto static metric 1 pref medium' , output
)
5143 def test_keep_configuration_on_restart ( self
):
5144 copy_network_unit ( '12-dummy.netdev' , '85-static-ipv6.network' , '85-unmanaged.link' )
5146 # Add an unmanaged interface with an up address
5147 call ( 'ip link add unmanaged0 type dummy' )
5148 call ( 'ip link set unmanaged0 up' )
5149 call ( 'ip -4 addr add 10.20.30.40/32 dev unmanaged0' )
5150 call ( 'ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0' )
5152 # Wait for all addresses
5153 self
. wait_address ( 'unmanaged0' , 'inet 10.20.30.40/32' , scope
= 'global' , ipv
= '-4' , timeout_sec
= 10 )
5154 self
. wait_address ( 'unmanaged0' , 'inet6 2001:db8:9999:f101::15/64' , scope
= 'global' , ipv
= '-6' , timeout_sec
= 10 )
5155 self
. wait_address ( 'unmanaged0' , 'inet6 fe80::[0-9a-f:]*/64' , scope
= 'link' , ipv
= '-6' , timeout_sec
= 10 )
5157 # Wait for all routes
5158 self
. wait_route ( 'unmanaged0' , 'local 10.20.30.40 proto kernel' , table
= 'local' , ipv
= '-4' , timeout_sec
= 10 )
5159 self
. wait_route ( 'unmanaged0' , 'local fe80::[0-9a-f:]* proto kernel' , table
= 'local' , ipv
= '-6' , timeout_sec
= 10 )
5160 self
. wait_route ( 'unmanaged0' , 'multicast ff00::/8 proto kernel' , table
= 'local' , ipv
= '-6' , timeout_sec
= 10 )
5161 self
. wait_route ( 'unmanaged0' , '2001:db8:9999:f101::/64 proto kernel' , table
= 'main' , ipv
= '-6' , timeout_sec
= 10 )
5162 self
. wait_route ( 'unmanaged0' , 'fe80::/64 proto kernel' , table
= 'main' , ipv
= '-6' , timeout_sec
= 10 )
5164 # Start `ip monitor` with output to a temporary file
5165 with tempfile
. TemporaryFile ( mode
= 'r+' , prefix
= 'ip_monitor_u' ) as logfile_unmanaged
:
5166 process_u
= subprocess
. Popen ([ 'ip' , 'monitor' , 'dev' , 'unmanaged0' ], stdout
= logfile_unmanaged
, text
= True )
5169 self
. check_keep_configuration_on_restart ()
5171 # Start `ip monitor` with output to a temporary file
5172 with tempfile
. TemporaryFile ( mode
= 'r+' , prefix
= 'ip_monitor' ) as logfile
:
5173 process
= subprocess
. Popen ([ 'ip' , 'monitor' , 'dev' , 'dummy98' ], stdout
= logfile
, text
= True )
5176 self
. check_keep_configuration_on_restart ()
5178 process
. send_signal ( signal
. SIGTERM
)
5181 print ( '### ip monitor dev dummy98 BEGIN' )
5183 # Read the `ip monitor` output looking for network changes
5185 for line
in logfile
:
5186 line
= line
. rstrip ()
5188 # Check if a link went down
5189 self
. assertNotRegex ( line
, 'dummy98: .* state DOWN' )
5190 # Check if an address was removed
5191 self
. assertNotRegex ( line
, '^Deleted .* 2001:db8:' )
5192 self
. assertNotRegex ( line
, '^Deleted 2001:db8:.*/64' )
5193 # Check if the default route was removed
5194 self
. assertNotRegex ( line
, '^Deleted default via fe80::f0ca:cc1a' )
5196 print ( '### ip monitor dev dummy98 END' )
5198 process_u
. send_signal ( signal
. SIGTERM
)
5201 print ( '### ip monitor dev unmanaged0 BEGIN' )
5203 # Read the `ip monitor` output looking for network changes and check if something happened
5204 logfile_unmanaged
. seek ( 0 )
5205 self
. assertEqual ( logfile_unmanaged
. read (). rstrip (), '' )
5207 print ( '### ip monitor dev unmanaged0 END' )
5209 def test_keep_untracked_addresses ( self
):
5210 # Add an unmanaged interface with an up address
5212 copy_network_unit ( '12-dummy.netdev' , '85-static-ipv6.network' )
5214 self
. wait_online ( 'dummy98:routable' )
5216 print ( '### ip -4 addr add 10.234.77.111/32 dev dummy98' )
5217 output
= check_output ( 'ip -4 addr add 10.234.77.111/32 dev dummy98' )
5220 print ( '### ip -6 addr add 2222:3333::4444/64 dev dummy98' )
5221 output
= check_output ( 'ip -6 addr add 2222:3333::4444/64 dev dummy98' )
5226 output
= check_output ( 'ip -4 addr show dev dummy98' )
5228 self
. assertIn ( 'inet 10.234.77.111/32' , output
)
5230 output
= check_output ( 'ip -6 addr show dev dummy98' )
5232 self
. assertIn ( 'inet6 2222:3333::4444/64 scope global' , output
)
5234 def check_nexthop ( self
, manage_foreign_nexthops
, first
):
5235 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
5237 output
= check_output ( 'ip nexthop list dev veth99' )
5240 self
. assertIn ( 'id 1 via 192.168.5.1 dev veth99' , output
)
5241 self
. assertIn ( 'id 2 via 2001:1234:5:8f63::2 dev veth99' , output
)
5243 self
. assertIn ( 'id 6 via 192.168.5.1 dev veth99' , output
)
5244 self
. assertIn ( 'id 7 via 2001:1234:5:8f63::2 dev veth99' , output
)
5245 self
. assertIn ( 'id 3 dev veth99' , output
)
5246 self
. assertIn ( 'id 4 dev veth99' , output
)
5248 self
. assertRegex ( output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink' )
5250 self
. assertIn ( 'id 5 via 192.168.5.3 dev veth99' , output
)
5251 self
. assertNotRegex ( output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink' )
5252 self
. assertIn ( 'id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99' , output
)
5253 if manage_foreign_nexthops
:
5254 self
. assertRegex ( output
, r
'id [0-9]* via 192.168.5.2 dev veth99' )
5256 output
= check_output ( 'ip nexthop list dev dummy98' )
5259 self
. assertIn ( 'id 20 via 192.168.20.1 dev dummy98' , output
)
5261 self
. assertIn ( 'id 21 via 192.168.20.1 dev dummy98' , output
)
5262 if manage_foreign_nexthops
:
5263 self
. assertNotIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
5265 self
. assertIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
5267 # kernel manages blackhole nexthops on lo
5268 output
= check_output ( 'ip nexthop list dev lo' )
5271 self
. assertIn ( 'id 6 blackhole' , output
)
5272 self
. assertIn ( 'id 7 blackhole' , output
)
5274 self
. assertIn ( 'id 1 blackhole' , output
)
5275 self
. assertIn ( 'id 2 blackhole' , output
)
5277 # group nexthops are shown with -0 option
5279 output
= check_output ( 'ip -0 nexthop list id 21' )
5281 self
. assertRegex ( output
, r
'id 21 group (1,3/20|20/1,3)' )
5283 output
= check_output ( 'ip -0 nexthop list id 20' )
5285 self
. assertRegex ( output
, r
'id 20 group (5,3/21|21/5,3)' )
5287 output
= check_output ( 'ip route show dev veth99 10.10.10.10' )
5290 self
. assertEqual ( '10.10.10.10 nhid 1 via 192.168.5.1 proto static' , output
)
5292 self
. assertEqual ( '10.10.10.10 nhid 6 via 192.168.5.1 proto static' , output
)
5294 output
= check_output ( 'ip route show dev veth99 10.10.10.11' )
5297 self
. assertEqual ( '10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static' , output
)
5299 self
. assertEqual ( '10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static' , output
)
5301 output
= check_output ( 'ip route show dev veth99 10.10.10.12' )
5304 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink' , output
)
5306 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.5.3 proto static' , output
)
5308 output
= check_output ( 'ip -6 route show dev veth99 2001:1234:5:8f62::1' )
5311 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
5313 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
5315 output
= check_output ( 'ip route show 10.10.10.13' )
5318 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 6 dev lo proto static' , output
)
5320 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 1 dev lo proto static' , output
)
5322 output
= check_output ( 'ip -6 route show 2001:1234:5:8f62::2' )
5325 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium' , output
)
5327 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium' , output
)
5329 output
= check_output ( 'ip route show 10.10.10.14' )
5332 self
. assertIn ( '10.10.10.14 nhid 21 proto static' , output
)
5333 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
5335 self
. assertIn ( '10.10.10.14 nhid 20 proto static' , output
)
5336 self
. assertIn ( 'nexthop via 192.168.5.3 dev veth99 weight 3' , output
)
5337 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
5339 output
= networkctl_json ()
5341 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
5343 def _test_nexthop ( self
, manage_foreign_nexthops
):
5344 if not manage_foreign_nexthops
:
5345 copy_networkd_conf_dropin ( 'networkd-manage-foreign-nexthops-no.conf' )
5347 check_output ( 'ip link add dummy98 type dummy' )
5348 check_output ( 'ip link set dummy98 up' )
5349 check_output ( 'ip address add 192.168.20.20/24 dev dummy98' )
5350 check_output ( 'ip nexthop add id 42 via 192.168.20.2 dev dummy98' )
5352 copy_network_unit ( '25-nexthop-1.network' , '25-veth.netdev' , '25-veth-peer.network' ,
5353 '12-dummy.netdev' , '25-nexthop-dummy-1.network' )
5356 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
5358 remove_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
5359 copy_network_unit ( '25-nexthop-2.network' , '25-nexthop-dummy-2.network' )
5361 self
. check_nexthop ( manage_foreign_nexthops
, first
= False )
5363 remove_network_unit ( '25-nexthop-2.network' )
5364 copy_network_unit ( '25-nexthop-nothing.network' )
5366 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5368 output
= check_output ( 'ip nexthop list dev veth99' )
5370 self
. assertEqual ( output
, '' )
5371 output
= check_output ( 'ip nexthop list dev lo' )
5373 self
. assertEqual ( output
, '' )
5375 remove_network_unit ( '25-nexthop-nothing.network' , '25-nexthop-dummy-2.network' )
5376 copy_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
5377 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
5378 # here to test reconfiguring with different .network files does not trigger race.
5379 # See also comments in link_drop_requests().
5380 networkctl_reconfigure ( 'dummy98' ) # reconfigured with 25-nexthop-dummy-2.network
5381 networkctl_reload () # reconfigured with 25-nexthop-dummy-1.network
5383 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
5385 # Remove nexthop with ID 20
5386 check_output ( 'ip nexthop del id 20' )
5388 # Check the nexthop ID 20 is dropped from the group nexthop.
5389 output
= check_output ( 'ip -0 nexthop list id 21' )
5391 self
. assertRegex ( output
, r
'id 21 group 1,3' )
5393 # Remove nexthop with ID 21
5394 check_output ( 'ip nexthop del id 21' )
5395 copy_network_unit ( '11-dummy.netdev' , '25-nexthop-test1.network' )
5398 # 25-nexthop-test1.network requests a route with nexthop ID 21, which is removed in the above,
5399 # hence test1 should be stuck in the configuring state.
5400 self
. wait_operstate ( 'test1' , operstate
= 'routable' , setup_state
= 'configuring' )
5402 # Wait for a while, and check if the interface is still in the configuring state.
5404 output
= networkctl_status ( 'test1' )
5405 self
. assertIn ( 'State: routable (configuring)' , output
)
5407 # Check if the route which needs nexthop 21 are forgotten.
5408 output
= networkctl_json ()
5410 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
5412 # Reconfigure the interface that has nexthop with ID 20 and 21,
5413 # then the route requested by test1 can be configured.
5414 networkctl_reconfigure ( 'dummy98' )
5415 self
. wait_online ( 'test1:routable' )
5417 # Check if the requested route actually configured.
5418 output
= check_output ( 'ip route show 10.10.11.10' )
5420 self
. assertIn ( '10.10.11.10 nhid 21 proto static' , output
)
5421 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
5422 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
5424 remove_link ( 'veth99' )
5427 output
= check_output ( 'ip nexthop list dev lo' )
5429 self
. assertEqual ( output
, '' )
5431 @expectedFailureIfNexthopIsNotAvailable ()
5432 def test_nexthop ( self
):
5434 for manage_foreign_nexthops
in [ True , False ]:
5440 print ( f
'### test_nexthop(manage_foreign_nexthops= {manage_foreign_nexthops} )' )
5441 with self
. subTest ( manage_foreign_nexthops
= manage_foreign_nexthops
):
5442 self
._ test
_ nexthop
( manage_foreign_nexthops
)
5444 class NetworkdTCTests ( unittest
. TestCase
, Utilities
):
5452 @expectedFailureIfModuleIsNotAvailable ( 'sch_cake' )
5453 def test_qdisc_cake ( self
):
5454 copy_network_unit ( '25-qdisc-cake.network' , '12-dummy.netdev' )
5456 self
. wait_online ( 'dummy98:routable' )
5458 output
= check_output ( 'tc qdisc show dev dummy98' )
5460 self
. assertIn ( 'qdisc cake 3a: root' , output
)
5461 self
. assertIn ( 'bandwidth 500Mbit' , output
)
5462 self
. assertIn ( 'autorate-ingress' , output
)
5463 self
. assertIn ( 'diffserv8' , output
)
5464 self
. assertIn ( 'dual-dsthost' , output
)
5465 self
. assertIn ( ' nat' , output
)
5466 self
. assertIn ( ' wash' , output
)
5467 self
. assertIn ( ' split-gso' , output
)
5468 self
. assertIn ( ' raw' , output
)
5469 self
. assertIn ( ' atm' , output
)
5470 self
. assertIn ( 'overhead 128' , output
)
5471 self
. assertIn ( 'mpu 20' , output
)
5472 self
. assertIn ( 'fwmark 0xff00' , output
)
5473 self
. assertIn ( 'rtt 1s' , output
)
5474 self
. assertIn ( 'ack-filter-aggressive' , output
)
5476 # Test for replacing existing qdisc. See #31226.
5477 with
open ( os
. path
. join ( network_unit_dir
, '25-qdisc-cake.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5478 f
. write ( 'Bandwidth=250M \n ' )
5481 self
. wait_online ( 'dummy98:routable' )
5483 output
= check_output ( 'tc qdisc show dev dummy98' )
5485 self
. assertIn ( 'bandwidth 250Mbit' , output
)
5487 @expectedFailureIfModuleIsNotAvailable ( 'sch_codel' )
5488 def test_qdisc_codel ( self
):
5489 copy_network_unit ( '25-qdisc-codel.network' , '12-dummy.netdev' )
5491 self
. wait_online ( 'dummy98:routable' )
5493 output
= check_output ( 'tc qdisc show dev dummy98' )
5495 self
. assertRegex ( output
, 'qdisc codel 33: root' )
5496 self
. assertRegex ( output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn' )
5498 @expectedFailureIfModuleIsNotAvailable ( 'sch_drr' )
5499 def test_qdisc_drr ( self
):
5500 copy_network_unit ( '25-qdisc-drr.network' , '12-dummy.netdev' )
5502 self
. wait_online ( 'dummy98:routable' )
5504 output
= check_output ( 'tc qdisc show dev dummy98' )
5506 self
. assertRegex ( output
, 'qdisc drr 2: root' )
5507 output
= check_output ( 'tc class show dev dummy98' )
5509 self
. assertRegex ( output
, 'class drr 2:30 root quantum 2000b' )
5511 @expectedFailureIfModuleIsNotAvailable ( 'sch_ets' )
5512 def test_qdisc_ets ( self
):
5513 copy_network_unit ( '25-qdisc-ets.network' , '12-dummy.netdev' )
5515 self
. wait_online ( 'dummy98:routable' )
5517 output
= check_output ( 'tc qdisc show dev dummy98' )
5520 self
. assertRegex ( output
, 'qdisc ets 3a: root' )
5521 self
. assertRegex ( output
, 'bands 10 strict 3' )
5522 self
. assertRegex ( output
, 'quanta 1 2 3 4 5' )
5523 self
. assertRegex ( output
, 'priomap 3 4 5 6 7' )
5525 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq' )
5526 def test_qdisc_fq ( self
):
5527 copy_network_unit ( '25-qdisc-fq.network' , '12-dummy.netdev' )
5529 self
. wait_online ( 'dummy98:routable' )
5531 output
= check_output ( 'tc qdisc show dev dummy98' )
5533 self
. assertRegex ( output
, 'qdisc fq 32: root' )
5534 self
. assertRegex ( output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511' )
5535 self
. assertRegex ( output
, 'quantum 1500' )
5536 self
. assertRegex ( output
, 'initial_quantum 13000' )
5537 self
. assertRegex ( output
, 'maxrate 1Mbit' )
5539 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_codel' )
5540 def test_qdisc_fq_codel ( self
):
5541 copy_network_unit ( '25-qdisc-fq_codel.network' , '12-dummy.netdev' )
5543 self
. wait_online ( 'dummy98:routable' )
5545 output
= check_output ( 'tc qdisc show dev dummy98' )
5547 self
. assertRegex ( output
, 'qdisc fq_codel 34: root' )
5548 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' )
5550 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_pie' )
5551 def test_qdisc_fq_pie ( self
):
5552 copy_network_unit ( '25-qdisc-fq_pie.network' , '12-dummy.netdev' )
5554 self
. wait_online ( 'dummy98:routable' )
5556 output
= check_output ( 'tc qdisc show dev dummy98' )
5559 self
. assertRegex ( output
, 'qdisc fq_pie 3a: root' )
5560 self
. assertRegex ( output
, 'limit 200000p' )
5562 @expectedFailureIfModuleIsNotAvailable ( 'sch_gred' )
5563 def test_qdisc_gred ( self
):
5564 copy_network_unit ( '25-qdisc-gred.network' , '12-dummy.netdev' )
5566 self
. wait_online ( 'dummy98:routable' )
5568 output
= check_output ( 'tc qdisc show dev dummy98' )
5570 self
. assertRegex ( output
, 'qdisc gred 38: root' )
5571 self
. assertRegex ( output
, 'vqs 12 default 10 grio' )
5573 @expectedFailureIfModuleIsNotAvailable ( 'sch_hhf' )
5574 def test_qdisc_hhf ( self
):
5575 copy_network_unit ( '25-qdisc-hhf.network' , '12-dummy.netdev' )
5577 self
. wait_online ( 'dummy98:routable' )
5579 output
= check_output ( 'tc qdisc show dev dummy98' )
5581 self
. assertRegex ( output
, 'qdisc hhf 3a: root' )
5582 self
. assertRegex ( output
, 'limit 1022p' )
5584 @expectedFailureIfModuleIsNotAvailable ( 'sch_htb' )
5585 def test_qdisc_htb_fifo ( self
):
5586 copy_network_unit ( '25-qdisc-htb-fifo.network' , '12-dummy.netdev' )
5588 self
. wait_online ( 'dummy98:routable' )
5590 output
= check_output ( 'tc qdisc show dev dummy98' )
5592 self
. assertRegex ( output
, 'qdisc htb 2: root' )
5593 self
. assertRegex ( output
, r
'default (0x30|30)' )
5595 self
. assertRegex ( output
, 'qdisc pfifo 37: parent 2:37' )
5596 self
. assertRegex ( output
, 'limit 100000p' )
5598 self
. assertRegex ( output
, 'qdisc bfifo 3a: parent 2:3a' )
5599 self
. assertRegex ( output
, 'limit 1000000' )
5601 self
. assertRegex ( output
, 'qdisc pfifo_head_drop 3b: parent 2:3b' )
5602 self
. assertRegex ( output
, 'limit 1023p' )
5604 self
. assertRegex ( output
, 'qdisc pfifo_fast 3c: parent 2:3c' )
5606 output
= check_output ( 'tc -d class show dev dummy98' )
5608 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
5609 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
5610 # which is fixed in v6.3.0 by
5611 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
5612 self
. assertRegex ( output
, 'class htb 2:37 root leaf 37(:|prio) ' )
5613 self
. assertRegex ( output
, 'class htb 2:3a root leaf 3a(:|prio) ' )
5614 self
. assertRegex ( output
, 'class htb 2:3b root leaf 3b(:|prio) ' )
5615 self
. assertRegex ( output
, 'class htb 2:3c root leaf 3c(:|prio) ' )
5616 self
. assertRegex ( output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit' )
5617 self
. assertRegex ( output
, 'burst 123456' )
5618 self
. assertRegex ( output
, 'cburst 123457' )
5620 @expectedFailureIfModuleIsNotAvailable ( 'sch_ingress' )
5621 def test_qdisc_ingress ( self
):
5622 copy_network_unit ( '25-qdisc-clsact.network' , '12-dummy.netdev' ,
5623 '25-qdisc-ingress.network' , '11-dummy.netdev' )
5625 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
5627 output
= check_output ( 'tc qdisc show dev dummy98' )
5629 self
. assertRegex ( output
, 'qdisc clsact' )
5631 output
= check_output ( 'tc qdisc show dev test1' )
5633 self
. assertRegex ( output
, 'qdisc ingress' )
5635 def test_qdisc_mq ( self
):
5636 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '25-qdisc-mq.network' )
5638 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
5640 output
= check_output ( 'tc qdisc show dev testtun99' )
5642 self
. assertIn ( 'qdisc mq 2: root' , output
)
5644 @expectedFailureIfModuleIsNotAvailable ( 'sch_multiq' )
5645 def test_qdisc_multiq ( self
):
5646 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '25-qdisc-multiq.network' )
5648 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
5650 output
= check_output ( 'tc qdisc show dev testtun99' )
5652 self
. assertIn ( 'qdisc multiq 2: root' , output
)
5654 @expectedFailureIfModuleIsNotAvailable ( 'sch_netem' )
5655 def test_qdisc_netem ( self
):
5656 copy_network_unit ( '25-qdisc-netem.network' , '12-dummy.netdev' ,
5657 '25-qdisc-netem-compat.network' , '11-dummy.netdev' )
5659 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
5661 output
= check_output ( 'tc qdisc show dev dummy98' )
5663 self
. assertRegex ( output
, 'qdisc netem 30: root' )
5664 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
5666 output
= check_output ( 'tc qdisc show dev test1' )
5668 self
. assertRegex ( output
, 'qdisc netem [0-9a-f]*: root' )
5669 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
5671 @expectedFailureIfModuleIsNotAvailable ( 'sch_pie' )
5672 def test_qdisc_pie ( self
):
5673 copy_network_unit ( '25-qdisc-pie.network' , '12-dummy.netdev' )
5675 self
. wait_online ( 'dummy98:routable' )
5677 output
= check_output ( 'tc qdisc show dev dummy98' )
5679 self
. assertRegex ( output
, 'qdisc pie 3a: root' )
5680 self
. assertRegex ( output
, 'limit 200000' )
5682 @expectedFailureIfModuleIsNotAvailable ( 'sch_qfq' )
5683 def test_qdisc_qfq ( self
):
5684 copy_network_unit ( '25-qdisc-qfq.network' , '12-dummy.netdev' )
5686 self
. wait_online ( 'dummy98:routable' )
5688 output
= check_output ( 'tc qdisc show dev dummy98' )
5690 self
. assertRegex ( output
, 'qdisc qfq 2: root' )
5691 output
= check_output ( 'tc class show dev dummy98' )
5693 self
. assertRegex ( output
, 'class qfq 2:30 root weight 2 maxpkt 16000' )
5694 self
. assertRegex ( output
, 'class qfq 2:31 root weight 10 maxpkt 8000' )
5696 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfb' )
5697 def test_qdisc_sfb ( self
):
5698 copy_network_unit ( '25-qdisc-sfb.network' , '12-dummy.netdev' )
5700 self
. wait_online ( 'dummy98:routable' )
5702 output
= check_output ( 'tc qdisc show dev dummy98' )
5704 self
. assertRegex ( output
, 'qdisc sfb 39: root' )
5705 self
. assertRegex ( output
, 'limit 200000' )
5707 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfq' )
5708 def test_qdisc_sfq ( self
):
5709 copy_network_unit ( '25-qdisc-sfq.network' , '12-dummy.netdev' )
5711 self
. wait_online ( 'dummy98:routable' )
5713 output
= check_output ( 'tc qdisc show dev dummy98' )
5715 self
. assertRegex ( output
, 'qdisc sfq 36: root' )
5716 self
. assertRegex ( output
, 'perturb 5sec' )
5718 @expectedFailureIfModuleIsNotAvailable ( 'sch_tbf' )
5719 def test_qdisc_tbf ( self
):
5720 copy_network_unit ( '25-qdisc-tbf.network' , '12-dummy.netdev' )
5722 self
. wait_online ( 'dummy98:routable' )
5724 output
= check_output ( 'tc qdisc show dev dummy98' )
5726 self
. assertRegex ( output
, 'qdisc tbf 35: root' )
5727 self
. assertRegex ( output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms' )
5729 @expectedFailureIfModuleIsNotAvailable ( 'sch_teql' )
5730 def test_qdisc_teql ( self
):
5731 call_quiet ( 'rmmod sch_teql' )
5733 copy_network_unit ( '25-qdisc-teql.network' , '12-dummy.netdev' )
5735 self
. wait_links ( 'dummy98' )
5736 check_output ( 'modprobe sch_teql max_equalizers=2' )
5737 self
. wait_online ( 'dummy98:routable' )
5739 output
= check_output ( 'tc qdisc show dev dummy98' )
5741 self
. assertRegex ( output
, 'qdisc teql1 31: root' )
5743 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq' , 'sch_sfq' , 'sch_tbf' )
5744 def test_qdisc_drop ( self
):
5745 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' )
5747 self
. wait_online ( 'dummy98:routable' )
5749 # Test case for issue #32247 and #32254.
5751 check_output ( 'tc qdisc replace dev dummy98 root fq' )
5752 self
. assertFalse ( networkd_is_failed ())
5753 check_output ( 'tc qdisc replace dev dummy98 root fq pacing' )
5754 self
. assertFalse ( networkd_is_failed ())
5755 check_output ( 'tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540' )
5756 self
. assertFalse ( networkd_is_failed ())
5757 check_output ( 'tc qdisc add dev dummy98 parent 10:1 handle 100: sfq' )
5758 self
. assertFalse ( networkd_is_failed ())
5760 class NetworkdStateFileTests ( unittest
. TestCase
, Utilities
):
5768 def test_state_file ( self
):
5769 copy_network_unit ( '12-dummy.netdev' , '25-state-file-tests.network' )
5771 self
. wait_online ( 'dummy98:routable' )
5773 # make link state file updated
5774 resolvectl ( 'revert' , 'dummy98' )
5776 check_json ( networkctl_json ())
5778 output
= read_link_state_file ( 'dummy98' )
5780 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
5781 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
5782 self
. assertIn ( 'ADMIN_STATE=configured' , output
)
5783 self
. assertIn ( 'OPER_STATE=routable' , output
)
5784 self
. assertIn ( 'REQUIRED_FOR_ONLINE=yes' , output
)
5785 self
. assertIn ( 'REQUIRED_OPER_STATE_FOR_ONLINE=routable' , output
)
5786 self
. assertIn ( 'REQUIRED_FAMILY_FOR_ONLINE=both' , output
)
5787 self
. assertIn ( 'ACTIVATION_POLICY=up' , output
)
5788 self
. assertIn ( 'NETWORK_FILE=/run/systemd/network/25-state-file-tests.network' , output
)
5789 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
5790 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5791 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
5792 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
5793 self
. assertIn ( 'LLMNR=no' , output
)
5794 self
. assertIn ( 'MDNS=yes' , output
)
5795 self
. assertIn ( 'DNSSEC=no' , output
)
5797 resolvectl ( 'dns' , 'dummy98' , '10.10.10.12#ccc.com' , '10.10.10.13' , '1111:2222::3333' )
5798 resolvectl ( 'domain' , 'dummy98' , 'hogehogehoge' , '~foofoofoo' )
5799 resolvectl ( 'llmnr' , 'dummy98' , 'yes' )
5800 resolvectl ( 'mdns' , 'dummy98' , 'no' )
5801 resolvectl ( 'dnssec' , 'dummy98' , 'yes' )
5802 timedatectl ( 'ntp-servers' , 'dummy98' , '2.fedora.pool.ntp.org' , '3.fedora.pool.ntp.org' )
5804 check_json ( networkctl_json ())
5806 output
= read_link_state_file ( 'dummy98' )
5808 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
5809 self
. assertIn ( 'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org' , output
)
5810 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
5811 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
5812 self
. assertIn ( 'LLMNR=yes' , output
)
5813 self
. assertIn ( 'MDNS=no' , output
)
5814 self
. assertIn ( 'DNSSEC=yes' , output
)
5816 timedatectl ( 'revert' , 'dummy98' )
5818 check_json ( networkctl_json ())
5820 output
= read_link_state_file ( 'dummy98' )
5822 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
5823 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5824 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
5825 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
5826 self
. assertIn ( 'LLMNR=yes' , output
)
5827 self
. assertIn ( 'MDNS=no' , output
)
5828 self
. assertIn ( 'DNSSEC=yes' , output
)
5830 resolvectl ( 'revert' , 'dummy98' )
5832 check_json ( networkctl_json ())
5834 output
= read_link_state_file ( 'dummy98' )
5836 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
5837 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
5838 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
5839 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
5840 self
. assertIn ( 'LLMNR=no' , output
)
5841 self
. assertIn ( 'MDNS=yes' , output
)
5842 self
. assertIn ( 'DNSSEC=no' , output
)
5844 def test_address_state ( self
):
5845 copy_network_unit ( '12-dummy.netdev' , '12-dummy-no-address.network' )
5848 self
. wait_online ( 'dummy98:degraded' )
5850 output
= read_link_state_file ( 'dummy98' )
5851 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
5852 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
5854 # with a routable IPv4 address
5855 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
5856 self
. wait_online ( 'dummy98:routable' , ipv4
= True )
5857 self
. wait_online ( 'dummy98:routable' )
5859 output
= read_link_state_file ( 'dummy98' )
5860 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
5861 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
5863 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
5865 # with a routable IPv6 address
5866 check_output ( 'ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98' )
5867 self
. wait_online ( 'dummy98:routable' , ipv6
= True )
5868 self
. wait_online ( 'dummy98:routable' )
5870 output
= read_link_state_file ( 'dummy98' )
5871 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
5872 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
5874 class NetworkdBondTests ( unittest
. TestCase
, Utilities
):
5882 def test_bond_keep_master ( self
):
5883 check_output ( 'ip link add bond199 type bond mode active-backup' )
5884 check_output ( 'ip link add dummy98 type dummy' )
5885 check_output ( 'ip link set dummy98 master bond199' )
5887 copy_network_unit ( '23-keep-master.network' )
5889 self
. wait_online ( 'dummy98:enslaved' )
5891 output
= check_output ( 'ip -d link show bond199' )
5893 self
. assertRegex ( output
, 'active_slave dummy98' )
5895 output
= check_output ( 'ip -d link show dummy98' )
5897 self
. assertRegex ( output
, 'master bond199' )
5899 # Test case for #37629
5901 # When a slave leaved from its master bonding interface, the kernel brings down the slave.
5902 check_output ( 'ip link set dummy98 nomaster' )
5903 self
. wait_online ( 'dummy98:off' )
5905 # Bring up the interface to check if networkd recognizes the interface has no master now.
5906 check_output ( 'ip link set dummy98 up' )
5907 self
. wait_online ( 'dummy98:carrier' )
5909 # We need to first bring down the interface to make it join a bonding interface.
5910 check_output ( 'ip link set dummy98 down' )
5911 check_output ( 'ip link set dummy98 master bond199' )
5912 self
. wait_online ( 'dummy98:enslaved' )
5914 def test_bond_active_slave ( self
):
5915 copy_network_unit ( '23-active-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
5917 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5919 output
= check_output ( 'ip -d link show bond199' )
5921 self
. assertIn ( 'active_slave dummy98' , output
)
5923 # test case for issue #31165.
5924 since
= datetime
. datetime
. now ()
5925 networkctl_reconfigure ( 'dummy98' )
5926 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5927 self
. assertNotIn ( 'dummy98: Bringing link down' , read_networkd_log ( since
= since
))
5929 # test for reloading.
5931 '23-active-slave.network' ,
5932 '23-bond199.network' ,
5933 '25-bond-active-backup-slave.netdev' ,
5936 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5938 def test_bond_primary_slave ( self
):
5939 copy_network_unit ( '23-primary-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
5941 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5943 output
= check_output ( 'ip -d link show bond199' )
5945 self
. assertIn ( 'primary dummy98' , output
)
5948 mkdir_p ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d' ))
5949 for mac
in [ '00:11:22:33:44:55' , '00:11:22:33:44:56' ]:
5950 with
open ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d/mac.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
5951 f
. write ( f
'[Link] \n MACAddress= {mac} \n ' )
5954 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
5956 output
= check_output ( 'ip -d link show bond199' )
5958 self
. assertIn ( f
'link/ether {mac} ' , output
)
5960 def test_bond_operstate ( self
):
5961 copy_network_unit ( '25-bond.netdev' , '11-dummy.netdev' , '12-dummy.netdev' ,
5962 '25-bond99.network' , '25-bond-slave.network' )
5964 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bond99:routable' )
5966 output
= check_output ( 'ip -d link show dummy98' )
5968 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
5970 output
= check_output ( 'ip -d link show test1' )
5972 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
5974 output
= check_output ( 'ip -d link show bond99' )
5976 self
. assertRegex ( output
, 'MASTER,UP,LOWER_UP' )
5978 # test case for issue #32186
5980 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bond99:routable' )
5982 self
. wait_operstate ( 'dummy98' , 'enslaved' )
5983 self
. wait_operstate ( 'test1' , 'enslaved' )
5984 self
. wait_operstate ( 'bond99' , 'routable' )
5986 check_output ( 'ip link set dummy98 down' )
5988 self
. wait_operstate ( 'dummy98' , 'off' )
5989 self
. wait_operstate ( 'test1' , 'enslaved' )
5990 self
. wait_operstate ( 'bond99' , 'routable' )
5992 check_output ( 'ip link set dummy98 up' )
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' )
5999 check_output ( 'ip link set test1 down' )
6001 self
. wait_operstate ( 'dummy98' , 'off' )
6002 self
. wait_operstate ( 'test1' , 'off' )
6004 if not self
. wait_operstate ( 'bond99' , 'no-carrier' , setup_timeout
= 30 , fail_assert
= False ):
6005 # Huh? Kernel does not recognize that all slave interfaces are down?
6006 # Let's confirm that networkd's operstate is consistent with ip's result.
6007 output
= check_output ( 'ip -d link show bond99' )
6009 self
. assertNotRegex ( output
, 'NO-CARRIER' )
6011 class NetworkdBridgeTests ( unittest
. TestCase
, Utilities
):
6019 def test_bridge_mac_none ( self
):
6020 copy_network_unit ( '12-dummy-mac.netdev' , '26-bridge-mac-slave.network' ,
6021 '26-bridge-mac.netdev' , '26-bridge-mac-master.network' , '26-bridge-mac.link' )
6023 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:degraded' )
6025 output
= check_output ( 'ip link show dev dummy98' )
6027 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
6029 output
= check_output ( 'ip link show dev bridge99' )
6031 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
6033 def test_bridge_vlan ( self
):
6034 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave.network' ,
6035 '26-bridge.netdev' , '26-bridge-vlan-master.network' ,
6038 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6040 output
= check_output ( 'bridge vlan show dev test1' )
6042 # check if the default VID is removed
6043 self
. assertNotIn ( '1 Egress Untagged' , output
)
6044 for i
in range ( 1000 , 3000 ):
6046 self
. assertIn ( f
' {i} PVID' , output
)
6047 elif i
in range ( 1012 , 1016 ) or i
in range ( 1103 , 1109 ):
6048 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6049 elif i
in range ( 1008 , 1014 ) or i
in range ( 1100 , 1111 ):
6050 self
. assertIn ( f
' {i} ' , output
)
6052 self
. assertNotIn ( f
' {i} ' , output
)
6054 output
= check_output ( 'bridge vlan show dev bridge99' )
6056 # check if the default VID is removed
6057 self
. assertNotIn ( '1 Egress Untagged' , output
)
6058 for i
in range ( 1000 , 3000 ):
6060 self
. assertIn ( f
' {i} PVID' , output
)
6061 elif i
in range ( 1022 , 1026 ) or i
in range ( 1203 , 1209 ):
6062 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6063 elif i
in range ( 1018 , 1024 ) or i
in range ( 1200 , 1211 ):
6064 self
. assertIn ( f
' {i} ' , output
)
6066 self
. assertNotIn ( f
' {i} ' , output
)
6069 copy_network_unit ( '26-bridge-vlan-slave.network.d/10-override.conf' ,
6070 '26-bridge-vlan-master.network.d/10-override.conf' )
6072 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6074 output
= check_output ( 'bridge vlan show dev test1' )
6076 for i
in range ( 1000 , 3000 ):
6078 self
. assertIn ( f
' {i} PVID' , output
)
6079 elif i
in range ( 2012 , 2016 ) or i
in range ( 2103 , 2109 ):
6080 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6081 elif i
in range ( 2008 , 2014 ) or i
in range ( 2100 , 2111 ):
6082 self
. assertIn ( f
' {i} ' , output
)
6084 self
. assertNotIn ( f
' {i} ' , output
)
6086 output
= check_output ( 'bridge vlan show dev bridge99' )
6088 for i
in range ( 1000 , 3000 ):
6090 self
. assertIn ( f
' {i} PVID' , output
)
6091 elif i
in range ( 2022 , 2026 ) or i
in range ( 2203 , 2209 ):
6092 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6093 elif i
in range ( 2018 , 2024 ) or i
in range ( 2200 , 2211 ):
6094 self
. assertIn ( f
' {i} ' , output
)
6096 self
. assertNotIn ( f
' {i} ' , output
)
6098 # Remove several vlan IDs
6099 copy_network_unit ( '26-bridge-vlan-slave.network.d/20-override.conf' ,
6100 '26-bridge-vlan-master.network.d/20-override.conf' )
6102 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6104 output
= check_output ( 'bridge vlan show dev test1' )
6106 for i
in range ( 1000 , 3000 ):
6108 self
. assertIn ( f
' {i} PVID' , output
)
6109 elif i
in range ( 2012 , 2016 ):
6110 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6111 elif i
in range ( 2008 , 2014 ):
6112 self
. assertIn ( f
' {i} ' , output
)
6114 self
. assertNotIn ( f
' {i} ' , output
)
6116 output
= check_output ( 'bridge vlan show dev bridge99' )
6118 for i
in range ( 1000 , 3000 ):
6120 self
. assertIn ( f
' {i} PVID' , output
)
6121 elif i
in range ( 2022 , 2026 ):
6122 self
. assertIn ( f
' {i} Egress Untagged' , output
)
6123 elif i
in range ( 2018 , 2024 ):
6124 self
. assertIn ( f
' {i} ' , output
)
6126 self
. assertNotIn ( f
' {i} ' , output
)
6128 # Remove all vlan IDs
6129 copy_network_unit ( '26-bridge-vlan-slave.network.d/30-override.conf' ,
6130 '26-bridge-vlan-master.network.d/30-override.conf' )
6132 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6134 output
= check_output ( 'bridge vlan show dev test1' )
6136 self
. assertNotIn ( 'PVID' , output
)
6137 for i
in range ( 1000 , 3000 ):
6138 self
. assertNotIn ( f
' {i} ' , output
)
6140 output
= check_output ( 'bridge vlan show dev bridge99' )
6142 self
. assertNotIn ( 'PVID' , output
)
6143 for i
in range ( 1000 , 3000 ):
6144 self
. assertNotIn ( f
' {i} ' , output
)
6146 def test_bridge_vlan_issue_20373 ( self
):
6147 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave-issue-20373.network' ,
6148 '26-bridge-issue-20373.netdev' , '26-bridge-vlan-master-issue-20373.network' ,
6149 '21-vlan.netdev' , '21-vlan.network' )
6151 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' , 'vlan99:routable' )
6153 output
= check_output ( 'bridge vlan show dev test1' )
6155 self
. assertIn ( '100 PVID Egress Untagged' , output
)
6156 self
. assertIn ( '560' , output
)
6157 self
. assertIn ( '600' , output
)
6159 output
= check_output ( 'bridge vlan show dev bridge99' )
6161 self
. assertIn ( '1 PVID Egress Untagged' , output
)
6162 self
. assertIn ( '100' , output
)
6163 self
. assertIn ( '600' , output
)
6165 def test_bridge_mdb ( self
):
6166 copy_network_unit ( '11-dummy.netdev' , '26-bridge-mdb-slave.network' ,
6167 '26-bridge.netdev' , '26-bridge-mdb-master.network' )
6169 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
6171 output
= check_output ( 'bridge mdb show dev bridge99' )
6173 self
. assertRegex ( output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064' )
6174 self
. assertRegex ( output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065' )
6176 # Old kernel may not support bridge MDB entries on bridge master
6177 if call_quiet ( 'bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068' ) == 0 :
6178 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066' )
6179 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067' )
6181 # Old kernel may not support L2 bridge MDB entries
6182 if call_quiet ( 'bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070' ) == 0 :
6183 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069' )
6185 def test_bridge_keep_master ( self
):
6186 check_output ( 'ip link add bridge99 type bridge' )
6187 check_output ( 'ip link set bridge99 up' )
6188 check_output ( 'ip link add dummy98 type dummy' )
6189 check_output ( 'ip link set dummy98 master bridge99' )
6191 copy_network_unit ( '23-keep-master.network' )
6193 self
. wait_online ( 'dummy98:enslaved' )
6195 output
= check_output ( 'ip -d link show dummy98' )
6197 self
. assertRegex ( output
, 'master bridge99' )
6198 self
. assertRegex ( output
, 'bridge' )
6200 output
= check_output ( 'bridge -d link show dummy98' )
6202 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
6203 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
6204 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
6205 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
6206 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
6207 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6208 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
6209 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
6210 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
6211 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
6212 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
6213 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
6215 def check_bridge_property ( self
):
6216 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
6218 output
= check_output ( 'ip -d link show bridge99' )
6220 self
. assertIn ( 'mtu 9000 ' , output
)
6222 output
= check_output ( 'ip -d link show test1' )
6224 self
. assertIn ( 'master bridge99 ' , output
)
6225 self
. assertIn ( 'bridge_slave' , output
)
6226 self
. assertIn ( 'mtu 9000 ' , output
)
6228 output
= check_output ( 'ip -d link show dummy98' )
6230 self
. assertIn ( 'master bridge99 ' , output
)
6231 self
. assertIn ( 'bridge_slave' , output
)
6232 self
. assertIn ( 'mtu 9000 ' , output
)
6234 output
= check_output ( 'ip addr show bridge99' )
6236 self
. assertIn ( '192.168.0.15/24' , output
)
6238 output
= check_output ( 'bridge -d link show dummy98' )
6240 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
6241 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
6242 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'isolated' , '1' )
6243 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
6244 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
6245 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
6246 # CONFIG_BRIDGE_IGMP_SNOOPING=y
6247 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
6248 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
6249 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
6250 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
6251 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
6252 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
6254 output
= check_output ( 'bridge -d link show test1' )
6256 self
. check_bridge_port_attr ( 'bridge99' , 'test1' , 'priority' , '0' )
6257 self
. assertIn ( 'locked on' , output
)
6258 if ' mab ' in output
: # This is new in kernel and iproute2 v6.2
6259 self
. assertIn ( 'mab on' , output
)
6261 def test_bridge_property ( self
):
6262 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
6263 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
6264 '25-bridge99.network' , '14-dummy.netdev' , '26-bridge-vlan-tunnel.network' )
6266 self
. check_bridge_property ()
6273 '26-bridge-slave-interface-1.network' ,
6274 '26-bridge-slave-interface-2.network' ,
6275 '26-bridge-vlan-tunnel.network' ,
6276 '25-bridge99.network' )
6278 self
. check_bridge_property ()
6280 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
6281 output
= check_output ( 'ip addr show bridge99' )
6283 self
. assertIn ( '192.168.0.16/24' , output
)
6286 print ( '### ip -6 route list table all dev bridge99' )
6287 output
= check_output ( 'ip -6 route list table all dev bridge99' )
6289 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
6291 remove_link ( 'test1' )
6292 self
. wait_operstate ( 'bridge99' , 'routable' )
6294 output
= check_output ( 'ip -d link show bridge99' )
6296 self
. assertIn ( 'mtu 9000 ' , output
)
6298 output
= check_output ( 'ip -d link show dummy98' )
6300 self
. assertIn ( 'master bridge99 ' , output
)
6301 self
. assertIn ( 'bridge_slave' , output
)
6302 self
. assertIn ( 'mtu 9000 ' , output
)
6304 output
= check_output ( 'ip -d link show dummy97' )
6305 self
. assertIn ( 'vlan_tunnel on ' , output
)
6307 remove_link ( 'dummy98' )
6308 remove_link ( 'dummy97' )
6309 self
. wait_operstate ( 'bridge99' , 'no-carrier' )
6311 output
= check_output ( 'ip -d link show bridge99' )
6313 # When no carrier, the kernel may reset the MTU
6314 self
. assertIn ( 'NO-CARRIER' , output
)
6316 output
= check_output ( 'ip address show bridge99' )
6318 self
. assertNotIn ( '192.168.0.15/24' , output
)
6319 self
. assertIn ( '192.168.0.16/24' , output
) # foreign address is kept
6321 print ( '### ip -6 route list table all dev bridge99' )
6322 output
= check_output ( 'ip -6 route list table all dev bridge99' )
6324 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
6326 check_output ( 'ip link add dummy98 type dummy' )
6327 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:routable' )
6329 output
= check_output ( 'ip -d link show bridge99' )
6331 self
. assertIn ( 'mtu 9000 ' , output
)
6333 output
= check_output ( 'ip -d link show dummy98' )
6335 self
. assertIn ( 'master bridge99 ' , output
)
6336 self
. assertIn ( 'bridge_slave' , output
)
6337 self
. assertIn ( 'mtu 9000 ' , output
)
6339 def test_bridge_configure_without_carrier ( self
):
6340 copy_network_unit ( '26-bridge.netdev' , '26-bridge-configure-without-carrier.network' ,
6344 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
6345 for test
in [ 'no-slave' , 'add-slave' , 'slave-up' , 'slave-no-carrier' , 'slave-carrier' , 'slave-down' ]:
6346 with self
. subTest ( test
= test
):
6347 if test
== 'no-slave' :
6348 # bridge has no slaves; it's up but *might* not have carrier
6349 self
. wait_operstate ( 'bridge99' , operstate
= r
'(no-carrier|routable)' , setup_state
= None , setup_timeout
= 30 )
6350 # due to a bug in the kernel, newly-created bridges are brought up
6351 # *with* carrier, unless they have had any setting changed; e.g.
6352 # their mac set, priority set, etc. Then, they will lose carrier
6353 # as soon as a (down) slave interface is added, and regain carrier
6354 # again once the slave interface is brought up.
6355 #self.check_link_attr('bridge99', 'carrier', '0')
6356 elif test
== 'add-slave' :
6357 # add slave to bridge, but leave it down; bridge is definitely no-carrier
6358 self
. check_link_attr ( 'test1' , 'operstate' , 'down' )
6359 check_output ( 'ip link set dev test1 master bridge99' )
6360 self
. wait_operstate ( 'bridge99' , operstate
= 'no-carrier' , setup_state
= None )
6361 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6362 elif test
== 'slave-up' :
6363 # bring up slave, which will have carrier; bridge gains carrier
6364 check_output ( 'ip link set dev test1 up' )
6365 self
. wait_online ( 'bridge99:routable' )
6366 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
6367 elif test
== 'slave-no-carrier' :
6368 # drop slave carrier; bridge loses carrier
6369 check_output ( 'ip link set dev test1 carrier off' )
6370 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
6371 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6372 elif test
== 'slave-carrier' :
6373 # restore slave carrier; bridge gains carrier
6374 check_output ( 'ip link set dev test1 carrier on' )
6375 self
. wait_online ( 'bridge99:routable' )
6376 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
6377 elif test
== 'slave-down' :
6378 # bring down slave; bridge loses carrier
6379 check_output ( 'ip link set dev test1 down' )
6380 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
6381 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
6383 output
= networkctl_status ( 'bridge99' )
6384 self
. assertRegex ( output
, '10.1.2.3' )
6385 self
. assertRegex ( output
, '10.1.2.1' )
6387 def test_bridge_ignore_carrier_loss ( self
):
6388 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
6389 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
6390 '25-bridge99-ignore-carrier-loss.network' )
6392 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
6394 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
6395 remove_link ( 'test1' , 'dummy98' )
6398 output
= check_output ( 'ip address show bridge99' )
6400 self
. assertRegex ( output
, 'NO-CARRIER' )
6401 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
6402 self
. assertRegex ( output
, 'inet 192.168.0.16/24 scope global secondary bridge99' )
6404 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain ( self
):
6405 copy_network_unit ( '26-bridge.netdev' , '26-bridge-slave-interface-1.network' ,
6406 '25-bridge99-ignore-carrier-loss.network' )
6408 self
. wait_online ( 'bridge99:no-carrier' )
6410 for trial
in range ( 4 ):
6411 check_output ( 'ip link add dummy98 type dummy' )
6412 check_output ( 'ip link set dummy98 up' )
6414 remove_link ( 'dummy98' )
6416 self
. wait_online ( 'bridge99:routable' , 'dummy98:enslaved' )
6418 output
= check_output ( 'ip address show bridge99' )
6420 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
6422 output
= check_output ( 'ip rule list table 100' )
6424 self
. assertIn ( 'from all to 8.8.8.8 lookup 100' , output
)
6426 class NetworkdSRIOVTests ( unittest
. TestCase
, Utilities
):
6434 def setup_netdevsim ( self
, id = 99 , num_ports
= 1 , num_vfs
= 0 ):
6435 call ( 'modprobe netdevsim' )
6437 # Create netdevsim device.
6438 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
6439 f
. write ( f
' {id} {num_ports} ' )
6443 with
open ( f
'/sys/bus/netdevsim/devices/netdevsim {id} /sriov_numvfs' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
6444 f
. write ( f
' {num_vfs} ' )
6446 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
6447 def test_sriov ( self
):
6448 copy_network_unit ( '25-netdevsim.link' , '25-sriov.network' )
6450 self
. setup_netdevsim ( num_vfs
= 3 )
6453 self
. wait_online ( 'sim99:routable' )
6455 output
= check_output ( 'ip link show dev sim99' )
6457 self
. assertRegex ( output
,
6458 '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 *'
6459 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6460 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6463 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
6464 def test_sriov_udev ( self
):
6465 copy_network_unit ( '25-sriov.link' , '25-sriov-udev.network' )
6467 self
. setup_netdevsim ()
6470 self
. wait_online ( 'sim99:routable' )
6472 # The name sim99 is an alternative name, and cannot be used by udevadm below.
6473 ifname
= link_resolve ( 'sim99' )
6475 output
= check_output ( 'ip link show dev sim99' )
6477 self
. assertRegex ( output
,
6478 '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 *'
6479 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6480 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6482 self
. assertNotIn ( 'vf 3' , output
)
6483 self
. assertNotIn ( 'vf 4' , output
)
6485 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6486 f
. write ( '[Link] \n SR-IOVVirtualFunctions=4 \n ' )
6489 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6491 output
= check_output ( 'ip link show dev sim99' )
6493 self
. assertRegex ( output
,
6494 '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 *'
6495 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6496 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
6499 self
. assertNotIn ( 'vf 4' , output
)
6501 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6502 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
6505 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6507 output
= check_output ( 'ip link show dev sim99' )
6509 self
. assertRegex ( output
,
6510 '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 *'
6511 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6512 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
6515 self
. assertNotIn ( 'vf 4' , output
)
6517 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6518 f
. write ( '[Link] \n SR-IOVVirtualFunctions=2 \n ' )
6521 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6523 output
= check_output ( 'ip link show dev sim99' )
6525 self
. assertRegex ( output
,
6526 '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 *'
6527 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
6529 self
. assertNotIn ( 'vf 2' , output
)
6530 self
. assertNotIn ( 'vf 3' , output
)
6531 self
. assertNotIn ( 'vf 4' , output
)
6533 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6534 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
6537 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
6539 output
= check_output ( 'ip link show dev sim99' )
6541 self
. assertRegex ( output
,
6542 '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 *'
6543 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
6544 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
6546 self
. assertNotIn ( 'vf 3' , output
)
6547 self
. assertNotIn ( 'vf 4' , output
)
6549 class NetworkdLLDPTests ( unittest
. TestCase
, Utilities
):
6557 def test_lldp ( self
):
6558 copy_network_unit ( '23-emit-lldp.network' , '24-lldp.network' , '25-veth.netdev' )
6560 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' )
6563 output
= networkctl ( 'lldp' )
6565 if re
. search ( r
'veth99 .* veth-peer .* .......a...' , output
):
6571 # With interface name
6572 output
= networkctl ( 'lldp' , 'veth99' );
6574 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
6576 # With interface name pattern
6577 output
= networkctl ( 'lldp' , 've*9' );
6579 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
6582 output
= networkctl ( '--json=short' , 'lldp' )
6584 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6585 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6586 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6588 # json format with interface name
6589 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
6591 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6592 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6593 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6595 # json format with interface name pattern
6596 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
6598 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6599 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6600 self
. assertIn ( '"EnabledCapabilities":128' , output
)
6602 # LLDP neighbors in status
6603 output
= networkctl_status ( 'veth99' )
6605 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
6607 # enable forwarding, to enable the Router flag
6608 with
open ( os
. path
. join ( network_unit_dir
, '23-emit-lldp.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6609 f
. write ( '[Network] \n IPv4Forwarding=yes \n ' )
6612 self
. wait_online ( 'veth-peer:degraded' )
6615 output
= networkctl ( 'lldp' )
6617 if re
. search ( r
'veth99 .* veth-peer .* ....r......' , output
):
6623 # With interface name
6624 output
= networkctl ( 'lldp' , 'veth99' );
6626 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
6628 # With interface name pattern
6629 output
= networkctl ( 'lldp' , 've*9' );
6631 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
6634 output
= networkctl ( '--json=short' , 'lldp' )
6636 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6637 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6638 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6640 # json format with interface name
6641 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
6643 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6644 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6645 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6647 # json format with interface name pattern
6648 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
6650 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
6651 self
. assertIn ( '"PortID":"veth-peer"' , output
)
6652 self
. assertIn ( '"EnabledCapabilities":16' , output
)
6654 # LLDP neighbors in status
6655 output
= networkctl_status ( 'veth99' )
6657 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
6659 # Compare the json output from sender and receiver
6660 sender_json
= get_link_description ( 'veth-peer' )[ 'LLDP' ]
6661 receiver_json
= json
. loads ( networkctl ( '--json=short' , 'lldp' , 'veth99' ))[ 'Neighbors' ][ 0 ][ 'Neighbors' ][ 0 ]
6663 print ( receiver_json
)
6664 self
. assertEqual ( sender_json
, receiver_json
)
6666 class NetworkdRATests ( unittest
. TestCase
, Utilities
):
6674 def test_ipv6_prefix_delegation ( self
):
6675 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth.network' )
6676 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
6677 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
6678 self
. setup_nftset ( 'ifindex' , 'iface_index' )
6680 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6682 # IPv6SendRA=yes implies IPv6Forwarding.
6683 self
. check_ipv6_sysctl_attr ( 'veth-peer' , 'forwarding' , '1' )
6685 output
= resolvectl ( 'dns' , 'veth99' )
6687 self
. assertRegex ( output
, 'fe80::' )
6688 self
. assertRegex ( output
, '2002:da8:1::1' )
6690 output
= resolvectl ( 'domain' , 'veth99' )
6692 self
. assertIn ( 'hogehoge.test' , output
)
6694 output
= networkctl_status ( 'veth99' )
6696 self
. assertRegex ( output
, '2002:da8:1:0' )
6698 self
. check_ipv6_neigh_sysctl_attr ( 'veth99' , 'base_reachable_time_ms' , '42000' )
6699 self
. check_ipv6_neigh_sysctl_attr ( 'veth99' , 'retrans_time_ms' , '500' )
6701 self
. check_netlabel ( 'veth99' , '2002:da8:1::/64' )
6702 self
. check_netlabel ( 'veth99' , '2002:da8:2::/64' )
6704 self
. check_nftset ( 'addr6' , '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
6705 self
. check_nftset ( 'addr6' , '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
6706 self
. check_nftset ( 'network6' , '2002:da8:1::/64' )
6707 self
. check_nftset ( 'network6' , '2002:da8:2::/64' )
6708 self
. check_nftset ( 'ifindex' , 'veth99' )
6710 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
6712 def check_ipv6_token_static ( self
):
6713 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6715 output
= networkctl_status ( 'veth99' )
6717 self
. assertRegex ( output
, '2002:da8:1:0:1a:2b:3c:4d' )
6718 self
. assertRegex ( output
, '2002:da8:1:0:fa:de:ca:fe' )
6719 self
. assertRegex ( output
, '2002:da8:2:0:1a:2b:3c:4d' )
6720 self
. assertRegex ( output
, '2002:da8:2:0:fa:de:ca:fe' )
6722 def test_ipv6_token_static ( self
):
6723 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
6726 self
. check_ipv6_token_static ()
6729 check_output ( 'ip link set veth99 down' )
6730 check_output ( 'ip link set veth99 up' )
6732 self
. check_ipv6_token_static ()
6735 check_output ( 'ip link set veth99 down' )
6736 time
. sleep ( random
. uniform ( 0 , 0.1 ))
6737 check_output ( 'ip link set veth99 up' )
6738 time
. sleep ( random
. uniform ( 0 , 0.1 ))
6740 self
. check_ipv6_token_static ()
6742 def test_ndisc_redirect ( self
):
6743 if not os
. path
. exists ( test_ndisc_send
):
6744 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
6746 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
6749 self
. check_ipv6_token_static ()
6751 # Introduce three redirect routes.
6752 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' )
6753 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' )
6754 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' )
6755 self
. wait_route ( 'veth99' , '2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6756 self
. wait_route ( 'veth99' , '2002:da8:1:2:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6757 self
. wait_route ( 'veth99' , '2002:da8:1:3:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6759 # Change the target address of the redirects.
6760 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' )
6761 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' )
6762 self
. wait_route_dropped ( 'veth99' , '2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6763 self
. wait_route_dropped ( 'veth99' , '2002:da8:1:2:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6764 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 )
6765 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 )
6767 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
6768 # Then, verify that all redirect routes and the default route are dropped.
6769 output
= check_output ( 'ip -6 address show dev veth-peer scope link' )
6770 veth_peer_ipv6ll
= re
. search ( 'fe80:[:0-9a-f]*' , output
). group ()
6771 print ( f
'veth-peer IPv6LL address: {veth_peer_ipv6ll} ' )
6772 check_output ( f
' {test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no' )
6773 self
. wait_route_dropped ( 'veth99' , 'proto ra' , ipv
= '-6' , timeout_sec
= 10 )
6774 self
. wait_route_dropped ( 'veth99' , 'proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
6776 # Check if sd-radv refuses RS from the same interface.
6777 # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
6778 since
= datetime
. datetime
. now ()
6779 check_output ( f
' {test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll} ' )
6780 self
. check_networkd_log ( 'veth-peer: RADV: Received RS from the same interface, ignoring.' , since
= since
)
6782 def check_ndisc_mtu ( self
, mtu
):
6784 output
= read_ipv6_sysctl_attr ( 'veth99' , 'mtu' )
6785 if output
== f
' {mtu} ' :
6789 self
. fail ( f
'IPv6 MTU does not matches: value= {output} , expected= {mtu} ' )
6791 def test_ndisc_mtu ( self
):
6792 if not os
. path
. exists ( test_ndisc_send
):
6793 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
6795 copy_network_unit ( '25-veth.netdev' ,
6796 '25-veth-peer-no-address.network' ,
6797 '25-ipv6-prefix-veth-token-static.network' )
6799 self
. wait_online ( 'veth-peer:degraded' )
6801 self
. check_networkd_log ( 'veth99: NDISC: Started IPv6 Router Solicitation client' )
6803 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400' )
6804 self
. check_ndisc_mtu ( 1400 )
6806 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410' )
6807 self
. check_ndisc_mtu ( 1410 )
6809 check_output ( 'ip link set dev veth99 mtu 1600' )
6810 check_output ( 'ip link set dev veth-peer mtu 1600' )
6811 self
. check_ndisc_mtu ( 1410 )
6813 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700' )
6814 self
. check_ndisc_mtu ( 1600 )
6816 check_output ( 'ip link set dev veth99 mtu 1800' )
6817 check_output ( 'ip link set dev veth-peer mtu 1800' )
6818 self
. check_ndisc_mtu ( 1700 )
6820 def test_ipv6_token_prefixstable ( self
):
6821 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable.network' )
6823 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6825 output
= check_output ( 'ip -6 address show dev veth99' )
6827 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
6828 self
. assertIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
6830 with
open ( os
. path
. join ( network_unit_dir
, '25-ipv6-prefix-veth-token-prefixstable.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6831 f
. write ( ' \n [IPv6AcceptRA] \n PrefixAllowList=2002:da8:1:0::/64 \n ' )
6834 self
. wait_online ( 'veth99:routable' )
6836 output
= check_output ( 'ip -6 address show dev veth99' )
6838 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
6839 self
. assertNotIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
6841 check_output ( 'ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99' )
6842 check_output ( 'ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad' )
6844 networkctl_reconfigure ( 'veth99' )
6845 self
. wait_online ( 'veth99:routable' )
6846 self
. wait_address ( 'veth99' , '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 2nd prefixstable
6847 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 1st prefixstable
6849 check_output ( 'ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99' )
6850 check_output ( 'ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad' )
6852 networkctl_reconfigure ( 'veth99' )
6853 self
. wait_online ( 'veth99:routable' )
6854 self
. wait_address ( 'veth99' , '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 3rd prefixstable
6855 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 2nd prefixstable
6856 self
. wait_address_dropped ( 'veth99' , '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , ipv
= '-6' , timeout_sec
= 10 ) # the 1st prefixstable
6858 def test_ipv6_token_prefixstable_without_address ( self
):
6859 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable-without-address.network' )
6861 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
6863 output
= networkctl_status ( 'veth99' )
6865 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e' , output
)
6866 self
. assertIn ( '2002:da8:2:0:f689:561a:8eda:7443' , output
)
6868 def test_router_hop_limit ( self
):
6869 copy_network_unit ( '25-veth-client.netdev' ,
6870 '25-veth-router.netdev' ,
6872 '25-veth-bridge.network' ,
6873 '25-veth-client.network' ,
6874 '25-veth-router-hop-limit.network' ,
6875 '25-bridge99.network' )
6877 self
. wait_online ( 'client:routable' , 'client-p:enslaved' ,
6878 'router:degraded' , 'router-p:enslaved' ,
6879 'bridge99:routable' )
6881 self
. check_ipv6_sysctl_attr ( 'client' , 'hop_limit' , '42' )
6883 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-hop-limit.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6884 f
. write ( ' \n [IPv6SendRA] \n HopLimit=43 \n ' )
6889 output
= read_ipv6_sysctl_attr ( 'client' , 'hop_limit' )
6894 self
. check_ipv6_sysctl_attr ( 'client' , 'hop_limit' , '43' )
6896 def check_router_preference ( self
, suffix
, metric_1
, preference_1
, metric_2
, preference_2
):
6897 self
. wait_online ( 'client:routable' )
6898 self
. wait_address ( 'client' , f
'2002:da8:1:99:1034:56ff:fe78:9a {suffix} /64' , ipv
= '-6' , timeout_sec
= 10 )
6899 self
. wait_address ( 'client' , f
'2002:da8:1:98:1034:56ff:fe78:9a {suffix} /64' , ipv
= '-6' , timeout_sec
= 10 )
6900 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 )
6901 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 )
6903 print ( '### ip -6 route show dev client default' )
6904 output
= check_output ( 'ip -6 route show dev client default' )
6906 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} ' )
6907 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} ' )
6909 for i
in [ 100 , 200 , 300 , 512 , 1024 , 2048 ]:
6910 if i
not in [ metric_1
, metric_2
]:
6911 self
. assertNotIn ( f
'metric {i} ' , output
)
6913 for i
in [ 'low' , 'medium' , 'high' ]:
6914 if i
not in [ preference_1
, preference_2
]:
6915 self
. assertNotIn ( f
'pref {i} ' , output
)
6917 def test_router_preference ( self
):
6918 copy_network_unit ( '25-veth-client.netdev' ,
6919 '25-veth-router-high.netdev' ,
6920 '25-veth-router-low.netdev' ,
6922 '25-veth-bridge.network' ,
6923 '25-veth-client.network' ,
6924 '25-veth-router-high.network' ,
6925 '25-veth-router-low.network' ,
6926 '25-bridge99.network' )
6928 self
. wait_online ( 'client-p:enslaved' ,
6929 'router-high:degraded' , 'router-high-p:enslaved' ,
6930 'router-low:degraded' , 'router-low-p:enslaved' ,
6931 'bridge99:routable' )
6933 networkctl_reconfigure ( 'client' )
6934 self
. wait_online ( 'client:routable' )
6935 self
. check_router_preference ( '00' , 512 , 'high' , 2048 , 'low' )
6937 # change the map from preference to metric.
6938 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6939 f
. write ( ' \n [Link] \n MACAddress=12:34:56:78:9a:01 \n [IPv6AcceptRA] \n RouteMetric=100:200:300 \n ' )
6941 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
6943 # swap the preference (for issue #28439)
6944 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6945 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=low \n ' )
6946 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6947 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=high \n ' )
6949 self
. check_router_preference ( '01' , 300 , 'low' , 100 , 'high' )
6951 # Use the same preference, and check if the two routes are not coalesced. See issue #33470.
6952 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6953 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=medium \n ' )
6954 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6955 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=medium \n ' )
6957 self
. check_router_preference ( '01' , 200 , 'medium' , 200 , 'medium' )
6959 # Use route options to configure default routes.
6960 # The preference specified in the RA header should be ignored. See issue #33468.
6961 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6962 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=high \n [IPv6RoutePrefix] \n Route=::/0 \n LifetimeSec=1200 \n ' )
6963 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6964 f
. write ( ' \n [IPv6SendRA] \n RouterPreference=low \n [IPv6RoutePrefix] \n Route=::/0 \n LifetimeSec=1200 \n ' )
6966 self
. check_router_preference ( '01' , 200 , 'medium' , 200 , 'medium' )
6968 # Set zero lifetime to the route options.
6969 # The preference specified in the RA header should be used.
6970 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6971 f
. write ( 'LifetimeSec=0 \n ' )
6972 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6973 f
. write ( 'LifetimeSec=0 \n ' )
6975 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
6977 # Use route options with preference to configure default routes.
6978 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6979 f
. write ( 'LifetimeSec=1200 \n Preference=low \n ' )
6980 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6981 f
. write ( 'LifetimeSec=1200 \n Preference=high \n ' )
6983 self
. check_router_preference ( '01' , 300 , 'low' , 100 , 'high' )
6985 # Set zero lifetime again to the route options.
6986 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-high.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6987 f
. write ( 'LifetimeSec=0 \n ' )
6988 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-low.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6989 f
. write ( 'LifetimeSec=0 \n ' )
6991 self
. check_router_preference ( '01' , 100 , 'high' , 300 , 'low' )
6993 def _test_ndisc_vs_static_route ( self
, manage_foreign_nexthops
):
6994 if not manage_foreign_nexthops
:
6995 copy_networkd_conf_dropin ( 'networkd-manage-foreign-nexthops-no.conf' )
6996 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-static-route.network' )
6998 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
7000 # If a conflicting static route is already configured, do not override the static route.
7001 print ( '### ip -6 route show dev veth99 default' )
7002 output
= check_output ( 'ip -6 route show dev veth99 default' )
7004 self
. assertIn ( 'via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium' , output
)
7005 if manage_foreign_nexthops
:
7006 self
. assertRegex ( output
, r
'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium' )
7008 self
. assertNotIn ( 'proto ra' , output
)
7010 print ( '### ip -6 nexthop show dev veth99' )
7011 output
= check_output ( 'ip -6 nexthop show dev veth99' )
7013 if manage_foreign_nexthops
:
7014 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra' )
7016 self
. assertEqual ( output
, '' )
7018 # Also check if the static route is protected from RA with zero lifetime
7019 with
open ( os
. path
. join ( network_unit_dir
, '25-ipv6-prefix.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7020 f
. write ( ' \n [Network] \n IPv6SendRA=no \n ' )
7021 networkctl_reload () # This makes veth-peer being reconfigured, and send RA with zero lifetime
7022 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 )
7024 print ( '### ip -6 route show dev veth99 default' )
7025 output
= check_output ( 'ip -6 route show dev veth99 default' )
7027 self
. assertIn ( 'via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium' , output
)
7028 self
. assertNotIn ( 'proto ra' , output
)
7030 # Check if nexthop is removed.
7031 print ( '### ip -6 nexthop show dev veth99' )
7032 output
= check_output ( 'ip -6 nexthop show dev veth99' )
7034 self
. assertEqual ( output
, '' )
7036 def test_ndisc_vs_static_route ( self
):
7038 for manage_foreign_nexthops
in [ True , False ]:
7044 print ( f
'### test_ndisc_vs_static_route(manage_foreign_nexthops= {manage_foreign_nexthops} )' )
7045 with self
. subTest ( manage_foreign_nexthops
= manage_foreign_nexthops
):
7046 self
._ test
_ ndisc
_ vs
_ static
_ route
( manage_foreign_nexthops
)
7048 # radvd supports captive portal since v2.20.
7049 # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc
7050 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
7051 def test_captive_portal ( self
):
7052 copy_network_unit ( '25-veth-client.netdev' ,
7053 '25-veth-router-captive.netdev' ,
7055 '25-veth-client-captive.network' ,
7056 '25-veth-router-captive.network' ,
7057 '25-veth-bridge-captive.network' ,
7058 '25-bridge99.network' )
7060 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
7061 'router-captive:degraded' , 'router-captivep:enslaved' )
7063 start_radvd ( config_file
= 'captive-portal.conf' )
7064 networkctl_reconfigure ( 'client' )
7065 self
. wait_online ( 'client:routable' )
7067 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
7068 output
= networkctl_status ( 'client' )
7070 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
7072 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
7073 def test_invalid_captive_portal ( self
):
7074 def radvd_write_config ( captive_portal_uri
):
7075 with
open ( os
. path
. join ( networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
7076 f
. write ( f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI " {captive_portal_uri} "; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};' )
7078 captive_portal_uris
= [
7079 "42ěščěškd ěšč ě s" ,
7084 copy_network_unit ( '25-veth-client.netdev' ,
7085 '25-veth-router-captive.netdev' ,
7087 '25-veth-client-captive.network' ,
7088 '25-veth-router-captive.network' ,
7089 '25-veth-bridge-captive.network' ,
7090 '25-bridge99.network' )
7092 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
7093 'router-captive:degraded' , 'router-captivep:enslaved' )
7095 for uri
in captive_portal_uris
:
7096 print ( f
"Captive portal: {uri} " )
7097 radvd_write_config ( uri
)
7099 start_radvd ( config_file
= 'bogus-captive-portal.conf' )
7100 networkctl_reconfigure ( 'client' )
7101 self
. wait_online ( 'client:routable' )
7103 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
7104 output
= networkctl_status ( 'client' )
7106 self
. assertNotIn ( 'Captive Portal:' , output
)
7108 class NetworkdDHCPServerTests ( unittest
. TestCase
, Utilities
):
7116 def check_dhcp_server ( self
, persist_leases
= 'yes' ):
7117 output
= networkctl_status ( 'veth99' )
7119 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
7120 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
7121 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
7122 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
7124 output
= networkctl_status ( 'veth-peer' )
7126 self
. assertRegex ( output
, "Offered DHCP leases: 192.168.5.[0-9]*" )
7128 if persist_leases
== 'yes' :
7129 path
= '/var/lib/systemd/network/dhcp-server-lease/veth-peer'
7130 elif persist_leases
== 'runtime' :
7131 path
= '/run/systemd/netif/dhcp-server-lease/veth-peer'
7136 with
open ( path
, encoding
= 'utf-8' ) as f
:
7137 check_json ( f
. read ())
7139 def test_dhcp_server ( self
):
7140 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7142 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7144 self
. check_dhcp_server ()
7146 networkctl_reconfigure ( 'veth-peer' )
7147 self
. wait_online ( 'veth-peer:routable' )
7150 output
= check_output (* networkctl_cmd
, '-n' , '0' , 'status' , 'veth-peer' , env
= env
)
7151 if 'Offered DHCP leases: 192.168.5.' in output
:
7157 def test_dhcp_server_persist_leases_no ( self
):
7158 copy_networkd_conf_dropin ( 'persist-leases-no.conf' )
7159 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7161 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7163 self
. check_dhcp_server ( persist_leases
= 'no' )
7165 remove_networkd_conf_dropin ( 'persist-leases-no.conf' )
7166 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-server.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7167 f
. write ( '[DHCPServer] \n PersistLeases=no' )
7169 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7171 self
. check_dhcp_server ( persist_leases
= 'no' )
7173 def test_dhcp_server_persist_leases_runtime ( self
):
7174 copy_networkd_conf_dropin ( 'persist-leases-runtime.conf' )
7175 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
7177 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7179 self
. check_dhcp_server ( persist_leases
= 'runtime' )
7181 remove_networkd_conf_dropin ( 'persist-leases-runtime.conf' )
7182 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-server.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7183 f
. write ( '[DHCPServer] \n PersistLeases=runtime' )
7185 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7187 self
. check_dhcp_server ( persist_leases
= 'runtime' )
7189 def test_dhcp_server_null_server_address ( self
):
7190 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-null-server-address.network' )
7192 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7194 output
= check_output ( 'ip --json address show dev veth-peer' )
7195 server_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
7196 print ( server_address
)
7198 output
= check_output ( 'ip --json address show dev veth99' )
7199 client_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
7200 print ( client_address
)
7202 output
= networkctl_status ( 'veth99' )
7204 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCPv4 via {server_address} \)' )
7205 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
7206 self
. assertIn ( f
'DNS: {server_address} ' , output
)
7207 self
. assertIn ( f
'NTP: {server_address} ' , output
)
7209 output
= networkctl_status ( 'veth-peer' )
7210 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
7212 # Check if the same addresses are used even if the service is restarted.
7214 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7216 output
= check_output ( 'ip -4 address show dev veth-peer' )
7218 self
. assertIn ( f
' {server_address} ' , output
)
7220 output
= check_output ( 'ip -4 address show dev veth99' )
7222 self
. assertIn ( f
' {client_address} ' , output
)
7224 output
= networkctl_status ( 'veth99' )
7226 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCPv4 via {server_address} \)' )
7227 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
7228 self
. assertIn ( f
'DNS: {server_address} ' , output
)
7229 self
. assertIn ( f
'NTP: {server_address} ' , output
)
7231 output
= networkctl_status ( 'veth-peer' )
7232 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
7234 def test_dhcp_server_with_uplink ( self
):
7235 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-downstream.network' ,
7236 '12-dummy.netdev' , '25-dhcp-server-uplink.network' )
7238 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7240 output
= networkctl_status ( 'veth99' )
7242 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)' )
7243 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
7244 self
. assertIn ( 'DNS: 192.168.5.1' , output
)
7245 self
. assertIn ( 'NTP: 192.168.5.1' , output
)
7247 def test_emit_router_timezone ( self
):
7248 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-timezone-router.network' , '25-dhcp-server-timezone-router.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.1' , output
)
7256 self
. assertIn ( 'Time Zone: Europe/Berlin' , output
)
7258 def test_dhcp_server_static_lease_mac_by_network ( self
):
7259 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-static-lease.network' , '25-dhcp-server-static-lease.network' )
7260 copy_networkd_conf_dropin ( '10-dhcp-client-id-duid.conf' )
7262 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7264 output
= networkctl_status ( 'veth99' )
7266 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7267 self
. assertIn ( 'DHCPv4 Client ID: 12:34:56:78:9a:bc' , output
)
7269 def test_dhcp_server_static_lease_mac_by_global ( self
):
7270 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-static-lease.network' )
7271 copy_networkd_conf_dropin ( '10-dhcp-client-id-mac.conf' )
7273 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7275 output
= networkctl_status ( 'veth99' )
7277 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7278 self
. assertIn ( 'DHCPv4 Client ID: 12:34:56:78:9a:bc' , output
)
7280 def test_dhcp_server_static_lease_duid ( self
):
7281 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-static-lease.network' )
7283 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7285 output
= networkctl_status ( 'veth99' )
7287 self
. assertIn ( 'Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)' , output
)
7288 self
. assertRegex ( output
, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID' )
7290 class NetworkdDHCPServerRelayAgentTests ( unittest
. TestCase
, Utilities
):
7298 def test_relay_agent ( self
):
7299 copy_network_unit ( '25-agent-veth-client.netdev' ,
7300 '25-agent-veth-server.netdev' ,
7301 '25-agent-client.network' ,
7302 '25-agent-server.network' ,
7303 '25-agent-client-peer.network' ,
7304 '25-agent-server-peer.network' )
7307 self
. wait_online ( 'client:routable' )
7309 output
= networkctl_status ( 'client' )
7311 self
. assertRegex ( output
, r
'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)' )
7313 def test_relay_agent_on_bridge ( self
):
7314 copy_network_unit ( '25-agent-bridge.netdev' ,
7315 '25-agent-veth-client.netdev' ,
7316 '25-agent-bridge.network' ,
7317 '25-agent-bridge-port.network' ,
7318 '25-agent-client.network' )
7320 self
. wait_online ( 'bridge-relay:routable' , 'client-peer:enslaved' )
7323 self
. check_networkd_log ( 'bridge-relay: DHCPv4 server: STARTED' )
7325 class NetworkdDHCPClientTests ( unittest
. TestCase
, Utilities
):
7333 def test_dhcp_client_ipv6_only ( self
):
7334 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
7337 self
. wait_online ( 'veth-peer:carrier' )
7339 # information request mode
7340 # The name ipv6-only option may not be supported by older dnsmasq
7341 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
7342 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7343 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7344 '--dhcp-option=option6:ntp-server,[2600::ff]' ,
7345 ra_mode
= 'ra-stateless' )
7346 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7348 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
7349 # Let's wait for the expected DNS server being listed in the state file.
7350 for _
in range ( 100 ):
7351 output
= read_link_state_file ( 'veth99' )
7352 if 'DNS=2600::ee' in output
:
7356 # Check link state file
7357 print ( '## link state file' )
7358 output
= read_link_state_file ( 'veth99' )
7360 self
. assertIn ( 'DNS=2600::ee' , output
)
7361 self
. assertIn ( 'NTP=2600::ff' , output
)
7363 # Check manager state file
7364 print ( '## manager state file' )
7365 output
= read_manager_state_file ()
7367 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7368 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7370 print ( '## dnsmasq log' )
7371 output
= read_dnsmasq_log_file ()
7373 self
. assertIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7374 self
. assertNotIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7375 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7376 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7377 self
. assertNotIn ( 'DHCPREPLY(veth-peer)' , output
)
7380 check_json ( networkctl_json ( 'veth99' ))
7384 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7385 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7386 '--dhcp-option=option6:ntp-server,[2600::ff]' )
7387 networkctl_reconfigure ( 'veth99' )
7388 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7391 output
= check_output ( 'ip address show dev veth99 scope global' )
7393 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7394 self
. assertNotIn ( '192.168.5' , output
)
7396 # checking semi-static route
7397 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
7399 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
7401 # Confirm that ipv6 token is not set in the kernel
7402 output
= check_output ( 'ip token show dev veth99' )
7404 self
. assertRegex ( output
, 'token :: dev veth99' )
7406 # Make manager and link state file updated
7407 resolvectl ( 'revert' , 'veth99' )
7409 # Check link state file
7410 print ( '## link state file' )
7411 output
= read_link_state_file ( 'veth99' )
7413 self
. assertIn ( 'DNS=2600::ee' , output
)
7414 self
. assertIn ( 'NTP=2600::ff' , output
)
7416 # Check manager state file
7417 print ( '## manager state file' )
7418 output
= read_manager_state_file ()
7420 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7421 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7423 print ( '## dnsmasq log' )
7424 output
= read_dnsmasq_log_file ()
7426 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7427 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7428 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7429 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7430 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7431 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
7434 check_json ( networkctl_json ( 'veth99' ))
7436 # Testing without rapid commit support
7437 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-ipv6-only.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7438 f
. write ( ' \n [DHCPv6] \n RapidCommit=no \n ' )
7441 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
7442 '--dhcp-option=option6:dns-server,[2600::ee]' ,
7443 '--dhcp-option=option6:ntp-server,[2600::ff]' )
7446 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7449 output
= check_output ( 'ip address show dev veth99 scope global' )
7451 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7452 self
. assertNotIn ( '192.168.5' , output
)
7454 # checking semi-static route
7455 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
7457 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
7459 # Make manager and link state file updated
7460 resolvectl ( 'revert' , 'veth99' )
7462 # Check link state file
7463 print ( '## link state file' )
7464 output
= read_link_state_file ( 'veth99' )
7466 self
. assertIn ( 'DNS=2600::ee' , output
)
7467 self
. assertIn ( 'NTP=2600::ff' , output
)
7469 # Check manager state file
7470 print ( '## manager state file' )
7471 output
= read_manager_state_file ()
7473 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
7474 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
7476 print ( '## dnsmasq log' )
7477 output
= read_dnsmasq_log_file ()
7479 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
7480 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
7481 self
. assertIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7482 self
. assertIn ( 'DHCPREQUEST(veth-peer)' , output
)
7483 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7484 self
. assertNotIn ( 'rapid-commit' , output
)
7487 check_json ( networkctl_json ( 'veth99' ))
7489 def test_dhcp_client_ipv6_dbus_status ( self
):
7490 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
7492 self
. wait_online ( 'veth-peer:carrier' )
7494 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
7495 # bit set) has yet been received and the configuration does not include WithoutRA=true
7496 state
= get_dhcp6_client_state ( 'veth99' )
7497 print ( f
"DHCPv6 client state = {state} " )
7498 self
. assertEqual ( state
, 'stopped' )
7500 state
= get_dhcp4_client_state ( 'veth99' )
7501 print ( f
"DHCPv4 client state = {state} " )
7502 self
. assertEqual ( state
, 'selecting' )
7504 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
7505 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7507 state
= get_dhcp6_client_state ( 'veth99' )
7508 print ( f
"DHCPv6 client state = {state} " )
7509 self
. assertEqual ( state
, 'bound' )
7511 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
7512 for _
in range ( 100 ):
7513 state
= get_dhcp4_client_state ( 'veth99' )
7514 if state
== 'stopped' :
7518 print ( f
"DHCPv4 client state = {state} " )
7519 self
. assertEqual ( state
, 'stopped' )
7521 # restart dnsmasq to clear log
7523 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
7525 # Test renew command
7526 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
7527 networkctl ( 'renew' , 'veth99' )
7529 for _
in range ( 100 ):
7530 state
= get_dhcp4_client_state ( 'veth99' )
7531 if state
== 'stopped' :
7535 print ( f
"DHCPv4 client state = {state} " )
7536 self
. assertEqual ( state
, 'stopped' )
7538 print ( '## dnsmasq log' )
7539 output
= read_dnsmasq_log_file ()
7541 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc' , output
)
7542 self
. assertIn ( 'DHCPOFFER(veth-peer)' , output
)
7543 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7544 self
. assertNotIn ( 'DHCPACK(veth-peer)' , output
)
7546 def test_dhcp_client_ipv6_only_with_custom_client_identifier ( self
):
7547 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only-custom-client-identifier.network' )
7550 self
. wait_online ( 'veth-peer:carrier' )
7552 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7555 output
= check_output ( 'ip address show dev veth99 scope global' )
7557 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
7558 self
. assertNotIn ( '192.168.5' , output
)
7560 print ( '## dnsmasq log' )
7561 output
= read_dnsmasq_log_file ()
7563 self
. assertIn ( 'DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00' , output
)
7564 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
7565 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7566 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
7567 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
7569 @expectedFailureIfKernelReturnsInvalidFlags ()
7570 def test_dhcp_client_ipv4_only ( self
):
7571 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' ,
7572 '25-sit-dhcp4.netdev' , '25-sit-dhcp4.network' )
7574 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
7575 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
7576 self
. setup_nftset ( 'ifindex' , 'iface_index' )
7579 self
. wait_online ( 'veth-peer:carrier' )
7580 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
7581 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22' ,
7582 '--dhcp-option=option:domain-search,example.com' ,
7583 '--dhcp-alternate-port=67,5555' ,
7584 ipv4_range
= '192.168.5.110,192.168.5.119' )
7585 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'sit-dhcp4:carrier' )
7586 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
7588 print ( '## ip address show dev veth99 scope global' )
7589 output
= check_output ( 'ip address show dev veth99 scope global' )
7591 self
. assertIn ( 'mtu 1492' , output
)
7592 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
7593 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' )
7594 self
. assertNotIn ( '2600::' , output
)
7596 output
= check_output ( 'ip -4 --json address show dev veth99' )
7597 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
7598 if i
[ 'label' ] == 'test-label' :
7599 address1
= i
[ 'local' ]
7602 self
. assertFalse ( True )
7604 self
. assertRegex ( address1
, r
'^192.168.5.11[0-9]$' )
7606 print ( '## ip route show table main dev veth99' )
7607 output
= check_output ( 'ip route show table main dev veth99' )
7609 # no DHCP routes assigned to the main table
7610 self
. assertNotIn ( 'proto dhcp' , output
)
7612 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
7613 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
7614 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
7615 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
7617 print ( '## ip route show table 211 dev veth99' )
7618 output
= check_output ( 'ip route show table 211 dev veth99' )
7620 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24' )
7621 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24' )
7622 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24' )
7623 self
. assertRegex ( output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24' )
7624 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24' )
7625 self
. assertRegex ( output
, f
'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1} ' )
7627 print ( '## ip route show table 212 dev veth99' )
7628 output
= check_output ( 'ip route show table 212 dev veth99' )
7630 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24' )
7631 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24' )
7632 self
. assertRegex ( output
, f
'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1} ' )
7634 print ( '## link state file' )
7635 output
= read_link_state_file ( 'veth99' )
7637 # checking DNS server, SIP server, and Domains
7638 self
. assertIn ( 'DNS=192.168.5.6 192.168.5.7' , output
)
7639 self
. assertIn ( 'SIP=192.168.5.21 192.168.5.22' , output
)
7640 self
. assertIn ( 'DOMAINS=example.com' , output
)
7643 j
= json
. loads ( networkctl_json ( 'veth99' ))
7645 self
. assertEqual ( len ( j
[ 'DNS' ]), 2 )
7648 self
. assertEqual ( i
[ 'Family' ], 2 )
7649 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7650 self
. assertRegex ( a
, '^192.168.5.[67]$' )
7651 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7652 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7653 self
. assertEqual ( '192.168.5.1' , a
)
7655 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
7658 self
. assertEqual ( i
[ 'Family' ], 2 )
7659 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7660 self
. assertRegex ( a
, '^192.168.5.2[12]$' )
7661 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7662 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7663 self
. assertEqual ( '192.168.5.1' , a
)
7666 output
= check_output ( 'ip -d link show sit-dhcp4' )
7668 self
. assertRegex ( output
, fr
'sit (ip6ip )?remote any local {address1} dev veth99' )
7670 print ( '## dnsmasq log' )
7671 output
= read_dnsmasq_log_file ()
7673 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
7674 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc' , output
)
7675 self
. assertIn ( 'client provides name: test-hostname' , output
)
7676 self
. assertIn ( '26:mtu' , output
)
7678 # change address range, DNS servers, and Domains
7680 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8' ,
7681 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24' ,
7682 '--dhcp-option=option:domain-search,foo.example.com' ,
7683 '--dhcp-alternate-port=67,5555' ,
7684 ipv4_range
= '192.168.5.120,192.168.5.129' ,)
7686 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
7687 print ( 'Wait for the DHCP lease to be expired' )
7688 self
. wait_address_dropped ( 'veth99' , f
'inet {address1} /24' , ipv
= '-4' , timeout_sec
= 120 )
7689 self
. wait_address ( 'veth99' , r
'inet 192.168.5.12[0-9]*/24' , ipv
= '-4' )
7691 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7693 print ( '## ip address show dev veth99 scope global' )
7694 output
= check_output ( 'ip address show dev veth99 scope global' )
7696 self
. assertIn ( 'mtu 1492' , output
)
7697 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
7698 self
. assertNotIn ( f
' {address1} ' , output
)
7699 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' )
7700 self
. assertNotIn ( '2600::' , output
)
7702 output
= check_output ( 'ip -4 --json address show dev veth99' )
7703 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
7704 if i
[ 'label' ] == 'test-label' :
7705 address2
= i
[ 'local' ]
7708 self
. assertFalse ( True )
7710 self
. assertRegex ( address2
, r
'^192.168.5.12[0-9]$' )
7712 print ( '## ip route show table main dev veth99' )
7713 output
= check_output ( 'ip route show table main dev veth99' )
7715 # no DHCP routes assigned to the main table
7716 self
. assertNotIn ( 'proto dhcp' , output
)
7718 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
7719 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
7720 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
7721 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
7723 print ( '## ip route show table 211 dev veth99' )
7724 output
= check_output ( 'ip route show table 211 dev veth99' )
7726 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24' )
7727 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24' )
7728 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24' )
7729 self
. assertNotIn ( '192.168.5.6' , output
)
7730 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24' )
7731 self
. assertRegex ( output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24' )
7732 self
. assertRegex ( output
, f
'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2} ' )
7734 print ( '## ip route show table 212 dev veth99' )
7735 output
= check_output ( 'ip route show table 212 dev veth99' )
7737 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24' )
7738 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24' )
7739 self
. assertRegex ( output
, f
'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2} ' )
7741 print ( '## link state file' )
7742 output
= read_link_state_file ( 'veth99' )
7744 # checking DNS server, SIP server, and Domains
7745 self
. assertIn ( 'DNS=192.168.5.1 192.168.5.7 192.168.5.8' , output
)
7746 self
. assertIn ( 'SIP=192.168.5.23 192.168.5.24' , output
)
7747 self
. assertIn ( 'DOMAINS=foo.example.com' , output
)
7750 j
= json
. loads ( networkctl_json ( 'veth99' ))
7752 self
. assertEqual ( len ( j
[ 'DNS' ]), 3 )
7755 self
. assertEqual ( i
[ 'Family' ], 2 )
7756 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7757 self
. assertRegex ( a
, '^192.168.5.[178]$' )
7758 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7759 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7760 self
. assertEqual ( '192.168.5.1' , a
)
7762 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
7765 self
. assertEqual ( i
[ 'Family' ], 2 )
7766 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
7767 self
. assertRegex ( a
, '^192.168.5.2[34]$' )
7768 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
7769 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
7770 self
. assertEqual ( '192.168.5.1' , a
)
7773 output
= check_output ( 'ip -d link show sit-dhcp4' )
7775 self
. assertRegex ( output
, fr
'sit (ip6ip )?remote any local {address2} dev veth99' )
7777 print ( '## dnsmasq log' )
7778 output
= read_dnsmasq_log_file ()
7780 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
7781 self
. assertIn ( f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc' , output
)
7782 self
. assertIn ( 'client provides name: test-hostname' , output
)
7783 self
. assertIn ( '26:mtu' , output
)
7785 self
. check_netlabel ( 'veth99' , r
'192\.168\.5\.0/24' )
7787 self
. check_nftset ( 'addr4' , r
'192\.168\.5\.1' )
7788 self
. check_nftset ( 'network4' , r
'192\.168\.5\.0/24' )
7789 self
. check_nftset ( 'ifindex' , 'veth99' )
7791 # Check if DHCPv4 address and routes are removed on stop. For issue #34837.
7792 stop_networkd ( show_logs
= False )
7793 self
. wait_address_dropped ( 'veth99' , f
'inet {address2} /24' , ipv
= '-4' , timeout_sec
= 120 )
7795 print ( '## ip address show dev veth99 scope global' )
7796 output
= check_output ( 'ip address show dev veth99 scope global' )
7798 self
. assertNotIn ( f
' {address2} ' , output
)
7800 print ( '## ip route show table main dev veth99' )
7801 output
= check_output ( 'ip route show table main dev veth99' )
7803 self
. assertNotIn ( f
' {address2} ' , output
)
7805 print ( '## ip route show table 211 dev veth99' )
7806 output
= check_output ( 'ip route show table 211 dev veth99' )
7808 self
. assertNotIn ( f
' {address2} ' , output
)
7810 print ( '## ip route show table 212 dev veth99' )
7811 output
= check_output ( 'ip route show table 212 dev veth99' )
7813 self
. assertNotIn ( f
' {address2} ' , output
)
7815 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
7817 def test_dhcp_client_ipv4_dbus_status ( self
):
7818 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' )
7820 self
. wait_online ( 'veth-peer:carrier' )
7822 state
= get_dhcp4_client_state ( 'veth99' )
7823 print ( f
"State = {state} " )
7824 self
. assertEqual ( state
, 'rebooting' )
7826 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
7827 '--dhcp-option=option:domain-search,example.com' ,
7828 '--dhcp-alternate-port=67,5555' ,
7829 ipv4_range
= '192.168.5.110,192.168.5.119' )
7830 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7831 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
7833 state
= get_dhcp4_client_state ( 'veth99' )
7834 print ( f
"State = {state} " )
7835 self
. assertEqual ( state
, 'bound' )
7837 def test_dhcp_client_allow_list ( self
):
7838 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-allow-list.network' , copy_dropins
= False )
7841 self
. wait_online ( 'veth-peer:carrier' )
7842 since
= datetime
. datetime
. now ()
7845 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' , since
= since
)
7847 copy_network_unit ( '25-dhcp-client-allow-list.network.d/00-allow-list.conf' )
7848 since
= datetime
. datetime
. now ()
7851 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.' , since
= since
)
7853 copy_network_unit ( '25-dhcp-client-allow-list.network.d/10-deny-list.conf' )
7854 since
= datetime
. datetime
. now ()
7857 self
. check_networkd_log ( 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.' , since
= since
)
7859 @unittest . skipUnless ( "--dhcp-rapid-commit" in run ( "dnsmasq --help" ). stdout
, reason
= "dnsmasq is missing dhcp-rapid-commit support" )
7860 def test_dhcp_client_rapid_commit ( self
):
7861 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
7863 self
. wait_online ( 'veth-peer:carrier' )
7865 start_dnsmasq ( '--dhcp-rapid-commit' )
7866 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7867 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
7869 state
= get_dhcp4_client_state ( 'veth99' )
7870 print ( f
"DHCPv4 client state = {state} " )
7871 self
. assertEqual ( state
, 'bound' )
7873 output
= read_dnsmasq_log_file ()
7874 self
. assertIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7875 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7876 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7877 self
. assertIn ( 'DHCPACK(veth-peer)' , output
)
7879 def check_bootp_client ( self
, check_log
):
7880 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7881 output
= check_output ( 'ip -4 address show dev veth99' )
7883 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24' )
7885 state
= get_dhcp4_client_state ( 'veth99' )
7886 print ( f
"DHCPv4 client state = {state} " )
7887 self
. assertEqual ( state
, 'bound' )
7890 output
= read_dnsmasq_log_file ()
7892 self
. assertIn ( 'BOOTP(veth-peer)' , output
)
7893 self
. assertNotIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7894 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7895 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
7896 self
. assertNotIn ( 'DHCPACK(veth-peer)' , output
)
7898 def test_bootp_client ( self
):
7899 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-bootp-client.network' )
7901 self
. wait_online ( 'veth-peer:carrier' )
7902 start_dnsmasq ( '--dhcp-host=12:34:56:78:9a:bc,192.168.5.42,trixie-mule' )
7903 self
. check_bootp_client ( check_log
= True )
7905 touch_network_unit ( '25-bootp-client.network' )
7907 self
. check_bootp_client ( check_log
= True )
7909 with
open ( os
. path
. join ( network_unit_dir
, '25-bootp-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7910 f
. write ( '[DHCPv4] \n BOOTP=no \n ' )
7913 self
. check_bootp_client ( check_log
= False )
7915 output
= read_dnsmasq_log_file ()
7917 # Note, on reload, the DHCP client will be started from INIT-REBOOT state,
7918 # hence DISCOVER and OFFER message will not be sent/received.
7919 self
. assertNotIn ( 'DHCPDISCOVER(veth-peer)' , output
)
7920 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
7921 self
. assertIn ( 'DHCPREQUEST(veth-peer)' , output
)
7922 self
. assertIn ( 'DHCPACK(veth-peer)' , output
)
7924 with
open ( os
. path
. join ( network_unit_dir
, '25-bootp-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
7925 f
. write ( '[DHCPv4] \n BOOTP=yes \n ' )
7927 since
= datetime
. datetime
. now ()
7930 self
. check_bootp_client ( check_log
= False )
7932 # Check if the client send RELEASE message of the previous lease
7933 self
. check_networkd_log ( 'veth99: DHCPv4 client: RELEASE' , since
= since
)
7935 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity ( self
):
7936 copy_network_unit ( '25-veth.netdev' ,
7937 '25-dhcp-server-ipv6-only-mode.network' ,
7938 '25-dhcp-client-ipv6-only-mode.network' )
7940 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , timeout
= '40s' )
7941 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
7943 state
= get_dhcp4_client_state ( 'veth99' )
7944 print ( f
"State = {state} " )
7945 self
. assertEqual ( state
, 'bound' )
7947 def test_dhcp_client_ipv4_use_routes_gateway ( self
):
7949 for ( routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
. product ([ True , False ], repeat
= 4 ):
7955 print ( f
'### test_dhcp_client_ipv4_use_routes_gateway(routes= {routes} , gateway= {gateway} , dns_and_ntp_routes= {dns_and_ntp_routes} , classless= {classless} )' )
7956 with self
. subTest ( routes
= routes
, gateway
= gateway
, dns_and_ntp_routes
= dns_and_ntp_routes
, classless
= classless
):
7957 self
._ test
_ dhcp
_ client
_ ipv
4_u se
_ routes
_ gateway
( routes
, gateway
, dns_and_ntp_routes
, classless
)
7959 def _test_dhcp_client_ipv4_use_routes_gateway ( self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
7960 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
7961 testunits
= [ '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , testunit
]
7962 testunits
. append ( f
' {testunit} .d/use-routes- {use_routes} .conf' )
7963 testunits
. append ( f
' {testunit} .d/use-gateway- {use_gateway} .conf' )
7964 testunits
. append ( f
' {testunit} .d/use-dns-and-ntp-routes- {dns_and_ntp_routes} .conf' )
7965 copy_network_unit (* testunits
, copy_dropins
= False )
7968 self
. wait_online ( 'veth-peer:carrier' )
7969 additional_options
= [
7970 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8' ,
7971 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9' ,
7972 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
7975 additional_options
+= [
7976 '--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'
7978 start_dnsmasq (* additional_options
)
7979 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
7981 output
= check_output ( 'ip -4 route show dev veth99' )
7987 self
. assertRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7988 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7989 self
. assertRegex ( output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7990 self
. assertRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
7991 self
. assertRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
7993 self
. assertRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7994 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7995 self
. assertRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
7996 self
. assertRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
7998 self
. assertNotRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
7999 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8000 self
. assertNotRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8001 self
. assertNotRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8002 self
. assertNotRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8003 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8004 self
. assertNotRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8005 self
. assertNotRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8008 if use_gateway
and ( not classless
or not use_routes
):
8009 self
. assertRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8011 self
. assertNotRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8013 # Check route to gateway
8014 if ( use_gateway
or dns_and_ntp_routes
) and ( not classless
or not use_routes
):
8015 self
. assertRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8017 self
. assertNotRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8019 # Check RoutesToDNS= and RoutesToNTP=
8020 if dns_and_ntp_routes
:
8021 self
. assertRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8022 self
. assertRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8025 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8026 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8028 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8029 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8031 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8032 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
8034 self
. assertNotRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8035 self
. assertNotRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
8036 self
. assertNotRegex ( output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
8037 self
. assertNotRegex ( output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
8039 check_json ( networkctl_json ())
8041 def test_dhcp_client_settings_anonymize ( self
):
8042 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-anonymize.network' )
8044 self
. wait_online ( 'veth-peer:carrier' )
8046 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8048 print ( '## dnsmasq log' )
8049 output
= read_dnsmasq_log_file ()
8051 self
. assertNotIn ( 'VendorClassIdentifier=SusantVendorTest' , output
)
8052 self
. assertNotIn ( 'test-hostname' , output
)
8053 self
. assertNotIn ( '26:mtu' , output
)
8055 def test_dhcp_keep_configuration_dynamic ( self
):
8056 copy_network_unit ( '25-veth.netdev' ,
8057 '25-dhcp-server-veth-peer.network' ,
8058 '25-dhcp-client-keep-configuration-dynamic.network' )
8060 self
. wait_online ( 'veth-peer:carrier' )
8062 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8064 output
= check_output ( 'ip address show dev veth99 scope global' )
8066 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8067 'valid_lft forever preferred_lft forever' )
8069 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
8072 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
8073 print ( 'Wait for the DHCP lease to be expired' )
8076 # The lease address should be kept after the lease expired
8077 output
= check_output ( 'ip address show dev veth99 scope global' )
8079 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8080 'valid_lft forever preferred_lft forever' )
8084 # The lease address should be kept after networkd stopped
8085 output
= check_output ( 'ip address show dev veth99 scope global' )
8087 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
8088 'valid_lft forever preferred_lft forever' )
8090 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-keep-configuration-dynamic.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
8091 f
. write ( '[Network] \n DHCP=no \n ' )
8094 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8096 # Still the lease address should be kept after networkd restarted
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 def test_dhcp_keep_configuration_dynamic_on_stop ( self
):
8103 copy_network_unit ( '25-veth.netdev' ,
8104 '25-dhcp-server-veth-peer.network' ,
8105 '25-dhcp-client-keep-configuration-dynamic-on-stop.network' )
8107 self
. wait_online ( 'veth-peer:carrier' )
8109 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8111 output
= check_output ( 'ip address show dev veth99 scope global' )
8113 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8118 output
= check_output ( 'ip address show dev veth99 scope global' )
8120 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8123 self
. wait_online ( 'veth-peer:routable' )
8125 output
= check_output ( 'ip address show dev veth99 scope global' )
8127 self
. assertNotIn ( '192.168.5.' , output
)
8129 def test_dhcp_client_reuse_address_as_static ( self
):
8130 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
8132 self
. wait_online ( 'veth-peer:carrier' )
8134 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8136 # link become 'routable' when at least one protocol provide an valid address.
8137 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8138 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8140 output
= check_output ( 'ip address show dev veth99 scope global' )
8141 ipv4_address
= re
. search ( r
'192.168.5.[0-9]*/24' , output
). group ()
8142 ipv6_address
= re
. search ( r
'2600::[0-9a-f:]*/128' , output
). group ()
8143 static_network
= ' \n ' . join ([ '[Match]' , 'Name=veth99' , '[Network]' , 'IPv6AcceptRA=no' , 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
8144 print ( static_network
)
8146 remove_network_unit ( '25-dhcp-client.network' )
8148 with
open ( os
. path
. join ( network_unit_dir
, '25-static.network' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8149 f
. write ( static_network
)
8152 self
. wait_online ( 'veth99:routable' )
8154 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
8156 self
. assertRegex ( output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99 \n *'
8157 'valid_lft forever preferred_lft forever' )
8159 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8161 self
. assertRegex ( output
, f
'inet6 {ipv6_address} scope global * \n *'
8162 'valid_lft forever preferred_lft forever' )
8164 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
8165 def test_dhcp_client_vrf ( self
):
8166 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-vrf.network' ,
8167 '25-vrf.netdev' , '25-vrf.network' )
8169 self
. wait_online ( 'veth-peer:carrier' )
8171 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'vrf99:carrier' )
8173 # link become 'routable' when at least one protocol provide an valid address.
8174 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8175 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8177 print ( '## ip -d link show dev vrf99' )
8178 output
= check_output ( 'ip -d link show dev vrf99' )
8180 self
. assertRegex ( output
, 'vrf table 42' )
8182 print ( '## ip address show vrf vrf99' )
8183 output
= check_output ( 'ip address show vrf vrf99' )
8185 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8186 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8187 self
. assertRegex ( output
, 'inet6 .* scope link' )
8189 print ( '## ip address show dev veth99' )
8190 output
= check_output ( 'ip address show dev veth99' )
8192 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
8193 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8194 self
. assertRegex ( output
, 'inet6 .* scope link' )
8196 print ( '## ip route show vrf vrf99' )
8197 output
= check_output ( 'ip route show vrf vrf99' )
8199 self
. assertRegex ( output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.' )
8200 self
. assertRegex ( output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5' )
8201 self
. assertRegex ( output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5' )
8203 print ( '## ip route show table main dev veth99' )
8204 output
= check_output ( 'ip route show table main dev veth99' )
8206 self
. assertEqual ( output
, '' )
8208 def test_dhcp_client_gateway_onlink_implicit ( self
):
8209 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
8210 '25-dhcp-client-gateway-onlink-implicit.network' )
8212 self
. wait_online ( 'veth-peer:carrier' )
8214 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
8216 output
= networkctl_status ( 'veth99' )
8218 self
. assertRegex ( output
, '192.168.5' )
8220 output
= check_output ( 'ip route list dev veth99 10.0.0.0/8' )
8222 self
. assertRegex ( output
, 'onlink' )
8223 output
= check_output ( 'ip route list dev veth99 192.168.100.0/24' )
8225 self
. assertRegex ( output
, 'onlink' )
8227 def test_dhcp_client_with_ipv4ll ( self
):
8228 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
8229 '25-dhcp-client-with-ipv4ll.network' )
8231 # we need to increase timeout above default, as this will need to wait for
8232 # systemd-networkd to get the dhcpv4 transient failure event
8233 self
. wait_online ( 'veth99:degraded' , 'veth-peer:routable' , timeout
= '60s' )
8235 output
= check_output ( 'ip -4 address show dev veth99' )
8237 self
. assertNotIn ( '192.168.5.' , output
)
8238 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
8241 print ( 'Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped' )
8242 self
. wait_address ( 'veth99' , r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic' , ipv
= '-4' )
8243 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' )
8244 self
. wait_online ( 'veth99:routable' )
8246 output
= check_output ( 'ip -4 address show dev veth99' )
8248 self
. assertRegex ( output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99' )
8249 self
. assertNotIn ( '169.254.' , output
)
8250 self
. assertNotIn ( 'scope link' , output
)
8253 print ( 'Wait for the DHCP lease to be expired and an IPv4LL address to be acquired' )
8254 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 )
8255 self
. wait_address ( 'veth99' , r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link' , scope
= 'link' , ipv
= '-4' )
8257 output
= check_output ( 'ip -4 address show dev veth99' )
8259 self
. assertNotIn ( '192.168.5.' , output
)
8260 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
8262 def test_dhcp_client_use_dns ( self
):
8263 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8264 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8265 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8266 f
. write ( '[DHCPv4] \n UseDNS=' )
8267 f
. write ( 'yes' if ipv4
else 'no' )
8268 f
. write ( ' \n [DHCPv6] \n UseDNS=' )
8269 f
. write ( 'yes' if ipv6
else 'no' )
8270 f
. write ( ' \n [IPv6AcceptRA] \n UseDNS=no' )
8273 if needs_reconfigure
:
8274 networkctl_reconfigure ( 'veth99' )
8275 self
. wait_online ( 'veth99:routable' )
8277 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8278 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8279 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8281 # make resolved re-read the link state file
8282 resolvectl ( 'revert' , 'veth99' )
8284 output
= resolvectl ( 'dns' , 'veth99' )
8287 self
. assertIn ( '192.168.5.1' , output
)
8289 self
. assertNotIn ( '192.168.5.1' , output
)
8291 self
. assertIn ( '2600::1' , output
)
8293 self
. assertNotIn ( '2600::1' , output
)
8295 check_json ( networkctl_json ())
8297 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8300 self
. wait_online ( 'veth-peer:carrier' )
8301 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1' ,
8302 '--dhcp-option=option6:dns-server,[2600::1]' )
8304 check ( self
, True , True )
8305 check ( self
, True , False )
8306 check ( self
, False , True , needs_reconfigure
= True )
8307 check ( self
, False , False )
8309 def test_dhcp_client_default_use_domains ( self
):
8310 def check ( self
, common
, ipv4
, ipv6
):
8311 mkdir_p ( networkd_conf_dropin_dir
)
8312 with
open ( os
. path
. join ( networkd_conf_dropin_dir
, 'default_use_domains.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8313 f
. write ( '[Network] \n UseDomains=' )
8314 f
. write ( 'yes \n ' if common
else 'no \n ' )
8315 f
. write ( '[DHCPv4] \n UseDomains=' )
8316 f
. write ( 'yes \n ' if ipv4
else 'no \n ' )
8317 f
. write ( '[DHCPv6] \n UseDomains=' )
8318 f
. write ( 'yes \n ' if ipv6
else 'no \n ' )
8321 self
. wait_online ( 'veth-peer:carrier' )
8322 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1' ,
8323 '--dhcp-option=option6:dns-server,[2600::1]' ,
8324 '--dhcp-option=option:domain-search,example.com' ,
8325 '--dhcp-option=option6:domain-search,example.com' )
8327 self
. wait_online ( 'veth99:routable' )
8329 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8330 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8331 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8334 output
= resolvectl ( 'domain' , 'veth99' )
8335 if common
or ipv4
or ipv6
:
8336 if 'example.com' in output
:
8339 if 'example.com' not in output
:
8344 print ( read_link_state_file ( 'veth99' ))
8345 self
. fail ( 'unexpected domain setting in resolved...' )
8348 remove_networkd_conf_dropin ( 'default_use_domains.conf' )
8350 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8351 check ( self
, True , False , False )
8352 check ( self
, False , True , True )
8353 check ( self
, False , True , False )
8354 check ( self
, False , False , True )
8355 check ( self
, False , False , False )
8357 def test_dhcp_client_use_dnr ( self
):
8358 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8359 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8360 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8361 f
. write ( '[DHCPv4] \n UseDNS=' )
8362 f
. write ( 'yes' if ipv4
else 'no' )
8363 f
. write ( ' \n [DHCPv6] \n UseDNS=' )
8364 f
. write ( 'yes' if ipv6
else 'no' )
8365 f
. write ( ' \n [IPv6AcceptRA] \n UseDNS=no' )
8368 if needs_reconfigure
:
8369 networkctl_reconfigure ( 'veth99' )
8370 self
. wait_online ( 'veth99:routable' )
8372 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8373 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8374 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8376 # make resolved re-read the link state file
8377 resolvectl ( 'revert' , 'veth99' )
8379 output
= resolvectl ( 'dns' , 'veth99' )
8382 self
. assertIn ( '8.8.8.8#dns.google' , output
)
8383 self
. assertIn ( '0.7.4.2#homer.simpson' , output
)
8385 self
. assertNotIn ( '8.8.8.8#dns.google' , output
)
8387 self
. assertIn ( '2001:4860:4860::8888#dns.google' , output
)
8389 self
. assertNotIn ( '2001:4860:4860::8888#dns.google' , output
)
8391 check_json ( networkctl_json ())
8393 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8396 self
. wait_online ( 'veth-peer:carrier' )
8397 dnr_v4
= dnr_v4_instance_data ( adn
= "dns.google" , addrs
= [ "8.8.8.8" , "8.8.4.4" ])
8398 dnr_v4
+= dnr_v4_instance_data ( adn
= "homer.simpson" , addrs
= [ "0.7.4.2" ], alpns
= ( "dot" , "h2" , "h3" ), dohpath
= "/springfield{?dns}" )
8399 dnr_v6
= dnr_v6_instance_data ( adn
= "dns.google" , addrs
= [ "2001:4860:4860::8888" , "2001:4860:4860::8844" ])
8400 masq
= lambda bs
: ':' . join ( f
"{b:02x}" for b
in bs
)
8401 start_dnsmasq ( f
'--dhcp-option=162,{masq(dnr_v4)}' ,
8402 f
'--dhcp-option=option6:144,{masq(dnr_v6)}' )
8404 check ( self
, True , True )
8405 check ( self
, True , False )
8406 check ( self
, False , True , needs_reconfigure
= True )
8407 check ( self
, False , False )
8409 def test_dhcp_client_use_captive_portal ( self
):
8410 def check ( self
, ipv4
, ipv6
, needs_reconfigure
= False ):
8411 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8412 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8413 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
8414 f
. write ( 'yes' if ipv4
else 'no' )
8415 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
8416 f
. write ( 'yes' if ipv6
else 'no' )
8417 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
8420 if needs_reconfigure
:
8421 networkctl_reconfigure ( 'veth99' )
8422 self
. wait_online ( 'veth99:routable' )
8424 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8425 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8426 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8428 output
= networkctl_status ( 'veth99' )
8431 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
8433 self
. assertNotIn ( 'Captive Portal: http://systemd.io' , output
)
8435 check_json ( networkctl_json ())
8437 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8440 self
. wait_online ( 'veth-peer:carrier' )
8441 start_dnsmasq ( '--dhcp-option=114,http://systemd.io' ,
8442 '--dhcp-option=option6:103,http://systemd.io' )
8444 check ( self
, True , True )
8445 check ( self
, True , False )
8446 check ( self
, False , True , needs_reconfigure
= True )
8447 check ( self
, False , False )
8449 def test_dhcp_client_reject_captive_portal ( self
):
8450 def check ( self
, ipv4
, ipv6
):
8451 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
8452 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
8453 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
8454 f
. write ( 'yes' if ipv4
else 'no' )
8455 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
8456 f
. write ( 'yes' if ipv6
else 'no' )
8457 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
8460 self
. wait_online ( 'veth99:routable' )
8462 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
8463 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
8464 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
8466 output
= networkctl_status ( 'veth99' )
8468 self
. assertNotIn ( 'Captive Portal: ' , output
)
8469 self
. assertNotIn ( 'invalid/url' , output
)
8471 check_json ( networkctl_json ())
8473 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
8476 self
. wait_online ( 'veth-peer:carrier' )
8477 masq
= lambda bs
: ':' . join ( f
'{b:02x}' for b
in bs
)
8478 start_dnsmasq ( '--dhcp-option=114,' + masq ( b
'http:// \x00 invalid/url' ),
8479 '--dhcp-option=option6:103,' + masq ( b
'http:// \x00 /invalid/url' ))
8481 check ( self
, True , True )
8482 check ( self
, True , False )
8483 check ( self
, False , True )
8484 check ( self
, False , False )
8486 class NetworkdDHCPPDTests ( unittest
. TestCase
, Utilities
):
8494 def check_dhcp6_prefix ( self
, link
):
8495 description
= get_link_description ( link
)
8497 self
. assertIn ( 'DHCPv6Client' , description
. keys ())
8498 self
. assertIn ( 'Prefixes' , description
[ 'DHCPv6Client' ])
8500 prefixInfo
= description
[ 'DHCPv6Client' ][ 'Prefixes' ]
8502 self
. assertEqual ( len ( prefixInfo
), 1 )
8504 self
. assertIn ( 'Prefix' , prefixInfo
[ 0 ]. keys ())
8505 self
. assertIn ( 'PrefixLength' , prefixInfo
[ 0 ]. keys ())
8506 self
. assertIn ( 'PreferredLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
8507 self
. assertIn ( 'ValidLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
8509 self
. assertEqual ( prefixInfo
[ 0 ][ 'Prefix' ][ 0 : 6 ], [ 63 , 254 , 5 , 1 , 255 , 255 ])
8510 self
. assertEqual ( prefixInfo
[ 0 ][ 'PrefixLength' ], 56 )
8511 self
. assertGreater ( prefixInfo
[ 0 ][ 'PreferredLifetimeUSec' ], 0 )
8512 self
. assertGreater ( prefixInfo
[ 0 ][ 'ValidLifetimeUSec' ], 0 )
8514 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8515 def test_dhcp6pd_no_address ( self
):
8517 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-address.network' )
8520 self
. wait_online ( 'veth-peer:routable' )
8521 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
8522 self
. wait_online ( 'veth99:degraded' )
8524 print ( '### ip -6 address show dev veth99 scope global' )
8525 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8527 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
8529 print ( '### ip -6 route show dev lo' )
8530 output
= check_output ( 'ip -6 route show dev lo' )
8532 self
. assertNotRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/56' )
8534 self
. check_dhcp6_prefix ( 'veth99' )
8536 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8537 def test_dhcp6pd_no_assign ( self
):
8538 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
8539 # However, the server does not provide IA_NA. For issue #31349.
8540 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-assign.network' )
8543 self
. wait_online ( 'veth-peer:routable' )
8544 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd-no-range.conf' , ipv
= '-6' )
8545 self
. wait_online ( 'veth99:degraded' )
8547 print ( '### ip -6 address show dev veth99 scope global' )
8548 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8550 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
8552 print ( '### ip -6 route show type blackhole' )
8553 output
= check_output ( 'ip -6 route show type blackhole' )
8555 self
. assertRegex ( output
, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp' )
8557 self
. check_dhcp6_prefix ( 'veth99' )
8559 @unittest . skipUnless ( shutil
. which ( 'dhcpd' ), reason
= "dhcpd is not available" )
8560 def test_dhcp6pd ( self
):
8561 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream.network' ,
8562 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
8563 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
8564 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
8565 '25-dhcp-pd-downstream-dummy97.network' ,
8566 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
8567 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' )
8570 self
. wait_online ( 'veth-peer:routable' )
8571 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
8572 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
8573 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
8575 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
8576 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
8577 self
. setup_nftset ( 'ifindex' , 'iface_index' )
8579 # Check DBus assigned prefix information to veth99
8580 self
. check_dhcp6_prefix ( 'veth99' )
8582 print ( '### ip -6 address show dev veth-peer scope global' )
8583 output
= check_output ( 'ip -6 address show dev veth-peer scope global' )
8585 self
. assertIn ( 'inet6 3ffe:501:ffff:100::1/64 scope global' , output
)
8589 # dummy97: 0x01 (The link will appear later)
8591 # dummy99: auto -> 0x02 (No address assignment)
8596 print ( '### ip -6 address show dev veth99 scope global' )
8597 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8600 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
8601 # address in IA_PD (Token=static)
8602 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic' )
8603 # address in IA_PD (Token=eui64)
8604 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic' )
8605 # address in IA_PD (temporary)
8606 # Note that the temporary addresses may appear after the link enters configured state
8607 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' )
8609 print ( '### ip -6 address show dev test1 scope global' )
8610 output
= check_output ( 'ip -6 address show dev test1 scope global' )
8612 # address in IA_PD (Token=static)
8613 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8614 # address in IA_PD (temporary)
8615 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' )
8617 print ( '### ip -6 address show dev dummy98 scope global' )
8618 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8620 # address in IA_PD (Token=static)
8621 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8622 # address in IA_PD (temporary)
8623 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' )
8625 print ( '### ip -6 address show dev dummy99 scope global' )
8626 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8629 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
8631 print ( '### ip -6 address show dev veth97 scope global' )
8632 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
8634 # address in IA_PD (Token=static)
8635 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8636 # address in IA_PD (Token=eui64)
8637 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8638 # address in IA_PD (temporary)
8639 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' )
8641 print ( '### ip -6 address show dev veth97-peer scope global' )
8642 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
8644 # NDisc address (Token=static)
8645 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8646 # NDisc address (Token=eui64)
8647 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8648 # NDisc address (temporary)
8649 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' )
8651 print ( '### ip -6 address show dev veth98 scope global' )
8652 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
8654 # address in IA_PD (Token=static)
8655 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8656 # address in IA_PD (Token=eui64)
8657 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8658 # address in IA_PD (temporary)
8659 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' )
8661 print ( '### ip -6 address show dev veth98-peer scope global' )
8662 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
8664 # NDisc address (Token=static)
8665 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8666 # NDisc address (Token=eui64)
8667 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8668 # NDisc address (temporary)
8669 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' )
8671 print ( '### ip -6 route show type unreachable' )
8672 output
= check_output ( 'ip -6 route show type unreachable' )
8674 self
. assertRegex ( output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp' )
8676 print ( '### ip -6 route show dev veth99' )
8677 output
= check_output ( 'ip -6 route show dev veth99' )
8679 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires' )
8681 print ( '### ip -6 route show dev test1' )
8682 output
= check_output ( 'ip -6 route show dev test1' )
8684 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8686 print ( '### ip -6 route show dev dummy98' )
8687 output
= check_output ( 'ip -6 route show dev dummy98' )
8689 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8691 print ( '### ip -6 route show dev dummy99' )
8692 output
= check_output ( 'ip -6 route show dev dummy99' )
8694 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
8696 print ( '### ip -6 route show dev veth97' )
8697 output
= check_output ( 'ip -6 route show dev veth97' )
8699 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires' )
8701 print ( '### ip -6 route show dev veth97-peer' )
8702 output
= check_output ( 'ip -6 route show dev veth97-peer' )
8704 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires' )
8706 print ( '### ip -6 route show dev veth98' )
8707 output
= check_output ( 'ip -6 route show dev veth98' )
8709 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires' )
8711 print ( '### ip -6 route show dev veth98-peer' )
8712 output
= check_output ( 'ip -6 route show dev veth98-peer' )
8714 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires' )
8716 # Test case for a downstream which appears later
8717 check_output ( 'ip link add dummy97 type dummy' )
8718 self
. wait_online ( 'dummy97:routable' )
8720 print ( '### ip -6 address show dev dummy97 scope global' )
8721 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
8723 # address in IA_PD (Token=static)
8724 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8725 # address in IA_PD (temporary)
8726 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' )
8728 print ( '### ip -6 route show dev dummy97' )
8729 output
= check_output ( 'ip -6 route show dev dummy97' )
8731 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires' )
8733 # Test case for reconfigure
8734 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
8735 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
8737 print ( '### ip -6 address show dev dummy98 scope global' )
8738 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8740 # address in IA_PD (Token=static)
8741 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8742 # address in IA_PD (temporary)
8743 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' )
8745 print ( '### ip -6 address show dev dummy99 scope global' )
8746 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8749 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
8751 print ( '### ip -6 route show dev dummy98' )
8752 output
= check_output ( 'ip -6 route show dev dummy98' )
8754 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
8756 print ( '### ip -6 route show dev dummy99' )
8757 output
= check_output ( 'ip -6 route show dev dummy99' )
8759 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
8761 self
. check_netlabel ( 'dummy98' , '3ffe:501:ffff:[2-9a-f]00::/64' )
8763 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d' )
8764 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
8765 self
. check_nftset ( 'network6' , '3ffe:501:ffff:[2-9a-f]00::/64' )
8766 self
. check_nftset ( 'ifindex' , 'dummy98' )
8768 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
8770 def verify_dhcp4_6rd ( self
, tunnel_name
, address_prefix
, border_router
):
8771 print ( '### ip -4 address show dev veth-peer scope global' )
8772 output
= check_output ( 'ip -4 address show dev veth-peer scope global' )
8774 self
. assertIn ( 'inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer' , output
)
8778 # dummy97: 0x01 (The link will appear later)
8780 # dummy99: auto -> 0x0[23] (No address assignment)
8781 # 6rd-XXX: auto -> 0x0[23]
8786 print ( '### ip -4 address show dev veth99 scope global' )
8787 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
8789 self
. assertRegex ( output
, fr
'inet {address_prefix} [0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99' )
8791 print ( '### ip -6 address show dev veth99 scope global' )
8792 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
8794 # address in IA_PD (Token=static)
8795 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8796 # address in IA_PD (Token=eui64)
8797 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8798 # address in IA_PD (temporary)
8799 # Note that the temporary addresses may appear after the link enters configured state
8800 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' )
8802 print ( '### ip -6 address show dev test1 scope global' )
8803 output
= check_output ( 'ip -6 address show dev test1 scope global' )
8805 # address in IA_PD (Token=static)
8806 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8807 # address in IA_PD (temporary)
8808 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' )
8810 print ( '### ip -6 address show dev dummy98 scope global' )
8811 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
8813 # address in IA_PD (Token=static)
8814 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8815 # address in IA_PD (temporary)
8816 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' )
8818 print ( '### ip -6 address show dev dummy99 scope global' )
8819 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
8822 self
. assertNotRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]' )
8824 print ( '### ip -6 address show dev veth97 scope global' )
8825 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
8827 # address in IA_PD (Token=static)
8828 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8829 # address in IA_PD (Token=eui64)
8830 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8831 # address in IA_PD (temporary)
8832 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' )
8834 print ( '### ip -6 address show dev veth97-peer scope global' )
8835 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
8837 # NDisc address (Token=static)
8838 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8839 # NDisc address (Token=eui64)
8840 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8841 # NDisc address (temporary)
8842 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' )
8844 print ( '### ip -6 address show dev veth98 scope global' )
8845 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
8847 # address in IA_PD (Token=static)
8848 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8849 # address in IA_PD (Token=eui64)
8850 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8851 # address in IA_PD (temporary)
8852 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' )
8854 print ( '### ip -6 address show dev veth98-peer scope global' )
8855 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
8857 # NDisc address (Token=static)
8858 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8859 # NDisc address (Token=eui64)
8860 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8861 # NDisc address (temporary)
8862 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' )
8864 print ( '### ip -6 route show type unreachable' )
8865 output
= check_output ( 'ip -6 route show type unreachable' )
8867 self
. assertRegex ( output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp' )
8869 print ( '### ip -6 route show dev veth99' )
8870 output
= check_output ( 'ip -6 route show dev veth99' )
8872 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires' )
8874 print ( '### ip -6 route show dev test1' )
8875 output
= check_output ( 'ip -6 route show dev test1' )
8877 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
8879 print ( '### ip -6 route show dev dummy98' )
8880 output
= check_output ( 'ip -6 route show dev dummy98' )
8882 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
8884 print ( '### ip -6 route show dev dummy99' )
8885 output
= check_output ( 'ip -6 route show dev dummy99' )
8887 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires' )
8889 print ( '### ip -6 route show dev veth97' )
8890 output
= check_output ( 'ip -6 route show dev veth97' )
8892 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires' )
8894 print ( '### ip -6 route show dev veth97-peer' )
8895 output
= check_output ( 'ip -6 route show dev veth97-peer' )
8897 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires' )
8899 print ( '### ip -6 route show dev veth98' )
8900 output
= check_output ( 'ip -6 route show dev veth98' )
8902 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires' )
8904 print ( '### ip -6 route show dev veth98-peer' )
8905 output
= check_output ( 'ip -6 route show dev veth98-peer' )
8907 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires' )
8909 print ( '### ip -6 address show dev dummy97 scope global' )
8910 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
8912 # address in IA_PD (Token=static)
8913 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
8914 # address in IA_PD (temporary)
8915 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' )
8917 print ( '### ip -6 route show dev dummy97' )
8918 output
= check_output ( 'ip -6 route show dev dummy97' )
8920 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires' )
8922 print ( f
'### ip -d link show dev {tunnel_name} ' )
8923 output
= check_output ( f
'ip -d link show dev {tunnel_name} ' )
8925 self
. assertIn ( f
'link/sit {address_prefix} ' , output
)
8926 self
. assertIn ( f
'local {address_prefix} ' , output
)
8927 self
. assertIn ( 'ttl 64' , output
)
8928 self
. assertIn ( '6rd-prefix 2001:db8::/32' , output
)
8929 self
. assertIn ( '6rd-relay_prefix 10.0.0.0/8' , output
)
8931 print ( f
'### ip -6 address show dev {tunnel_name} ' )
8932 output
= check_output ( f
'ip -6 address show dev {tunnel_name} ' )
8934 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' )
8935 self
. assertRegex ( output
, fr
'inet6 :: {address_prefix} [0-9]+/96 scope global' )
8937 print ( f
'### ip -6 route show dev {tunnel_name} ' )
8938 output
= check_output ( f
'ip -6 route show dev {tunnel_name} ' )
8940 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires' )
8941 self
. assertRegex ( output
, '::/96 proto kernel metric [0-9]*' )
8943 print ( '### ip -6 route show default' )
8944 output
= check_output ( 'ip -6 route show default' )
8946 self
. assertIn ( 'default' , output
)
8947 self
. assertIn ( f
'via :: {border_router} dev {tunnel_name} ' , output
)
8949 def test_dhcp4_6rd ( self
):
8950 def get_dhcp_6rd_prefix ( link
):
8951 description
= get_link_description ( link
)
8953 self
. assertIn ( 'DHCPv4Client' , description
. keys ())
8954 self
. assertIn ( '6rdPrefix' , description
[ 'DHCPv4Client' ]. keys ())
8956 prefixInfo
= description
[ 'DHCPv4Client' ][ '6rdPrefix' ]
8957 self
. assertIn ( 'Prefix' , prefixInfo
. keys ())
8958 self
. assertIn ( 'PrefixLength' , prefixInfo
. keys ())
8959 self
. assertIn ( 'IPv4MaskLength' , prefixInfo
. keys ())
8960 self
. assertIn ( 'BorderRouters' , prefixInfo
. keys ())
8964 copy_network_unit ( '25-veth.netdev' , '25-dhcp4-6rd-server.network' , '25-dhcp4-6rd-upstream.network' ,
8965 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
8966 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
8967 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
8968 '25-dhcp-pd-downstream-dummy97.network' ,
8969 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
8970 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' ,
8971 '80-6rd-tunnel.network' )
8974 self
. wait_online ( 'veth-peer:routable' )
8977 # 6rd-prefix: 2001:db8::/32
8978 # br-addresss: 10.0.0.1
8980 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' ,
8981 ipv4_range
= '10.100.100.100,10.100.100.200' ,
8982 ipv4_router
= '10.0.0.1' )
8983 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
8984 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
8986 # Check the DBus interface for assigned prefix information
8987 prefixInfo
= get_dhcp_6rd_prefix ( 'veth99' )
8989 self
. assertEqual ( prefixInfo
[ 'Prefix' ], [ 32 , 1 , 13 , 184 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]) # 2001:db8::
8990 self
. assertEqual ( prefixInfo
[ 'PrefixLength' ], 32 )
8991 self
. assertEqual ( prefixInfo
[ 'IPv4MaskLength' ], 8 )
8992 self
. assertEqual ( prefixInfo
[ 'BorderRouters' ], [[ 10 , 0 , 0 , 1 ]])
8994 # Test case for a downstream which appears later
8995 check_output ( 'ip link add dummy97 type dummy' )
8996 self
. wait_online ( 'dummy97:routable' )
9000 for name
in os
. listdir ( '/sys/class/net/' ):
9001 if name
. startswith ( '6rd-' ):
9005 self
. wait_online ( f
' {tunnel_name} :routable' )
9007 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.1' , '10.0.0.1' )
9009 # Test case for reconfigure
9010 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
9011 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
9013 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.1' , '10.0.0.1' )
9015 # Change the address range and (border) router, then if check the same tunnel is reused.
9017 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' ,
9018 ipv4_range
= '10.100.100.200,10.100.100.250' ,
9019 ipv4_router
= '10.0.0.2' )
9021 print ( 'Wait for the DHCP lease to be renewed/rebind' )
9024 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy97:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
9025 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
9027 self
. verify_dhcp4_6rd ( tunnel_name
, '10.100.100.2' , '10.0.0.2' )
9029 class NetworkdIPv6PrefixTests ( unittest
. TestCase
, Utilities
):
9037 def test_ipv6_route_prefix ( self
):
9038 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client.network' , '25-ipv6ra-prefix.network' ,
9039 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
9042 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
9044 print ( '### ip -6 address show dev veth-peer' )
9045 output
= check_output ( 'ip -6 address show dev veth-peer' )
9047 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
9048 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
9049 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
9051 print ( '### ip -6 route show dev veth-peer' )
9052 output
= check_output ( 'ip -6 route show dev veth-peer' )
9054 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
9055 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
9056 self
. assertNotIn ( '2001:db8:0:3::/64 proto ra' , output
)
9057 self
. assertRegex ( output
, r
'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc' )
9058 self
. assertNotIn ( '2001:db1:fff::/64' , output
)
9059 self
. assertNotIn ( '2001:db2:fff::/64' , output
)
9061 print ( '### ip -6 nexthop show dev veth-peer' )
9062 output
= check_output ( 'ip -6 nexthop show dev veth-peer' )
9064 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra' )
9066 print ( '### ip -6 address show dev veth99' )
9067 output
= check_output ( 'ip -6 address show dev veth99' )
9069 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
9070 self
. assertIn ( 'inet6 2001:db8:0:2:1a:2b:3c:4d' , output
)
9071 self
. assertIn ( 'inet6 2001:db8:0:2:fa:de:ca:fe' , output
)
9072 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
9074 output
= resolvectl ( 'dns' , 'veth-peer' )
9076 self
. assertRegex ( output
, '2001:db8:1:1::2' )
9078 output
= resolvectl ( 'domain' , 'veth-peer' )
9080 self
. assertIn ( 'example.com' , output
)
9082 check_json ( networkctl_json ())
9084 output
= networkctl_json ( 'veth-peer' )
9088 pref64
= json
. loads ( output
)[ 'NDisc' ][ 'PREF64' ][ 0 ]
9090 prefix
= socket
. inet_ntop ( socket
. AF_INET6
, bytearray ( pref64
[ 'Prefix' ]))
9091 self
. assertEqual ( prefix
, '64:ff9b::' )
9093 prefix_length
= pref64
[ 'PrefixLength' ]
9094 self
. assertEqual ( prefix_length
, 96 )
9096 def test_ipv6_route_prefix_deny_list ( self
):
9097 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client-deny-list.network' , '25-ipv6ra-prefix.network' ,
9098 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
9101 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
9103 print ( '### ip -6 address show dev veth-peer' )
9104 output
= check_output ( 'ip -6 address show dev veth-peer' )
9106 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
9107 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
9109 print ( '### ip -6 route show dev veth-peer' )
9110 output
= check_output ( 'ip -6 route show dev veth-peer' )
9112 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
9113 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
9114 self
. assertRegex ( output
, r
'2001:db0:fff::/64 nhid [0-9]* via fe80::1034:56ff:fe78:9abc' )
9115 self
. assertNotIn ( '2001:db1:fff::/64' , output
)
9117 print ( '### ip -6 nexthop show dev veth-peer' )
9118 output
= check_output ( 'ip -6 nexthop show dev veth-peer' )
9120 self
. assertRegex ( output
, r
'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra' )
9122 print ( '### ip -6 address show dev veth99' )
9123 output
= check_output ( 'ip -6 address show dev veth99' )
9125 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
9126 self
. assertIn ( 'inet6 2001:db8:0:2:' , output
)
9128 output
= resolvectl ( 'dns' , 'veth-peer' )
9130 self
. assertRegex ( output
, '2001:db8:1:1::2' )
9132 output
= resolvectl ( 'domain' , 'veth-peer' )
9134 self
. assertIn ( 'example.com' , output
)
9136 class NetworkdMTUTests ( unittest
. TestCase
, Utilities
):
9144 def check_mtu ( self
, mtu
, ipv6_mtu
= None , reset
= True ):
9150 self
. wait_online ( 'dummy98:routable' )
9151 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
9152 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
9154 # test normal restart
9156 self
. wait_online ( 'dummy98:routable' )
9157 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
9158 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
9161 self
. reset_check_mtu ( mtu
, ipv6_mtu
)
9163 def reset_check_mtu ( self
, mtu
, ipv6_mtu
= None ):
9164 ''' test setting mtu/ipv6_mtu with interface already up '''
9167 # note - changing the device mtu resets the ipv6 mtu
9168 check_output ( 'ip link set up mtu 1501 dev dummy98' )
9169 check_output ( 'ip link set up mtu 1500 dev dummy98' )
9170 self
. check_link_attr ( 'dummy98' , 'mtu' , '1500' )
9171 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , '1500' )
9173 self
. check_mtu ( mtu
, ipv6_mtu
, reset
= False )
9175 def test_mtu_network ( self
):
9176 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' )
9177 self
. check_mtu ( '1600' )
9179 def test_mtu_netdev ( self
):
9180 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network' , copy_dropins
= False )
9181 # note - MTU set by .netdev happens ONLY at device creation!
9182 self
. check_mtu ( '1600' , reset
= False )
9184 def test_mtu_link ( self
):
9185 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network' , copy_dropins
= False )
9186 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
9187 self
. check_mtu ( '1600' , reset
= False )
9189 def test_ipv6_mtu ( self
):
9190 ''' set ipv6 mtu without setting device mtu '''
9191 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1400.conf' )
9192 self
. check_mtu ( '1500' , '1400' )
9194 def test_ipv6_mtu_toolarge ( self
):
9195 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
9196 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9197 self
. check_mtu ( '1500' , '1500' )
9199 def test_mtu_network_ipv6_mtu ( self
):
9200 ''' set ipv6 mtu and set device mtu via network file '''
9201 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9202 self
. check_mtu ( '1600' , '1550' )
9204 def test_mtu_netdev_ipv6_mtu ( self
):
9205 ''' set ipv6 mtu and set device mtu via netdev file '''
9206 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9207 self
. check_mtu ( '1600' , '1550' , reset
= False )
9209 def test_mtu_link_ipv6_mtu ( self
):
9210 ''' set ipv6 mtu and set device mtu via link file '''
9211 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
9212 self
. check_mtu ( '1600' , '1550' , reset
= False )
9214 class NetworkdSysctlTest ( unittest
. TestCase
, Utilities
):
9222 @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" )
9223 def check_sysctl_watch ( self
):
9224 copy_network_unit ( '12-dummy.network' , '12-dummy.netdev' , '12-dummy.link' )
9227 self
. wait_online ( 'dummy98:routable' )
9229 # Change managed sysctls
9230 call ( 'sysctl -w net.ipv6.conf.dummy98.accept_ra=1' )
9231 call ( 'sysctl -w net.ipv6.conf.dummy98.mtu=1360' )
9232 call ( 'sysctl -w net.ipv4.conf.dummy98.promote_secondaries=0' )
9233 call ( 'sysctl -w net.ipv6.conf.dummy98.proxy_ndp=1' )
9235 # And unmanaged ones
9236 call ( 'sysctl -w net.ipv6.conf.dummy98.hop_limit=4' )
9237 call ( 'sysctl -w net.ipv6.conf.dummy98.max_addresses=10' )
9239 log
= read_networkd_log ()
9240 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'" )
9241 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'" )
9242 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'" )
9243 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'" )
9244 self
. assertNotIn ( "changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'" , log
)
9245 self
. assertNotIn ( "changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'" , log
)
9247 if __name__
== '__main__' :
9248 parser
= argparse
. ArgumentParser ()
9249 parser
. add_argument ( '--build-dir' , help = 'Path to build dir' , dest
= 'build_dir' )
9250 parser
. add_argument ( '--source-dir' , help = 'Path to source dir/git tree' , dest
= 'source_dir' )
9251 parser
. add_argument ( '--valgrind' , help = 'Enable valgrind' , dest
= 'use_valgrind' , type = bool , nargs
= '?' , const
= True , default
= use_valgrind
)
9252 parser
. add_argument ( '--debug' , help = 'Generate debugging logs' , dest
= 'enable_debug' , type = bool , nargs
= '?' , const
= True , default
= enable_debug
)
9253 parser
. add_argument ( '--asan-options' , help = 'ASAN options' , dest
= 'asan_options' )
9254 parser
. add_argument ( '--lsan-options' , help = 'LSAN options' , dest
= 'lsan_options' )
9255 parser
. add_argument ( '--ubsan-options' , help = 'UBSAN options' , dest
= 'ubsan_options' )
9256 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
)
9257 parser
. add_argument ( '--no-journal' , help = 'Do not show journal of systemd-networkd on stop' , dest
= 'show_journal' , action
= 'store_false' )
9258 ns
, unknown_args
= parser
. parse_known_args ( namespace
= unittest
)
9261 networkd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd' )
9262 resolved_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-resolved' )
9263 timesyncd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-timesyncd' )
9264 wait_online_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd-wait-online' )
9265 networkctl_bin
= os
. path
. join ( ns
. build_dir
, 'networkctl' )
9266 resolvectl_bin
= os
. path
. join ( ns
. build_dir
, 'resolvectl' )
9267 timedatectl_bin
= os
. path
. join ( ns
. build_dir
, 'timedatectl' )
9268 udevadm_bin
= os
. path
. join ( ns
. build_dir
, 'udevadm' )
9269 build_dir
= ns
. build_dir
9272 source_dir
= ns
. source_dir
9273 assert os
. path
. exists ( os
. path
. join ( source_dir
, "meson_options.txt" )), f
" {source_dir} doesn't appear to be a systemd source tree."
9274 elif os
. path
. exists ( os
. path
. normpath ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), "../../meson_options.txt" ))):
9275 source_dir
= os
. path
. normpath ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), "../../" ))
9279 if networkd_bin
is None or resolved_bin
is None or timesyncd_bin
is None :
9280 print ( "networkd tests require networkd/resolved/timesyncd to be enabled" )
9283 use_valgrind
= ns
. use_valgrind
9284 enable_debug
= ns
. enable_debug
9285 asan_options
= ns
. asan_options
9286 lsan_options
= ns
. lsan_options
9287 ubsan_options
= ns
. ubsan_options
9288 with_coverage
= ns
. with_coverage
or "COVERAGE_BUILD_DIR" in os
. environ
9289 show_journal
= ns
. show_journal
9292 # Do not forget the trailing space.
9293 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
9295 networkctl_cmd
= valgrind_cmd
. split () + [ networkctl_bin
]
9296 resolvectl_cmd
= valgrind_cmd
. split () + [ resolvectl_bin
]
9297 timedatectl_cmd
= valgrind_cmd
. split () + [ timedatectl_bin
]
9298 udevadm_cmd
= valgrind_cmd
. split () + [ udevadm_bin
]
9299 wait_online_cmd
= valgrind_cmd
. split () + [ wait_online_bin
]
9302 test_ndisc_send
= os
. path
. normpath ( os
. path
. join ( build_dir
, 'test-ndisc-send' ))
9304 test_ndisc_send
= '/usr/lib/tests/test-ndisc-send'
9307 env
. update ({ 'ASAN_OPTIONS' : asan_options
})
9309 env
. update ({ 'LSAN_OPTIONS' : lsan_options
})
9311 env
. update ({ 'UBSAN_OPTIONS' : ubsan_options
})
9313 env
. update ({ 'SYSTEMD_MEMPOOL' : '0' })
9315 wait_online_env
= env
. copy ()
9317 wait_online_env
. update ({ 'SYSTEMD_LOG_LEVEL' : 'debug' })
9324 *([ "-k" , match
] if ( match
:= os
. getenv ( "TEST_MATCH_TESTCASE" )) else [])