]>
git.ipfire.org Git - thirdparty/systemd.git/blob - test/test-network/systemd-networkd-tests.py
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # systemd-networkd tests
5 # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
6 # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
8 # To run an individual test, specify it as a command line argument in the form
9 # of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
10 # function called test_ipv6_mtu(). To run just that test use:
12 # sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
14 # Similarly, other individual tests can be run, eg.:
16 # sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
37 network_unit_dir
= '/run/systemd/network'
38 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
39 networkd_ci_temp_dir
= '/run/networkd-ci'
40 udev_rules_dir
= '/run/udev/rules.d'
41 credstore_dir
= '/run/credstore'
43 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
44 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
45 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
47 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
48 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
50 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
52 systemd_lib_paths
= [ '/usr/lib/systemd' , '/lib/systemd' ]
53 which_paths
= ':' . join ( systemd_lib_paths
+ os
. getenv ( 'PATH' , os
. defpath
). lstrip ( ':' ). split ( ':' ))
55 networkd_bin
= shutil
. which ( 'systemd-networkd' , path
= which_paths
)
56 resolved_bin
= shutil
. which ( 'systemd-resolved' , path
= which_paths
)
57 timesyncd_bin
= shutil
. which ( 'systemd-timesyncd' , path
= which_paths
)
58 wait_online_bin
= shutil
. which ( 'systemd-networkd-wait-online' , path
= which_paths
)
59 networkctl_bin
= shutil
. which ( 'networkctl' , path
= which_paths
)
60 resolvectl_bin
= shutil
. which ( 'resolvectl' , path
= which_paths
)
61 timedatectl_bin
= shutil
. which ( 'timedatectl' , path
= which_paths
)
62 udevadm_bin
= shutil
. which ( 'udevadm' , path
= which_paths
)
63 test_ndisc_send
= None
93 saved_ipv4_rules
= None
94 saved_ipv6_rules
= None
98 if os
. path
. exists ( path
):
102 shutil
. rmtree ( path
, ignore_errors
= True )
105 shutil
. copy ( src
, dst
)
108 shutil
. copytree ( src
, dst
, copy_function
= shutil
. copy
)
111 os
. makedirs ( path
, exist_ok
= True )
114 pathlib
. Path ( path
). touch ()
116 # pylint: disable=R1710
117 def check_output (* command
, ** kwargs
):
118 # This checks the result and returns stdout (and stderr) on success.
119 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
120 ret
= subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. PIPE
, stderr
= subprocess
. STDOUT
, ** kwargs
)
121 if ret
. returncode
== 0 :
122 return ret
. stdout
. rstrip ()
123 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
125 ret
. check_returncode ()
127 def call (* command
, ** kwargs
):
128 # This returns returncode. stdout and stderr are merged and shown in console
129 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
130 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stderr
= subprocess
. STDOUT
, ** kwargs
). returncode
132 def call_check (* command
, ** kwargs
):
133 # Same as call() above, but it triggers CalledProcessError if rc != 0
134 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
135 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stderr
= subprocess
. STDOUT
, ** kwargs
). check_returncode ()
137 def call_quiet (* command
, ** kwargs
):
138 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
139 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. DEVNULL
, stderr
= subprocess
. DEVNULL
, ** kwargs
). returncode
141 def run (* command
, ** kwargs
):
142 # This returns CompletedProcess instance.
143 command
= command
[ 0 ]. split () + list ( command
[ 1 :])
144 return subprocess
. run ( command
, check
= False , universal_newlines
= True , stdout
= subprocess
. PIPE
, stderr
= subprocess
. PIPE
, ** kwargs
)
146 def check_json ( string
):
149 except json
. JSONDecodeError
:
150 print ( f
"String is not a valid JSON: ' {string} '" )
153 def is_module_available (* module_names
):
154 for module_name
in module_names
:
155 lsmod_output
= check_output ( 'lsmod' )
156 module_re
= re
. compile ( rf
'^{re.escape(module_name)} \b ' , re
. MULTILINE
)
157 if not module_re
. search ( lsmod_output
) and call_quiet ( 'modprobe' , module_name
) != 0 :
161 def expectedFailureIfModuleIsNotAvailable (* module_names
):
163 return func
if is_module_available (* module_names
) else unittest
. expectedFailure ( func
)
167 def expectedFailureIfERSPANv0IsNotSupported ():
168 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
170 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' )
171 remove_link ( 'erspan99' )
172 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
176 def expectedFailureIfERSPANv2IsNotSupported ():
177 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
179 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' )
180 remove_link ( 'erspan99' )
181 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
185 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable ():
187 rc
= call_quiet ( 'ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7' )
188 call_quiet ( 'ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7' )
189 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
193 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable ():
195 rc
= call_quiet ( 'ip rule add not from 192.168.100.19 ipproto tcp table 7' )
196 call_quiet ( 'ip rule del not from 192.168.100.19 ipproto tcp table 7' )
197 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
201 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable ():
204 if call_quiet ( 'ip rule add from 192.168.100.19 table 7 uidrange 200-300' ) == 0 :
205 ret
= run ( 'ip rule list from 192.168.100.19 table 7' )
206 supported
= ret
. returncode
== 0 and 'uidrange 200-300' in ret
. stdout
207 call_quiet ( 'ip rule del from 192.168.100.19 table 7 uidrange 200-300' )
208 return func
if supported
else unittest
. expectedFailure ( func
)
212 def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable ():
214 rc
= call_quiet ( 'ip rule add not from 192.168.100.19 l3mdev' )
215 call_quiet ( 'ip rule del not from 192.168.100.19 l3mdev' )
216 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
220 def expectedFailureIfNexthopIsNotAvailable ():
222 rc
= call_quiet ( 'ip nexthop list' )
223 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
227 def expectedFailureIfRTA_VIAIsNotSupported ():
229 call_quiet ( 'ip link add dummy98 type dummy' )
230 call_quiet ( 'ip link set up dev dummy98' )
231 call_quiet ( 'ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98' )
232 rc
= call_quiet ( 'ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98' )
233 remove_link ( 'dummy98' )
234 return func
if rc
== 0 else unittest
. expectedFailure ( func
)
238 def expectedFailureIfAlternativeNameIsNotAvailable ():
240 call_quiet ( 'ip link add dummy98 type dummy' )
242 call_quiet ( 'ip link prop add dev dummy98 altname hogehogehogehogehoge' ) == 0 and \
243 call_quiet ( 'ip link show dev hogehogehogehogehoge' ) == 0
244 remove_link ( 'dummy98' )
245 return func
if supported
else unittest
. expectedFailure ( func
)
249 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ():
251 def finalize ( func
, supported
):
252 call_quiet ( 'rmmod netdevsim' )
253 return func
if supported
else unittest
. expectedFailure ( func
)
255 call_quiet ( 'rmmod netdevsim' )
256 if call_quiet ( 'modprobe netdevsim' ) != 0 :
257 return finalize ( func
, False )
260 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
263 return finalize ( func
, False )
265 return finalize ( func
, os
. path
. exists ( '/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs' ))
269 # pylint: disable=C0415
270 def compare_kernel_version ( min_kernel_version
):
273 from packaging
import version
275 print ( 'Failed to import either platform or packaging module, assuming the comparison failed' )
278 # Get only the actual kernel version without any build/distro/arch stuff
279 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
280 kver
= platform
. release (). split ( '-' )[ 0 ]
281 # Get also rid of '+'
282 kver
= kver
. split ( '+' )[ 0 ]
284 return version
. parse ( kver
) >= version
. parse ( min_kernel_version
)
286 def copy_network_unit (* units
, copy_dropins
= True ):
288 Copy networkd unit files into the testbed.
290 Any networkd unit file type can be specified, as well as drop-in files.
292 By default, all drop-ins for a specified unit file are copied in;
293 to avoid that specify dropins=False.
295 When a drop-in file is specified, its unit file is also copied in automatically.
298 mkdir_p ( network_unit_dir
)
300 if copy_dropins
and os
. path
. exists ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' )):
301 cp_r ( os
. path
. join ( networkd_ci_temp_dir
, unit
+ '.d' ), os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
303 if unit
. endswith ( '.conf' ):
305 unit
= os
. path
. dirname ( dropin
). rstrip ( '.d' )
306 dropindir
= os
. path
. join ( network_unit_dir
, unit
+ '.d' )
308 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), dropindir
)
310 cp ( os
. path
. join ( networkd_ci_temp_dir
, unit
), network_unit_dir
)
312 if unit
. endswith ( '.link' ):
318 def copy_credential ( src
, target
):
319 mkdir_p ( credstore_dir
)
320 cp ( os
. path
. join ( networkd_ci_temp_dir
, src
),
321 os
. path
. join ( credstore_dir
, target
))
323 def remove_network_unit (* units
):
325 Remove previously copied unit files from the testbed.
327 Drop-ins will be removed automatically.
331 rm_f ( os
. path
. join ( network_unit_dir
, unit
))
332 rm_rf ( os
. path
. join ( network_unit_dir
, unit
+ '.d' ))
334 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
340 def clear_network_units ():
342 if os
. path
. exists ( network_unit_dir
):
343 units
= os
. listdir ( network_unit_dir
)
345 if unit
. endswith ( '.link' ) or unit
. endswith ( '.link.d' ):
348 rm_rf ( network_unit_dir
)
353 def copy_networkd_conf_dropin (* dropins
):
354 """Copy networkd.conf dropin files into the testbed."""
355 mkdir_p ( networkd_conf_dropin_dir
)
356 for dropin
in dropins
:
357 cp ( os
. path
. join ( networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
359 def remove_networkd_conf_dropin (* dropins
):
360 """Remove previously copied networkd.conf dropin files from the testbed."""
361 for dropin
in dropins
:
362 rm_f ( os
. path
. join ( networkd_conf_dropin_dir
, dropin
))
364 def clear_networkd_conf_dropins ():
365 rm_rf ( networkd_conf_dropin_dir
)
367 def setup_systemd_udev_rules ():
371 mkdir_p ( udev_rules_dir
)
373 for path
in [ build_dir
, source_dir
]:
374 path
= os
. path
. join ( path
, "rules.d" )
375 print ( f
"Copying udev rules from {path} to {udev_rules_dir} " )
377 for rule
in os
. listdir ( path
):
378 if not rule
. endswith ( ".rules" ):
380 cp ( os
. path
. join ( path
, rule
), udev_rules_dir
)
382 def clear_networkd_state_files ():
383 rm_rf ( '/var/lib/systemd/network/' )
385 def copy_udev_rule (* rules
):
386 """Copy udev rules"""
387 mkdir_p ( udev_rules_dir
)
389 cp ( os
. path
. join ( networkd_ci_temp_dir
, rule
), udev_rules_dir
)
391 def remove_udev_rule (* rules
):
392 """Remove previously copied udev rules"""
394 rm_f ( os
. path
. join ( udev_rules_dir
, rule
))
396 def clear_udev_rules ():
397 rm_rf ( udev_rules_dir
)
399 def save_active_units ():
400 for u
in [ 'systemd-networkd.socket' , 'systemd-networkd.service' ,
401 'systemd-resolved.service' , 'systemd-timesyncd.service' ,
402 'firewalld.service' ]:
403 if call ( f
'systemctl is-active --quiet {u} ' ) == 0 :
404 call ( f
'systemctl stop {u} ' )
405 active_units
. append ( u
)
407 def restore_active_units ():
408 if 'systemd-networkd.socket' in active_units
:
409 call ( 'systemctl stop systemd-networkd.socket systemd-networkd.service' )
410 for u
in active_units
:
411 call ( f
'systemctl restart {u} ' )
413 def create_unit_dropin ( unit
, contents
):
414 mkdir_p ( f
'/run/systemd/system/ {unit} .d' )
415 with
open ( f
'/run/systemd/system/ {unit} .d/00-override.conf' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
416 f
. write ( ' \n ' . join ( contents
))
418 def create_service_dropin ( service
, command
, additional_settings
= None ):
419 drop_in
= [ '[Service]' ]
423 f
'ExecStart=!! {valgrind_cmd}{command} ' ,
426 drop_in
+= [ 'Environment=SYSTEMD_LOG_LEVEL=debug' ]
428 drop_in
+= [ f
'Environment=ASAN_OPTIONS=" {asan_options} "' ]
430 drop_in
+= [ f
'Environment=LSAN_OPTIONS=" {lsan_options} "' ]
432 drop_in
+= [ f
'Environment=UBSAN_OPTIONS=" {ubsan_options} "' ]
433 if asan_options
or lsan_options
or ubsan_options
:
434 drop_in
+= [ 'SystemCallFilter=' ]
435 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
436 drop_in
+= [ 'MemoryDenyWriteExecute=no' ]
439 'Environment=SYSTEMD_MEMPOOL=0' ,
447 if additional_settings
:
448 drop_in
+= additional_settings
450 create_unit_dropin ( f
' {service} .service' , drop_in
)
452 def setup_system_units ():
454 mkdir_p ( '/run/systemd/system/' )
457 'systemd-networkd.service' ,
458 'systemd-networkd.socket' ,
459 'systemd-networkd-persistent-storage.service' ,
460 'systemd-resolved.service' ,
461 'systemd-timesyncd.service' ,
462 'systemd-udevd.service' ,
464 for path
in [ build_dir
, source_dir
]:
465 fullpath
= os
. path
. join ( os
. path
. join ( path
, "units" ), unit
)
466 if os
. path
. exists ( fullpath
):
467 print ( f
"Copying unit file from {fullpath} to /run/systemd/system/" )
468 cp ( fullpath
, '/run/systemd/system/' )
471 create_service_dropin ( 'systemd-networkd' , networkd_bin
,
474 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes' ,
476 'StartLimitIntervalSec=0' ])
477 create_service_dropin ( 'systemd-resolved' , resolved_bin
)
478 create_service_dropin ( 'systemd-timesyncd' , timesyncd_bin
)
480 # TODO: also run udevd with sanitizers, valgrind, or coverage
482 'systemd-udevd.service' ,
486 f
'ExecStart=!!@ {udevadm_bin} systemd-udevd' ,
490 'systemd-networkd.socket' ,
493 'StartLimitIntervalSec=0' ,
497 'systemd-networkd-persistent-storage.service' ,
500 'StartLimitIntervalSec=0' ,
503 f
'ExecStart= {networkctl_bin} persistent-storage yes' ,
505 f
'ExecStop= {networkctl_bin} persistent-storage no'
509 check_output ( 'systemctl daemon-reload' )
510 print ( check_output ( 'systemctl cat systemd-networkd.service' ))
511 print ( check_output ( 'systemctl cat systemd-networkd-persistent-storage.service' ))
512 print ( check_output ( 'systemctl cat systemd-resolved.service' ))
513 print ( check_output ( 'systemctl cat systemd-timesyncd.service' ))
514 print ( check_output ( 'systemctl cat systemd-udevd.service' ))
515 check_output ( 'systemctl restart systemd-resolved.service' )
516 check_output ( 'systemctl restart systemd-timesyncd.service' )
517 check_output ( 'systemctl restart systemd-udevd.service' )
519 def clear_system_units ():
521 rm_f ( f
'/run/systemd/system/ {name} ' )
522 rm_rf ( f
'/run/systemd/system/ {name} .d' )
524 rm_unit ( 'systemd-networkd.service' )
525 rm_unit ( 'systemd-networkd.socket' )
526 rm_unit ( 'systemd-networkd-persistent-storage.service' )
527 rm_unit ( 'systemd-resolved.service' )
528 rm_unit ( 'systemd-timesyncd.service' )
529 rm_unit ( 'systemd-udevd.service' )
530 check_output ( 'systemctl daemon-reload' )
531 check_output ( 'systemctl restart systemd-udevd.service' )
533 def link_exists ( link
):
534 return call_quiet ( f
'ip link show {link} ' ) == 0
536 def link_resolve ( link
):
537 return check_output ( f
'ip link show {link} ' ). split ( ':' )[ 1 ]. strip ()
539 def remove_link (* links
, protect
= False ):
541 if protect
and link
in protected_links
:
543 if link_exists ( link
):
544 call ( f
'ip link del dev {link} ' )
546 def save_existing_links ():
547 links
= os
. listdir ( '/sys/class/net' )
549 if link_exists ( link
):
550 protected_links
. add ( link
)
552 print ( '### The following links will be protected:' )
553 print ( ', ' . join ( sorted ( list ( protected_links
))))
556 links
= os
. listdir ( '/sys/class/net' )
557 remove_link (* links
, protect
= True )
559 def flush_nexthops ():
560 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
561 # Hence, we cannot restore nexthops in a simple way.
562 # Let's assume there is no nexthop used in the system
563 call_quiet ( 'ip nexthop flush' )
566 # pylint: disable=global-statement
568 saved_routes
= check_output ( 'ip route show table all' )
569 print ( '### The following routes will be protected:' )
574 output
= check_output ( 'ip route show table all' )
575 for line
in output
. splitlines ():
576 if line
in saved_routes
:
578 if 'proto kernel' in line
:
580 if ' dev ' in line
and not ' dev lo ' in line
:
584 print ( '### Removing routes that did not exist when the test started.' )
586 call ( f
'ip route del {line} ' )
588 def save_routing_policy_rules ():
589 # pylint: disable=global-statement
590 global saved_ipv4_rules
, saved_ipv6_rules
592 output
= check_output ( f
'ip - {ipv} rule show' )
593 print ( f
'### The following IPv {ipv} routing policy rules will be protected:' )
597 saved_ipv4_rules
= save ( 4 )
598 saved_ipv6_rules
= save ( 6 )
600 def flush_routing_policy_rules ():
601 def flush ( ipv
, saved_rules
):
603 output
= check_output ( f
'ip - {ipv} rule show' )
604 for line
in output
. splitlines ():
605 if line
in saved_rules
:
609 print ( f
'### Removing IPv {ipv} routing policy rules that did not exist when the test started.' )
611 words
= line
. replace ( 'lookup [l3mdev-table]' , 'l3mdev' ). split ()
612 priority
= words
[ 0 ]. rstrip ( ':' )
613 call ( f
'ip - {ipv} rule del priority {priority} ' + ' ' . join ( words
[ 1 :]))
615 flush ( 4 , saved_ipv4_rules
)
616 flush ( 6 , saved_ipv6_rules
)
618 def flush_fou_ports ():
619 ret
= run ( 'ip fou show' )
620 if ret
. returncode
!= 0 :
621 return # fou may not be supported
622 for line
in ret
. stdout
. splitlines ():
623 port
= line
. split ()[ 1 ]
624 call ( f
'ip fou del port {port} ' )
626 def flush_l2tp_tunnels ():
628 ret
= run ( 'ip l2tp show tunnel' )
629 if ret
. returncode
!= 0 :
630 return # l2tp may not be supported
631 for line
in ret
. stdout
. splitlines ():
633 if words
[ 0 ] == 'Tunnel' :
634 tid
= words
[ 1 ]. rstrip ( ',' )
635 call ( f
'ip l2tp del tunnel tunnel_id {tid} ' )
638 # Removing L2TP tunnel is asynchronous and slightly takes a time.
641 r
= run ( f
'ip l2tp show tunnel tunnel_id {tid} ' )
642 if r
. returncode
!= 0 or len ( r
. stdout
. rstrip ()) == 0 :
646 print ( f
'Cannot remove L2TP tunnel {tid} , ignoring.' )
649 # pylint: disable=global-statement
650 global saved_timezone
651 r
= run (* timedatectl_cmd
, 'show' , '--value' , '--property' , 'Timezone' , env
= env
)
652 if r
. returncode
== 0 :
653 saved_timezone
= r
. stdout
. rstrip ()
654 print ( f
'### Saved timezone: {saved_timezone} ' )
656 def restore_timezone ():
658 call (* timedatectl_cmd
, 'set-timezone' , f
' {saved_timezone} ' , env
= env
)
660 def read_link_attr (* args
):
661 with
open ( os
. path
. join ( '/sys/class/net' , * args
), encoding
= 'utf-8' ) as f
:
662 return f
. readline (). strip ()
664 def read_manager_state_file ():
665 with
open ( '/run/systemd/netif/state' , encoding
= 'utf-8' ) as f
:
668 def read_link_state_file ( link
):
669 ifindex
= read_link_attr ( link
, 'ifindex' )
670 path
= os
. path
. join ( '/run/systemd/netif/links' , ifindex
)
671 with
open ( path
, encoding
= 'utf-8' ) as f
:
674 def read_ip_sysctl_attr ( link
, attribute
, ipv
):
675 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'conf' , link
, attribute
), encoding
= 'utf-8' ) as f
:
676 return f
. readline (). strip ()
678 def read_ip_neigh_sysctl_attr ( link
, attribute
, ipv
):
679 with
open ( os
. path
. join ( '/proc/sys/net' , ipv
, 'neigh' , link
, attribute
), encoding
= 'utf-8' ) as f
:
680 return f
. readline (). strip ()
682 def read_ipv6_sysctl_attr ( link
, attribute
):
683 return read_ip_sysctl_attr ( link
, attribute
, 'ipv6' )
685 def read_ipv6_neigh_sysctl_attr ( link
, attribute
):
686 return read_ip_neigh_sysctl_attr ( link
, attribute
, 'ipv6' )
688 def read_ipv4_sysctl_attr ( link
, attribute
):
689 return read_ip_sysctl_attr ( link
, attribute
, 'ipv4' )
691 def stop_by_pid_file ( pid_file
):
692 if not os
. path
. exists ( pid_file
):
694 with
open ( pid_file
, 'r' , encoding
= 'utf-8' ) as f
:
695 pid
= f
. read (). rstrip ( ' \t\r\n \0' )
696 os
. kill ( int ( pid
), signal
. SIGTERM
)
700 print ( f
"PID {pid} is still alive, waiting..." )
703 if e
. errno
== errno
. ESRCH
:
705 print ( f
"Unexpected exception when waiting for {pid} to die: {e.errno}" )
708 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' ):
710 ra_mode
= f
', {ra_mode} '
716 f
'--log-facility= {dnsmasq_log_file} ' ,
717 '--log-queries=extra' ,
719 f
'--pid-file= {dnsmasq_pid_file} ' ,
720 '--conf-file=/dev/null' ,
722 f
'--interface= {interface} ' ,
723 f
'--dhcp-leasefile= {dnsmasq_lease_file} ' ,
725 f
'--dhcp-range= {ipv6_range}{ra_mode} ,2m' ,
726 f
'--dhcp-range= {ipv4_range} ,2m' ,
727 '--dhcp-option=option:mtu,1492' ,
728 f
'--dhcp-option=option:router, {ipv4_router} ' ,
731 ) + additional_options
732 check_output (* command
)
735 stop_by_pid_file ( dnsmasq_pid_file
)
736 rm_f ( dnsmasq_lease_file
)
737 rm_f ( dnsmasq_log_file
)
739 def read_dnsmasq_log_file ():
740 with
open ( dnsmasq_log_file
, encoding
= 'utf-8' ) as f
:
743 def start_isc_dhcpd ( conf_file
, ipv
, interface
= 'veth-peer' ):
744 conf_file_path
= os
. path
. join ( networkd_ci_temp_dir
, conf_file
)
745 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface} '
746 touch ( isc_dhcpd_lease_file
)
747 check_output ( isc_dhcpd_command
)
749 def stop_isc_dhcpd ():
750 stop_by_pid_file ( isc_dhcpd_pid_file
)
751 rm_f ( isc_dhcpd_lease_file
)
753 def get_dbus_link_path ( link
):
754 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
755 '/org/freedesktop/network1' , 'org.freedesktop.network1.Manager' ,
756 'GetLinkByName' , 's' , link
])
758 assert out
. startswith ( b
'io ' )
760 assert out
. endswith ( b
'"' )
762 return out
[:- 1 ]. split ( '"' )[ 1 ]
764 def get_dhcp_client_state ( link
, family
):
765 link_path
= get_dbus_link_path ( link
)
767 out
= subprocess
. check_output ([ 'busctl' , 'get-property' , 'org.freedesktop.network1' ,
768 link_path
, f
'org.freedesktop.network1.DHCPv {family} Client' , 'State' ])
769 assert out
. startswith ( b
's "' )
771 assert out
. endswith ( b
'"' )
772 return out
[ 3 :- 1 ]. decode ()
774 def get_dhcp4_client_state ( link
):
775 return get_dhcp_client_state ( link
, '4' )
777 def get_dhcp6_client_state ( link
):
778 return get_dhcp_client_state ( link
, '6' )
780 def get_link_description ( link
):
781 link_path
= get_dbus_link_path ( link
)
783 out
= subprocess
. check_output ([ 'busctl' , 'call' , 'org.freedesktop.network1' ,
784 link_path
, 'org.freedesktop.network1.Link' , 'Describe' ])
785 assert out
. startswith ( b
's "' )
787 assert out
. endswith ( b
'"' )
788 json_raw
= out
[ 2 :]. decode ()
790 description
= json
. loads ( json_raw
) # Convert from escaped sequences to json
791 check_json ( description
)
792 return json
. loads ( description
) # Now parse the json
794 def start_radvd (* additional_options
, config_file
):
795 config_file_path
= os
. path
. join ( networkd_ci_temp_dir
, 'radvd' , config_file
)
798 f
'--pidfile= {radvd_pid_file} ' ,
799 f
'--config= {config_file_path} ' ,
800 '--logmethod=stderr' ,
801 ) + additional_options
802 check_output (* command
)
805 stop_by_pid_file ( radvd_pid_file
)
807 def radvd_check_config ( config_file
):
808 if not shutil
. which ( 'radvd' ):
809 print ( 'radvd is not installed, assuming the config check failed' )
812 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
813 # set up (one instance is @unittest.skipX())
814 config_file_path
= os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf/radvd' , config_file
)
815 return call ( f
'radvd --config= {config_file_path} --configtest' ) == 0
817 def networkd_invocation_id ():
818 return check_output ( 'systemctl show --value -p InvocationID systemd-networkd.service' )
820 def read_networkd_log ( invocation_id
= None , since
= None ):
821 if not invocation_id
:
822 invocation_id
= networkd_invocation_id ()
826 '--output=short-monotonic' ,
827 f
'_SYSTEMD_INVOCATION_ID= {invocation_id} ' ,
830 command
. append ( f
'--since= {since} ' )
831 check_output ( 'journalctl --sync' )
832 return check_output (* command
)
834 def networkd_is_failed ():
835 return call_quiet ( 'systemctl is-failed -q systemd-networkd.service' ) != 1
837 def stop_networkd ( show_logs
= True ):
839 invocation_id
= networkd_invocation_id ()
840 check_output ( 'systemctl stop systemd-networkd.socket' )
841 check_output ( 'systemctl stop systemd-networkd.service' )
843 print ( read_networkd_log ( invocation_id
))
844 # Check if networkd exits cleanly.
845 assert not networkd_is_failed ()
847 def start_networkd ():
848 check_output ( 'systemctl start systemd-networkd' )
850 def restart_networkd ( show_logs
= True ):
852 invocation_id
= networkd_invocation_id ()
853 check_output ( 'systemctl restart systemd-networkd.service' )
855 print ( read_networkd_log ( invocation_id
))
858 return int ( check_output ( 'systemctl show --value -p MainPID systemd-networkd.service' ))
860 def networkctl (* args
):
861 # Do not call networkctl if networkd is in failed state.
862 # Otherwise, networkd may be restarted and we may get wrong results.
863 assert not networkd_is_failed ()
864 return check_output (*( networkctl_cmd
+ list ( args
)), env
= env
)
866 def networkctl_status (* args
):
867 return networkctl ( '-n' , '0' , 'status' , * args
)
869 def networkctl_json (* args
):
870 return networkctl ( '--json=short' , 'status' , * args
)
872 def networkctl_reconfigure (* links
):
873 networkctl ( 'reconfigure' , * links
)
875 def networkctl_reload ():
878 def resolvectl (* args
):
879 return check_output (*( resolvectl_cmd
+ list ( args
)), env
= env
)
881 def timedatectl (* args
):
882 return check_output (*( timedatectl_cmd
+ list ( args
)), env
= env
)
885 return check_output (*( udevadm_cmd
+ list ( args
)))
887 def udevadm_reload ():
888 udevadm ( 'control' , '--reload' )
890 def udevadm_trigger (* args
, action
= 'add' ):
891 udevadm ( 'trigger' , '--settle' , f
'--action= {action} ' , * args
)
896 def tear_down_common ():
897 # 1. stop DHCP/RA servers
903 call_quiet ( 'rmmod netdevsim' )
904 call_quiet ( 'rmmod sch_teql' )
906 # 3. remove network namespace
907 call_quiet ( 'ip netns del ns99' )
917 clear_network_units ()
918 clear_networkd_conf_dropins ()
919 clear_networkd_state_files ()
924 flush_routing_policy_rules ()
928 rm_rf ( networkd_ci_temp_dir
)
929 cp_r ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), 'conf' ), networkd_ci_temp_dir
)
931 clear_network_units ()
932 clear_networkd_conf_dropins ()
933 clear_networkd_state_files ()
936 setup_systemd_udev_rules ()
937 copy_udev_rule ( '00-debug-net.rules' )
941 save_existing_links ()
943 save_routing_policy_rules ()
948 def tearDownModule ():
949 rm_rf ( networkd_ci_temp_dir
)
951 clear_network_units ()
952 clear_networkd_conf_dropins ()
953 clear_networkd_state_files ()
958 restore_active_units ()
961 # pylint: disable=no-member
963 def check_link_exists ( self
, link
, expected
= True ):
965 self
. assertTrue ( link_exists ( link
))
967 self
. assertFalse ( link_exists ( link
))
969 def check_link_attr ( self
, * args
):
970 self
. assertEqual ( read_link_attr (* args
[:- 1 ]), args
[- 1 ])
972 def check_bridge_port_attr ( self
, master
, port
, attribute
, expected
, allow_enoent
= False ):
973 path
= os
. path
. join ( '/sys/devices/virtual/net' , master
, 'lower_' + port
, 'brport' , attribute
)
974 if allow_enoent
and not os
. path
. exists ( path
):
976 with
open ( path
, encoding
= 'utf-8' ) as f
:
977 self
. assertEqual ( f
. readline (). strip (), expected
)
979 def check_ipv4_sysctl_attr ( self
, link
, attribute
, expected
):
980 self
. assertEqual ( read_ipv4_sysctl_attr ( link
, attribute
), expected
)
982 def check_ipv6_sysctl_attr ( self
, link
, attribute
, expected
):
983 self
. assertEqual ( read_ipv6_sysctl_attr ( link
, attribute
), expected
)
985 def check_ipv6_neigh_sysctl_attr ( self
, link
, attribute
, expected
):
986 self
. assertEqual ( read_ipv6_neigh_sysctl_attr ( link
, attribute
), expected
)
988 def wait_links ( self
, * links
, timeout
= 20 , fail_assert
= True ):
989 def links_exist (* links
):
991 if not link_exists ( link
):
995 for iteration
in range ( timeout
+ 1 ):
999 if links_exist (* links
):
1002 self
. fail ( 'Timed out waiting for all links to be created: ' + ', ' . join ( list ( links
)))
1005 def wait_activated ( self
, link
, state
= 'down' , timeout
= 20 , fail_assert
= True ):
1006 # wait for the interface is activated.
1007 needle
= f
' {link} : Bringing link {state} '
1008 flag
= state
. upper ()
1009 for iteration
in range ( timeout
+ 1 ):
1012 if not link_exists ( link
):
1014 output
= read_networkd_log ()
1015 if needle
in output
and flag
in check_output ( f
'ip link show {link} ' ):
1018 self
. fail ( f
'Timed out waiting for {link} activated.' )
1021 def wait_operstate ( self
, link
, operstate
= 'degraded' , setup_state
= 'configured' , setup_timeout
= 5 , fail_assert
= True ):
1022 """Wait for the link to reach the specified operstate and/or setup state.
1024 Specify None or '' for either operstate or setup_state to ignore that state.
1025 This will recheck until the state conditions are met or the timeout expires.
1027 If the link successfully matches the requested state, this returns True.
1028 If this times out waiting for the link to match, the behavior depends on the
1029 'fail_assert' parameter; if True, this causes a test assertion failure,
1030 otherwise this returns False. The default is to cause assertion failure.
1032 Note that this function matches on *exactly* the given operstate and setup_state.
1033 To wait for a link to reach *or exceed* a given operstate, use wait_online().
1038 setup_state
= r
'\S+'
1040 for secs
in range ( setup_timeout
+ 1 ):
1043 if not link_exists ( link
):
1045 output
= networkctl_status ( link
)
1046 if re
. search ( rf
'(?m)^\s*State:\s+ {operstate} \s+\( {setup_state} \)\s*$' , output
):
1050 self
. fail ( f
'Timed out waiting for {link} to reach state {operstate} / {setup_state} ' )
1053 def wait_online ( self
, * links_with_operstate
, timeout
= '20s' , bool_any
= False , ipv4
= False , ipv6
= False , setup_state
= 'configured' , setup_timeout
= 5 ):
1054 """Wait for the links to reach the specified operstate and/or setup state.
1056 This is similar to wait_operstate() but can be used for multiple links,
1057 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1058 The operstate should be specified in the link name, like 'eth0:degraded'.
1059 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1061 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1062 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1064 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1065 If this is set, no setup_state checks are done.
1067 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1068 This is applied only for the operational state 'degraded' or above.
1070 Note that this function waits for the links to reach *or exceed* the given operstate.
1071 However, the setup_state, if specified, must be matched *exactly*.
1073 This returns if the links reached the requested operstate/setup_state; otherwise it
1074 raises CalledProcessError or fails test assertion.
1076 args
= wait_online_cmd
+ [ f
'--timeout= {timeout} ' ] + [ f
'--interface= {link} ' for link
in links_with_operstate
] + [ f
'--ignore= {link} ' for link
in protected_links
]
1084 check_output (* args
, env
= wait_online_env
)
1085 except subprocess
. CalledProcessError
:
1086 if networkd_is_failed ():
1087 print ( '!!!!! systemd-networkd.service is failed !!!!!' )
1088 call ( 'systemctl status systemd-networkd.service' )
1090 # show detailed status on failure
1091 for link
in links_with_operstate
:
1092 name
= link
. split ( ':' )[ 0 ]
1093 if link_exists ( name
):
1094 print ( networkctl_status ( name
))
1096 print ( f
'Interface {name} not found.' )
1098 if not bool_any
and setup_state
:
1099 for link
in links_with_operstate
:
1100 self
. wait_operstate ( link
. split ( ':' )[ 0 ], None , setup_state
, setup_timeout
)
1102 def wait_address ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1103 for i
in range ( timeout_sec
):
1106 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1107 if re
. search ( address_regex
, output
) and 'tentative' not in output
:
1110 self
. assertRegex ( output
, address_regex
)
1112 def wait_address_dropped ( self
, link
, address_regex
, scope
= 'global' , ipv
= '' , timeout_sec
= 100 ):
1113 for i
in range ( timeout_sec
):
1116 output
= check_output ( f
'ip {ipv} address show dev {link} scope {scope} ' )
1117 if not re
. search ( address_regex
, output
):
1120 self
. assertNotRegex ( output
, address_regex
)
1122 def wait_route ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1123 for i
in range ( timeout_sec
):
1126 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1127 if re
. search ( route_regex
, output
):
1130 self
. assertRegex ( output
, route_regex
)
1132 def wait_route_dropped ( self
, link
, route_regex
, table
= 'main' , ipv
= '' , timeout_sec
= 100 ):
1133 for i
in range ( timeout_sec
):
1136 output
= check_output ( f
'ip {ipv} route show dev {link} table {table} ' )
1137 if not re
. search ( route_regex
, output
):
1140 self
. assertNotRegex ( output
, route_regex
)
1142 def check_netlabel ( self
, interface
, address
, label
= 'system_u:object_r:root_t:s0' ):
1143 if not shutil
. which ( 'selinuxenabled' ):
1144 print ( '## Checking NetLabel skipped: selinuxenabled command not found.' )
1145 elif call_quiet ( 'selinuxenabled' ) != 0 :
1146 print ( '## Checking NetLabel skipped: SELinux disabled.' )
1147 elif not shutil
. which ( 'netlabelctl' ): # not packaged by all distros
1148 print ( '## Checking NetLabel skipped: netlabelctl command not found.' )
1150 output
= check_output ( 'netlabelctl unlbl list' )
1152 self
. assertRegex ( output
, f
'interface: {interface} ,address: {address} ,label:" {label} "' )
1154 def setup_nftset ( self
, filter_name
, filter_type
, flags
= '' ):
1155 if not shutil
. which ( 'nft' ):
1156 print ( '## Setting up NFT sets skipped: nft command not found.' )
1158 if call ( f
'nft add table inet sd_test' ) != 0 :
1159 print ( '## Setting up NFT table failed.' )
1161 if call ( f
'nft add set inet sd_test {filter_name} {{ type {filter_type} ; {flags} }}' ) != 0 :
1162 print ( '## Setting up NFT sets failed.' )
1165 def teardown_nftset ( self
, * filters
):
1166 if not shutil
. which ( 'nft' ):
1167 print ( '## Tearing down NFT sets skipped: nft command not found.' )
1169 for filter_name
in filters
:
1170 if call ( f
'nft delete set inet sd_test {filter_name} ' ) != 0 :
1171 print ( '## Tearing down NFT sets failed.' )
1173 if call ( f
'nft delete table inet sd_test' ) != 0 :
1174 print ( '## Tearing down NFT table failed.' )
1177 def check_nftset ( self
, filter_name
, contents
):
1178 if not shutil
. which ( 'nft' ):
1179 print ( '## Checking NFT sets skipped: nft command not found.' )
1181 output
= check_output ( f
'nft list set inet sd_test {filter_name} ' )
1183 self
. assertRegex ( output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*' )
1185 class NetworkctlTests ( unittest
. TestCase
, Utilities
):
1193 @expectedFailureIfAlternativeNameIsNotAvailable ()
1194 def test_altname ( self
):
1195 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' , '12-dummy.netdev' , '12-dummy.link' )
1197 self
. wait_online ( 'dummy98:degraded' )
1199 output
= networkctl_status ( 'dummy98' )
1200 self
. assertRegex ( output
, 'hogehogehogehogehogehoge' )
1202 @expectedFailureIfAlternativeNameIsNotAvailable ()
1203 def test_rename_to_altname ( self
):
1204 copy_network_unit ( '26-netdev-link-local-addressing-yes.network' ,
1205 '12-dummy.netdev' , '12-dummy-rename-to-altname.link' )
1207 self
. wait_online ( 'dummyalt:degraded' )
1209 output
= networkctl_status ( 'dummyalt' )
1210 self
. assertIn ( 'hogehogehogehogehogehoge' , output
)
1211 self
. assertNotIn ( 'dummy98' , output
)
1213 def test_reconfigure ( self
):
1214 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1216 self
. wait_online ( 'dummy98:routable' )
1218 output
= check_output ( 'ip -4 address show dev dummy98' )
1220 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1221 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1222 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1224 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
1225 check_output ( 'ip address del 10.1.2.4/16 dev dummy98' )
1226 check_output ( 'ip address del 10.2.2.4/16 dev dummy98' )
1228 networkctl_reconfigure ( 'dummy98' )
1229 self
. wait_online ( 'dummy98:routable' )
1231 output
= check_output ( 'ip -4 address show dev dummy98' )
1233 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1234 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1235 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1237 remove_network_unit ( '25-address-static.network' )
1240 self
. wait_operstate ( 'dummy98' , 'degraded' , setup_state
= 'unmanaged' )
1242 output
= check_output ( 'ip -4 address show dev dummy98' )
1244 self
. assertNotIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1245 self
. assertNotIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1246 self
. assertNotIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1248 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
1250 self
. wait_online ( 'dummy98:routable' )
1252 output
= check_output ( 'ip -4 address show dev dummy98' )
1254 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
1255 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
1256 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
1258 def test_renew ( self
):
1260 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
1261 output
= networkctl_status ( 'veth99' )
1263 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)' )
1264 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
1265 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
1266 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
1268 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
1271 check_json ( networkctl_json ( '--lines=0' , '--stats' , '--all' , '--full' ))
1273 for verb
in [ 'renew' , 'forcerenew' ]:
1274 networkctl ( verb
, 'veth99' )
1276 networkctl ( verb
, 'veth99' , 'veth99' , 'veth99' )
1279 def test_up_down ( self
):
1280 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
1282 self
. wait_online ( 'dummy98:routable' )
1284 networkctl ( 'down' , 'dummy98' )
1285 self
. wait_online ( 'dummy98:off' )
1286 networkctl ( 'up' , 'dummy98' )
1287 self
. wait_online ( 'dummy98:routable' )
1288 networkctl ( 'down' , 'dummy98' , 'dummy98' , 'dummy98' )
1289 self
. wait_online ( 'dummy98:off' )
1290 networkctl ( 'up' , 'dummy98' , 'dummy98' , 'dummy98' )
1291 self
. wait_online ( 'dummy98:routable' )
1293 def test_reload ( self
):
1296 copy_network_unit ( '11-dummy.netdev' )
1298 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'unmanaged' )
1300 copy_network_unit ( '11-dummy.network' )
1302 self
. wait_online ( 'test1:degraded' )
1304 remove_network_unit ( '11-dummy.network' )
1306 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1308 remove_network_unit ( '11-dummy.netdev' )
1310 self
. wait_operstate ( 'test1' , 'degraded' , setup_state
= 'unmanaged' )
1312 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1314 self
. wait_operstate ( 'test1' , 'degraded' )
1316 def test_glob ( self
):
1317 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1320 self
. wait_online ( 'test1:degraded' )
1322 output
= networkctl ( 'list' )
1323 self
. assertRegex ( output
, '1 lo ' )
1324 self
. assertRegex ( output
, 'test1' )
1326 output
= networkctl ( 'list' , 'test1' )
1327 self
. assertNotRegex ( output
, '1 lo ' )
1328 self
. assertRegex ( output
, 'test1' )
1330 output
= networkctl ( 'list' , 'te*' )
1331 self
. assertNotRegex ( output
, '1 lo ' )
1332 self
. assertRegex ( output
, 'test1' )
1334 output
= networkctl_status ( 'te*' )
1335 self
. assertNotRegex ( output
, '1: lo ' )
1336 self
. assertRegex ( output
, 'test1' )
1338 output
= networkctl_status ( 'tes[a-z][0-9]' )
1339 self
. assertNotRegex ( output
, '1: lo ' )
1340 self
. assertRegex ( output
, 'test1' )
1343 copy_network_unit ( '11-dummy-mtu.netdev' , '11-dummy.network' )
1346 self
. wait_online ( 'test1:degraded' )
1348 output
= networkctl_status ( 'test1' )
1349 self
. assertRegex ( output
, 'MTU: 1600' )
1351 def test_type ( self
):
1352 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' )
1354 self
. wait_online ( 'test1:degraded' )
1356 output
= networkctl_status ( 'test1' )
1358 self
. assertRegex ( output
, 'Type: ether' )
1360 output
= networkctl_status ( 'lo' )
1362 self
. assertRegex ( output
, 'Type: loopback' )
1364 def test_unit_file ( self
):
1365 copy_network_unit ( '11-test-unit-file.netdev' , '11-test-unit-file.network' , '11-test-unit-file.link' )
1367 self
. wait_online ( 'test1:degraded' )
1369 output
= networkctl_status ( 'test1' )
1371 self
. assertIn ( 'Link File: /run/systemd/network/11-test-unit-file.link' , output
)
1372 self
. assertIn ( '/run/systemd/network/11-test-unit-file.link.d/dropin.conf' , output
)
1373 self
. assertIn ( 'Network File: /run/systemd/network/11-test-unit-file.network' , output
)
1374 self
. assertIn ( '/run/systemd/network/11-test-unit-file.network.d/dropin.conf' , output
)
1376 output
= read_networkd_log ()
1377 self
. assertIn ( 'test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).' , output
)
1379 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1380 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1381 # Let's reprocess the interface and drop the property.
1382 udevadm_trigger ( '/sys/class/net/lo' )
1383 output
= networkctl_status ( 'lo' )
1385 self
. assertIn ( 'Link File: n/a' , output
)
1386 self
. assertIn ( 'Network File: n/a' , output
)
1388 def test_delete_links ( self
):
1389 copy_network_unit ( '11-dummy.netdev' , '11-dummy.network' ,
1390 '25-veth.netdev' , '26-netdev-link-local-addressing-yes.network' )
1393 self
. wait_online ( 'test1:degraded' , 'veth99:degraded' , 'veth-peer:degraded' )
1395 networkctl ( 'delete' , 'test1' , 'veth99' )
1396 self
. check_link_exists ( 'test1' , expected
= False )
1397 self
. check_link_exists ( 'veth99' , expected
= False )
1398 self
. check_link_exists ( 'veth-peer' , expected
= False )
1400 def test_label ( self
):
1403 class NetworkdMatchTests ( unittest
. TestCase
, Utilities
):
1411 @expectedFailureIfAlternativeNameIsNotAvailable ()
1412 def test_match ( self
):
1413 copy_network_unit ( '12-dummy-mac.netdev' ,
1414 '12-dummy-match-mac-01.network' ,
1415 '12-dummy-match-mac-02.network' ,
1416 '12-dummy-match-renamed.network' ,
1417 '12-dummy-match-altname.network' ,
1418 '12-dummy-altname.link' )
1421 self
. wait_online ( 'dummy98:routable' )
1422 output
= networkctl_status ( 'dummy98' )
1423 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-01.network' , output
)
1424 output
= check_output ( 'ip -4 address show dev dummy98' )
1425 self
. assertIn ( '10.0.0.1/16' , output
)
1427 check_output ( 'ip link set dev dummy98 down' )
1428 check_output ( 'ip link set dev dummy98 address 12:34:56:78:9a:02' )
1430 self
. wait_address ( 'dummy98' , '10.0.0.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1431 self
. wait_online ( 'dummy98:routable' )
1432 output
= networkctl_status ( 'dummy98' )
1433 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-mac-02.network' , output
)
1435 check_output ( 'ip link set dev dummy98 down' )
1436 check_output ( 'ip link set dev dummy98 name dummy98-1' )
1438 self
. wait_address ( 'dummy98-1' , '10.0.1.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1439 self
. wait_online ( 'dummy98-1:routable' )
1440 output
= networkctl_status ( 'dummy98-1' )
1441 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-renamed.network' , output
)
1443 check_output ( 'ip link set dev dummy98-1 down' )
1444 check_output ( 'ip link set dev dummy98-1 name dummy98-2' )
1445 udevadm_trigger ( '/sys/class/net/dummy98-2' )
1447 self
. wait_address ( 'dummy98-2' , '10.0.2.2/16' , ipv
= '-4' , timeout_sec
= 10 )
1448 self
. wait_online ( 'dummy98-2:routable' )
1449 output
= networkctl_status ( 'dummy98-2' )
1450 self
. assertIn ( 'Network File: /run/systemd/network/12-dummy-match-altname.network' , output
)
1452 def test_match_udev_property ( self
):
1453 copy_network_unit ( '12-dummy.netdev' , '13-not-match-udev-property.network' , '14-match-udev-property.network' )
1455 self
. wait_online ( 'dummy98:routable' )
1457 output
= networkctl_status ( 'dummy98' )
1459 self
. assertRegex ( output
, 'Network File: /run/systemd/network/14-match-udev-property' )
1461 class WaitOnlineTests ( unittest
. TestCase
, Utilities
):
1469 def test_wait_online_any ( self
):
1470 copy_network_unit ( '25-bridge.netdev' , '25-bridge.network' , '11-dummy.netdev' , '11-dummy.network' )
1473 self
. wait_online ( 'bridge99' , 'test1:degraded' , bool_any
= True )
1475 self
. wait_operstate ( 'bridge99' , '(off|no-carrier)' , setup_state
= 'configuring' )
1476 self
. wait_operstate ( 'test1' , 'degraded' )
1478 class NetworkdNetDevTests ( unittest
. TestCase
, Utilities
):
1486 def test_dropin_and_name_conflict ( self
):
1487 copy_network_unit ( '10-dropin-test.netdev' , '15-name-conflict-test.netdev' )
1490 self
. wait_online ( 'dropin-test:off' , setup_state
= 'unmanaged' )
1492 output
= check_output ( 'ip link show dropin-test' )
1494 self
. assertRegex ( output
, '00:50:56:c0:00:28' )
1496 @expectedFailureIfModuleIsNotAvailable ( 'bareudp' )
1497 def test_bareudp ( self
):
1498 copy_network_unit ( '25-bareudp.netdev' , '26-netdev-link-local-addressing-yes.network' )
1501 self
. wait_online ( 'bareudp99:degraded' )
1503 output
= check_output ( 'ip -d link show bareudp99' )
1505 self
. assertRegex ( output
, 'dstport 1000 ' )
1506 self
. assertRegex ( output
, 'ethertype ip ' )
1508 @expectedFailureIfModuleIsNotAvailable ( 'batman-adv' )
1509 def test_batadv ( self
):
1510 copy_network_unit ( '25-batadv.netdev' , '26-netdev-link-local-addressing-yes.network' )
1513 self
. wait_online ( 'batadv99:degraded' )
1515 output
= check_output ( 'ip -d link show batadv99' )
1517 self
. assertRegex ( output
, 'batadv' )
1519 def test_bridge ( self
):
1520 copy_network_unit ( '25-bridge.netdev' , '25-bridge-configure-without-carrier.network' )
1523 self
. wait_online ( 'bridge99:no-carrier' )
1525 tick
= os
. sysconf ( 'SC_CLK_TCK' )
1526 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'hello_time' )) / tick
))
1527 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'max_age' )) / tick
))
1528 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'forward_delay' )) / tick
))
1529 self
. assertEqual ( 9 , round ( float ( read_link_attr ( 'bridge99' , 'bridge' , 'ageing_time' )) / tick
))
1530 self
. assertEqual ( 9 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'priority' )))
1531 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_querier' )))
1532 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_snooping' )))
1533 self
. assertEqual ( 1 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'stp_state' )))
1534 self
. assertEqual ( 3 , int ( read_link_attr ( 'bridge99' , 'bridge' , 'multicast_igmp_version' )))
1536 output
= networkctl_status ( 'bridge99' )
1538 self
. assertRegex ( output
, 'Priority: 9' )
1539 self
. assertRegex ( output
, 'STP: yes' )
1540 self
. assertRegex ( output
, 'Multicast IGMP Version: 3' )
1542 output
= check_output ( 'ip -d link show bridge99' )
1544 self
. assertIn ( 'vlan_filtering 1 ' , output
)
1545 self
. assertIn ( 'vlan_protocol 802.1ad ' , output
)
1546 self
. assertIn ( 'vlan_default_pvid 9 ' , output
)
1548 def test_bond ( self
):
1549 copy_network_unit ( '25-bond.netdev' , '25-bond-balanced-tlb.netdev' , '25-bond-property.netdev' )
1552 self
. wait_online ( 'bond99:off' , 'bond98:off' , 'bond97:off' , setup_state
= 'unmanaged' )
1554 self
. check_link_attr ( 'bond99' , 'bonding' , 'mode' , '802.3ad 4' )
1555 self
. check_link_attr ( 'bond99' , 'bonding' , 'xmit_hash_policy' , 'layer3+4 1' )
1556 self
. check_link_attr ( 'bond99' , 'bonding' , 'miimon' , '1000' )
1557 self
. check_link_attr ( 'bond99' , 'bonding' , 'lacp_rate' , 'fast 1' )
1558 self
. check_link_attr ( 'bond99' , 'bonding' , 'updelay' , '2000' )
1559 self
. check_link_attr ( 'bond99' , 'bonding' , 'downdelay' , '2000' )
1560 self
. check_link_attr ( 'bond99' , 'bonding' , 'resend_igmp' , '4' )
1561 self
. check_link_attr ( 'bond99' , 'bonding' , 'min_links' , '1' )
1562 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_sys_prio' , '1218' )
1563 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_user_port_key' , '811' )
1564 self
. check_link_attr ( 'bond99' , 'bonding' , 'ad_actor_system' , '00:11:22:33:44:55' )
1566 self
. check_link_attr ( 'bond98' , 'bonding' , 'mode' , 'balance-tlb 5' )
1567 self
. check_link_attr ( 'bond98' , 'bonding' , 'tlb_dynamic_lb' , '1' )
1569 output
= networkctl_status ( 'bond99' )
1571 self
. assertIn ( 'Mode: 802.3ad' , output
)
1572 self
. assertIn ( 'Miimon: 1s' , output
)
1573 self
. assertIn ( 'Updelay: 2s' , output
)
1574 self
. assertIn ( 'Downdelay: 2s' , output
)
1576 output
= networkctl_status ( 'bond98' )
1578 self
. assertIn ( 'Mode: balance-tlb' , output
)
1580 output
= networkctl_status ( 'bond97' )
1583 self
. check_link_attr ( 'bond97' , 'bonding' , 'arp_missed_max' , '10' )
1584 self
. check_link_attr ( 'bond97' , 'bonding' , 'peer_notif_delay' , '300000' )
1586 def test_vlan ( self
):
1587 copy_network_unit ( '21-vlan.netdev' , '11-dummy.netdev' ,
1588 '21-vlan.network' , '21-vlan-test1.network' )
1591 self
. wait_online ( 'test1:degraded' , 'vlan99:routable' )
1593 output
= check_output ( 'ip -d link show test1' )
1595 self
. assertRegex ( output
, ' mtu 2000 ' )
1597 output
= check_output ( 'ip -d link show vlan99' )
1599 self
. assertIn ( ' mtu 2000 ' , output
)
1600 self
. assertIn ( 'REORDER_HDR' , output
)
1601 self
. assertIn ( 'LOOSE_BINDING' , output
)
1602 self
. assertIn ( 'GVRP' , output
)
1603 self
. assertIn ( 'MVRP' , output
)
1604 self
. assertIn ( ' id 99 ' , output
)
1605 self
. assertIn ( 'ingress-qos-map { 4:100 7:13 }' , output
)
1606 self
. assertIn ( 'egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }' , output
)
1608 output
= check_output ( 'ip -4 address show dev test1' )
1610 self
. assertRegex ( output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1' )
1611 self
. assertRegex ( output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1' )
1613 output
= check_output ( 'ip -4 address show dev vlan99' )
1615 self
. assertRegex ( output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99' )
1617 def test_vlan_on_bond ( self
):
1618 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1619 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1621 copy_network_unit ( '21-bond-802.3ad.netdev' , '21-bond-802.3ad.network' ,
1622 '21-vlan-on-bond.netdev' , '21-vlan-on-bond.network' )
1624 self
. wait_online ( 'bond99:off' )
1625 self
. wait_operstate ( 'vlan99' , operstate
= 'off' , setup_state
= 'configuring' , setup_timeout
= 10 )
1627 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1628 # that the issue is fixed by the commit, let's allow to match both string.
1629 log_re
= re
. compile ( 'vlan99: Could not bring up interface(, ignoring|): Network is down$' , re
. MULTILINE
)
1633 if log_re
. search ( read_networkd_log ()):
1638 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '21-dummy-bond-slave.network' )
1640 self
. wait_online ( 'test1:enslaved' , 'dummy98:enslaved' , 'bond99:carrier' , 'vlan99:routable' )
1642 def test_macvtap ( self
):
1644 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1650 print ( f
'### test_macvtap(mode= {mode} )' )
1651 with self
. subTest ( mode
= mode
):
1652 copy_network_unit ( '21-macvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1653 '11-dummy.netdev' , '25-macvtap.network' )
1654 with
open ( os
. path
. join ( network_unit_dir
, '21-macvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1655 f
. write ( '[MACVTAP] \n Mode=' + mode
)
1658 self
. wait_online ( 'macvtap99:degraded' ,
1659 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1661 output
= check_output ( 'ip -d link show macvtap99' )
1663 self
. assertRegex ( output
, 'macvtap mode ' + mode
+ ' ' )
1665 def test_macvlan ( self
):
1667 for mode
in [ 'private' , 'vepa' , 'bridge' , 'passthru' ]:
1673 print ( f
'### test_macvlan(mode= {mode} )' )
1674 with self
. subTest ( mode
= mode
):
1675 copy_network_unit ( '21-macvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1676 '11-dummy.netdev' , '25-macvlan.network' )
1677 with
open ( os
. path
. join ( network_unit_dir
, '21-macvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1678 f
. write ( '[MACVLAN] \n Mode=' + mode
)
1681 self
. wait_online ( 'macvlan99:degraded' ,
1682 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1684 output
= check_output ( 'ip -d link show test1' )
1686 self
. assertIn ( ' mtu 2000 ' , output
)
1688 output
= check_output ( 'ip -d link show macvlan99' )
1690 self
. assertIn ( ' mtu 2000 ' , output
)
1691 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
1693 remove_link ( 'test1' )
1696 check_output ( "ip link add test1 type dummy" )
1697 self
. wait_online ( 'macvlan99:degraded' ,
1698 'test1:carrier' if mode
== 'passthru' else 'test1:degraded' )
1700 output
= check_output ( 'ip -d link show test1' )
1702 self
. assertIn ( ' mtu 2000 ' , output
)
1704 output
= check_output ( 'ip -d link show macvlan99' )
1706 self
. assertIn ( ' mtu 2000 ' , output
)
1707 self
. assertIn ( f
' macvlan mode {mode} ' , output
)
1708 self
. assertIn ( ' bcqueuelen 1234 ' , output
)
1709 self
. assertIn ( ' bclim 2147483647 ' , output
)
1711 @expectedFailureIfModuleIsNotAvailable ( 'ipvlan' )
1712 def test_ipvlan ( self
):
1714 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
1720 print ( f
'### test_ipvlan(mode= {mode} , flag= {flag} )' )
1721 with self
. subTest ( mode
= mode
, flag
= flag
):
1722 copy_network_unit ( '25-ipvlan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1723 '11-dummy.netdev' , '25-ipvlan.network' )
1724 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvlan.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1725 f
. write ( '[IPVLAN] \n Mode=' + mode
+ ' \n Flags=' + flag
)
1728 self
. wait_online ( 'ipvlan99:degraded' , 'test1:degraded' )
1730 output
= check_output ( 'ip -d link show ipvlan99' )
1732 self
. assertRegex ( output
, 'ipvlan *mode ' + mode
. lower () + ' ' + flag
)
1734 @expectedFailureIfModuleIsNotAvailable ( 'ipvtap' )
1735 def test_ipvtap ( self
):
1737 for mode
, flag
in [[ 'L2' , 'private' ], [ 'L3' , 'vepa' ], [ 'L3S' , 'bridge' ]]:
1743 print ( f
'### test_ipvtap(mode= {mode} , flag= {flag} )' )
1744 with self
. subTest ( mode
= mode
, flag
= flag
):
1745 copy_network_unit ( '25-ipvtap.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1746 '11-dummy.netdev' , '25-ipvtap.network' )
1747 with
open ( os
. path
. join ( network_unit_dir
, '25-ipvtap.netdev' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
1748 f
. write ( '[IPVTAP] \n Mode=' + mode
+ ' \n Flags=' + flag
)
1751 self
. wait_online ( 'ipvtap99:degraded' , 'test1:degraded' )
1753 output
= check_output ( 'ip -d link show ipvtap99' )
1755 self
. assertRegex ( output
, 'ipvtap *mode ' + mode
. lower () + ' ' + flag
)
1757 def test_veth ( self
):
1758 copy_network_unit ( '25-veth.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1759 '25-veth-mtu.netdev' )
1762 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' , 'veth-mtu:degraded' , 'veth-mtu-peer:degraded' )
1764 output
= check_output ( 'ip -d link show veth99' )
1766 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bc' )
1767 output
= check_output ( 'ip -d link show veth-peer' )
1769 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bd' )
1771 output
= check_output ( 'ip -d link show veth-mtu' )
1773 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:be' )
1774 self
. assertRegex ( output
, 'mtu 1800' )
1775 output
= check_output ( 'ip -d link show veth-mtu-peer' )
1777 self
. assertRegex ( output
, 'link/ether 12:34:56:78:9a:bf' )
1778 self
. assertRegex ( output
, 'mtu 1800' )
1780 def test_tuntap ( self
):
1781 copy_network_unit ( '25-tun.netdev' , '25-tap.netdev' , '26-netdev-link-local-addressing-yes.network' )
1784 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' )
1786 pid
= networkd_pid ()
1787 name
= psutil
. Process ( pid
). name ()[: 15 ]
1789 output
= check_output ( 'ip -d tuntap show' )
1791 self
. assertRegex ( output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes: {name} \( {pid} \)systemd\(1\)$' )
1792 self
. assertRegex ( output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes: {name} \( {pid} \)systemd\(1\)$' )
1794 output
= check_output ( 'ip -d link show testtun99' )
1796 # Old ip command does not support IFF_ flags
1797 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
1798 self
. assertIn ( 'UP,LOWER_UP' , output
)
1800 output
= check_output ( 'ip -d link show testtap99' )
1802 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
1803 self
. assertIn ( 'UP,LOWER_UP' , output
)
1805 remove_network_unit ( '26-netdev-link-local-addressing-yes.network' )
1808 self
. wait_online ( 'testtun99:degraded' , 'testtap99:degraded' , setup_state
= 'unmanaged' )
1810 pid
= networkd_pid ()
1811 name
= psutil
. Process ( pid
). name ()[: 15 ]
1813 output
= check_output ( 'ip -d tuntap show' )
1815 self
. assertRegex ( output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes: {name} \( {pid} \)systemd\(1\)$' )
1816 self
. assertRegex ( output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes: {name} \( {pid} \)systemd\(1\)$' )
1818 output
= check_output ( 'ip -d link show testtun99' )
1820 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
1821 self
. assertIn ( 'UP,LOWER_UP' , output
)
1823 output
= check_output ( 'ip -d link show testtap99' )
1825 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
1826 self
. assertIn ( 'UP,LOWER_UP' , output
)
1828 clear_network_units ()
1830 self
. wait_online ( 'testtun99:off' , 'testtap99:off' , setup_state
= 'unmanaged' )
1832 output
= check_output ( 'ip -d tuntap show' )
1834 self
. assertRegex ( output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$' )
1835 self
. assertRegex ( output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$' )
1840 output
= check_output ( 'ip -d link show testtun99' )
1842 self
. assertRegex ( output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ' )
1843 if 'NO-CARRIER' in output
:
1851 output
= check_output ( 'ip -d link show testtap99' )
1853 self
. assertRegex ( output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ' )
1854 if 'NO-CARRIER' in output
:
1859 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
1861 copy_network_unit ( '25-vrf.netdev' , '26-netdev-link-local-addressing-yes.network' )
1864 self
. wait_online ( 'vrf99:carrier' )
1866 @expectedFailureIfModuleIsNotAvailable ( 'vcan' )
1867 def test_vcan ( self
):
1868 copy_network_unit ( '25-vcan.netdev' , '26-netdev-link-local-addressing-yes.network' ,
1869 '25-vcan98.netdev' , '25-vcan98.network' )
1872 self
. wait_online ( 'vcan99:carrier' , 'vcan98:carrier' )
1873 # For can devices, 'carrier' is the default required operational state.
1874 self
. wait_online ( 'vcan99' , 'vcan98' )
1876 # https://github.com/systemd/systemd/issues/30140
1877 output
= check_output ( 'ip -d link show vcan99' )
1879 self
. assertIn ( 'mtu 16 ' , output
)
1881 output
= check_output ( 'ip -d link show vcan98' )
1883 self
. assertIn ( 'mtu 16 ' , output
)
1885 @expectedFailureIfModuleIsNotAvailable ( 'vxcan' )
1886 def test_vxcan ( self
):
1887 copy_network_unit ( '25-vxcan.netdev' , '26-netdev-link-local-addressing-yes.network' )
1890 self
. wait_online ( 'vxcan99:carrier' , 'vxcan-peer:carrier' )
1891 # For can devices, 'carrier' is the default required operational state.
1892 self
. wait_online ( 'vxcan99' , 'vxcan-peer' )
1894 @expectedFailureIfModuleIsNotAvailable ( 'wireguard' )
1895 def test_wireguard ( self
):
1896 copy_credential ( '25-wireguard-endpoint-peer0-cred.txt' , 'network.wireguard.peer0.endpoint' )
1897 copy_credential ( '25-wireguard-preshared-key-peer2-cred.txt' , 'network.wireguard.peer2.psk' )
1898 copy_credential ( '25-wireguard-no-peer-private-key-cred.txt' , 'network.wireguard.private.25-wireguard-no-peer' )
1900 copy_network_unit ( '25-wireguard.netdev' , '25-wireguard.network' ,
1901 '25-wireguard-23-peers.netdev' , '25-wireguard-23-peers.network' ,
1902 '25-wireguard-preshared-key.txt' , '25-wireguard-private-key.txt' ,
1903 '25-wireguard-no-peer.netdev' , '25-wireguard-no-peer.network' )
1905 self
. wait_online ( 'wg99:routable' , 'wg98:routable' , 'wg97:carrier' )
1907 output
= check_output ( 'ip -4 address show dev wg99' )
1909 self
. assertIn ( 'inet 192.168.124.1/24 scope global wg99' , output
)
1911 output
= check_output ( 'ip -4 address show dev wg99' )
1913 self
. assertIn ( 'inet 169.254.11.1/24 scope link wg99' , output
)
1915 output
= check_output ( 'ip -6 address show dev wg99' )
1917 self
. assertIn ( 'inet6 fe80::1/64 scope link' , output
)
1919 output
= check_output ( 'ip -4 address show dev wg98' )
1921 self
. assertIn ( 'inet 192.168.123.123/24 scope global wg98' , output
)
1923 output
= check_output ( 'ip -6 address show dev wg98' )
1925 self
. assertIn ( 'inet6 fd8d:4d6d:3ccb:500::1/64 scope global' , output
)
1927 output
= check_output ( 'ip -4 route show dev wg99 table 1234' )
1929 self
. assertIn ( '192.168.26.0/24 proto static scope link metric 123' , output
)
1931 output
= check_output ( 'ip -6 route show dev wg99 table 1234' )
1933 self
. assertIn ( 'fd31:bf08:57cb::/48 proto static metric 123 pref medium' , output
)
1935 output
= check_output ( 'ip -6 route show dev wg98 table 1234' )
1937 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium' , output
)
1938 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium' , output
)
1939 self
. assertIn ( 'fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium' , output
)
1940 self
. assertIn ( 'fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium' , output
)
1941 self
. assertIn ( 'fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium' , output
)
1942 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium' , output
)
1943 self
. assertIn ( 'fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium' , output
)
1944 self
. assertIn ( 'fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium' , output
)
1945 self
. assertIn ( 'fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium' , output
)
1946 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium' , output
)
1947 self
. assertIn ( 'fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium' , output
)
1948 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium' , output
)
1949 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium' , output
)
1950 self
. assertIn ( 'fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium' , output
)
1951 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium' , output
)
1952 self
. assertIn ( 'fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium' , output
)
1953 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium' , output
)
1954 self
. assertIn ( 'fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium' , output
)
1955 self
. assertIn ( 'fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium' , output
)
1956 self
. assertIn ( 'fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium' , output
)
1957 self
. assertIn ( 'fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium' , output
)
1958 self
. assertIn ( 'fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium' , output
)
1959 self
. assertIn ( 'fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium' , output
)
1960 self
. assertIn ( 'fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium' , output
)
1961 self
. assertIn ( 'fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium' , output
)
1962 self
. assertIn ( 'fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium' , output
)
1963 self
. assertIn ( 'fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium' , output
)
1964 self
. assertIn ( 'fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium' , output
)
1965 self
. assertIn ( 'fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium' , output
)
1966 self
. assertIn ( 'fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium' , output
)
1967 self
. assertIn ( 'fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium' , output
)
1968 self
. assertIn ( 'fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium' , output
)
1969 self
. assertIn ( 'fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium' , output
)
1970 self
. assertIn ( 'fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium' , output
)
1971 self
. assertIn ( 'fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium' , output
)
1972 self
. assertIn ( 'fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium' , output
)
1973 self
. assertIn ( 'fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium' , output
)
1974 self
. assertIn ( 'fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium' , output
)
1975 self
. assertIn ( 'fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium' , output
)
1976 self
. assertIn ( 'fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium' , output
)
1977 self
. assertIn ( 'fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium' , output
)
1978 self
. assertIn ( 'fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium' , output
)
1979 self
. assertIn ( 'fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium' , output
)
1980 self
. assertIn ( 'fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium' , output
)
1981 self
. assertIn ( 'fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium' , output
)
1982 self
. assertIn ( 'fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium' , output
)
1984 if shutil
. which ( 'wg' ):
1987 output
= check_output ( 'wg show wg99 listen-port' )
1988 self
. assertEqual ( output
, '51820' )
1989 output
= check_output ( 'wg show wg99 fwmark' )
1990 self
. assertEqual ( output
, '0x4d2' )
1991 output
= check_output ( 'wg show wg99 private-key' )
1992 self
. assertEqual ( output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=' )
1993 output
= check_output ( 'wg show wg99 allowed-ips' )
1994 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 192.168.124.3/32' , output
)
1995 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t 192.168.124.2/32' , output
)
1996 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t fdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128' , output
)
1997 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.26.0/24 fd31:bf08:57cb::/48' , output
)
1998 output
= check_output ( 'wg show wg99 persistent-keepalive' )
1999 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t off' , output
)
2000 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t off' , output
)
2001 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t off' , output
)
2002 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 20' , output
)
2003 output
= check_output ( 'wg show wg99 endpoints' )
2004 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t (none)' , output
)
2005 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t (none)' , output
)
2006 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t (none)' , output
)
2007 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t 192.168.27.3:51820' , output
)
2008 output
= check_output ( 'wg show wg99 preshared-keys' )
2009 self
. assertIn ( '9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c= \t 6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=' , output
)
2010 self
. assertIn ( 'TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10= \t it7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=' , output
)
2011 self
. assertIn ( 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= \t cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=' , output
)
2012 self
. assertIn ( 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= \t IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=' , output
)
2014 output
= check_output ( 'wg show wg98 private-key' )
2015 self
. assertEqual ( output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=' )
2017 output
= check_output ( 'wg show wg97 listen-port' )
2018 self
. assertEqual ( output
, '51821' )
2019 output
= check_output ( 'wg show wg97 fwmark' )
2020 self
. assertEqual ( output
, '0x4d3' )
2022 def test_geneve ( self
):
2023 copy_network_unit ( '25-geneve.netdev' , '26-netdev-link-local-addressing-yes.network' )
2026 self
. wait_online ( 'geneve99:degraded' )
2028 output
= check_output ( 'ip -d link show geneve99' )
2030 self
. assertRegex ( output
, '192.168.22.1' )
2031 self
. assertRegex ( output
, '6082' )
2032 self
. assertRegex ( output
, 'udpcsum' )
2033 self
. assertRegex ( output
, 'udp6zerocsumrx' )
2035 def test_ipip_tunnel ( self
):
2036 copy_network_unit ( '12-dummy.netdev' , '25-ipip.network' ,
2037 '25-ipip-tunnel.netdev' , '25-tunnel.network' ,
2038 '25-ipip-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2039 '25-ipip-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2040 '25-ipip-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2042 self
. wait_online ( 'ipiptun99:routable' , 'ipiptun98:routable' , 'ipiptun97:routable' , 'ipiptun96:routable' , 'dummy98:degraded' )
2044 output
= check_output ( 'ip -d link show ipiptun99' )
2046 self
. assertRegex ( output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98' )
2047 output
= check_output ( 'ip -d link show ipiptun98' )
2049 self
. assertRegex ( output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98' )
2050 output
= check_output ( 'ip -d link show ipiptun97' )
2052 self
. assertRegex ( output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98' )
2053 output
= check_output ( 'ip -d link show ipiptun96' )
2055 self
. assertRegex ( output
, 'ipip (ipip )?remote any local any dev dummy98' )
2057 def test_gre_tunnel ( self
):
2058 copy_network_unit ( '12-dummy.netdev' , '25-gretun.network' ,
2059 '25-gre-tunnel.netdev' , '25-tunnel.network' ,
2060 '25-gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2061 '25-gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2062 '25-gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2064 self
. wait_online ( 'gretun99:routable' , 'gretun98:routable' , 'gretun97:routable' , 'gretun96:routable' , 'dummy98:degraded' )
2066 output
= check_output ( 'ip -d link show gretun99' )
2068 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2069 self
. assertRegex ( output
, 'ikey 1.2.3.103' )
2070 self
. assertRegex ( output
, 'okey 1.2.4.103' )
2071 self
. assertRegex ( output
, 'iseq' )
2072 self
. assertRegex ( output
, 'oseq' )
2073 output
= check_output ( 'ip -d link show gretun98' )
2075 self
. assertRegex ( output
, 'gre remote 10.65.223.239 local any dev dummy98' )
2076 self
. assertRegex ( output
, 'ikey 0.0.0.104' )
2077 self
. assertRegex ( output
, 'okey 0.0.0.104' )
2078 self
. assertNotRegex ( output
, 'iseq' )
2079 self
. assertNotRegex ( output
, 'oseq' )
2080 output
= check_output ( 'ip -d link show gretun97' )
2082 self
. assertRegex ( output
, 'gre remote any local 10.65.223.238 dev dummy98' )
2083 self
. assertRegex ( output
, 'ikey 0.0.0.105' )
2084 self
. assertRegex ( output
, 'okey 0.0.0.105' )
2085 self
. assertNotRegex ( output
, 'iseq' )
2086 self
. assertNotRegex ( output
, 'oseq' )
2087 output
= check_output ( 'ip -d link show gretun96' )
2089 self
. assertRegex ( output
, 'gre remote any local any dev dummy98' )
2090 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2091 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2092 self
. assertNotRegex ( output
, 'iseq' )
2093 self
. assertNotRegex ( output
, 'oseq' )
2095 def test_ip6gre_tunnel ( self
):
2096 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretun.network' ,
2097 '25-ip6gre-tunnel.netdev' , '25-tunnel.network' ,
2098 '25-ip6gre-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2099 '25-ip6gre-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2100 '25-ip6gre-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2103 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2105 self
. wait_links ( 'dummy98' , 'ip6gretun99' , 'ip6gretun98' , 'ip6gretun97' , 'ip6gretun96' )
2107 output
= check_output ( 'ip -d link show ip6gretun99' )
2109 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2110 output
= check_output ( 'ip -d link show ip6gretun98' )
2112 self
. assertRegex ( output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2113 output
= check_output ( 'ip -d link show ip6gretun97' )
2115 self
. assertRegex ( output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98' )
2116 output
= check_output ( 'ip -d link show ip6gretun96' )
2118 self
. assertRegex ( output
, 'ip6gre remote any local any dev dummy98' )
2120 def test_gretap_tunnel ( self
):
2121 copy_network_unit ( '12-dummy.netdev' , '25-gretap.network' ,
2122 '25-gretap-tunnel.netdev' , '25-tunnel.network' ,
2123 '25-gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2125 self
. wait_online ( 'gretap99:routable' , 'gretap98:routable' , 'dummy98:degraded' )
2127 output
= check_output ( 'ip -d link show gretap99' )
2129 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2130 self
. assertRegex ( output
, 'ikey 0.0.0.106' )
2131 self
. assertRegex ( output
, 'okey 0.0.0.106' )
2132 self
. assertRegex ( output
, 'iseq' )
2133 self
. assertRegex ( output
, 'oseq' )
2134 self
. assertIn ( 'nopmtudisc' , output
)
2135 self
. assertIn ( 'ignore-df' , output
)
2136 output
= check_output ( 'ip -d link show gretap98' )
2138 self
. assertRegex ( output
, 'gretap remote 10.65.223.239 local any dev dummy98' )
2139 self
. assertRegex ( output
, 'ikey 0.0.0.107' )
2140 self
. assertRegex ( output
, 'okey 0.0.0.107' )
2141 self
. assertRegex ( output
, 'iseq' )
2142 self
. assertRegex ( output
, 'oseq' )
2144 def test_ip6gretap_tunnel ( self
):
2145 copy_network_unit ( '12-dummy.netdev' , '25-ip6gretap.network' ,
2146 '25-ip6gretap-tunnel.netdev' , '25-tunnel.network' ,
2147 '25-ip6gretap-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2149 self
. wait_online ( 'ip6gretap99:routable' , 'ip6gretap98:routable' , 'dummy98:degraded' )
2151 output
= check_output ( 'ip -d link show ip6gretap99' )
2153 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2154 output
= check_output ( 'ip -d link show ip6gretap98' )
2156 self
. assertRegex ( output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98' )
2158 def test_vti_tunnel ( self
):
2159 copy_network_unit ( '12-dummy.netdev' , '25-vti.network' ,
2160 '25-vti-tunnel.netdev' , '25-tunnel.network' ,
2161 '25-vti-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2162 '25-vti-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2163 '25-vti-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2165 self
. wait_online ( 'vtitun99:routable' , 'vtitun98:routable' , 'vtitun97:routable' , 'vtitun96:routable' , 'dummy98:degraded' )
2167 output
= check_output ( 'ip -d link show vtitun99' )
2169 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98' )
2170 output
= check_output ( 'ip -d link show vtitun98' )
2172 self
. assertRegex ( output
, 'vti remote 10.65.223.239 local any dev dummy98' )
2173 output
= check_output ( 'ip -d link show vtitun97' )
2175 self
. assertRegex ( output
, 'vti remote any local 10.65.223.238 dev dummy98' )
2176 output
= check_output ( 'ip -d link show vtitun96' )
2178 self
. assertRegex ( output
, 'vti remote any local any dev dummy98' )
2180 def test_vti6_tunnel ( self
):
2181 copy_network_unit ( '12-dummy.netdev' , '25-vti6.network' ,
2182 '25-vti6-tunnel.netdev' , '25-tunnel.network' ,
2183 '25-vti6-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2184 '25-vti6-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' )
2186 self
. wait_online ( 'vti6tun99:routable' , 'vti6tun98:routable' , 'vti6tun97:routable' , 'dummy98:degraded' )
2188 output
= check_output ( 'ip -d link show vti6tun99' )
2190 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' )
2191 output
= check_output ( 'ip -d link show vti6tun98' )
2193 self
. assertRegex ( output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98' )
2194 output
= check_output ( 'ip -d link show vti6tun97' )
2196 self
. assertRegex ( output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98' )
2198 def test_ip6tnl_tunnel ( self
):
2199 copy_network_unit ( '12-dummy.netdev' , '25-ip6tnl.network' ,
2200 '25-ip6tnl-tunnel.netdev' , '25-tunnel.network' ,
2201 '25-ip6tnl-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2202 '25-ip6tnl-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2203 '25-veth.netdev' , '25-ip6tnl-slaac.network' , '25-ipv6-prefix.network' ,
2204 '25-ip6tnl-tunnel-local-slaac.netdev' , '25-ip6tnl-tunnel-local-slaac.network' ,
2205 '25-ip6tnl-tunnel-external.netdev' , '26-netdev-link-local-addressing-yes.network' )
2207 self
. wait_online ( 'ip6tnl99:routable' , 'ip6tnl98:routable' , 'ip6tnl97:routable' ,
2208 'ip6tnl-slaac:degraded' , 'ip6tnl-external:degraded' ,
2209 'dummy98:degraded' , 'veth99:routable' , 'veth-peer:degraded' )
2211 output
= check_output ( 'ip -d link show ip6tnl99' )
2213 self
. assertIn ( 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98' , output
)
2214 output
= check_output ( 'ip -d link show ip6tnl98' )
2216 self
. assertRegex ( output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98' )
2217 output
= check_output ( 'ip -d link show ip6tnl97' )
2219 self
. assertRegex ( output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98' )
2220 output
= check_output ( 'ip -d link show ip6tnl-external' )
2222 self
. assertIn ( 'ip6tnl-external@NONE:' , output
)
2223 self
. assertIn ( 'ip6tnl external ' , output
)
2224 output
= check_output ( 'ip -d link show ip6tnl-slaac' )
2226 self
. assertIn ( 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
2228 output
= check_output ( 'ip -6 address show veth99' )
2230 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
2232 output
= check_output ( 'ip -4 route show default' )
2234 self
. assertIn ( 'default dev ip6tnl-slaac proto static' , output
)
2236 def test_sit_tunnel ( self
):
2237 copy_network_unit ( '12-dummy.netdev' , '25-sit.network' ,
2238 '25-sit-tunnel.netdev' , '25-tunnel.network' ,
2239 '25-sit-tunnel-local-any.netdev' , '25-tunnel-local-any.network' ,
2240 '25-sit-tunnel-remote-any.netdev' , '25-tunnel-remote-any.network' ,
2241 '25-sit-tunnel-any-any.netdev' , '25-tunnel-any-any.network' )
2243 self
. wait_online ( 'sittun99:routable' , 'sittun98:routable' , 'sittun97:routable' , 'sittun96:routable' , 'dummy98:degraded' )
2245 output
= check_output ( 'ip -d link show sittun99' )
2247 self
. assertRegex ( output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98" )
2248 output
= check_output ( 'ip -d link show sittun98' )
2250 self
. assertRegex ( output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98" )
2251 output
= check_output ( 'ip -d link show sittun97' )
2253 self
. assertRegex ( output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98" )
2254 output
= check_output ( 'ip -d link show sittun96' )
2256 self
. assertRegex ( output
, "sit (ip6ip )?remote any local any dev dummy98" )
2258 def test_isatap_tunnel ( self
):
2259 copy_network_unit ( '12-dummy.netdev' , '25-isatap.network' ,
2260 '25-isatap-tunnel.netdev' , '25-tunnel.network' )
2262 self
. wait_online ( 'isataptun99:routable' , 'dummy98:degraded' )
2264 output
= check_output ( 'ip -d link show isataptun99' )
2266 self
. assertRegex ( output
, "isatap " )
2268 def test_6rd_tunnel ( self
):
2269 copy_network_unit ( '12-dummy.netdev' , '25-6rd.network' ,
2270 '25-6rd-tunnel.netdev' , '25-tunnel.network' )
2272 self
. wait_online ( 'sittun99:routable' , 'dummy98:degraded' )
2274 output
= check_output ( 'ip -d link show sittun99' )
2276 self
. assertRegex ( output
, '6rd-prefix 2602::/24' )
2278 @expectedFailureIfERSPANv0IsNotSupported ()
2279 def test_erspan_tunnel_v0 ( self
):
2280 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2281 '25-erspan0-tunnel.netdev' , '25-tunnel.network' ,
2282 '25-erspan0-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2284 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2286 output
= check_output ( 'ip -d link show erspan99' )
2288 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2289 self
. assertIn ( 'erspan_ver 0' , output
)
2290 self
. assertNotIn ( 'erspan_index 123' , output
)
2291 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2292 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2293 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2294 self
. assertIn ( 'iseq' , output
)
2295 self
. assertIn ( 'nopmtudisc' , output
)
2296 self
. assertIn ( 'ignore-df' , output
)
2297 output
= check_output ( 'ip -d link show erspan98' )
2299 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2300 self
. assertIn ( 'erspan_ver 0' , output
)
2301 self
. assertNotIn ( 'erspan_index 124' , output
)
2302 self
. assertNotIn ( 'erspan_dir egress' , output
)
2303 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
2304 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2305 self
. assertIn ( 'iseq' , output
)
2307 def test_erspan_tunnel_v1 ( self
):
2308 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2309 '25-erspan1-tunnel.netdev' , '25-tunnel.network' ,
2310 '25-erspan1-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2312 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2314 output
= check_output ( 'ip -d link show erspan99' )
2316 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2317 self
. assertIn ( 'erspan_ver 1' , output
)
2318 self
. assertIn ( 'erspan_index 123' , output
)
2319 self
. assertNotIn ( 'erspan_dir ingress' , output
)
2320 self
. assertNotIn ( 'erspan_hwid 1f' , output
)
2321 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2322 self
. assertIn ( 'okey 0.0.0.101' , output
)
2323 self
. assertIn ( 'iseq' , output
)
2324 self
. assertIn ( 'oseq' , output
)
2325 output
= check_output ( 'ip -d link show erspan98' )
2327 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2328 self
. assertIn ( 'erspan_ver 1' , output
)
2329 self
. assertIn ( 'erspan_index 124' , output
)
2330 self
. assertNotIn ( 'erspan_dir egress' , output
)
2331 self
. assertNotIn ( 'erspan_hwid 2f' , output
)
2332 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2333 self
. assertIn ( 'okey 0.0.0.102' , output
)
2334 self
. assertIn ( 'iseq' , output
)
2335 self
. assertIn ( 'oseq' , output
)
2337 @expectedFailureIfERSPANv2IsNotSupported ()
2338 def test_erspan_tunnel_v2 ( self
):
2339 copy_network_unit ( '12-dummy.netdev' , '25-erspan.network' ,
2340 '25-erspan2-tunnel.netdev' , '25-tunnel.network' ,
2341 '25-erspan2-tunnel-local-any.netdev' , '25-tunnel-local-any.network' )
2343 self
. wait_online ( 'erspan99:routable' , 'erspan98:routable' , 'dummy98:degraded' )
2345 output
= check_output ( 'ip -d link show erspan99' )
2347 self
. assertIn ( 'erspan remote 172.16.1.100 local 172.16.1.200' , output
)
2348 self
. assertIn ( 'erspan_ver 2' , output
)
2349 self
. assertNotIn ( 'erspan_index 123' , output
)
2350 self
. assertIn ( 'erspan_dir ingress' , output
)
2351 self
. assertIn ( 'erspan_hwid 0x1f' , output
)
2352 self
. assertIn ( 'ikey 0.0.0.101' , output
)
2353 self
. assertIn ( 'okey 0.0.0.101' , output
)
2354 self
. assertIn ( 'iseq' , output
)
2355 self
. assertIn ( 'oseq' , output
)
2356 output
= check_output ( 'ip -d link show erspan98' )
2358 self
. assertIn ( 'erspan remote 172.16.1.100 local any' , output
)
2359 self
. assertIn ( 'erspan_ver 2' , output
)
2360 self
. assertNotIn ( 'erspan_index 124' , output
)
2361 self
. assertIn ( 'erspan_dir egress' , output
)
2362 self
. assertIn ( 'erspan_hwid 0x2f' , output
)
2363 self
. assertIn ( 'ikey 0.0.0.102' , output
)
2364 self
. assertIn ( 'okey 0.0.0.102' , output
)
2365 self
. assertIn ( 'iseq' , output
)
2366 self
. assertIn ( 'oseq' , output
)
2368 def test_tunnel_independent ( self
):
2369 copy_network_unit ( '25-ipip-tunnel-independent.netdev' , '26-netdev-link-local-addressing-yes.network' )
2372 self
. wait_online ( 'ipiptun99:carrier' )
2374 def test_tunnel_independent_loopback ( self
):
2375 copy_network_unit ( '25-ipip-tunnel-independent-loopback.netdev' , '26-netdev-link-local-addressing-yes.network' )
2378 self
. wait_online ( 'ipiptun99:carrier' )
2380 @expectedFailureIfModuleIsNotAvailable ( 'xfrm_interface' )
2381 def test_xfrm ( self
):
2382 copy_network_unit ( '12-dummy.netdev' , '25-xfrm.network' ,
2383 '25-xfrm.netdev' , '25-xfrm-independent.netdev' ,
2384 '26-netdev-link-local-addressing-yes.network' )
2387 self
. wait_online ( 'dummy98:degraded' , 'xfrm98:degraded' , 'xfrm99:degraded' )
2389 output
= check_output ( 'ip -d link show dev xfrm98' )
2391 self
. assertIn ( 'xfrm98@dummy98:' , output
)
2392 self
. assertIn ( 'xfrm if_id 0x98 ' , output
)
2394 output
= check_output ( 'ip -d link show dev xfrm99' )
2396 self
. assertIn ( 'xfrm99@lo:' , output
)
2397 self
. assertIn ( 'xfrm if_id 0x99 ' , output
)
2399 @expectedFailureIfModuleIsNotAvailable ( 'fou' )
2401 # The following redundant check is necessary for CentOS CI.
2402 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2403 self
. assertTrue ( is_module_available ( 'fou' ))
2405 copy_network_unit ( '25-fou-ipproto-ipip.netdev' , '25-fou-ipproto-gre.netdev' ,
2406 '25-fou-ipip.netdev' , '25-fou-sit.netdev' ,
2407 '25-fou-gre.netdev' , '25-fou-gretap.netdev' )
2410 self
. wait_online ( 'ipiptun96:off' , 'sittun96:off' , 'gretun96:off' , 'gretap96:off' , setup_state
= 'unmanaged' )
2412 output
= check_output ( 'ip fou show' )
2414 self
. assertRegex ( output
, 'port 55555 ipproto 4' )
2415 self
. assertRegex ( output
, 'port 55556 ipproto 47' )
2417 output
= check_output ( 'ip -d link show ipiptun96' )
2419 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
2420 output
= check_output ( 'ip -d link show sittun96' )
2422 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55555' )
2423 output
= check_output ( 'ip -d link show gretun96' )
2425 self
. assertRegex ( output
, 'encap fou encap-sport 1001 encap-dport 55556' )
2426 output
= check_output ( 'ip -d link show gretap96' )
2428 self
. assertRegex ( output
, 'encap fou encap-sport auto encap-dport 55556' )
2430 def test_vxlan ( self
):
2431 copy_network_unit ( '11-dummy.netdev' , '25-vxlan-test1.network' ,
2432 '25-vxlan.netdev' , '25-vxlan.network' ,
2433 '25-vxlan-ipv6.netdev' , '25-vxlan-ipv6.network' ,
2434 '25-vxlan-independent.netdev' , '26-netdev-link-local-addressing-yes.network' ,
2435 '25-veth.netdev' , '25-vxlan-veth99.network' , '25-ipv6-prefix.network' ,
2436 '25-vxlan-local-slaac.netdev' , '25-vxlan-local-slaac.network' )
2439 self
. wait_online ( 'test1:degraded' , 'veth99:routable' , 'veth-peer:degraded' ,
2440 'vxlan99:degraded' , 'vxlan98:degraded' , 'vxlan97:degraded' , 'vxlan-slaac:degraded' )
2442 output
= check_output ( 'ip -d -d link show vxlan99' )
2444 self
. assertIn ( '999' , output
)
2445 self
. assertIn ( '5555' , output
)
2446 self
. assertIn ( 'l2miss' , output
)
2447 self
. assertIn ( 'l3miss' , output
)
2448 self
. assertIn ( 'gbp' , output
)
2449 # Since [0] some of the options use slightly different names and some
2450 # options with default values are shown only if the -d(etails) setting
2452 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2453 self
. assertRegex ( output
, '(udpcsum|udp_csum)' )
2454 self
. assertRegex ( output
, '(udp6zerocsumtx|udp_zero_csum6_tx)' )
2455 self
. assertRegex ( output
, '(udp6zerocsumrx|udp_zero_csum6_rx)' )
2456 self
. assertRegex ( output
, '(remcsumtx|remcsum_tx)' )
2457 self
. assertRegex ( output
, '(remcsumrx|remcsum_rx)' )
2459 output
= check_output ( 'bridge fdb show dev vxlan99' )
2461 self
. assertIn ( '00:11:22:33:44:55 dst 10.0.0.5 self permanent' , output
)
2462 self
. assertIn ( '00:11:22:33:44:66 dst 10.0.0.6 self permanent' , output
)
2463 self
. assertIn ( '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent' , output
)
2465 output
= networkctl_status ( 'vxlan99' )
2467 self
. assertIn ( 'VNI: 999' , output
)
2468 self
. assertIn ( 'Destination Port: 5555' , output
)
2469 self
. assertIn ( 'Underlying Device: test1' , output
)
2471 output
= check_output ( 'bridge fdb show dev vxlan97' )
2473 self
. assertIn ( '00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent' , output
)
2474 self
. assertIn ( '00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent' , output
)
2475 self
. assertIn ( '00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent' , output
)
2477 output
= check_output ( 'ip -d link show vxlan-slaac' )
2479 self
. assertIn ( 'vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99' , output
)
2481 output
= check_output ( 'ip -6 address show veth99' )
2483 self
. assertIn ( 'inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic' , output
)
2485 @unittest . skipUnless ( compare_kernel_version ( "6" ), reason
= "Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315" )
2486 def test_macsec ( self
):
2487 copy_network_unit ( '25-macsec.netdev' , '25-macsec.network' , '25-macsec.key' ,
2488 '26-macsec.network' , '12-dummy.netdev' )
2491 self
. wait_online ( 'dummy98:degraded' , 'macsec99:routable' )
2493 output
= check_output ( 'ip -d link show macsec99' )
2495 self
. assertRegex ( output
, 'macsec99@dummy98' )
2496 self
. assertRegex ( output
, 'macsec sci [0-9a-f]*000b' )
2497 self
. assertRegex ( output
, 'encrypt on' )
2499 output
= check_output ( 'ip macsec show macsec99' )
2501 self
. assertRegex ( output
, 'encrypt on' )
2502 self
. assertRegex ( output
, 'TXSC: [0-9a-f]*000b on SA 1' )
2503 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000' )
2504 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000' )
2505 self
. assertRegex ( output
, 'RXSC: c619528fe6a00100, state on' )
2506 self
. assertRegex ( output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000' )
2507 self
. assertRegex ( output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000' )
2508 self
. assertRegex ( output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000' )
2509 self
. assertRegex ( output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000' )
2510 self
. assertNotRegex ( output
, 'key 02030405067080900000000000000000' )
2511 self
. assertRegex ( output
, 'RXSC: 8c16456c83a90002, state on' )
2512 self
. assertRegex ( output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000' )
2514 def test_nlmon ( self
):
2515 copy_network_unit ( '25-nlmon.netdev' , '26-netdev-link-local-addressing-yes.network' )
2518 self
. wait_online ( 'nlmon99:carrier' )
2520 @expectedFailureIfModuleIsNotAvailable ( 'ifb' )
2522 copy_network_unit ( '25-ifb.netdev' , '26-netdev-link-local-addressing-yes.network' )
2525 self
. wait_online ( 'ifb99:degraded' )
2527 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
2528 def test_rps_cpu_1 ( self
):
2529 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-1.link' )
2532 self
. wait_online ( 'dummy98:carrier' )
2534 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2536 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 2 )
2538 @unittest . skipUnless ( os
. cpu_count () >= 2 , reason
= "CPU count should be >= 2 to pass this test" )
2539 def test_rps_cpu_0_1 ( self
):
2540 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-0-1.link' )
2543 self
. wait_online ( 'dummy98:carrier' )
2545 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2547 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 3 )
2549 @unittest . skipUnless ( os
. cpu_count () >= 4 , reason
= "CPU count should be >= 4 to pass this test" )
2550 def test_rps_cpu_multi ( self
):
2551 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' , '25-rps-cpu-multi.link' )
2554 self
. wait_online ( 'dummy98:carrier' )
2556 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2558 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 15 )
2560 def test_rps_cpu ( self
):
2561 cpu_count
= os
. cpu_count ()
2563 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network' )
2566 self
. wait_online ( 'dummy98:carrier' )
2569 copy_network_unit ( '25-rps-cpu-0.link' )
2570 udevadm_trigger ( '/sys/class/net/dummy98' )
2571 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2573 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
2574 remove_network_unit ( '25-rps-cpu-0.link' )
2577 copy_network_unit ( '25-rps-cpu-all.link' )
2578 udevadm_trigger ( '/sys/class/net/dummy98' )
2579 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2581 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
2582 remove_network_unit ( '25-rps-cpu-all.link' )
2585 copy_network_unit ( '24-rps-cpu-disable.link' )
2586 udevadm_trigger ( '/sys/class/net/dummy98' )
2587 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2589 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 0 )
2590 remove_network_unit ( '24-rps-cpu-disable.link' )
2593 copy_network_unit ( '25-rps-cpu-all.link' )
2594 udevadm_trigger ( '/sys/class/net/dummy98' )
2595 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2597 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
2598 remove_network_unit ( '25-rps-cpu-all.link' )
2600 # empty -> unchanged
2601 copy_network_unit ( '24-rps-cpu-empty.link' )
2602 udevadm_trigger ( '/sys/class/net/dummy98' )
2603 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2605 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
2606 remove_network_unit ( '24-rps-cpu-empty.link' )
2608 # 0, then empty -> unchanged
2609 copy_network_unit ( '25-rps-cpu-0-empty.link' )
2610 udevadm_trigger ( '/sys/class/net/dummy98' )
2611 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2613 self
. assertEqual ( f
"{int(output.replace(',', ''), base=16):x}" , f
'{(1 << cpu_count) - 1:x}' )
2614 remove_network_unit ( '25-rps-cpu-0-empty.link' )
2616 # 0, then invalid -> 0
2617 copy_network_unit ( '25-rps-cpu-0-invalid.link' )
2618 udevadm_trigger ( '/sys/class/net/dummy98' )
2619 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2621 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
2622 remove_network_unit ( '25-rps-cpu-0-invalid.link' )
2624 # invalid -> unchanged
2625 copy_network_unit ( '24-rps-cpu-invalid.link' )
2626 udevadm_trigger ( '/sys/class/net/dummy98' )
2627 output
= check_output ( 'cat /sys/class/net/dummy98/queues/rx-0/rps_cpus' )
2629 self
. assertEqual ( int ( output
. replace ( ',' , '' ), base
= 16 ), 1 )
2630 remove_network_unit ( '24-rps-cpu-invalid.link' )
2632 class NetworkdL2TPTests ( unittest
. TestCase
, Utilities
):
2640 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_netlink' )
2641 def test_l2tp_udp ( self
):
2642 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
2643 '25-l2tp-udp.netdev' , '25-l2tp.network' )
2646 self
. wait_online ( 'test1:routable' , 'l2tp-ses1:degraded' , 'l2tp-ses2:degraded' )
2648 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
2650 self
. assertRegex ( output
, "Tunnel 10, encap UDP" )
2651 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
2652 self
. assertRegex ( output
, "Peer tunnel 11" )
2653 self
. assertRegex ( output
, "UDP source / dest ports: 3000/4000" )
2654 self
. assertRegex ( output
, "UDP checksum: enabled" )
2656 output
= check_output ( 'ip l2tp show session tid 10 session_id 15' )
2658 self
. assertRegex ( output
, "Session 15 in tunnel 10" )
2659 self
. assertRegex ( output
, "Peer session 16, tunnel 11" )
2660 self
. assertRegex ( output
, "interface name: l2tp-ses1" )
2662 output
= check_output ( 'ip l2tp show session tid 10 session_id 17' )
2664 self
. assertRegex ( output
, "Session 17 in tunnel 10" )
2665 self
. assertRegex ( output
, "Peer session 18, tunnel 11" )
2666 self
. assertRegex ( output
, "interface name: l2tp-ses2" )
2668 @expectedFailureIfModuleIsNotAvailable ( 'l2tp_eth' , 'l2tp_ip' , 'l2tp_netlink' )
2669 def test_l2tp_ip ( self
):
2670 copy_network_unit ( '11-dummy.netdev' , '25-l2tp-dummy.network' ,
2671 '25-l2tp-ip.netdev' , '25-l2tp.network' )
2674 self
. wait_online ( 'test1:routable' , 'l2tp-ses3:degraded' , 'l2tp-ses4:degraded' )
2676 output
= check_output ( 'ip l2tp show tunnel tunnel_id 10' )
2678 self
. assertRegex ( output
, "Tunnel 10, encap IP" )
2679 self
. assertRegex ( output
, "From 192.168.30.100 to 192.168.30.101" )
2680 self
. assertRegex ( output
, "Peer tunnel 12" )
2682 output
= check_output ( 'ip l2tp show session tid 10 session_id 25' )
2684 self
. assertRegex ( output
, "Session 25 in tunnel 10" )
2685 self
. assertRegex ( output
, "Peer session 26, tunnel 12" )
2686 self
. assertRegex ( output
, "interface name: l2tp-ses3" )
2688 output
= check_output ( 'ip l2tp show session tid 10 session_id 27' )
2690 self
. assertRegex ( output
, "Session 27 in tunnel 10" )
2691 self
. assertRegex ( output
, "Peer session 28, tunnel 12" )
2692 self
. assertRegex ( output
, "interface name: l2tp-ses4" )
2694 class NetworkdNetworkTests ( unittest
. TestCase
, Utilities
):
2702 def verify_address_static (
2732 output
= check_output ( 'ip address show dev dummy98' )
2736 self
. assertIn ( 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98' , output
)
2737 self
. assertIn ( 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98' , output
)
2738 self
. assertIn ( 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98' , output
)
2739 self
. assertIn ( 'inet6 2001:db8:0:f101::15/64 scope global' , output
)
2740 self
. assertIn ( 'inet6 2001:db8:0:f101::16/64 scope global' , output
)
2741 self
. assertIn ( 'inet6 2001:db8:0:f102::15/64 scope global' , output
)
2744 self
. assertIn ( f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1} ' , output
)
2745 self
. assertIn ( f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2} ' , output
)
2746 self
. assertIn ( f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3} ' , output
)
2749 self
. assertIn ( f
'inet 10.4.1.1/24 {broadcast1} scope global dummy98' , output
)
2750 self
. assertIn ( f
'inet 10.4.2.1/24 {broadcast2} scope global dummy98' , output
)
2751 self
. assertIn ( f
'inet 10.4.3.1/24 {broadcast3} scope global dummy98' , output
)
2754 self
. assertIn ( f
'inet 10.5.1.1 {peer1} scope global dummy98' , output
)
2755 self
. assertIn ( f
'inet 10.5.2.1 {peer2} scope global dummy98' , output
)
2756 self
. assertIn ( f
'inet 10.5.3.1 {peer3} scope global dummy98' , output
)
2757 self
. assertIn ( f
'inet6 2001:db8:0:f103::1 {peer4} scope global' , output
)
2758 self
. assertIn ( f
'inet6 2001:db8:0:f103::2 {peer5} scope global' , output
)
2759 self
. assertIn ( f
'inet6 2001:db8:0:f103::3 {peer6} scope global' , output
)
2762 self
. assertIn ( f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98' , output
)
2763 self
. assertIn ( f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98' , output
)
2766 self
. assertIn ( f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global {deprecated1} dummy98' , output
)
2767 self
. assertIn ( f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global {deprecated2} dummy98' , output
)
2768 self
. assertIn ( f
'inet6 2001:db8:0:f104::1/64 scope global {deprecated3} ' , output
)
2769 self
. assertIn ( f
'inet6 2001:db8:0:f104::2/64 scope global {deprecated4} ' , output
)
2772 self
. assertRegex ( output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98' )
2773 self
. assertRegex ( output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global' )
2775 output_route
= check_output ( 'ip -4 route show dev dummy98 10.8.1.0/24' )
2777 self
. assertIn ( f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric} ' , output_route
)
2779 output_route
= check_output ( 'ip -6 route show dev dummy98 2001:db8:0:f105::/64' )
2781 self
. assertIn ( f
'2001:db8:0:f105::/64 proto kernel metric {route_metric} ' , output_route
)
2784 self
. assertIn ( f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global {flag1} dummy98' , output
)
2785 self
. assertIn ( f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global {flag2} dummy98' , output
)
2786 self
. assertIn ( f
'inet6 2001:db8:0:f106::1/64 scope global {flag3} ' , output
)
2787 self
. assertIn ( f
'inet6 2001:db8:0:f106::2/64 scope global {flag4} ' , output
)
2790 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
2791 prefix16
= ip4_null_16
[:- len ( '.0.1' )]
2792 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
2793 prefix24
= ip4_null_24
[:- len ( '.1' )]
2794 self
. assertIn ( f
'inet {ip4_null_16} /16 brd {prefix16} .255.255 scope global subnet16' , output
)
2795 self
. assertIn ( f
'inet {ip4_null_24} /24 brd {prefix24} .255 scope global subnet24' , output
)
2796 self
. assertIn ( f
'inet6 {ip6_null_73} /73 scope global' , output
)
2797 self
. assertIn ( f
'inet6 {ip6_null_74} /74 scope global' , output
)
2800 self
. assertNotIn ( '10.4.4.1' , output
)
2801 self
. assertNotIn ( '10.5.4.1' , output
)
2802 self
. assertNotIn ( '10.5.5.1' , output
)
2803 self
. assertNotIn ( '10.8.2.1' , output
)
2804 self
. assertNotIn ( '10.9.3.1' , output
)
2805 self
. assertNotIn ( '2001:db8:0:f101::2' , output
)
2806 self
. assertNotIn ( '2001:db8:0:f103::4' , output
)
2809 self
. check_netlabel ( 'dummy98' , r
'10\.10\.1\.0/24' )
2811 check_json ( networkctl_json ())
2813 def test_address_static ( self
):
2814 copy_network_unit ( '25-address-static.network' , '12-dummy.netdev' , copy_dropins
= False )
2815 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
2816 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
2817 self
. setup_nftset ( 'ifindex' , 'iface_index' )
2820 self
. wait_online ( 'dummy98:routable' )
2824 output
= check_output ( 'ip -4 --json address show dev dummy98' )
2825 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
2826 if i
[ 'label' ] == 'subnet16' :
2827 ip4_null_16
= i
[ 'local' ]
2828 elif i
[ 'label' ] == 'subnet24' :
2829 ip4_null_24
= i
[ 'local' ]
2830 self
. assertTrue ( ip4_null_16
. endswith ( '.0.1' ))
2831 self
. assertTrue ( ip4_null_24
. endswith ( '.1' ))
2835 output
= check_output ( 'ip -6 --json address show dev dummy98' )
2836 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
2837 if i
[ 'prefixlen' ] == 73 :
2838 ip6_null_73
= i
[ 'local' ]
2839 elif i
[ 'prefixlen' ] == 74 :
2840 ip6_null_74
= i
[ 'local' ]
2841 self
. assertTrue ( ip6_null_73
. endswith ( ':1' ))
2842 self
. assertTrue ( ip6_null_74
. endswith ( ':1' ))
2844 self
. verify_address_static (
2849 broadcast2
= ' brd 10.4.2.255' ,
2850 broadcast3
= ' brd 10.4.3.63' ,
2851 peer1
= ' peer 10.5.1.101/24' ,
2852 peer2
= ' peer 10.5.2.101/24' ,
2853 peer3
= '/24 brd 10.5.3.255' ,
2854 peer4
= ' peer 2001:db8:0:f103::101/128' ,
2855 peer5
= ' peer 2001:db8:0:f103::102/128' ,
2860 deprecated2
= ' deprecated' ,
2862 deprecated4
= ' deprecated' ,
2864 flag1
= ' noprefixroute' ,
2866 flag3
= ' noprefixroute' ,
2867 flag4
= ' home mngtmpaddr' ,
2868 ip4_null_16
= ip4_null_16
,
2869 ip4_null_24
= ip4_null_24
,
2870 ip6_null_73
= ip6_null_73
,
2871 ip6_null_74
= ip6_null_74
,
2874 self
. check_nftset ( 'addr4' , r
'10\.10\.1\.1' )
2875 self
. check_nftset ( 'network4' , r
'10\.10\.1\.0/24' )
2876 self
. check_nftset ( 'ifindex' , 'dummy98' )
2878 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
2880 copy_network_unit ( '25-address-static.network.d/10-override.conf' )
2882 self
. wait_online ( 'dummy98:routable' )
2883 self
. verify_address_static (
2884 label1
= 'new-label1' ,
2886 label3
= 'new-label3' ,
2887 broadcast1
= ' brd 10.4.1.255' ,
2889 broadcast3
= ' brd 10.4.3.31' ,
2890 peer1
= ' peer 10.5.1.102/24' ,
2891 peer2
= '/24 brd 10.5.2.255' ,
2892 peer3
= ' peer 10.5.3.102/24' ,
2893 peer4
= ' peer 2001:db8:0:f103::201/128' ,
2895 peer6
= ' peer 2001:db8:0:f103::203/128' ,
2898 deprecated1
= ' deprecated' ,
2900 deprecated3
= ' deprecated' ,
2904 flag2
= ' noprefixroute' ,
2905 flag3
= ' home mngtmpaddr' ,
2906 flag4
= ' noprefixroute' ,
2907 ip4_null_16
= ip4_null_16
,
2908 ip4_null_24
= ip4_null_24
,
2909 ip6_null_73
= ip6_null_73
,
2910 ip6_null_74
= ip6_null_74
,
2913 networkctl_reconfigure ( 'dummy98' )
2914 self
. wait_online ( 'dummy98:routable' )
2915 self
. verify_address_static (
2916 label1
= 'new-label1' ,
2918 label3
= 'new-label3' ,
2919 broadcast1
= ' brd 10.4.1.255' ,
2921 broadcast3
= ' brd 10.4.3.31' ,
2922 peer1
= ' peer 10.5.1.102/24' ,
2923 peer2
= '/24 brd 10.5.2.255' ,
2924 peer3
= ' peer 10.5.3.102/24' ,
2925 peer4
= ' peer 2001:db8:0:f103::201/128' ,
2927 peer6
= ' peer 2001:db8:0:f103::203/128' ,
2930 deprecated1
= ' deprecated' ,
2932 deprecated3
= ' deprecated' ,
2936 flag2
= ' noprefixroute' ,
2937 flag3
= ' home mngtmpaddr' ,
2938 flag4
= ' noprefixroute' ,
2939 ip4_null_16
= ip4_null_16
,
2940 ip4_null_24
= ip4_null_24
,
2941 ip6_null_73
= ip6_null_73
,
2942 ip6_null_74
= ip6_null_74
,
2946 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2947 check_output ( 'ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever' )
2948 check_output ( 'ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever' )
2949 output
= check_output ( 'ip address show dev dummy98' )
2951 self
. assertNotRegex ( output
, '10.7.1.1/24 .* deprecated' )
2952 self
. assertNotRegex ( output
, '2001:db8:0:f104::1/64 .* deprecated' )
2954 # 2. reconfigure the interface, and check the deprecated flag is set again
2955 networkctl_reconfigure ( 'dummy98' )
2956 self
. wait_online ( 'dummy98:routable' )
2957 self
. verify_address_static (
2958 label1
= 'new-label1' ,
2960 label3
= 'new-label3' ,
2961 broadcast1
= ' brd 10.4.1.255' ,
2963 broadcast3
= ' brd 10.4.3.31' ,
2964 peer1
= ' peer 10.5.1.102/24' ,
2965 peer2
= '/24 brd 10.5.2.255' ,
2966 peer3
= ' peer 10.5.3.102/24' ,
2967 peer4
= ' peer 2001:db8:0:f103::201/128' ,
2969 peer6
= ' peer 2001:db8:0:f103::203/128' ,
2972 deprecated1
= ' deprecated' ,
2974 deprecated3
= ' deprecated' ,
2978 flag2
= ' noprefixroute' ,
2979 flag3
= ' home mngtmpaddr' ,
2980 flag4
= ' noprefixroute' ,
2981 ip4_null_16
= ip4_null_16
,
2982 ip4_null_24
= ip4_null_24
,
2983 ip6_null_73
= ip6_null_73
,
2984 ip6_null_74
= ip6_null_74
,
2987 # test for ENOBUFS issue #17012 (with reload)
2988 copy_network_unit ( '25-address-static.network.d/10-many-address.conf' )
2990 self
. wait_online ( 'dummy98:routable' )
2991 output
= check_output ( 'ip -4 address show dev dummy98' )
2992 for i
in range ( 1 , 254 ):
2993 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
2995 # (with reconfigure)
2996 networkctl_reconfigure ( 'dummy98' )
2997 self
. wait_online ( 'dummy98:routable' )
2998 output
= check_output ( 'ip -4 address show dev dummy98' )
2999 for i
in range ( 1 , 254 ):
3000 self
. assertIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3002 # test for an empty string assignment for Address= in [Network]
3003 copy_network_unit ( '25-address-static.network.d/20-clear-addresses.conf' )
3005 self
. wait_online ( 'dummy98:routable' )
3006 output
= check_output ( 'ip -4 address show dev dummy98' )
3007 for i
in range ( 1 , 254 ):
3008 self
. assertNotIn ( f
'inet 10.3.3. {i} /16 brd 10.3.255.255' , output
)
3009 self
. assertIn ( 'inet 10.4.0.1/16 brd 10.4.255.255' , output
)
3011 def test_address_ipv4acd ( self
):
3012 check_output ( 'ip netns add ns99' )
3013 check_output ( 'ip link add veth99 type veth peer veth-peer' )
3014 check_output ( 'ip link set veth-peer netns ns99' )
3015 check_output ( 'ip link set veth99 up' )
3016 check_output ( 'ip netns exec ns99 ip link set veth-peer up' )
3017 check_output ( 'ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer' )
3019 copy_network_unit ( '25-address-ipv4acd-veth99.network' , copy_dropins
= False )
3021 self
. wait_online ( 'veth99:routable' )
3023 output
= check_output ( 'ip -4 address show dev veth99' )
3025 self
. assertNotIn ( '192.168.100.10/24' , output
)
3026 self
. assertIn ( '192.168.100.11/24' , output
)
3028 copy_network_unit ( '25-address-ipv4acd-veth99.network.d/conflict-address.conf' )
3030 self
. wait_operstate ( 'veth99' , operstate
= 'routable' , setup_state
= 'configuring' , setup_timeout
= 10 )
3032 output
= check_output ( 'ip -4 address show dev veth99' )
3034 self
. assertNotIn ( '192.168.100.10/24' , output
)
3035 self
. assertIn ( '192.168.100.11/24' , output
)
3037 def test_address_peer_ipv4 ( self
):
3038 # test for issue #17304
3039 copy_network_unit ( '25-address-peer-ipv4.network' , '12-dummy.netdev' )
3041 for trial
in range ( 2 ):
3047 self
. wait_online ( 'dummy98:routable' )
3049 output
= check_output ( 'ip -4 address show dev dummy98' )
3050 self
. assertIn ( 'inet 100.64.0.1 peer 100.64.0.2/32 scope global' , output
)
3052 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
3053 def test_prefix_route ( self
):
3054 copy_network_unit ( '25-prefix-route-with-vrf.network' , '12-dummy.netdev' ,
3055 '25-prefix-route-without-vrf.network' , '11-dummy.netdev' ,
3056 '25-vrf.netdev' , '25-vrf.network' )
3057 for trial
in range ( 2 ):
3063 self
. wait_online ( 'dummy98:routable' , 'test1:routable' , 'vrf99:carrier' )
3065 output
= check_output ( 'ip route show table 42 dev dummy98' )
3066 print ( '### ip route show table 42 dev dummy98' )
3068 self
. assertRegex ( output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1' )
3069 self
. assertRegex ( output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1' )
3070 self
. assertRegex ( output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1' )
3071 self
. assertRegex ( output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1' )
3072 self
. assertRegex ( output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1' )
3073 self
. assertRegex ( output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1' )
3074 self
. assertRegex ( output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1' )
3075 output
= check_output ( 'ip -6 route show table 42 dev dummy98' )
3076 print ( '### ip -6 route show table 42 dev dummy98' )
3080 self
. assertRegex ( output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium' )
3081 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3082 self
. assertRegex ( output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium' )
3083 self
. assertRegex ( output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium' )
3084 self
. assertRegex ( output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium' )
3085 self
. assertRegex ( output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium' )
3086 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3087 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3091 output
= check_output ( 'ip route show dev test1' )
3092 print ( '### ip route show dev test1' )
3094 self
. assertRegex ( output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1' )
3095 output
= check_output ( 'ip route show table local dev test1' )
3096 print ( '### ip route show table local dev test1' )
3098 self
. assertRegex ( output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1' )
3099 self
. assertRegex ( output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1' )
3100 self
. assertRegex ( output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1' )
3101 self
. assertRegex ( output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1' )
3102 self
. assertRegex ( output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1' )
3103 self
. assertRegex ( output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1' )
3104 output
= check_output ( 'ip -6 route show dev test1' )
3105 print ( '### ip -6 route show dev test1' )
3107 self
. assertRegex ( output
, 'fdde:12:22::1 proto kernel metric 256 pref medium' )
3108 self
. assertRegex ( output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium' )
3109 self
. assertRegex ( output
, 'fe80::/64 proto kernel metric 256 pref medium' )
3110 output
= check_output ( 'ip -6 route show table local dev test1' )
3111 print ( '### ip -6 route show table local dev test1' )
3113 self
. assertRegex ( output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium' )
3114 self
. assertRegex ( output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium' )
3115 self
. assertRegex ( output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium' )
3116 self
. assertRegex ( output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium' )
3117 self
. assertRegex ( output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium' )
3119 def test_configure_without_carrier ( self
):
3120 copy_network_unit ( '11-dummy.netdev' )
3122 self
. wait_operstate ( 'test1' , 'off' , '' )
3123 check_output ( 'ip link set dev test1 up carrier off' )
3125 copy_network_unit ( '25-test1.network.d/configure-without-carrier.conf' , copy_dropins
= False )
3127 self
. wait_online ( 'test1:no-carrier' )
3129 carrier_map
= { 'on' : '1' , 'off' : '0' }
3130 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3131 for carrier
in [ 'off' , 'on' , 'off' ]:
3132 with self
. subTest ( carrier
= carrier
):
3133 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3134 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3135 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3137 output
= networkctl_status ( 'test1' )
3139 self
. assertRegex ( output
, '192.168.0.15' )
3140 self
. assertRegex ( output
, '192.168.0.1' )
3141 self
. assertRegex ( output
, routable_map
[ carrier
])
3143 def test_configure_without_carrier_yes_ignore_carrier_loss_no ( self
):
3144 copy_network_unit ( '11-dummy.netdev' )
3146 self
. wait_operstate ( 'test1' , 'off' , '' )
3147 check_output ( 'ip link set dev test1 up carrier off' )
3149 copy_network_unit ( '25-test1.network' )
3151 self
. wait_online ( 'test1:no-carrier' )
3153 carrier_map
= { 'on' : '1' , 'off' : '0' }
3154 routable_map
= { 'on' : 'routable' , 'off' : 'no-carrier' }
3155 for ( carrier
, have_config
) in [( 'off' , True ), ( 'on' , True ), ( 'off' , False )]:
3156 with self
. subTest ( carrier
= carrier
, have_config
= have_config
):
3157 if carrier_map
[ carrier
] != read_link_attr ( 'test1' , 'carrier' ):
3158 check_output ( f
'ip link set dev test1 carrier {carrier} ' )
3159 self
. wait_online ( f
'test1:{routable_map[carrier]}:{routable_map[carrier]}' )
3161 output
= networkctl_status ( 'test1' )
3164 self
. assertRegex ( output
, '192.168.0.15' )
3165 self
. assertRegex ( output
, '192.168.0.1' )
3167 self
. assertNotRegex ( output
, '192.168.0.15' )
3168 self
. assertNotRegex ( output
, '192.168.0.1' )
3169 self
. assertRegex ( output
, routable_map
[ carrier
])
3171 def test_routing_policy_rule ( self
):
3172 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' )
3174 self
. wait_online ( 'test1:degraded' )
3176 output
= check_output ( 'ip rule list iif test1 priority 111' )
3178 self
. assertRegex ( output
, '111:' )
3179 self
. assertRegex ( output
, 'from 192.168.100.18' )
3180 self
. assertRegex ( output
, r
'tos (0x08|throughput)\s' )
3181 self
. assertRegex ( output
, 'iif test1' )
3182 self
. assertRegex ( output
, 'oif test1' )
3183 self
. assertRegex ( output
, 'lookup 7' )
3185 output
= check_output ( 'ip rule list iif test1 priority 101' )
3187 self
. assertRegex ( output
, '101:' )
3188 self
. assertRegex ( output
, 'from all' )
3189 self
. assertRegex ( output
, 'iif test1' )
3190 self
. assertRegex ( output
, 'lookup 9' )
3192 output
= check_output ( 'ip -6 rule list iif test1 priority 100' )
3194 self
. assertRegex ( output
, '100:' )
3195 self
. assertRegex ( output
, 'from all' )
3196 self
. assertRegex ( output
, 'iif test1' )
3197 self
. assertRegex ( output
, 'lookup 8' )
3199 output
= check_output ( 'ip rule list iif test1 priority 102' )
3201 self
. assertRegex ( output
, '102:' )
3202 self
. assertRegex ( output
, 'from 0.0.0.0/8' )
3203 self
. assertRegex ( output
, 'iif test1' )
3204 self
. assertRegex ( output
, 'lookup 10' )
3206 check_json ( networkctl_json ())
3208 def test_routing_policy_rule_issue_11280 ( self
):
3209 copy_network_unit ( '25-routing-policy-rule-test1.network' , '11-dummy.netdev' ,
3210 '25-routing-policy-rule-dummy98.network' , '12-dummy.netdev' )
3212 for trial
in range ( 3 ):
3213 restart_networkd ( show_logs
=( trial
> 0 ))
3214 self
. wait_online ( 'test1:degraded' , 'dummy98:degraded' )
3216 output
= check_output ( 'ip rule list table 7' )
3218 self
. assertRegex ( output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7' )
3220 output
= check_output ( 'ip rule list table 8' )
3222 self
. assertRegex ( output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8' )
3224 def test_routing_policy_rule_reconfigure ( self
):
3225 copy_network_unit ( '25-routing-policy-rule-reconfigure2.network' , '11-dummy.netdev' )
3227 self
. wait_online ( 'test1:degraded' )
3229 output
= check_output ( 'ip rule list table 1011' )
3231 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
3232 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
3233 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
3234 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
3236 output
= check_output ( 'ip -6 rule list table 1011' )
3238 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
3240 copy_network_unit ( '25-routing-policy-rule-reconfigure1.network' , '11-dummy.netdev' )
3242 self
. wait_online ( 'test1:degraded' )
3244 output
= check_output ( 'ip rule list table 1011' )
3246 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
3247 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
3248 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
3249 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
3251 output
= check_output ( 'ip -6 rule list table 1011' )
3253 self
. assertNotIn ( '10112: from all oif test1 lookup 1011' , output
)
3254 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
3256 call ( 'ip rule delete priority 10111' )
3257 call ( 'ip rule delete priority 10112' )
3258 call ( 'ip rule delete priority 10113' )
3259 call ( 'ip rule delete priority 10114' )
3260 call ( 'ip -6 rule delete priority 10113' )
3262 output
= check_output ( 'ip rule list table 1011' )
3264 self
. assertEqual ( output
, '' )
3266 output
= check_output ( 'ip -6 rule list table 1011' )
3268 self
. assertEqual ( output
, '' )
3270 networkctl_reconfigure ( 'test1' )
3271 self
. wait_online ( 'test1:degraded' )
3273 output
= check_output ( 'ip rule list table 1011' )
3275 self
. assertIn ( '10111: from all fwmark 0x3f3 lookup 1011' , output
)
3276 self
. assertIn ( '10112: from all oif test1 lookup 1011' , output
)
3277 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
3278 self
. assertIn ( '10114: from 192.168.8.254 lookup 1011' , output
)
3280 output
= check_output ( 'ip -6 rule list table 1011' )
3282 self
. assertIn ( '10113: from all iif test1 lookup 1011' , output
)
3284 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable ()
3285 def test_routing_policy_rule_port_range ( self
):
3286 copy_network_unit ( '25-fibrule-port-range.network' , '11-dummy.netdev' )
3288 self
. wait_online ( 'test1:degraded' )
3290 output
= check_output ( 'ip rule' )
3292 self
. assertRegex ( output
, '111' )
3293 self
. assertRegex ( output
, 'from 192.168.100.18' )
3294 self
. assertRegex ( output
, '1123-1150' )
3295 self
. assertRegex ( output
, '3224-3290' )
3296 self
. assertRegex ( output
, 'tcp' )
3297 self
. assertRegex ( output
, 'lookup 7' )
3299 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable ()
3300 def test_routing_policy_rule_invert ( self
):
3301 copy_network_unit ( '25-fibrule-invert.network' , '11-dummy.netdev' )
3303 self
. wait_online ( 'test1:degraded' )
3305 output
= check_output ( 'ip rule' )
3307 self
. assertRegex ( output
, '111' )
3308 self
. assertRegex ( output
, 'not.*?from.*?192.168.100.18' )
3309 self
. assertRegex ( output
, 'tcp' )
3310 self
. assertRegex ( output
, 'lookup 7' )
3312 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable ()
3313 def test_routing_policy_rule_l3mdev ( self
):
3314 copy_network_unit ( '25-fibrule-l3mdev.network' , '11-dummy.netdev' )
3316 self
. wait_online ( 'test1:degraded' )
3318 output
= check_output ( 'ip rule' )
3320 self
. assertIn ( '1500: from all lookup [l3mdev-table]' , output
)
3321 self
. assertIn ( '2000: from all lookup [l3mdev-table] unreachable' , output
)
3323 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable ()
3324 def test_routing_policy_rule_uidrange ( self
):
3325 copy_network_unit ( '25-fibrule-uidrange.network' , '11-dummy.netdev' )
3327 self
. wait_online ( 'test1:degraded' )
3329 output
= check_output ( 'ip rule' )
3331 self
. assertRegex ( output
, '111' )
3332 self
. assertRegex ( output
, 'from 192.168.100.18' )
3333 self
. assertRegex ( output
, 'lookup 7' )
3334 self
. assertRegex ( output
, 'uidrange 100-200' )
3336 def _test_route_static ( self
, manage_foreign_routes
):
3337 if not manage_foreign_routes
:
3338 copy_networkd_conf_dropin ( 'networkd-manage-foreign-routes-no.conf' )
3340 copy_network_unit ( '25-route-static.network' , '12-dummy.netdev' ,
3341 '25-route-static-test1.network' , '11-dummy.netdev' )
3343 self
. wait_online ( 'dummy98:routable' )
3345 output
= networkctl_status ( 'dummy98' )
3348 print ( '### ip -6 route show dev dummy98' )
3349 output
= check_output ( 'ip -6 route show dev dummy98' )
3351 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
3352 self
. assertIn ( '2001:1234:5:8f63::1 proto kernel' , output
)
3353 self
. assertIn ( '2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static' , output
)
3355 print ( '### ip -6 route show default' )
3356 output
= check_output ( 'ip -6 route show default' )
3358 self
. assertIn ( 'default' , output
)
3359 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff' , output
)
3361 print ( '### ip -4 route show dev dummy98' )
3362 output
= check_output ( 'ip -4 route show dev dummy98' )
3364 self
. assertIn ( '149.10.124.48/28 proto kernel scope link src 149.10.124.58' , output
)
3365 self
. assertIn ( '149.10.124.64 proto static scope link' , output
)
3366 self
. assertIn ( '169.254.0.0/16 proto static scope link metric 2048' , output
)
3367 self
. assertIn ( '192.168.1.1 proto static scope link initcwnd 20' , output
)
3368 self
. assertIn ( '192.168.1.2 proto static scope link initrwnd 30' , output
)
3369 self
. assertIn ( '192.168.1.3 proto static scope link advmss 30' , output
)
3370 self
. assertIn ( '192.168.1.4 proto static scope link hoplimit 122' , output
)
3371 self
. assertIn ( 'multicast 149.10.123.4 proto static' , output
)
3373 print ( '### ip -4 route show dev dummy98 default' )
3374 output
= check_output ( 'ip -4 route show dev dummy98 default' )
3376 self
. assertIn ( 'default via 149.10.125.65 proto static onlink' , output
)
3377 self
. assertIn ( 'default via 149.10.124.64 proto static' , output
)
3378 self
. assertIn ( 'default proto static' , output
)
3379 self
. assertIn ( 'default via 1.1.8.104 proto static' , output
)
3381 print ( '### ip -4 route show table local dev dummy98' )
3382 output
= check_output ( 'ip -4 route show table local dev dummy98' )
3384 self
. assertIn ( 'local 149.10.123.1 proto static scope host' , output
)
3385 self
. assertIn ( 'anycast 149.10.123.2 proto static scope link' , output
)
3386 self
. assertIn ( 'broadcast 149.10.123.3 proto static scope link' , output
)
3388 print ( '### ip -4 route show type blackhole' )
3389 output
= check_output ( 'ip -4 route show type blackhole' )
3391 self
. assertIn ( 'blackhole 202.54.1.2 proto static' , output
)
3393 print ( '### ip -4 route show type unreachable' )
3394 output
= check_output ( 'ip -4 route show type unreachable' )
3396 self
. assertIn ( 'unreachable 202.54.1.3 proto static' , output
)
3398 print ( '### ip -4 route show type prohibit' )
3399 output
= check_output ( 'ip -4 route show type prohibit' )
3401 self
. assertIn ( 'prohibit 202.54.1.4 proto static' , output
)
3403 print ( '### ip -6 route show type blackhole' )
3404 output
= check_output ( 'ip -6 route show type blackhole' )
3406 self
. assertIn ( 'blackhole 2001:1234:5678::2 dev lo proto static' , output
)
3408 print ( '### ip -6 route show type unreachable' )
3409 output
= check_output ( 'ip -6 route show type unreachable' )
3411 self
. assertIn ( 'unreachable 2001:1234:5678::3 dev lo proto static' , output
)
3413 print ( '### ip -6 route show type prohibit' )
3414 output
= check_output ( 'ip -6 route show type prohibit' )
3416 self
. assertIn ( 'prohibit 2001:1234:5678::4 dev lo proto static' , output
)
3418 print ( '### ip route show 192.168.10.1' )
3419 output
= check_output ( 'ip route show 192.168.10.1' )
3421 self
. assertIn ( '192.168.10.1 proto static' , output
)
3422 self
. assertIn ( 'nexthop via 149.10.123.59 dev test1 weight 20' , output
)
3423 self
. assertIn ( 'nexthop via 149.10.123.60 dev test1 weight 30' , output
)
3424 self
. assertIn ( 'nexthop via 149.10.124.59 dev dummy98 weight 10' , output
)
3425 self
. assertIn ( 'nexthop via 149.10.124.60 dev dummy98 weight 5' , output
)
3427 print ( '### ip route show 192.168.10.2' )
3428 output
= check_output ( 'ip route show 192.168.10.2' )
3430 # old ip command does not show IPv6 gateways...
3431 self
. assertIn ( '192.168.10.2 proto static' , output
)
3432 self
. assertIn ( 'nexthop' , output
)
3433 self
. assertIn ( 'dev test1 weight 20' , output
)
3434 self
. assertIn ( 'dev test1 weight 30' , output
)
3435 self
. assertIn ( 'dev dummy98 weight 10' , output
)
3436 self
. assertIn ( 'dev dummy98 weight 5' , output
)
3438 print ( '### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff' )
3439 output
= check_output ( 'ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff' )
3441 # old ip command does not show 'nexthop' keyword and weight...
3442 self
. assertIn ( '2001:1234:5:7fff:ff:ff:ff:ff' , output
)
3443 self
. assertIn ( 'via 2001:1234:5:6fff:ff:ff:ff:ff dev test1' , output
)
3444 self
. assertIn ( 'via 2001:1234:5:7fff:ff:ff:ff:ff dev test1' , output
)
3445 self
. assertIn ( 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98' , output
)
3446 self
. assertIn ( 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98' , output
)
3448 check_json ( networkctl_json ())
3450 copy_network_unit ( '25-address-static.network' , copy_dropins
= False )
3452 self
. wait_online ( 'dummy98:routable' )
3454 # check all routes managed by Manager are removed
3455 print ( '### ip -4 route show type blackhole' )
3456 output
= check_output ( 'ip -4 route show type blackhole' )
3458 self
. assertEqual ( output
, '' )
3460 print ( '### ip -4 route show type unreachable' )
3461 output
= check_output ( 'ip -4 route show type unreachable' )
3463 self
. assertEqual ( output
, '' )
3465 print ( '### ip -4 route show type prohibit' )
3466 output
= check_output ( 'ip -4 route show type prohibit' )
3468 self
. assertEqual ( output
, '' )
3470 print ( '### ip -6 route show type blackhole' )
3471 output
= check_output ( 'ip -6 route show type blackhole' )
3473 self
. assertEqual ( output
, '' )
3475 print ( '### ip -6 route show type unreachable' )
3476 output
= check_output ( 'ip -6 route show type unreachable' )
3478 self
. assertEqual ( output
, '' )
3480 print ( '### ip -6 route show type prohibit' )
3481 output
= check_output ( 'ip -6 route show type prohibit' )
3483 self
. assertEqual ( output
, '' )
3485 remove_network_unit ( '25-address-static.network' )
3487 self
. wait_online ( 'dummy98:routable' )
3489 # check all routes managed by Manager are reconfigured
3490 print ( '### ip -4 route show type blackhole' )
3491 output
= check_output ( 'ip -4 route show type blackhole' )
3493 self
. assertIn ( 'blackhole 202.54.1.2 proto static' , output
)
3495 print ( '### ip -4 route show type unreachable' )
3496 output
= check_output ( 'ip -4 route show type unreachable' )
3498 self
. assertIn ( 'unreachable 202.54.1.3 proto static' , output
)
3500 print ( '### ip -4 route show type prohibit' )
3501 output
= check_output ( 'ip -4 route show type prohibit' )
3503 self
. assertIn ( 'prohibit 202.54.1.4 proto static' , output
)
3505 print ( '### ip -6 route show type blackhole' )
3506 output
= check_output ( 'ip -6 route show type blackhole' )
3508 self
. assertIn ( 'blackhole 2001:1234:5678::2 dev lo proto static' , output
)
3510 print ( '### ip -6 route show type unreachable' )
3511 output
= check_output ( 'ip -6 route show type unreachable' )
3513 self
. assertIn ( 'unreachable 2001:1234:5678::3 dev lo proto static' , output
)
3515 print ( '### ip -6 route show type prohibit' )
3516 output
= check_output ( 'ip -6 route show type prohibit' )
3518 self
. assertIn ( 'prohibit 2001:1234:5678::4 dev lo proto static' , output
)
3520 remove_link ( 'dummy98' )
3523 # check all routes managed by Manager are removed
3524 print ( '### ip -4 route show type blackhole' )
3525 output
= check_output ( 'ip -4 route show type blackhole' )
3527 self
. assertEqual ( output
, '' )
3529 print ( '### ip -4 route show type unreachable' )
3530 output
= check_output ( 'ip -4 route show type unreachable' )
3532 self
. assertEqual ( output
, '' )
3534 print ( '### ip -4 route show type prohibit' )
3535 output
= check_output ( 'ip -4 route show type prohibit' )
3537 self
. assertEqual ( output
, '' )
3539 print ( '### ip -6 route show type blackhole' )
3540 output
= check_output ( 'ip -6 route show type blackhole' )
3542 self
. assertEqual ( output
, '' )
3544 print ( '### ip -6 route show type unreachable' )
3545 output
= check_output ( 'ip -6 route show type unreachable' )
3547 self
. assertEqual ( output
, '' )
3549 print ( '### ip -6 route show type prohibit' )
3550 output
= check_output ( 'ip -6 route show type prohibit' )
3552 self
. assertEqual ( output
, '' )
3554 def test_route_static ( self
):
3556 for manage_foreign_routes
in [ True , False ]:
3562 print ( f
'### test_route_static(manage_foreign_routes= {manage_foreign_routes} )' )
3563 with self
. subTest ( manage_foreign_routes
= manage_foreign_routes
):
3564 self
._ test
_ route
_ static
( manage_foreign_routes
)
3566 @expectedFailureIfRTA_VIAIsNotSupported ()
3567 def test_route_via_ipv6 ( self
):
3568 copy_network_unit ( '25-route-via-ipv6.network' , '12-dummy.netdev' )
3570 self
. wait_online ( 'dummy98:routable' )
3572 output
= networkctl_status ( 'dummy98' )
3575 print ( '### ip -6 route show dev dummy98' )
3576 output
= check_output ( 'ip -6 route show dev dummy98' )
3578 self
. assertRegex ( output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static' )
3579 self
. assertRegex ( output
, '2001:1234:5:8f63::1 proto kernel' )
3581 print ( '### ip -4 route show dev dummy98' )
3582 output
= check_output ( 'ip -4 route show dev dummy98' )
3584 self
. assertRegex ( output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58' )
3585 self
. assertRegex ( output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static' )
3587 @expectedFailureIfModuleIsNotAvailable ( 'tcp_dctcp' )
3588 def test_route_congctl ( self
):
3589 copy_network_unit ( '25-route-congctl.network' , '12-dummy.netdev' )
3591 self
. wait_online ( 'dummy98:routable' )
3593 print ( '### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
3594 output
= check_output ( 'ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff' )
3596 self
. assertIn ( '2001:1234:5:8fff:ff:ff:ff:ff proto static' , output
)
3597 self
. assertIn ( 'congctl dctcp' , output
)
3599 print ( '### ip -4 route show dev dummy98 149.10.124.66' )
3600 output
= check_output ( 'ip -4 route show dev dummy98 149.10.124.66' )
3602 self
. assertIn ( '149.10.124.66 proto static' , output
)
3603 self
. assertIn ( 'congctl dctcp' , output
)
3604 self
. assertIn ( 'rto_min 300s' , output
)
3606 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
3607 def test_route_vrf ( self
):
3608 copy_network_unit ( '25-route-vrf.network' , '12-dummy.netdev' ,
3609 '25-vrf.netdev' , '25-vrf.network' )
3611 self
. wait_online ( 'dummy98:routable' , 'vrf99:carrier' )
3613 output
= check_output ( 'ip route show vrf vrf99' )
3615 self
. assertRegex ( output
, 'default via 192.168.100.1' )
3617 output
= check_output ( 'ip route show' )
3619 self
. assertNotRegex ( output
, 'default via 192.168.100.1' )
3621 def test_gateway_reconfigure ( self
):
3622 copy_network_unit ( '25-gateway-static.network' , '12-dummy.netdev' )
3624 self
. wait_online ( 'dummy98:routable' )
3625 print ( '### ip -4 route show dev dummy98 default' )
3626 output
= check_output ( 'ip -4 route show dev dummy98 default' )
3628 self
. assertIn ( 'default via 149.10.124.59 proto static' , output
)
3629 self
. assertNotIn ( '149.10.124.60' , output
)
3631 remove_network_unit ( '25-gateway-static.network' )
3632 copy_network_unit ( '25-gateway-next-static.network' )
3634 self
. wait_online ( 'dummy98:routable' )
3635 print ( '### ip -4 route show dev dummy98 default' )
3636 output
= check_output ( 'ip -4 route show dev dummy98 default' )
3638 self
. assertNotIn ( '149.10.124.59' , output
)
3639 self
. assertIn ( 'default via 149.10.124.60 proto static' , output
)
3641 def test_ip_route_ipv6_src_route ( self
):
3642 # a dummy device does not make the addresses go through tentative state, so we
3643 # reuse a bond from an earlier test, which does make the addresses go through
3644 # tentative state, and do our test on that
3645 copy_network_unit ( '23-active-slave.network' , '25-route-ipv6-src.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
3647 self
. wait_online ( 'dummy98:enslaved' , 'bond199:routable' )
3649 output
= check_output ( 'ip -6 route list dev bond199' )
3651 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2' , output
)
3653 def test_route_preferred_source_with_existing_address ( self
):
3655 copy_network_unit ( '25-route-preferred-source.network' , '12-dummy.netdev' )
3660 networkctl_reconfigure ( 'dummy98' )
3662 self
. wait_online ( 'dummy98:routable' )
3664 output
= check_output ( 'ip -6 route list dev dummy98' )
3666 self
. assertIn ( 'abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1' , output
)
3668 def test_ip_link_mac_address ( self
):
3669 copy_network_unit ( '25-address-link-section.network' , '12-dummy.netdev' )
3671 self
. wait_online ( 'dummy98:degraded' )
3673 output
= check_output ( 'ip link show dummy98' )
3675 self
. assertRegex ( output
, '00:01:02:aa:bb:cc' )
3677 def test_ip_link_unmanaged ( self
):
3678 copy_network_unit ( '25-link-section-unmanaged.network' , '12-dummy.netdev' )
3681 self
. wait_operstate ( 'dummy98' , 'off' , setup_state
= 'unmanaged' )
3683 def test_ipv6_address_label ( self
):
3684 copy_network_unit ( '25-ipv6-address-label-section.network' , '12-dummy.netdev' )
3686 self
. wait_online ( 'dummy98:degraded' )
3688 output
= check_output ( 'ip addrlabel list' )
3690 self
. assertRegex ( output
, '2004:da8:1::/64' )
3692 def test_ipv6_proxy_ndp ( self
):
3693 copy_network_unit ( '25-ipv6-proxy-ndp.network' , '12-dummy.netdev' )
3696 self
. wait_online ( 'dummy98:routable' )
3698 output
= check_output ( 'ip neighbor show proxy dev dummy98' )
3700 for i
in range ( 1 , 5 ):
3701 self
. assertRegex ( output
, f
'2607:5300:203:5215: {i} ::1 *proxy' )
3703 def test_ipv6_neigh_retrans_time ( self
):
3705 copy_network_unit ( '25-dummy.netdev' , '25-dummy.network' )
3708 self
. wait_online ( f
' {link} :degraded' )
3709 remove_network_unit ( '25-dummy.network' )
3711 # expect retrans_time_ms updated
3712 copy_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
3714 self
. wait_online ( f
' {link} :degraded' )
3715 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
3716 remove_network_unit ( '25-ipv6-neigh-retrans-time-3s.network' )
3718 # expect retrans_time_ms unchanged
3719 copy_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
3721 self
. wait_online ( f
' {link} :degraded' )
3722 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
3723 remove_network_unit ( '25-ipv6-neigh-retrans-time-0s.network' )
3725 # expect retrans_time_ms unchanged
3726 copy_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
3728 self
. wait_online ( f
' {link} :degraded' )
3729 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
3730 remove_network_unit ( '25-ipv6-neigh-retrans-time-toobig.network' )
3732 # expect retrans_time_ms unchanged
3733 copy_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
3735 self
. wait_online ( f
' {link} :degraded' )
3736 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
3737 remove_network_unit ( '25-ipv6-neigh-retrans-time-infinity.network' )
3739 # expect retrans_time_ms unchanged
3740 copy_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
3742 self
. wait_online ( f
' {link} :degraded' )
3743 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '3000' )
3744 remove_network_unit ( '25-ipv6-neigh-retrans-time-invalid.network' )
3746 # expect retrans_time_ms updated
3747 copy_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
3749 self
. wait_online ( f
' {link} :degraded' )
3750 self
. check_ipv6_neigh_sysctl_attr ( link
, 'retrans_time_ms' , '4000' )
3751 remove_network_unit ( '25-ipv6-neigh-retrans-time-4s.network' )
3753 def test_neighbor ( self
):
3754 copy_network_unit ( '12-dummy.netdev' , '25-neighbor-dummy.network' , '25-neighbor-dummy.network.d/10-step1.conf' ,
3755 '25-gre-tunnel-remote-any.netdev' , '25-neighbor-ip.network' ,
3756 '25-ip6gre-tunnel-remote-any.netdev' , '25-neighbor-ipv6.network' ,
3759 self
. wait_online ( 'dummy98:degraded' , 'gretun97:routable' , 'ip6gretun97:routable' )
3761 print ( '### ip neigh list dev gretun97' )
3762 output
= check_output ( 'ip neigh list dev gretun97' )
3764 self
. assertIn ( '10.0.0.22 lladdr 10.65.223.239 PERMANENT' , output
)
3765 self
. assertNotIn ( '10.0.0.23' , output
)
3767 print ( '### ip neigh list dev ip6gretun97' )
3768 output
= check_output ( 'ip neigh list dev ip6gretun97' )
3770 self
. assertRegex ( output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT' )
3771 self
. assertNotIn ( '2001:db8:0:f102::18' , output
)
3773 print ( '### ip neigh list dev dummy98' )
3774 output
= check_output ( 'ip neigh list dev dummy98' )
3776 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT' , output
)
3777 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT' , output
)
3778 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
3779 self
. assertNotIn ( '192.168.10.2' , output
)
3780 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
3782 check_json ( networkctl_json ())
3784 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3785 # the valid configurations in 10-step1.conf.
3786 copy_network_unit ( '25-neighbor-dummy.network.d/10-step2.conf' )
3788 self
. wait_online ( 'dummy98:degraded' )
3790 print ( '### ip neigh list dev dummy98' )
3791 output
= check_output ( 'ip neigh list dev dummy98' )
3793 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT' , output
)
3794 self
. assertIn ( '2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
3795 self
. assertNotIn ( '2004:da8:1:0::2' , output
)
3796 self
. assertNotIn ( '192.168.10.2' , output
)
3797 self
. assertNotIn ( '00:00:5e:00:02:67' , output
)
3799 check_json ( networkctl_json ())
3801 remove_network_unit ( '25-neighbor-dummy.network.d/10-step1.conf' ,
3802 '25-neighbor-dummy.network.d/10-step2.conf' )
3803 copy_network_unit ( '25-neighbor-dummy.network.d/10-step3.conf' )
3805 self
. wait_online ( 'dummy98:degraded' )
3807 print ( '### ip neigh list dev dummy98' )
3808 output
= check_output ( 'ip neigh list dev dummy98' )
3810 self
. assertIn ( '192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT' , output
)
3811 self
. assertNotIn ( '00:00:5e:00:02:65' , output
)
3812 self
. assertNotIn ( '00:00:5e:00:02:66' , output
)
3813 self
. assertNotIn ( '00:00:5e:00:03:65' , output
)
3814 self
. assertNotIn ( '2004:da8:1::1' , output
)
3816 def test_link_local_addressing ( self
):
3817 copy_network_unit ( '25-link-local-addressing-yes.network' , '11-dummy.netdev' ,
3818 '25-link-local-addressing-no.network' , '12-dummy.netdev' )
3820 self
. wait_online ( 'test1:degraded' , 'dummy98:carrier' )
3822 output
= check_output ( 'ip address show dev test1' )
3824 self
. assertRegex ( output
, 'inet .* scope link' )
3825 self
. assertRegex ( output
, 'inet6 .* scope link' )
3827 output
= check_output ( 'ip address show dev dummy98' )
3829 self
. assertNotRegex ( output
, 'inet6* .* scope link' )
3831 # Documentation/networking/ip-sysctl.txt
3833 # addr_gen_mode - INTEGER
3834 # Defines how link-local and autoconf addresses are generated.
3836 # 0: generate address based on EUI64 (default)
3837 # 1: do no generate a link-local address, use EUI64 for addresses generated
3839 # 2: generate stable privacy addresses, using the secret from
3840 # stable_secret (RFC7217)
3841 # 3: generate stable privacy addresses, using a random secret if unset
3843 self
. check_ipv6_sysctl_attr ( 'test1' , 'stable_secret' , '0123:4567:89ab:cdef:0123:4567:89ab:cdef' )
3844 self
. check_ipv6_sysctl_attr ( 'test1' , 'addr_gen_mode' , '2' )
3845 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'addr_gen_mode' , '1' )
3847 def test_link_local_addressing_ipv6ll ( self
):
3848 copy_network_unit ( '26-link-local-addressing-ipv6.network' , '12-dummy.netdev' )
3850 self
. wait_online ( 'dummy98:degraded' )
3852 # An IPv6LL address exists by default.
3853 output
= check_output ( 'ip address show dev dummy98' )
3855 self
. assertRegex ( output
, 'inet6 .* scope link' )
3857 copy_network_unit ( '25-link-local-addressing-no.network' )
3859 self
. wait_online ( 'dummy98:carrier' )
3861 # Check if the IPv6LL address is removed.
3862 output
= check_output ( 'ip address show dev dummy98' )
3864 self
. assertNotRegex ( output
, 'inet6 .* scope link' )
3866 remove_network_unit ( '25-link-local-addressing-no.network' )
3868 self
. wait_online ( 'dummy98:degraded' )
3870 # Check if a new IPv6LL address is assigned.
3871 output
= check_output ( 'ip address show dev dummy98' )
3873 self
. assertRegex ( output
, 'inet6 .* scope link' )
3875 def test_sysctl ( self
):
3876 copy_networkd_conf_dropin ( '25-global-ipv6-privacy-extensions.conf' )
3877 copy_network_unit ( '25-sysctl.network' , '12-dummy.netdev' , copy_dropins
= False )
3879 self
. wait_online ( 'dummy98:degraded' )
3881 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
3882 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '1' )
3883 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'dad_transmits' , '3' )
3884 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'hop_limit' , '5' )
3885 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'proxy_ndp' , '1' )
3886 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'forwarding' , '1' )
3887 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp' , '1' )
3888 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'proxy_arp_pvlan' , '1' )
3889 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'accept_local' , '1' )
3890 self
. check_ipv4_sysctl_attr ( 'dummy98' , 'rp_filter' , '0' )
3892 copy_network_unit ( '25-sysctl.network.d/25-ipv6-privacy-extensions.conf' )
3894 self
. wait_online ( 'dummy98:degraded' )
3896 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'use_tempaddr' , '2' )
3898 def test_sysctl_disable_ipv6 ( self
):
3899 copy_network_unit ( '25-sysctl-disable-ipv6.network' , '12-dummy.netdev' )
3901 print ( '## Disable ipv6' )
3902 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=1' )
3903 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=1' )
3906 self
. wait_online ( 'dummy98:routable' )
3908 output
= check_output ( 'ip -4 address show dummy98' )
3910 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
3911 output
= check_output ( 'ip -6 address show dummy98' )
3913 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
3914 self
. assertRegex ( output
, 'inet6 .* scope link' )
3915 output
= check_output ( 'ip -4 route show dev dummy98' )
3917 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
3918 output
= check_output ( 'ip -6 route show default' )
3920 self
. assertRegex ( output
, 'default' )
3921 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
3923 remove_link ( 'dummy98' )
3925 print ( '## Enable ipv6' )
3926 check_output ( 'sysctl net.ipv6.conf.all.disable_ipv6=0' )
3927 check_output ( 'sysctl net.ipv6.conf.default.disable_ipv6=0' )
3930 self
. wait_online ( 'dummy98:routable' )
3932 output
= check_output ( 'ip -4 address show dummy98' )
3934 self
. assertRegex ( output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98' )
3935 output
= check_output ( 'ip -6 address show dummy98' )
3937 self
. assertRegex ( output
, 'inet6 2607:5300:203:3906::/64 scope global' )
3938 self
. assertRegex ( output
, 'inet6 .* scope link' )
3939 output
= check_output ( 'ip -4 route show dev dummy98' )
3941 self
. assertRegex ( output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4' )
3942 output
= check_output ( 'ip -6 route show default' )
3944 self
. assertRegex ( output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff' )
3946 def test_bind_carrier ( self
):
3947 copy_network_unit ( '25-bind-carrier.network' , '11-dummy.netdev' )
3950 # no bound interface.
3951 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
3952 output
= check_output ( 'ip address show test1' )
3954 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
3955 self
. assertIn ( 'DOWN' , output
)
3956 self
. assertNotIn ( '192.168.10' , output
)
3958 # add one bound interface. The interface will be up.
3959 check_output ( 'ip link add dummy98 type dummy' )
3960 check_output ( 'ip link set dummy98 up' )
3961 self
. wait_online ( 'test1:routable' )
3962 output
= check_output ( 'ip address show test1' )
3964 self
. assertIn ( 'UP,LOWER_UP' , output
)
3965 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
3967 # add another bound interface. The interface is still up.
3968 check_output ( 'ip link add dummy99 type dummy' )
3969 check_output ( 'ip link set dummy99 up' )
3970 self
. wait_operstate ( 'dummy99' , 'degraded' , setup_state
= 'unmanaged' )
3971 output
= check_output ( 'ip address show test1' )
3973 self
. assertIn ( 'UP,LOWER_UP' , output
)
3974 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
3976 # remove one of the bound interfaces. The interface is still up
3977 remove_link ( 'dummy98' )
3978 output
= check_output ( 'ip address show test1' )
3980 self
. assertIn ( 'UP,LOWER_UP' , output
)
3981 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
3983 # bring down the remaining bound interface. The interface will be down.
3984 check_output ( 'ip link set dummy99 down' )
3985 self
. wait_operstate ( 'test1' , 'off' )
3986 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
3987 output
= check_output ( 'ip address show test1' )
3989 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
3990 self
. assertIn ( 'DOWN' , output
)
3991 self
. assertNotIn ( '192.168.10' , output
)
3993 # bring up the bound interface. The interface will be up.
3994 check_output ( 'ip link set dummy99 up' )
3995 self
. wait_online ( 'test1:routable' )
3996 output
= check_output ( 'ip address show test1' )
3998 self
. assertIn ( 'UP,LOWER_UP' , output
)
3999 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4001 # remove the remaining bound interface. The interface will be down.
4002 remove_link ( 'dummy99' )
4003 self
. wait_operstate ( 'test1' , 'off' )
4004 self
. wait_address_dropped ( 'test1' , r
'192.168.10' , ipv
= '-4' , timeout_sec
= 10 )
4005 output
= check_output ( 'ip address show test1' )
4007 self
. assertNotIn ( 'UP,LOWER_UP' , output
)
4008 self
. assertIn ( 'DOWN' , output
)
4009 self
. assertNotIn ( '192.168.10' , output
)
4011 # re-add one bound interface. The interface will be up.
4012 check_output ( 'ip link add dummy98 type dummy' )
4013 check_output ( 'ip link set dummy98 up' )
4014 self
. wait_online ( 'test1:routable' )
4015 output
= check_output ( 'ip address show test1' )
4017 self
. assertIn ( 'UP,LOWER_UP' , output
)
4018 self
. assertIn ( 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1' , output
)
4020 def _test_activation_policy ( self
, interface
, test
):
4021 conffile
= '25-activation-policy.network'
4023 conffile
= f
' {conffile} .d/ {test} .conf'
4024 if interface
== 'vlan99' :
4025 copy_network_unit ( '21-vlan.netdev' , '21-vlan-test1.network' )
4026 copy_network_unit ( '11-dummy.netdev' , conffile
, copy_dropins
= False )
4029 always
= test
. startswith ( 'always' )
4030 initial_up
= test
!= 'manual' and not test
. endswith ( 'down' ) # note: default is up
4031 expect_up
= initial_up
4032 next_up
= not expect_up
4034 if test
. endswith ( 'down' ):
4035 self
. wait_activated ( interface
)
4037 for iteration
in range ( 4 ):
4038 with self
. subTest ( iteration
= iteration
, expect_up
= expect_up
):
4039 operstate
= 'routable' if expect_up
else 'off'
4040 setup_state
= 'configured' if expect_up
else ( 'configuring' if iteration
== 0 else None )
4041 self
. wait_operstate ( interface
, operstate
, setup_state
= setup_state
, setup_timeout
= 20 )
4044 self
. assertIn ( 'UP' , check_output ( f
'ip link show {interface} ' ))
4045 self
. assertIn ( '192.168.10.30/24' , check_output ( f
'ip address show {interface} ' ))
4046 self
. assertIn ( 'default via 192.168.10.1' , check_output ( f
'ip route show dev {interface} ' ))
4048 self
. assertIn ( 'DOWN' , check_output ( f
'ip link show {interface} ' ))
4051 check_output ( f
'ip link set dev {interface} up' )
4053 check_output ( f
'ip link set dev {interface} down' )
4054 expect_up
= initial_up
if always
else next_up
4055 next_up
= not next_up
4059 def test_activation_policy ( self
):
4061 for interface
in [ 'test1' , 'vlan99' ]:
4062 for test
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , '' ]:
4068 print ( f
'### test_activation_policy(interface= {interface} , test= {test} )' )
4069 with self
. subTest ( interface
= interface
, test
= test
):
4070 self
._ test
_ activation
_ policy
( interface
, test
)
4072 def _test_activation_policy_required_for_online ( self
, policy
, required
):
4073 conffile
= '25-activation-policy.network'
4074 units
= [ '11-dummy.netdev' , '12-dummy.netdev' , '12-dummy.network' , conffile
]
4076 units
+= [ f
' {conffile} .d/ {policy} .conf' ]
4078 units
+= [ f
' {conffile} .d/required- {required} .conf' ]
4079 copy_network_unit (* units
, copy_dropins
= False )
4082 if policy
. endswith ( 'down' ):
4083 self
. wait_activated ( 'test1' )
4085 if policy
. endswith ( 'down' ) or policy
== 'manual' :
4086 self
. wait_operstate ( 'test1' , 'off' , setup_state
= 'configuring' )
4088 self
. wait_online ( 'test1' )
4090 if policy
== 'always-down' :
4091 # if always-down, required for online is forced to no
4094 # otherwise if required for online is specified, it should match that
4095 expected
= required
== 'yes'
4097 # otherwise if only policy specified, required for online defaults to
4098 # true if policy is up, always-up, or bound
4099 expected
= policy
. endswith ( 'up' ) or policy
== 'bound'
4101 # default is true, if neither are specified
4104 output
= networkctl_status ( 'test1' )
4107 yesno
= 'yes' if expected
else 'no'
4108 self
. assertRegex ( output
, f
'Required For Online: {yesno} ' )
4110 def test_activation_policy_required_for_online ( self
):
4112 for policy
in [ 'up' , 'always-up' , 'manual' , 'always-down' , 'down' , 'bound' , '' ]:
4113 for required
in [ 'yes' , 'no' , '' ]:
4119 print ( f
'### test_activation_policy_required_for_online(policy= {policy} , required= {required} )' )
4120 with self
. subTest ( policy
= policy
, required
= required
):
4121 self
._ test
_ activation
_ policy
_ required
_ for
_ online
( policy
, required
)
4123 def test_domain ( self
):
4124 copy_network_unit ( '12-dummy.netdev' , '24-search-domain.network' )
4126 self
. wait_online ( 'dummy98:routable' )
4128 output
= networkctl_status ( 'dummy98' )
4130 self
. assertRegex ( output
, 'Address: 192.168.42.100' )
4131 self
. assertRegex ( output
, 'DNS: 192.168.42.1' )
4132 self
. assertRegex ( output
, 'Search Domains: one' )
4134 def test_keep_configuration_static ( self
):
4135 check_output ( 'ip link add name dummy98 type dummy' )
4136 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
4137 check_output ( 'ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500' )
4138 output
= check_output ( 'ip address show dummy98' )
4140 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
4141 self
. assertRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
4142 output
= check_output ( 'ip route show dev dummy98' )
4145 copy_network_unit ( '24-keep-configuration-static.network' )
4147 self
. wait_online ( 'dummy98:routable' )
4149 output
= check_output ( 'ip address show dummy98' )
4151 self
. assertRegex ( output
, 'inet 10.1.2.3/16 scope global dummy98' )
4152 self
. assertNotRegex ( output
, 'inet 10.2.3.4/16 scope global dynamic dummy98' )
4154 def check_nexthop ( self
, manage_foreign_nexthops
, first
):
4155 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
4157 output
= check_output ( 'ip nexthop list dev veth99' )
4160 self
. assertIn ( 'id 1 via 192.168.5.1 dev veth99' , output
)
4161 self
. assertIn ( 'id 2 via 2001:1234:5:8f63::2 dev veth99' , output
)
4163 self
. assertIn ( 'id 6 via 192.168.5.1 dev veth99' , output
)
4164 self
. assertIn ( 'id 7 via 2001:1234:5:8f63::2 dev veth99' , output
)
4165 self
. assertIn ( 'id 3 dev veth99' , output
)
4166 self
. assertIn ( 'id 4 dev veth99' , output
)
4168 self
. assertRegex ( output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink' )
4170 self
. assertIn ( 'id 5 via 192.168.5.3 dev veth99' , output
)
4171 self
. assertNotRegex ( output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink' )
4172 self
. assertIn ( 'id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99' , output
)
4173 if manage_foreign_nexthops
:
4174 self
. assertRegex ( output
, r
'id [0-9]* via 192.168.5.2 dev veth99' )
4176 output
= check_output ( 'ip nexthop list dev dummy98' )
4179 self
. assertIn ( 'id 20 via 192.168.20.1 dev dummy98' , output
)
4181 self
. assertIn ( 'id 21 via 192.168.20.1 dev dummy98' , output
)
4182 if manage_foreign_nexthops
:
4183 self
. assertNotIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
4185 self
. assertIn ( 'id 42 via 192.168.20.2 dev dummy98' , output
)
4187 # kernel manages blackhole nexthops on lo
4188 output
= check_output ( 'ip nexthop list dev lo' )
4191 self
. assertIn ( 'id 6 blackhole' , output
)
4192 self
. assertIn ( 'id 7 blackhole' , output
)
4194 self
. assertIn ( 'id 1 blackhole' , output
)
4195 self
. assertIn ( 'id 2 blackhole' , output
)
4197 # group nexthops are shown with -0 option
4199 output
= check_output ( 'ip -0 nexthop list id 21' )
4201 self
. assertRegex ( output
, r
'id 21 group (1,3/20|20/1,3)' )
4203 output
= check_output ( 'ip -0 nexthop list id 20' )
4205 self
. assertRegex ( output
, r
'id 20 group (5,3/21|21/5,3)' )
4207 output
= check_output ( 'ip route show dev veth99 10.10.10.10' )
4210 self
. assertEqual ( '10.10.10.10 nhid 1 via 192.168.5.1 proto static' , output
)
4212 self
. assertEqual ( '10.10.10.10 nhid 6 via 192.168.5.1 proto static' , output
)
4214 output
= check_output ( 'ip route show dev veth99 10.10.10.11' )
4217 self
. assertEqual ( '10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static' , output
)
4219 self
. assertEqual ( '10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static' , output
)
4221 output
= check_output ( 'ip route show dev veth99 10.10.10.12' )
4224 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink' , output
)
4226 self
. assertEqual ( '10.10.10.12 nhid 5 via 192.168.5.3 proto static' , output
)
4228 output
= check_output ( 'ip -6 route show dev veth99 2001:1234:5:8f62::1' )
4231 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
4233 self
. assertEqual ( '2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium' , output
)
4235 output
= check_output ( 'ip route show 10.10.10.13' )
4238 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 6 dev lo proto static' , output
)
4240 self
. assertEqual ( 'blackhole 10.10.10.13 nhid 1 dev lo proto static' , output
)
4242 output
= check_output ( 'ip -6 route show 2001:1234:5:8f62::2' )
4245 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium' , output
)
4247 self
. assertEqual ( 'blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium' , output
)
4249 output
= check_output ( 'ip route show 10.10.10.14' )
4252 self
. assertIn ( '10.10.10.14 nhid 21 proto static' , output
)
4253 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
4255 self
. assertIn ( '10.10.10.14 nhid 20 proto static' , output
)
4256 self
. assertIn ( 'nexthop via 192.168.5.3 dev veth99 weight 3' , output
)
4257 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
4259 output
= networkctl_json ()
4261 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
4263 def _test_nexthop ( self
, manage_foreign_nexthops
):
4264 if not manage_foreign_nexthops
:
4265 copy_networkd_conf_dropin ( 'networkd-manage-foreign-nexthops-no.conf' )
4267 check_output ( 'ip link add dummy98 type dummy' )
4268 check_output ( 'ip link set dummy98 up' )
4269 check_output ( 'ip address add 192.168.20.20/24 dev dummy98' )
4270 check_output ( 'ip nexthop add id 42 via 192.168.20.2 dev dummy98' )
4272 copy_network_unit ( '25-nexthop-1.network' , '25-veth.netdev' , '25-veth-peer.network' ,
4273 '12-dummy.netdev' , '25-nexthop-dummy-1.network' )
4276 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
4278 remove_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
4279 copy_network_unit ( '25-nexthop-2.network' , '25-nexthop-dummy-2.network' )
4281 self
. check_nexthop ( manage_foreign_nexthops
, first
= False )
4283 remove_network_unit ( '25-nexthop-2.network' )
4284 copy_network_unit ( '25-nexthop-nothing.network' )
4286 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
4288 output
= check_output ( 'ip nexthop list dev veth99' )
4290 self
. assertEqual ( output
, '' )
4291 output
= check_output ( 'ip nexthop list dev lo' )
4293 self
. assertEqual ( output
, '' )
4295 remove_network_unit ( '25-nexthop-nothing.network' , '25-nexthop-dummy-2.network' )
4296 copy_network_unit ( '25-nexthop-1.network' , '25-nexthop-dummy-1.network' )
4297 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4298 # here to test reconfiguring with different .network files does not trigger race.
4299 # See also comments in link_drop_requests().
4300 networkctl_reconfigure ( 'dummy98' ) # reconfigured with 25-nexthop-dummy-2.network
4301 networkctl_reload () # reconfigured with 25-nexthop-dummy-1.network
4303 self
. check_nexthop ( manage_foreign_nexthops
, first
= True )
4305 # Remove nexthop with ID 20
4306 check_output ( 'ip nexthop del id 20' )
4307 copy_network_unit ( '11-dummy.netdev' , '25-nexthop-test1.network' )
4310 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4311 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4312 # hence test1 should be stuck in the configuring state.
4313 self
. wait_operstate ( 'test1' , operstate
= 'routable' , setup_state
= 'configuring' )
4315 # Wait for a while, and check if the interface is still in the configuring state.
4317 output
= networkctl_status ( 'test1' )
4318 self
. assertIn ( 'State: routable (configuring)' , output
)
4320 # Check if the route which needs nexthop 20 and 21 are forgotten.
4321 output
= networkctl_json ()
4323 self
. assertNotIn ( '"Destination":[10.10.10.14]' , output
)
4325 # Reconfigure the interface that has nexthop with ID 20 and 21,
4326 # then the route requested by test1 can be configured.
4327 networkctl_reconfigure ( 'dummy98' )
4328 self
. wait_online ( 'test1:routable' )
4330 # Check if the requested route actually configured.
4331 output
= check_output ( 'ip route show 10.10.11.10' )
4333 self
. assertIn ( '10.10.11.10 nhid 21 proto static' , output
)
4334 self
. assertIn ( 'nexthop via 192.168.5.1 dev veth99 weight 3' , output
)
4335 self
. assertIn ( 'nexthop via 192.168.20.1 dev dummy98 weight 1' , output
)
4337 remove_link ( 'veth99' )
4340 output
= check_output ( 'ip nexthop list dev lo' )
4342 self
. assertEqual ( output
, '' )
4344 @expectedFailureIfNexthopIsNotAvailable ()
4345 def test_nexthop ( self
):
4347 for manage_foreign_nexthops
in [ True , False ]:
4353 print ( f
'### test_nexthop(manage_foreign_nexthops= {manage_foreign_nexthops} )' )
4354 with self
. subTest ( manage_foreign_nexthops
= manage_foreign_nexthops
):
4355 self
._ test
_ nexthop
( manage_foreign_nexthops
)
4357 class NetworkdTCTests ( unittest
. TestCase
, Utilities
):
4365 @expectedFailureIfModuleIsNotAvailable ( 'sch_cake' )
4366 def test_qdisc_cake ( self
):
4367 copy_network_unit ( '25-qdisc-cake.network' , '12-dummy.netdev' )
4369 self
. wait_online ( 'dummy98:routable' )
4371 output
= check_output ( 'tc qdisc show dev dummy98' )
4373 self
. assertIn ( 'qdisc cake 3a: root' , output
)
4374 self
. assertIn ( 'bandwidth 500Mbit' , output
)
4375 self
. assertIn ( 'autorate-ingress' , output
)
4376 self
. assertIn ( 'diffserv8' , output
)
4377 self
. assertIn ( 'dual-dsthost' , output
)
4378 self
. assertIn ( ' nat' , output
)
4379 self
. assertIn ( ' wash' , output
)
4380 self
. assertIn ( ' split-gso' , output
)
4381 self
. assertIn ( ' raw' , output
)
4382 self
. assertIn ( ' atm' , output
)
4383 self
. assertIn ( 'overhead 128' , output
)
4384 self
. assertIn ( 'mpu 20' , output
)
4385 self
. assertIn ( 'fwmark 0xff00' , output
)
4386 self
. assertIn ( 'rtt 1s' , output
)
4387 self
. assertIn ( 'ack-filter-aggressive' , output
)
4389 @expectedFailureIfModuleIsNotAvailable ( 'sch_codel' )
4390 def test_qdisc_codel ( self
):
4391 copy_network_unit ( '25-qdisc-codel.network' , '12-dummy.netdev' )
4393 self
. wait_online ( 'dummy98:routable' )
4395 output
= check_output ( 'tc qdisc show dev dummy98' )
4397 self
. assertRegex ( output
, 'qdisc codel 33: root' )
4398 self
. assertRegex ( output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn' )
4400 @expectedFailureIfModuleIsNotAvailable ( 'sch_drr' )
4401 def test_qdisc_drr ( self
):
4402 copy_network_unit ( '25-qdisc-drr.network' , '12-dummy.netdev' )
4404 self
. wait_online ( 'dummy98:routable' )
4406 output
= check_output ( 'tc qdisc show dev dummy98' )
4408 self
. assertRegex ( output
, 'qdisc drr 2: root' )
4409 output
= check_output ( 'tc class show dev dummy98' )
4411 self
. assertRegex ( output
, 'class drr 2:30 root quantum 2000b' )
4413 @expectedFailureIfModuleIsNotAvailable ( 'sch_ets' )
4414 def test_qdisc_ets ( self
):
4415 copy_network_unit ( '25-qdisc-ets.network' , '12-dummy.netdev' )
4417 self
. wait_online ( 'dummy98:routable' )
4419 output
= check_output ( 'tc qdisc show dev dummy98' )
4422 self
. assertRegex ( output
, 'qdisc ets 3a: root' )
4423 self
. assertRegex ( output
, 'bands 10 strict 3' )
4424 self
. assertRegex ( output
, 'quanta 1 2 3 4 5' )
4425 self
. assertRegex ( output
, 'priomap 3 4 5 6 7' )
4427 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq' )
4428 def test_qdisc_fq ( self
):
4429 copy_network_unit ( '25-qdisc-fq.network' , '12-dummy.netdev' )
4431 self
. wait_online ( 'dummy98:routable' )
4433 output
= check_output ( 'tc qdisc show dev dummy98' )
4435 self
. assertRegex ( output
, 'qdisc fq 32: root' )
4436 self
. assertRegex ( output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511' )
4437 self
. assertRegex ( output
, 'quantum 1500' )
4438 self
. assertRegex ( output
, 'initial_quantum 13000' )
4439 self
. assertRegex ( output
, 'maxrate 1Mbit' )
4441 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_codel' )
4442 def test_qdisc_fq_codel ( self
):
4443 copy_network_unit ( '25-qdisc-fq_codel.network' , '12-dummy.netdev' )
4445 self
. wait_online ( 'dummy98:routable' )
4447 output
= check_output ( 'tc qdisc show dev dummy98' )
4449 self
. assertRegex ( output
, 'qdisc fq_codel 34: root' )
4450 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' )
4452 @expectedFailureIfModuleIsNotAvailable ( 'sch_fq_pie' )
4453 def test_qdisc_fq_pie ( self
):
4454 copy_network_unit ( '25-qdisc-fq_pie.network' , '12-dummy.netdev' )
4456 self
. wait_online ( 'dummy98:routable' )
4458 output
= check_output ( 'tc qdisc show dev dummy98' )
4461 self
. assertRegex ( output
, 'qdisc fq_pie 3a: root' )
4462 self
. assertRegex ( output
, 'limit 200000p' )
4464 @expectedFailureIfModuleIsNotAvailable ( 'sch_gred' )
4465 def test_qdisc_gred ( self
):
4466 copy_network_unit ( '25-qdisc-gred.network' , '12-dummy.netdev' )
4468 self
. wait_online ( 'dummy98:routable' )
4470 output
= check_output ( 'tc qdisc show dev dummy98' )
4472 self
. assertRegex ( output
, 'qdisc gred 38: root' )
4473 self
. assertRegex ( output
, 'vqs 12 default 10 grio' )
4475 @expectedFailureIfModuleIsNotAvailable ( 'sch_hhf' )
4476 def test_qdisc_hhf ( self
):
4477 copy_network_unit ( '25-qdisc-hhf.network' , '12-dummy.netdev' )
4479 self
. wait_online ( 'dummy98:routable' )
4481 output
= check_output ( 'tc qdisc show dev dummy98' )
4483 self
. assertRegex ( output
, 'qdisc hhf 3a: root' )
4484 self
. assertRegex ( output
, 'limit 1022p' )
4486 @expectedFailureIfModuleIsNotAvailable ( 'sch_htb' )
4487 def test_qdisc_htb_fifo ( self
):
4488 copy_network_unit ( '25-qdisc-htb-fifo.network' , '12-dummy.netdev' )
4490 self
. wait_online ( 'dummy98:routable' )
4492 output
= check_output ( 'tc qdisc show dev dummy98' )
4494 self
. assertRegex ( output
, 'qdisc htb 2: root' )
4495 self
. assertRegex ( output
, r
'default (0x30|30)' )
4497 self
. assertRegex ( output
, 'qdisc pfifo 37: parent 2:37' )
4498 self
. assertRegex ( output
, 'limit 100000p' )
4500 self
. assertRegex ( output
, 'qdisc bfifo 3a: parent 2:3a' )
4501 self
. assertRegex ( output
, 'limit 1000000' )
4503 self
. assertRegex ( output
, 'qdisc pfifo_head_drop 3b: parent 2:3b' )
4504 self
. assertRegex ( output
, 'limit 1023p' )
4506 self
. assertRegex ( output
, 'qdisc pfifo_fast 3c: parent 2:3c' )
4508 output
= check_output ( 'tc -d class show dev dummy98' )
4510 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4511 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4512 # which is fixed in v6.3.0 by
4513 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4514 self
. assertRegex ( output
, 'class htb 2:37 root leaf 37(:|prio) ' )
4515 self
. assertRegex ( output
, 'class htb 2:3a root leaf 3a(:|prio) ' )
4516 self
. assertRegex ( output
, 'class htb 2:3b root leaf 3b(:|prio) ' )
4517 self
. assertRegex ( output
, 'class htb 2:3c root leaf 3c(:|prio) ' )
4518 self
. assertRegex ( output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit' )
4519 self
. assertRegex ( output
, 'burst 123456' )
4520 self
. assertRegex ( output
, 'cburst 123457' )
4522 @expectedFailureIfModuleIsNotAvailable ( 'sch_ingress' )
4523 def test_qdisc_ingress ( self
):
4524 copy_network_unit ( '25-qdisc-clsact.network' , '12-dummy.netdev' ,
4525 '25-qdisc-ingress.network' , '11-dummy.netdev' )
4527 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4529 output
= check_output ( 'tc qdisc show dev dummy98' )
4531 self
. assertRegex ( output
, 'qdisc clsact' )
4533 output
= check_output ( 'tc qdisc show dev test1' )
4535 self
. assertRegex ( output
, 'qdisc ingress' )
4537 @expectedFailureIfModuleIsNotAvailable ( 'sch_netem' )
4538 def test_qdisc_netem ( self
):
4539 copy_network_unit ( '25-qdisc-netem.network' , '12-dummy.netdev' ,
4540 '25-qdisc-netem-compat.network' , '11-dummy.netdev' )
4542 self
. wait_online ( 'dummy98:routable' , 'test1:routable' )
4544 output
= check_output ( 'tc qdisc show dev dummy98' )
4546 self
. assertRegex ( output
, 'qdisc netem 30: root' )
4547 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
4549 output
= check_output ( 'tc qdisc show dev test1' )
4551 self
. assertRegex ( output
, 'qdisc netem [0-9a-f]*: root' )
4552 self
. assertRegex ( output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%' )
4554 @expectedFailureIfModuleIsNotAvailable ( 'sch_pie' )
4555 def test_qdisc_pie ( self
):
4556 copy_network_unit ( '25-qdisc-pie.network' , '12-dummy.netdev' )
4558 self
. wait_online ( 'dummy98:routable' )
4560 output
= check_output ( 'tc qdisc show dev dummy98' )
4562 self
. assertRegex ( output
, 'qdisc pie 3a: root' )
4563 self
. assertRegex ( output
, 'limit 200000' )
4565 @expectedFailureIfModuleIsNotAvailable ( 'sch_qfq' )
4566 def test_qdisc_qfq ( self
):
4567 copy_network_unit ( '25-qdisc-qfq.network' , '12-dummy.netdev' )
4569 self
. wait_online ( 'dummy98:routable' )
4571 output
= check_output ( 'tc qdisc show dev dummy98' )
4573 self
. assertRegex ( output
, 'qdisc qfq 2: root' )
4574 output
= check_output ( 'tc class show dev dummy98' )
4576 self
. assertRegex ( output
, 'class qfq 2:30 root weight 2 maxpkt 16000' )
4577 self
. assertRegex ( output
, 'class qfq 2:31 root weight 10 maxpkt 8000' )
4579 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfb' )
4580 def test_qdisc_sfb ( self
):
4581 copy_network_unit ( '25-qdisc-sfb.network' , '12-dummy.netdev' )
4583 self
. wait_online ( 'dummy98:routable' )
4585 output
= check_output ( 'tc qdisc show dev dummy98' )
4587 self
. assertRegex ( output
, 'qdisc sfb 39: root' )
4588 self
. assertRegex ( output
, 'limit 200000' )
4590 @expectedFailureIfModuleIsNotAvailable ( 'sch_sfq' )
4591 def test_qdisc_sfq ( self
):
4592 copy_network_unit ( '25-qdisc-sfq.network' , '12-dummy.netdev' )
4594 self
. wait_online ( 'dummy98:routable' )
4596 output
= check_output ( 'tc qdisc show dev dummy98' )
4598 self
. assertRegex ( output
, 'qdisc sfq 36: root' )
4599 self
. assertRegex ( output
, 'perturb 5sec' )
4601 @expectedFailureIfModuleIsNotAvailable ( 'sch_tbf' )
4602 def test_qdisc_tbf ( self
):
4603 copy_network_unit ( '25-qdisc-tbf.network' , '12-dummy.netdev' )
4605 self
. wait_online ( 'dummy98:routable' )
4607 output
= check_output ( 'tc qdisc show dev dummy98' )
4609 self
. assertRegex ( output
, 'qdisc tbf 35: root' )
4610 self
. assertRegex ( output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms' )
4612 @expectedFailureIfModuleIsNotAvailable ( 'sch_teql' )
4613 def test_qdisc_teql ( self
):
4614 call_quiet ( 'rmmod sch_teql' )
4616 copy_network_unit ( '25-qdisc-teql.network' , '12-dummy.netdev' )
4618 self
. wait_links ( 'dummy98' )
4619 check_output ( 'modprobe sch_teql max_equalizers=2' )
4620 self
. wait_online ( 'dummy98:routable' )
4622 output
= check_output ( 'tc qdisc show dev dummy98' )
4624 self
. assertRegex ( output
, 'qdisc teql1 31: root' )
4626 class NetworkdStateFileTests ( unittest
. TestCase
, Utilities
):
4634 def test_state_file ( self
):
4635 copy_network_unit ( '12-dummy.netdev' , '25-state-file-tests.network' )
4637 self
. wait_online ( 'dummy98:routable' )
4639 # make link state file updated
4640 resolvectl ( 'revert' , 'dummy98' )
4642 check_json ( networkctl_json ())
4644 output
= read_link_state_file ( 'dummy98' )
4646 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
4647 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
4648 self
. assertIn ( 'ADMIN_STATE=configured' , output
)
4649 self
. assertIn ( 'OPER_STATE=routable' , output
)
4650 self
. assertIn ( 'REQUIRED_FOR_ONLINE=yes' , output
)
4651 self
. assertIn ( 'REQUIRED_OPER_STATE_FOR_ONLINE=routable' , output
)
4652 self
. assertIn ( 'REQUIRED_FAMILY_FOR_ONLINE=both' , output
)
4653 self
. assertIn ( 'ACTIVATION_POLICY=up' , output
)
4654 self
. assertIn ( 'NETWORK_FILE=/run/systemd/network/25-state-file-tests.network' , output
)
4655 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
4656 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
4657 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
4658 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
4659 self
. assertIn ( 'LLMNR=no' , output
)
4660 self
. assertIn ( 'MDNS=yes' , output
)
4661 self
. assertIn ( 'DNSSEC=no' , output
)
4663 resolvectl ( 'dns' , 'dummy98' , '10.10.10.12#ccc.com' , '10.10.10.13' , '1111:2222::3333' )
4664 resolvectl ( 'domain' , 'dummy98' , 'hogehogehoge' , '~foofoofoo' )
4665 resolvectl ( 'llmnr' , 'dummy98' , 'yes' )
4666 resolvectl ( 'mdns' , 'dummy98' , 'no' )
4667 resolvectl ( 'dnssec' , 'dummy98' , 'yes' )
4668 timedatectl ( 'ntp-servers' , 'dummy98' , '2.fedora.pool.ntp.org' , '3.fedora.pool.ntp.org' )
4670 check_json ( networkctl_json ())
4672 output
= read_link_state_file ( 'dummy98' )
4674 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
4675 self
. assertIn ( 'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org' , output
)
4676 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
4677 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
4678 self
. assertIn ( 'LLMNR=yes' , output
)
4679 self
. assertIn ( 'MDNS=no' , output
)
4680 self
. assertIn ( 'DNSSEC=yes' , output
)
4682 timedatectl ( 'revert' , 'dummy98' )
4684 check_json ( networkctl_json ())
4686 output
= read_link_state_file ( 'dummy98' )
4688 self
. assertIn ( 'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333' , output
)
4689 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
4690 self
. assertIn ( 'DOMAINS=hogehogehoge' , output
)
4691 self
. assertIn ( 'ROUTE_DOMAINS=foofoofoo' , output
)
4692 self
. assertIn ( 'LLMNR=yes' , output
)
4693 self
. assertIn ( 'MDNS=no' , output
)
4694 self
. assertIn ( 'DNSSEC=yes' , output
)
4696 resolvectl ( 'revert' , 'dummy98' )
4698 check_json ( networkctl_json ())
4700 output
= read_link_state_file ( 'dummy98' )
4702 self
. assertIn ( 'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com' , output
)
4703 self
. assertIn ( 'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org' , output
)
4704 self
. assertIn ( 'DOMAINS=hogehoge' , output
)
4705 self
. assertIn ( 'ROUTE_DOMAINS=foofoo' , output
)
4706 self
. assertIn ( 'LLMNR=no' , output
)
4707 self
. assertIn ( 'MDNS=yes' , output
)
4708 self
. assertIn ( 'DNSSEC=no' , output
)
4710 def test_address_state ( self
):
4711 copy_network_unit ( '12-dummy.netdev' , '12-dummy-no-address.network' )
4714 self
. wait_online ( 'dummy98:degraded' )
4716 output
= read_link_state_file ( 'dummy98' )
4717 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
4718 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
4720 # with a routable IPv4 address
4721 check_output ( 'ip address add 10.1.2.3/16 dev dummy98' )
4722 self
. wait_online ( 'dummy98:routable' , ipv4
= True )
4723 self
. wait_online ( 'dummy98:routable' )
4725 output
= read_link_state_file ( 'dummy98' )
4726 self
. assertIn ( 'IPV4_ADDRESS_STATE=routable' , output
)
4727 self
. assertIn ( 'IPV6_ADDRESS_STATE=degraded' , output
)
4729 check_output ( 'ip address del 10.1.2.3/16 dev dummy98' )
4731 # with a routable IPv6 address
4732 check_output ( 'ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98' )
4733 self
. wait_online ( 'dummy98:routable' , ipv6
= True )
4734 self
. wait_online ( 'dummy98:routable' )
4736 output
= read_link_state_file ( 'dummy98' )
4737 self
. assertIn ( 'IPV4_ADDRESS_STATE=off' , output
)
4738 self
. assertIn ( 'IPV6_ADDRESS_STATE=routable' , output
)
4740 class NetworkdBondTests ( unittest
. TestCase
, Utilities
):
4748 def test_bond_keep_master ( self
):
4749 check_output ( 'ip link add bond199 type bond mode active-backup' )
4750 check_output ( 'ip link add dummy98 type dummy' )
4751 check_output ( 'ip link set dummy98 master bond199' )
4753 copy_network_unit ( '23-keep-master.network' )
4755 self
. wait_online ( 'dummy98:enslaved' )
4757 output
= check_output ( 'ip -d link show bond199' )
4759 self
. assertRegex ( output
, 'active_slave dummy98' )
4761 output
= check_output ( 'ip -d link show dummy98' )
4763 self
. assertRegex ( output
, 'master bond199' )
4765 def test_bond_active_slave ( self
):
4766 copy_network_unit ( '23-active-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
4768 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
4770 output
= check_output ( 'ip -d link show bond199' )
4772 self
. assertIn ( 'active_slave dummy98' , output
)
4774 # test case for issue #31165.
4775 since
= datetime
. datetime
. now ()
4776 networkctl_reconfigure ( 'dummy98' )
4777 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
4778 self
. assertNotIn ( 'dummy98: Bringing link down' , read_networkd_log ( since
= since
))
4780 def test_bond_primary_slave ( self
):
4781 copy_network_unit ( '23-primary-slave.network' , '23-bond199.network' , '25-bond-active-backup-slave.netdev' , '12-dummy.netdev' )
4783 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
4785 output
= check_output ( 'ip -d link show bond199' )
4787 self
. assertIn ( 'primary dummy98' , output
)
4790 mkdir_p ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d' ))
4791 for mac
in [ '00:11:22:33:44:55' , '00:11:22:33:44:56' ]:
4792 with
open ( os
. path
. join ( network_unit_dir
, '23-bond199.network.d/mac.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
4793 f
. write ( f
'[Link] \n MACAddress= {mac} \n ' )
4796 self
. wait_online ( 'dummy98:enslaved' , 'bond199:degraded' )
4798 output
= check_output ( 'ip -d link show bond199' )
4800 self
. assertIn ( f
'link/ether {mac} ' , output
)
4802 def test_bond_operstate ( self
):
4803 copy_network_unit ( '25-bond.netdev' , '11-dummy.netdev' , '12-dummy.netdev' ,
4804 '25-bond99.network' , '25-bond-slave.network' )
4806 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bond99:routable' )
4808 output
= check_output ( 'ip -d link show dummy98' )
4810 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
4812 output
= check_output ( 'ip -d link show test1' )
4814 self
. assertRegex ( output
, 'SLAVE,UP,LOWER_UP' )
4816 output
= check_output ( 'ip -d link show bond99' )
4818 self
. assertRegex ( output
, 'MASTER,UP,LOWER_UP' )
4820 self
. wait_operstate ( 'dummy98' , 'enslaved' )
4821 self
. wait_operstate ( 'test1' , 'enslaved' )
4822 self
. wait_operstate ( 'bond99' , 'routable' )
4824 check_output ( 'ip link set dummy98 down' )
4826 self
. wait_operstate ( 'dummy98' , 'off' )
4827 self
. wait_operstate ( 'test1' , 'enslaved' )
4828 self
. wait_operstate ( 'bond99' , 'routable' )
4830 check_output ( 'ip link set dummy98 up' )
4832 self
. wait_operstate ( 'dummy98' , 'enslaved' )
4833 self
. wait_operstate ( 'test1' , 'enslaved' )
4834 self
. wait_operstate ( 'bond99' , 'routable' )
4836 check_output ( 'ip link set dummy98 down' )
4837 check_output ( 'ip link set test1 down' )
4839 self
. wait_operstate ( 'dummy98' , 'off' )
4840 self
. wait_operstate ( 'test1' , 'off' )
4842 if not self
. wait_operstate ( 'bond99' , 'no-carrier' , setup_timeout
= 30 , fail_assert
= False ):
4843 # Huh? Kernel does not recognize that all slave interfaces are down?
4844 # Let's confirm that networkd's operstate is consistent with ip's result.
4845 self
. assertNotRegex ( output
, 'NO-CARRIER' )
4847 class NetworkdBridgeTests ( unittest
. TestCase
, Utilities
):
4855 def test_bridge_mac_none ( self
):
4856 copy_network_unit ( '12-dummy-mac.netdev' , '26-bridge-mac-slave.network' ,
4857 '26-bridge-mac.netdev' , '26-bridge-mac-master.network' , '26-bridge-mac.link' )
4859 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:degraded' )
4861 output
= check_output ( 'ip link show dev dummy98' )
4863 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
4865 output
= check_output ( 'ip link show dev bridge99' )
4867 self
. assertIn ( 'link/ether 12:34:56:78:9a:01' , output
)
4869 def test_bridge_vlan ( self
):
4870 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave.network' ,
4871 '26-bridge.netdev' , '26-bridge-vlan-master.network' ,
4874 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
4876 output
= check_output ( 'bridge vlan show dev test1' )
4878 # check if the default VID is removed
4879 self
. assertNotIn ( '1 Egress Untagged' , output
)
4880 for i
in range ( 1000 , 3000 ):
4882 self
. assertIn ( f
' {i} PVID' , output
)
4883 elif i
in range ( 1012 , 1016 ) or i
in range ( 1103 , 1109 ):
4884 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4885 elif i
in range ( 1008 , 1014 ) or i
in range ( 1100 , 1111 ):
4886 self
. assertIn ( f
' {i} ' , output
)
4888 self
. assertNotIn ( f
' {i} ' , output
)
4890 output
= check_output ( 'bridge vlan show dev bridge99' )
4892 # check if the default VID is removed
4893 self
. assertNotIn ( '1 Egress Untagged' , output
)
4894 for i
in range ( 1000 , 3000 ):
4896 self
. assertIn ( f
' {i} PVID' , output
)
4897 elif i
in range ( 1022 , 1026 ) or i
in range ( 1203 , 1209 ):
4898 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4899 elif i
in range ( 1018 , 1024 ) or i
in range ( 1200 , 1211 ):
4900 self
. assertIn ( f
' {i} ' , output
)
4902 self
. assertNotIn ( f
' {i} ' , output
)
4905 copy_network_unit ( '26-bridge-vlan-slave.network.d/10-override.conf' ,
4906 '26-bridge-vlan-master.network.d/10-override.conf' )
4908 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
4910 output
= check_output ( 'bridge vlan show dev test1' )
4912 for i
in range ( 1000 , 3000 ):
4914 self
. assertIn ( f
' {i} PVID' , output
)
4915 elif i
in range ( 2012 , 2016 ) or i
in range ( 2103 , 2109 ):
4916 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4917 elif i
in range ( 2008 , 2014 ) or i
in range ( 2100 , 2111 ):
4918 self
. assertIn ( f
' {i} ' , output
)
4920 self
. assertNotIn ( f
' {i} ' , output
)
4922 output
= check_output ( 'bridge vlan show dev bridge99' )
4924 for i
in range ( 1000 , 3000 ):
4926 self
. assertIn ( f
' {i} PVID' , output
)
4927 elif i
in range ( 2022 , 2026 ) or i
in range ( 2203 , 2209 ):
4928 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4929 elif i
in range ( 2018 , 2024 ) or i
in range ( 2200 , 2211 ):
4930 self
. assertIn ( f
' {i} ' , output
)
4932 self
. assertNotIn ( f
' {i} ' , output
)
4934 # Remove several vlan IDs
4935 copy_network_unit ( '26-bridge-vlan-slave.network.d/20-override.conf' ,
4936 '26-bridge-vlan-master.network.d/20-override.conf' )
4938 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
4940 output
= check_output ( 'bridge vlan show dev test1' )
4942 for i
in range ( 1000 , 3000 ):
4944 self
. assertIn ( f
' {i} PVID' , output
)
4945 elif i
in range ( 2012 , 2016 ):
4946 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4947 elif i
in range ( 2008 , 2014 ):
4948 self
. assertIn ( f
' {i} ' , output
)
4950 self
. assertNotIn ( f
' {i} ' , output
)
4952 output
= check_output ( 'bridge vlan show dev bridge99' )
4954 for i
in range ( 1000 , 3000 ):
4956 self
. assertIn ( f
' {i} PVID' , output
)
4957 elif i
in range ( 2022 , 2026 ):
4958 self
. assertIn ( f
' {i} Egress Untagged' , output
)
4959 elif i
in range ( 2018 , 2024 ):
4960 self
. assertIn ( f
' {i} ' , output
)
4962 self
. assertNotIn ( f
' {i} ' , output
)
4964 # Remove all vlan IDs
4965 copy_network_unit ( '26-bridge-vlan-slave.network.d/30-override.conf' ,
4966 '26-bridge-vlan-master.network.d/30-override.conf' )
4968 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
4970 output
= check_output ( 'bridge vlan show dev test1' )
4972 self
. assertNotIn ( 'PVID' , output
)
4973 for i
in range ( 1000 , 3000 ):
4974 self
. assertNotIn ( f
' {i} ' , output
)
4976 output
= check_output ( 'bridge vlan show dev bridge99' )
4978 self
. assertNotIn ( 'PVID' , output
)
4979 for i
in range ( 1000 , 3000 ):
4980 self
. assertNotIn ( f
' {i} ' , output
)
4982 def test_bridge_vlan_issue_20373 ( self
):
4983 copy_network_unit ( '11-dummy.netdev' , '26-bridge-vlan-slave-issue-20373.network' ,
4984 '26-bridge-issue-20373.netdev' , '26-bridge-vlan-master-issue-20373.network' ,
4985 '21-vlan.netdev' , '21-vlan.network' )
4987 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' , 'vlan99:routable' )
4989 output
= check_output ( 'bridge vlan show dev test1' )
4991 self
. assertIn ( '100 PVID Egress Untagged' , output
)
4992 self
. assertIn ( '560' , output
)
4993 self
. assertIn ( '600' , output
)
4995 output
= check_output ( 'bridge vlan show dev bridge99' )
4997 self
. assertIn ( '1 PVID Egress Untagged' , output
)
4998 self
. assertIn ( '100' , output
)
4999 self
. assertIn ( '600' , output
)
5001 def test_bridge_mdb ( self
):
5002 copy_network_unit ( '11-dummy.netdev' , '26-bridge-mdb-slave.network' ,
5003 '26-bridge.netdev' , '26-bridge-mdb-master.network' )
5005 self
. wait_online ( 'test1:enslaved' , 'bridge99:degraded' )
5007 output
= check_output ( 'bridge mdb show dev bridge99' )
5009 self
. assertRegex ( output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064' )
5010 self
. assertRegex ( output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065' )
5012 # Old kernel may not support bridge MDB entries on bridge master
5013 if call_quiet ( 'bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068' ) == 0 :
5014 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066' )
5015 self
. assertRegex ( output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067' )
5017 def test_bridge_keep_master ( self
):
5018 check_output ( 'ip link add bridge99 type bridge' )
5019 check_output ( 'ip link set bridge99 up' )
5020 check_output ( 'ip link add dummy98 type dummy' )
5021 check_output ( 'ip link set dummy98 master bridge99' )
5023 copy_network_unit ( '23-keep-master.network' )
5025 self
. wait_online ( 'dummy98:enslaved' )
5027 output
= check_output ( 'ip -d link show dummy98' )
5029 self
. assertRegex ( output
, 'master bridge99' )
5030 self
. assertRegex ( output
, 'bridge' )
5032 output
= check_output ( 'bridge -d link show dummy98' )
5034 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
5035 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
5036 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
5037 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
5038 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
5039 # CONFIG_BRIDGE_IGMP_SNOOPING=y
5040 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
5041 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
5042 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
5043 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
5044 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
5045 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
5047 def test_bridge_property ( self
):
5048 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
5049 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
5050 '25-bridge99.network' )
5052 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
5054 output
= check_output ( 'ip -d link show bridge99' )
5056 self
. assertIn ( 'mtu 9000 ' , output
)
5058 output
= check_output ( 'ip -d link show test1' )
5060 self
. assertIn ( 'master bridge99 ' , output
)
5061 self
. assertIn ( 'bridge_slave' , output
)
5062 self
. assertIn ( 'mtu 9000 ' , output
)
5064 output
= check_output ( 'ip -d link show dummy98' )
5066 self
. assertIn ( 'master bridge99 ' , output
)
5067 self
. assertIn ( 'bridge_slave' , output
)
5068 self
. assertIn ( 'mtu 9000 ' , output
)
5070 output
= check_output ( 'ip addr show bridge99' )
5072 self
. assertIn ( '192.168.0.15/24' , output
)
5074 output
= check_output ( 'bridge -d link show dummy98' )
5076 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'path_cost' , '400' )
5077 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'hairpin_mode' , '1' )
5078 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'isolated' , '1' )
5079 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_fast_leave' , '1' )
5080 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'unicast_flood' , '1' )
5081 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_flood' , '0' )
5082 # CONFIG_BRIDGE_IGMP_SNOOPING=y
5083 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'multicast_to_unicast' , '1' , allow_enoent
= True )
5084 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'neigh_suppress' , '1' , allow_enoent
= True )
5085 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'learning' , '0' )
5086 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'priority' , '23' )
5087 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'bpdu_guard' , '0' )
5088 self
. check_bridge_port_attr ( 'bridge99' , 'dummy98' , 'root_block' , '0' )
5090 output
= check_output ( 'bridge -d link show test1' )
5092 self
. check_bridge_port_attr ( 'bridge99' , 'test1' , 'priority' , '0' )
5094 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
5095 output
= check_output ( 'ip addr show bridge99' )
5097 self
. assertIn ( '192.168.0.16/24' , output
)
5100 print ( '### ip -6 route list table all dev bridge99' )
5101 output
= check_output ( 'ip -6 route list table all dev bridge99' )
5103 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
5105 remove_link ( 'test1' )
5106 self
. wait_operstate ( 'bridge99' , 'routable' )
5108 output
= check_output ( 'ip -d link show bridge99' )
5110 self
. assertIn ( 'mtu 9000 ' , output
)
5112 output
= check_output ( 'ip -d link show dummy98' )
5114 self
. assertIn ( 'master bridge99 ' , output
)
5115 self
. assertIn ( 'bridge_slave' , output
)
5116 self
. assertIn ( 'mtu 9000 ' , output
)
5118 remove_link ( 'dummy98' )
5119 self
. wait_operstate ( 'bridge99' , 'no-carrier' )
5121 output
= check_output ( 'ip -d link show bridge99' )
5123 # When no carrier, the kernel may reset the MTU
5124 self
. assertIn ( 'NO-CARRIER' , output
)
5126 output
= check_output ( 'ip address show bridge99' )
5128 self
. assertNotIn ( '192.168.0.15/24' , output
)
5129 self
. assertIn ( '192.168.0.16/24' , output
) # foreign address is kept
5131 print ( '### ip -6 route list table all dev bridge99' )
5132 output
= check_output ( 'ip -6 route list table all dev bridge99' )
5134 self
. assertRegex ( output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium' )
5136 check_output ( 'ip link add dummy98 type dummy' )
5137 self
. wait_online ( 'dummy98:enslaved' , 'bridge99:routable' )
5139 output
= check_output ( 'ip -d link show bridge99' )
5141 self
. assertIn ( 'mtu 9000 ' , output
)
5143 output
= check_output ( 'ip -d link show dummy98' )
5145 self
. assertIn ( 'master bridge99 ' , output
)
5146 self
. assertIn ( 'bridge_slave' , output
)
5147 self
. assertIn ( 'mtu 9000 ' , output
)
5149 def test_bridge_configure_without_carrier ( self
):
5150 copy_network_unit ( '26-bridge.netdev' , '26-bridge-configure-without-carrier.network' ,
5154 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5155 for test
in [ 'no-slave' , 'add-slave' , 'slave-up' , 'slave-no-carrier' , 'slave-carrier' , 'slave-down' ]:
5156 with self
. subTest ( test
= test
):
5157 if test
== 'no-slave' :
5158 # bridge has no slaves; it's up but *might* not have carrier
5159 self
. wait_operstate ( 'bridge99' , operstate
= r
'(no-carrier|routable)' , setup_state
= None , setup_timeout
= 30 )
5160 # due to a bug in the kernel, newly-created bridges are brought up
5161 # *with* carrier, unless they have had any setting changed; e.g.
5162 # their mac set, priority set, etc. Then, they will lose carrier
5163 # as soon as a (down) slave interface is added, and regain carrier
5164 # again once the slave interface is brought up.
5165 #self.check_link_attr('bridge99', 'carrier', '0')
5166 elif test
== 'add-slave' :
5167 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5168 self
. check_link_attr ( 'test1' , 'operstate' , 'down' )
5169 check_output ( 'ip link set dev test1 master bridge99' )
5170 self
. wait_operstate ( 'bridge99' , operstate
= 'no-carrier' , setup_state
= None )
5171 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
5172 elif test
== 'slave-up' :
5173 # bring up slave, which will have carrier; bridge gains carrier
5174 check_output ( 'ip link set dev test1 up' )
5175 self
. wait_online ( 'bridge99:routable' )
5176 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
5177 elif test
== 'slave-no-carrier' :
5178 # drop slave carrier; bridge loses carrier
5179 check_output ( 'ip link set dev test1 carrier off' )
5180 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
5181 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
5182 elif test
== 'slave-carrier' :
5183 # restore slave carrier; bridge gains carrier
5184 check_output ( 'ip link set dev test1 carrier on' )
5185 self
. wait_online ( 'bridge99:routable' )
5186 self
. check_link_attr ( 'bridge99' , 'carrier' , '1' )
5187 elif test
== 'slave-down' :
5188 # bring down slave; bridge loses carrier
5189 check_output ( 'ip link set dev test1 down' )
5190 self
. wait_online ( 'bridge99:no-carrier:no-carrier' )
5191 self
. check_link_attr ( 'bridge99' , 'carrier' , '0' )
5193 output
= networkctl_status ( 'bridge99' )
5194 self
. assertRegex ( output
, '10.1.2.3' )
5195 self
. assertRegex ( output
, '10.1.2.1' )
5197 def test_bridge_ignore_carrier_loss ( self
):
5198 copy_network_unit ( '11-dummy.netdev' , '12-dummy.netdev' , '26-bridge.netdev' ,
5199 '26-bridge-slave-interface-1.network' , '26-bridge-slave-interface-2.network' ,
5200 '25-bridge99-ignore-carrier-loss.network' )
5202 self
. wait_online ( 'dummy98:enslaved' , 'test1:enslaved' , 'bridge99:routable' )
5204 check_output ( 'ip address add 192.168.0.16/24 dev bridge99' )
5205 remove_link ( 'test1' , 'dummy98' )
5208 output
= check_output ( 'ip address show bridge99' )
5210 self
. assertRegex ( output
, 'NO-CARRIER' )
5211 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
5212 self
. assertRegex ( output
, 'inet 192.168.0.16/24 scope global secondary bridge99' )
5214 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain ( self
):
5215 copy_network_unit ( '26-bridge.netdev' , '26-bridge-slave-interface-1.network' ,
5216 '25-bridge99-ignore-carrier-loss.network' )
5218 self
. wait_online ( 'bridge99:no-carrier' )
5220 for trial
in range ( 4 ):
5221 check_output ( 'ip link add dummy98 type dummy' )
5222 check_output ( 'ip link set dummy98 up' )
5224 remove_link ( 'dummy98' )
5226 self
. wait_online ( 'bridge99:routable' , 'dummy98:enslaved' )
5228 output
= check_output ( 'ip address show bridge99' )
5230 self
. assertRegex ( output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99' )
5232 output
= check_output ( 'ip rule list table 100' )
5234 self
. assertIn ( 'from all to 8.8.8.8 lookup 100' , output
)
5236 class NetworkdSRIOVTests ( unittest
. TestCase
, Utilities
):
5244 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
5245 def test_sriov ( self
):
5246 copy_network_unit ( '25-default.link' , '25-sriov.network' )
5248 call ( 'modprobe netdevsim' )
5250 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
5253 with
open ( '/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
5257 self
. wait_online ( 'eni99np1:routable' )
5259 output
= check_output ( 'ip link show dev eni99np1' )
5261 self
. assertRegex ( output
,
5262 '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 *'
5263 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
5264 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5267 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable ()
5268 def test_sriov_udev ( self
):
5269 copy_network_unit ( '25-sriov.link' , '25-sriov-udev.network' )
5271 call ( 'modprobe netdevsim' )
5273 with
open ( '/sys/bus/netdevsim/new_device' , mode
= 'w' , encoding
= 'utf-8' ) as f
:
5277 self
. wait_online ( 'eni99np1:routable' )
5279 # the name eni99np1 may be an alternative name.
5280 ifname
= link_resolve ( 'eni99np1' )
5282 output
= check_output ( 'ip link show dev eni99np1' )
5284 self
. assertRegex ( output
,
5285 '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 *'
5286 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
5287 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5289 self
. assertNotIn ( 'vf 3' , output
)
5290 self
. assertNotIn ( 'vf 4' , output
)
5292 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5293 f
. write ( '[Link] \n SR-IOVVirtualFunctions=4 \n ' )
5296 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
5298 output
= check_output ( 'ip link show dev eni99np1' )
5300 self
. assertRegex ( output
,
5301 '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 *'
5302 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
5303 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
5306 self
. assertNotIn ( 'vf 4' , output
)
5308 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5309 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
5312 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
5314 output
= check_output ( 'ip link show dev eni99np1' )
5316 self
. assertRegex ( output
,
5317 '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 *'
5318 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
5319 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off \n *'
5322 self
. assertNotIn ( 'vf 4' , output
)
5324 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5325 f
. write ( '[Link] \n SR-IOVVirtualFunctions=2 \n ' )
5328 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
5330 output
= check_output ( 'ip link show dev eni99np1' )
5332 self
. assertRegex ( output
,
5333 '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 *'
5334 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5336 self
. assertNotIn ( 'vf 2' , output
)
5337 self
. assertNotIn ( 'vf 3' , output
)
5338 self
. assertNotIn ( 'vf 4' , output
)
5340 with
open ( os
. path
. join ( network_unit_dir
, '25-sriov.link' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5341 f
. write ( '[Link] \n SR-IOVVirtualFunctions= \n ' )
5344 udevadm_trigger ( f
'/sys/devices/netdevsim99/net/ {ifname} ' )
5346 output
= check_output ( 'ip link show dev eni99np1' )
5348 self
. assertRegex ( output
,
5349 '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 *'
5350 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off \n *'
5351 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5353 self
. assertNotIn ( 'vf 3' , output
)
5354 self
. assertNotIn ( 'vf 4' , output
)
5356 class NetworkdLLDPTests ( unittest
. TestCase
, Utilities
):
5364 def test_lldp ( self
):
5365 copy_network_unit ( '23-emit-lldp.network' , '24-lldp.network' , '25-veth.netdev' )
5367 self
. wait_online ( 'veth99:degraded' , 'veth-peer:degraded' )
5369 for trial
in range ( 10 ):
5373 output
= networkctl ( 'lldp' )
5375 if re
. search ( r
'veth99 .* veth-peer .* .......a...' , output
):
5380 # With interface name
5381 output
= networkctl ( 'lldp' , 'veth99' );
5383 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
5385 # With interface name pattern
5386 output
= networkctl ( 'lldp' , 've*9' );
5388 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* .......a...' )
5391 output
= networkctl ( '--json=short' , 'lldp' )
5393 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5394 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5395 self
. assertIn ( '"EnabledCapabilities":128' , output
)
5397 # json format with interface name
5398 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
5400 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5401 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5402 self
. assertIn ( '"EnabledCapabilities":128' , output
)
5404 # json format with interface name pattern
5405 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
5407 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5408 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5409 self
. assertIn ( '"EnabledCapabilities":128' , output
)
5411 # LLDP neighbors in status
5412 output
= networkctl_status ( 'veth99' )
5414 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
5416 # enable forwarding, to enable the Router flag
5417 with
open ( os
. path
. join ( network_unit_dir
, '23-emit-lldp.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5418 f
. write ( '[Network] \n IPv4Forwarding=yes \n ' )
5421 self
. wait_online ( 'veth-peer:degraded' )
5423 for trial
in range ( 10 ):
5427 output
= networkctl ( 'lldp' )
5429 if re
. search ( r
'veth99 .* veth-peer .* ....r......' , output
):
5434 # With interface name
5435 output
= networkctl ( 'lldp' , 'veth99' );
5437 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
5439 # With interface name pattern
5440 output
= networkctl ( 'lldp' , 've*9' );
5442 self
. assertRegex ( output
, r
'veth99 .* veth-peer .* ....r......' )
5445 output
= networkctl ( '--json=short' , 'lldp' )
5447 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5448 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5449 self
. assertIn ( '"EnabledCapabilities":16' , output
)
5451 # json format with interface name
5452 output
= networkctl ( '--json=short' , 'lldp' , 'veth99' )
5454 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5455 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5456 self
. assertIn ( '"EnabledCapabilities":16' , output
)
5458 # json format with interface name pattern
5459 output
= networkctl ( '--json=short' , 'lldp' , 've*9' )
5461 self
. assertIn ( '"InterfaceName":"veth99"' , output
)
5462 self
. assertIn ( '"PortID":"veth-peer"' , output
)
5463 self
. assertIn ( '"EnabledCapabilities":16' , output
)
5465 # LLDP neighbors in status
5466 output
= networkctl_status ( 'veth99' )
5468 self
. assertRegex ( output
, r
'Connected To: .* on port veth-peer' )
5470 class NetworkdRATests ( unittest
. TestCase
, Utilities
):
5478 def test_ipv6_prefix_delegation ( self
):
5479 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth.network' )
5480 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
5481 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
5482 self
. setup_nftset ( 'ifindex' , 'iface_index' )
5484 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
5486 # IPv6SendRA=yes implies IPv6Forwarding.
5487 self
. check_ipv6_sysctl_attr ( 'veth-peer' , 'forwarding' , '1' )
5489 output
= resolvectl ( 'dns' , 'veth99' )
5491 self
. assertRegex ( output
, 'fe80::' )
5492 self
. assertRegex ( output
, '2002:da8:1::1' )
5494 output
= resolvectl ( 'domain' , 'veth99' )
5496 self
. assertIn ( 'hogehoge.test' , output
)
5498 output
= networkctl_status ( 'veth99' )
5500 self
. assertRegex ( output
, '2002:da8:1:0' )
5502 self
. check_netlabel ( 'veth99' , '2002:da8:1::/64' )
5503 self
. check_netlabel ( 'veth99' , '2002:da8:2::/64' )
5505 self
. check_nftset ( 'addr6' , '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
5506 self
. check_nftset ( 'addr6' , '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
5507 self
. check_nftset ( 'network6' , '2002:da8:1::/64' )
5508 self
. check_nftset ( 'network6' , '2002:da8:2::/64' )
5509 self
. check_nftset ( 'ifindex' , 'veth99' )
5511 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
5513 def check_ipv6_token_static ( self
):
5514 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
5516 output
= networkctl_status ( 'veth99' )
5518 self
. assertRegex ( output
, '2002:da8:1:0:1a:2b:3c:4d' )
5519 self
. assertRegex ( output
, '2002:da8:1:0:fa:de:ca:fe' )
5520 self
. assertRegex ( output
, '2002:da8:2:0:1a:2b:3c:4d' )
5521 self
. assertRegex ( output
, '2002:da8:2:0:fa:de:ca:fe' )
5523 def test_ipv6_token_static ( self
):
5524 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
5527 self
. check_ipv6_token_static ()
5530 check_output ( 'ip link set veth99 down' )
5531 check_output ( 'ip link set veth99 up' )
5533 self
. check_ipv6_token_static ()
5536 check_output ( 'ip link set veth99 down' )
5537 time
. sleep ( random
. uniform ( 0 , 0.1 ))
5538 check_output ( 'ip link set veth99 up' )
5539 time
. sleep ( random
. uniform ( 0 , 0.1 ))
5541 self
. check_ipv6_token_static ()
5543 def test_ndisc_redirect ( self
):
5544 if not os
. path
. exists ( test_ndisc_send
):
5545 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
5547 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-static.network' )
5550 self
. check_ipv6_token_static ()
5552 # Introduce two redirect routes.
5553 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' )
5554 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::1 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d' )
5555 self
. wait_route ( 'veth99' , r
'2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5556 self
. wait_route ( 'veth99' , r
'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5558 # Change the target address of the redirects.
5559 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::2 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d' )
5560 check_output ( f
' {test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1::3 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d' )
5561 self
. wait_route_dropped ( 'veth99' , r
'2002:da8:1:1:1a:2b:3c:4d proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5562 self
. wait_route_dropped ( 'veth99' , r
'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::1 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5563 self
. wait_route ( 'veth99' , r
'2002:da8:1:1:1a:2b:3c:4d via 2002:da8:1::2 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5564 self
. wait_route ( 'veth99' , r
'2002:da8:1:2:1a:2b:3c:4d via 2002:da8:1::3 proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5566 # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
5567 # Then, verify that all redirect routes and the default route are dropped.
5568 output
= check_output ( 'ip -6 address show dev veth-peer scope link' )
5569 veth_peer_ipv6ll
= re
. search ( 'fe80:[:0-9a-f]*' , output
). group ()
5570 print ( f
'veth-peer IPv6LL address: {veth_peer_ipv6ll} ' )
5571 check_output ( f
' {test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no' )
5572 self
. wait_route_dropped ( 'veth99' , 'proto redirect' , ipv
= '-6' , timeout_sec
= 10 )
5573 self
. wait_route_dropped ( 'veth99' , 'proto ra' , ipv
= '-6' , timeout_sec
= 10 )
5575 def check_ndisc_mtu ( self
, mtu
):
5577 output
= read_ipv6_sysctl_attr ( 'veth99' , 'mtu' )
5578 if output
== f
' {mtu} ' :
5582 self
. fail ( f
'IPv6 MTU does not matches: value= {output} , expected= {mtu} ' )
5584 def test_ndisc_mtu ( self
):
5585 if not os
. path
. exists ( test_ndisc_send
):
5586 self
. skipTest ( f
" {test_ndisc_send} does not exist." )
5588 copy_network_unit ( '25-veth.netdev' ,
5589 '25-veth-peer-no-address.network' ,
5590 '25-ipv6-prefix-veth-token-static.network' )
5592 self
. wait_online ( 'veth-peer:degraded' )
5595 output
= read_networkd_log ()
5596 if 'veth99: NDISC: Started IPv6 Router Solicitation client' in output
:
5600 self
. fail ( 'sd-ndisc does not started on veth99.' )
5602 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400' )
5603 self
. check_ndisc_mtu ( 1400 )
5605 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410' )
5606 self
. check_ndisc_mtu ( 1410 )
5608 check_output ( 'ip link set dev veth99 mtu 1600' )
5609 self
. check_ndisc_mtu ( 1410 )
5611 check_output ( f
' {test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700' )
5612 self
. check_ndisc_mtu ( 1600 )
5614 check_output ( 'ip link set dev veth99 mtu 1800' )
5615 self
. check_ndisc_mtu ( 1700 )
5617 def test_ipv6_token_prefixstable ( self
):
5618 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable.network' )
5620 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
5622 output
= check_output ( 'ip -6 address show dev veth99' )
5624 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
5625 self
. assertIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
5627 with
open ( os
. path
. join ( network_unit_dir
, '25-ipv6-prefix-veth-token-prefixstable.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5628 f
. write ( ' \n [IPv6AcceptRA] \n PrefixAllowList=2002:da8:1:0::/64 \n ' )
5631 self
. wait_online ( 'veth99:routable' )
5633 output
= check_output ( 'ip -6 address show dev veth99' )
5635 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
5636 self
. assertNotIn ( '2002:da8:2:0:1034:56ff:fe78:9abc/64' , output
) # EUI64
5638 check_output ( 'ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99' )
5639 check_output ( 'ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad' )
5641 networkctl_reconfigure ( 'veth99' )
5642 self
. wait_online ( 'veth99:routable' )
5644 output
= check_output ( 'ip -6 address show dev veth99' )
5646 self
. assertNotIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
5647 self
. assertIn ( '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , output
) # the 2nd prefixstable
5649 check_output ( 'ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99' )
5650 check_output ( 'ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad' )
5652 networkctl_reconfigure ( 'veth99' )
5653 self
. wait_online ( 'veth99:routable' )
5655 output
= check_output ( 'ip -6 address show dev veth99' )
5657 self
. assertNotIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e/64' , output
) # the 1st prefixstable
5658 self
. assertNotIn ( '2002:da8:1:0:da5d:e50a:43fd:5d0f/64' , output
) # the 2nd prefixstable
5659 self
. assertIn ( '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64' , output
) # the 3rd prefixstable
5661 def test_ipv6_token_prefixstable_without_address ( self
):
5662 copy_network_unit ( '25-veth.netdev' , '25-ipv6-prefix.network' , '25-ipv6-prefix-veth-token-prefixstable-without-address.network' )
5664 self
. wait_online ( 'veth99:routable' , 'veth-peer:degraded' )
5666 output
= networkctl_status ( 'veth99' )
5668 self
. assertIn ( '2002:da8:1:0:b47e:7975:fc7a:7d6e' , output
)
5669 self
. assertIn ( '2002:da8:2:0:f689:561a:8eda:7443' , output
)
5671 def check_router_hop_limit ( self
, hop_limit
):
5672 self
. wait_route ( 'client' , rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit} ' , ipv
= '-6' , timeout_sec
= 10 )
5674 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99' )
5676 self
. assertIn ( f
'hoplimit {hop_limit} ' , output
)
5678 self
. check_ipv6_sysctl_attr ( 'client' , 'hop_limit' , f
' {hop_limit} ' )
5680 def test_router_hop_limit ( self
):
5681 copy_network_unit ( '25-veth-client.netdev' ,
5682 '25-veth-router.netdev' ,
5684 '25-veth-bridge.network' ,
5685 '25-veth-client.network' ,
5686 '25-veth-router-hop-limit.network' ,
5687 '25-bridge99.network' )
5689 self
. wait_online ( 'client-p:enslaved' ,
5690 'router:degraded' , 'router-p:enslaved' ,
5691 'bridge99:routable' )
5693 self
. check_router_hop_limit ( 42 )
5695 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-router-hop-limit.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5696 f
. write ( ' \n [IPv6SendRA] \n HopLimit=43 \n ' )
5700 self
. check_router_hop_limit ( 43 )
5702 def test_router_preference ( self
):
5703 copy_network_unit ( '25-veth-client.netdev' ,
5704 '25-veth-router-high.netdev' ,
5705 '25-veth-router-low.netdev' ,
5707 '25-veth-bridge.network' ,
5708 '25-veth-client.network' ,
5709 '25-veth-router-high.network' ,
5710 '25-veth-router-low.network' ,
5711 '25-bridge99.network' )
5713 self
. wait_online ( 'client-p:enslaved' ,
5714 'router-high:degraded' , 'router-high-p:enslaved' ,
5715 'router-low:degraded' , 'router-low-p:enslaved' ,
5716 'bridge99:routable' )
5718 networkctl_reconfigure ( 'client' )
5719 self
. wait_online ( 'client:routable' )
5721 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
5722 self
. wait_address ( 'client' , '2002:da8:1:98:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
5723 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512' , ipv
= '-6' , timeout_sec
= 10 )
5724 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048' , ipv
= '-6' , timeout_sec
= 10 )
5726 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99' )
5728 self
. assertIn ( 'metric 512' , output
)
5729 self
. assertIn ( 'pref high' , output
)
5730 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98' )
5732 self
. assertIn ( 'metric 2048' , output
)
5733 self
. assertIn ( 'pref low' , output
)
5735 with
open ( os
. path
. join ( network_unit_dir
, '25-veth-client.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5736 f
. write ( ' \n [Link] \n MACAddress=12:34:56:78:9a:01 \n [IPv6AcceptRA] \n RouteMetric=100:200:300 \n ' )
5739 self
. wait_online ( 'client:routable' )
5741 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a01/64' , ipv
= '-6' , timeout_sec
= 10 )
5742 self
. wait_address ( 'client' , '2002:da8:1:98:1034:56ff:fe78:9a01/64' , ipv
= '-6' , timeout_sec
= 10 )
5743 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100' , ipv
= '-6' , timeout_sec
= 10 )
5744 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300' , ipv
= '-6' , timeout_sec
= 10 )
5746 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99' )
5748 self
. assertIn ( 'metric 100' , output
)
5749 self
. assertNotIn ( 'metric 512' , output
)
5750 self
. assertIn ( 'pref high' , output
)
5751 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98' )
5753 self
. assertIn ( 'metric 300' , output
)
5754 self
. assertNotIn ( 'metric 2048' , output
)
5755 self
. assertIn ( 'pref low' , output
)
5757 # swap the preference (for issue #28439)
5758 remove_network_unit ( '25-veth-router-high.network' , '25-veth-router-low.network' )
5759 copy_network_unit ( '25-veth-router-high2.network' , '25-veth-router-low2.network' )
5761 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300' , ipv
= '-6' , timeout_sec
= 10 )
5762 self
. wait_route ( 'client' , 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100' , ipv
= '-6' , timeout_sec
= 10 )
5764 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99' )
5766 self
. assertIn ( 'metric 300' , output
)
5767 self
. assertNotIn ( 'metric 100' , output
)
5768 self
. assertIn ( 'pref low' , output
)
5769 self
. assertNotIn ( 'pref high' , output
)
5770 output
= check_output ( 'ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98' )
5772 self
. assertIn ( 'metric 100' , output
)
5773 self
. assertNotIn ( 'metric 300' , output
)
5774 self
. assertIn ( 'pref high' , output
)
5775 self
. assertNotIn ( 'pref low' , output
)
5777 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
5778 def test_captive_portal ( self
):
5779 copy_network_unit ( '25-veth-client.netdev' ,
5780 '25-veth-router-captive.netdev' ,
5782 '25-veth-client-captive.network' ,
5783 '25-veth-router-captive.network' ,
5784 '25-veth-bridge-captive.network' ,
5785 '25-bridge99.network' )
5787 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
5788 'router-captive:degraded' , 'router-captivep:enslaved' )
5790 start_radvd ( config_file
= 'captive-portal.conf' )
5791 networkctl_reconfigure ( 'client' )
5792 self
. wait_online ( 'client:routable' )
5794 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
5795 output
= networkctl_status ( 'client' )
5797 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
5799 @unittest . skipUnless ( radvd_check_config ( 'captive-portal.conf' ), "Installed radvd doesn't support captive portals" )
5800 def test_invalid_captive_portal ( self
):
5801 def radvd_write_config ( captive_portal_uri
):
5802 with
open ( os
. path
. join ( networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
5803 f
. write ( f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI " {captive_portal_uri} "; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};' )
5805 captive_portal_uris
= [
5806 "42ěščěškd ěšč ě s" ,
5811 copy_network_unit ( '25-veth-client.netdev' ,
5812 '25-veth-router-captive.netdev' ,
5814 '25-veth-client-captive.network' ,
5815 '25-veth-router-captive.network' ,
5816 '25-veth-bridge-captive.network' ,
5817 '25-bridge99.network' )
5819 self
. wait_online ( 'bridge99:routable' , 'client-p:enslaved' ,
5820 'router-captive:degraded' , 'router-captivep:enslaved' )
5822 for uri
in captive_portal_uris
:
5823 print ( f
"Captive portal: {uri} " )
5824 radvd_write_config ( uri
)
5826 start_radvd ( config_file
= 'bogus-captive-portal.conf' )
5827 networkctl_reconfigure ( 'client' )
5828 self
. wait_online ( 'client:routable' )
5830 self
. wait_address ( 'client' , '2002:da8:1:99:1034:56ff:fe78:9a00/64' , ipv
= '-6' , timeout_sec
= 10 )
5831 output
= networkctl_status ( 'client' )
5833 self
. assertNotIn ( 'Captive Portal:' , output
)
5835 class NetworkdDHCPServerTests ( unittest
. TestCase
, Utilities
):
5843 def check_dhcp_server ( self
, persist_leases
= True ):
5844 output
= networkctl_status ( 'veth99' )
5846 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)' )
5847 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
5848 self
. assertRegex ( output
, 'DNS: 192.168.5.1 \n *192.168.5.10' )
5849 self
. assertRegex ( output
, 'NTP: 192.168.5.1 \n *192.168.5.11' )
5851 output
= networkctl_status ( 'veth-peer' )
5853 self
. assertRegex ( output
, "Offered DHCP leases: 192.168.5.[0-9]*" )
5856 with
open ( '/var/lib/systemd/network/dhcp-server-lease/veth-peer' , encoding
= 'utf-8' ) as f
:
5857 check_json ( f
. read ())
5859 self
. assertFalse ( os
. path
. exists ( '/var/lib/systemd/network/dhcp-server-lease/veth-peer' ))
5861 def test_dhcp_server ( self
):
5862 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
5864 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5866 self
. check_dhcp_server ()
5868 networkctl_reconfigure ( 'veth-peer' )
5869 self
. wait_online ( 'veth-peer:routable' )
5872 output
= check_output (* networkctl_cmd
, '-n' , '0' , 'status' , 'veth-peer' , env
= env
)
5873 if 'Offered DHCP leases: 192.168.5.' in output
:
5879 def test_dhcp_server_persist_leases_no ( self
):
5880 copy_networkd_conf_dropin ( 'persist-leases-no.conf' )
5881 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server.network' )
5883 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5885 self
. check_dhcp_server ( persist_leases
= False )
5887 remove_networkd_conf_dropin ( 'persist-leases-no.conf' )
5888 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-server.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
5889 f
. write ( '[DHCPServer] \n PersistLeases=no' )
5891 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5893 self
. check_dhcp_server ( persist_leases
= False )
5895 def test_dhcp_server_null_server_address ( self
):
5896 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-null-server-address.network' )
5898 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5900 output
= check_output ( 'ip --json address show dev veth-peer' )
5901 server_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
5902 print ( server_address
)
5904 output
= check_output ( 'ip --json address show dev veth99' )
5905 client_address
= json
. loads ( output
)[ 0 ][ 'addr_info' ][ 0 ][ 'local' ]
5906 print ( client_address
)
5908 output
= networkctl_status ( 'veth99' )
5910 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCP4 via {server_address} \)' )
5911 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
5912 self
. assertIn ( f
'DNS: {server_address} ' , output
)
5913 self
. assertIn ( f
'NTP: {server_address} ' , output
)
5915 output
= networkctl_status ( 'veth-peer' )
5916 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
5918 # Check if the same addresses are used even if the service is restarted.
5920 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5922 output
= check_output ( 'ip -4 address show dev veth-peer' )
5924 self
. assertIn ( f
' {server_address} ' , output
)
5926 output
= check_output ( 'ip -4 address show dev veth99' )
5928 self
. assertIn ( f
' {client_address} ' , output
)
5930 output
= networkctl_status ( 'veth99' )
5932 self
. assertRegex ( output
, rf
'Address: {client_address} \(DHCP4 via {server_address} \)' )
5933 self
. assertIn ( f
'Gateway: {server_address} ' , output
)
5934 self
. assertIn ( f
'DNS: {server_address} ' , output
)
5935 self
. assertIn ( f
'NTP: {server_address} ' , output
)
5937 output
= networkctl_status ( 'veth-peer' )
5938 self
. assertIn ( f
'Offered DHCP leases: {client_address} ' , output
)
5940 def test_dhcp_server_with_uplink ( self
):
5941 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-downstream.network' ,
5942 '12-dummy.netdev' , '25-dhcp-server-uplink.network' )
5944 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5946 output
= networkctl_status ( 'veth99' )
5948 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)' )
5949 self
. assertIn ( 'Gateway: 192.168.5.3' , output
)
5950 self
. assertIn ( 'DNS: 192.168.5.1' , output
)
5951 self
. assertIn ( 'NTP: 192.168.5.1' , output
)
5953 def test_emit_router_timezone ( self
):
5954 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-timezone-router.network' , '25-dhcp-server-timezone-router.network' )
5956 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5958 output
= networkctl_status ( 'veth99' )
5960 self
. assertRegex ( output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)' )
5961 self
. assertIn ( 'Gateway: 192.168.5.1' , output
)
5962 self
. assertIn ( 'Time Zone: Europe/Berlin' , output
)
5964 def test_dhcp_server_static_lease ( self
):
5965 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client-static-lease.network' , '25-dhcp-server-static-lease.network' )
5967 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5969 output
= networkctl_status ( 'veth99' )
5971 self
. assertIn ( 'Address: 10.1.1.200 (DHCP4 via 10.1.1.1)' , output
)
5972 self
. assertIn ( 'DHCP4 Client ID: 12:34:56:78:9a:bc' , output
)
5974 def test_dhcp_server_static_lease_default_client_id ( self
):
5975 copy_network_unit ( '25-veth.netdev' , '25-dhcp-client.network' , '25-dhcp-server-static-lease.network' )
5977 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
5979 output
= networkctl_status ( 'veth99' )
5981 self
. assertIn ( 'Address: 10.1.1.200 (DHCP4 via 10.1.1.1)' , output
)
5982 self
. assertRegex ( output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID' )
5984 class NetworkdDHCPServerRelayAgentTests ( unittest
. TestCase
, Utilities
):
5992 def test_relay_agent ( self
):
5993 copy_network_unit ( '25-agent-veth-client.netdev' ,
5994 '25-agent-veth-server.netdev' ,
5995 '25-agent-client.network' ,
5996 '25-agent-server.network' ,
5997 '25-agent-client-peer.network' ,
5998 '25-agent-server-peer.network' )
6001 self
. wait_online ( 'client:routable' )
6003 output
= networkctl_status ( 'client' )
6005 self
. assertRegex ( output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)' )
6007 def test_relay_agent_on_bridge ( self
):
6008 copy_network_unit ( '25-agent-bridge.netdev' ,
6009 '25-agent-veth-client.netdev' ,
6010 '25-agent-bridge.network' ,
6011 '25-agent-bridge-port.network' ,
6012 '25-agent-client.network' )
6014 self
. wait_online ( 'bridge-relay:routable' , 'client-peer:enslaved' )
6017 expect
= 'bridge-relay: DHCPv4 server: STARTED'
6019 if expect
in read_networkd_log ():
6025 class NetworkdDHCPClientTests ( unittest
. TestCase
, Utilities
):
6033 def test_dhcp_client_ipv6_only ( self
):
6034 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
6037 self
. wait_online ( 'veth-peer:carrier' )
6039 # information request mode
6040 # The name ipv6-only option may not be supported by older dnsmasq
6041 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
6042 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
6043 '--dhcp-option=option6:dns-server,[2600::ee]' ,
6044 '--dhcp-option=option6:ntp-server,[2600::ff]' ,
6045 ra_mode
= 'ra-stateless' )
6046 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6048 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
6049 # Let's wait for the expected DNS server being listed in the state file.
6050 for _
in range ( 100 ):
6051 output
= read_link_state_file ( 'veth99' )
6052 if 'DNS=2600::ee' in output
:
6056 # Check link state file
6057 print ( '## link state file' )
6058 output
= read_link_state_file ( 'veth99' )
6060 self
. assertIn ( 'DNS=2600::ee' , output
)
6061 self
. assertIn ( 'NTP=2600::ff' , output
)
6063 # Check manager state file
6064 print ( '## manager state file' )
6065 output
= read_manager_state_file ()
6067 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
6068 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
6070 print ( '## dnsmasq log' )
6071 output
= read_dnsmasq_log_file ()
6073 self
. assertIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
6074 self
. assertNotIn ( 'DHCPSOLICIT(veth-peer)' , output
)
6075 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
6076 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
6077 self
. assertNotIn ( 'DHCPREPLY(veth-peer)' , output
)
6080 check_json ( networkctl_json ( 'veth99' ))
6084 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
6085 '--dhcp-option=option6:dns-server,[2600::ee]' ,
6086 '--dhcp-option=option6:ntp-server,[2600::ff]' )
6087 networkctl_reconfigure ( 'veth99' )
6088 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6091 output
= check_output ( 'ip address show dev veth99 scope global' )
6093 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
6094 self
. assertNotIn ( '192.168.5' , output
)
6096 # checking semi-static route
6097 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
6099 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
6101 # Confirm that ipv6 token is not set in the kernel
6102 output
= check_output ( 'ip token show dev veth99' )
6104 self
. assertRegex ( output
, 'token :: dev veth99' )
6106 # Make manager and link state file updated
6107 resolvectl ( 'revert' , 'veth99' )
6109 # Check link state file
6110 print ( '## link state file' )
6111 output
= read_link_state_file ( 'veth99' )
6113 self
. assertIn ( 'DNS=2600::ee' , output
)
6114 self
. assertIn ( 'NTP=2600::ff' , output
)
6116 # Check manager state file
6117 print ( '## manager state file' )
6118 output
= read_manager_state_file ()
6120 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
6121 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
6123 print ( '## dnsmasq log' )
6124 output
= read_dnsmasq_log_file ()
6126 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
6127 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
6128 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
6129 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
6130 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
6131 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
6134 check_json ( networkctl_json ( 'veth99' ))
6136 # Testing without rapid commit support
6137 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-ipv6-only.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6138 f
. write ( ' \n [DHCPv6] \n RapidCommit=no \n ' )
6141 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' ,
6142 '--dhcp-option=option6:dns-server,[2600::ee]' ,
6143 '--dhcp-option=option6:ntp-server,[2600::ff]' )
6146 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6149 output
= check_output ( 'ip address show dev veth99 scope global' )
6151 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
6152 self
. assertNotIn ( '192.168.5' , output
)
6154 # checking semi-static route
6155 output
= check_output ( 'ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff' )
6157 self
. assertRegex ( output
, 'via fe80::1034:56ff:fe78:9abd' )
6159 # Make manager and link state file updated
6160 resolvectl ( 'revert' , 'veth99' )
6162 # Check link state file
6163 print ( '## link state file' )
6164 output
= read_link_state_file ( 'veth99' )
6166 self
. assertIn ( 'DNS=2600::ee' , output
)
6167 self
. assertIn ( 'NTP=2600::ff' , output
)
6169 # Check manager state file
6170 print ( '## manager state file' )
6171 output
= read_manager_state_file ()
6173 self
. assertRegex ( output
, 'DNS=.*2600::ee' )
6174 self
. assertRegex ( output
, 'NTP=.*2600::ff' )
6176 print ( '## dnsmasq log' )
6177 output
= read_dnsmasq_log_file ()
6179 self
. assertNotIn ( 'DHCPINFORMATION-REQUEST(veth-peer)' , output
)
6180 self
. assertIn ( 'DHCPSOLICIT(veth-peer)' , output
)
6181 self
. assertIn ( 'DHCPADVERTISE(veth-peer)' , output
)
6182 self
. assertIn ( 'DHCPREQUEST(veth-peer)' , output
)
6183 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
6184 self
. assertNotIn ( 'rapid-commit' , output
)
6187 check_json ( networkctl_json ( 'veth99' ))
6189 def test_dhcp_client_ipv6_dbus_status ( self
):
6190 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only.network' )
6192 self
. wait_online ( 'veth-peer:carrier' )
6194 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
6195 # bit set) has yet been received and the configuration does not include WithoutRA=true
6196 state
= get_dhcp6_client_state ( 'veth99' )
6197 print ( f
"DHCPv6 client state = {state} " )
6198 self
. assertEqual ( state
, 'stopped' )
6200 state
= get_dhcp4_client_state ( 'veth99' )
6201 print ( f
"DHCPv4 client state = {state} " )
6202 self
. assertEqual ( state
, 'selecting' )
6204 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
6205 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6207 state
= get_dhcp6_client_state ( 'veth99' )
6208 print ( f
"DHCPv6 client state = {state} " )
6209 self
. assertEqual ( state
, 'bound' )
6211 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
6212 for _
in range ( 100 ):
6213 state
= get_dhcp4_client_state ( 'veth99' )
6214 if state
== 'stopped' :
6218 print ( f
"DHCPv4 client state = {state} " )
6219 self
. assertEqual ( state
, 'stopped' )
6221 # restart dnsmasq to clear log
6223 start_dnsmasq ( '--dhcp-option=108,00:00:02:00' )
6225 # Test renew command
6226 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
6227 networkctl ( 'renew' , 'veth99' )
6229 for _
in range ( 100 ):
6230 state
= get_dhcp4_client_state ( 'veth99' )
6231 if state
== 'stopped' :
6235 print ( f
"DHCPv4 client state = {state} " )
6236 self
. assertEqual ( state
, 'stopped' )
6238 print ( '## dnsmasq log' )
6239 output
= read_dnsmasq_log_file ()
6241 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc' , output
)
6242 self
. assertIn ( 'DHCPOFFER(veth-peer)' , output
)
6243 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
6244 self
. assertNotIn ( 'DHCPACK(veth-peer)' , output
)
6246 def test_dhcp_client_ipv6_only_with_custom_client_identifier ( self
):
6247 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv6-only-custom-client-identifier.network' )
6250 self
. wait_online ( 'veth-peer:carrier' )
6252 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6255 output
= check_output ( 'ip address show dev veth99 scope global' )
6257 self
. assertRegex ( output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute' )
6258 self
. assertNotIn ( '192.168.5' , output
)
6260 print ( '## dnsmasq log' )
6261 output
= read_dnsmasq_log_file ()
6263 self
. assertIn ( 'DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00' , output
)
6264 self
. assertNotIn ( 'DHCPADVERTISE(veth-peer)' , output
)
6265 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
6266 self
. assertIn ( 'DHCPREPLY(veth-peer)' , output
)
6267 self
. assertIn ( 'sent size: 0 option: 14 rapid-commit' , output
)
6269 def test_dhcp_client_ipv4_only ( self
):
6270 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' )
6272 self
. setup_nftset ( 'addr4' , 'ipv4_addr' )
6273 self
. setup_nftset ( 'network4' , 'ipv4_addr' , 'flags interval;' )
6274 self
. setup_nftset ( 'ifindex' , 'iface_index' )
6277 self
. wait_online ( 'veth-peer:carrier' )
6278 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
6279 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22' ,
6280 '--dhcp-option=option:domain-search,example.com' ,
6281 '--dhcp-alternate-port=67,5555' ,
6282 ipv4_range
= '192.168.5.110,192.168.5.119' )
6283 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6284 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
6286 print ( '## ip address show dev veth99 scope global' )
6287 output
= check_output ( 'ip address show dev veth99 scope global' )
6289 self
. assertIn ( 'mtu 1492' , output
)
6290 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
6291 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' )
6292 self
. assertNotIn ( '2600::' , output
)
6294 output
= check_output ( 'ip -4 --json address show dev veth99' )
6295 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
6296 if i
[ 'label' ] == 'test-label' :
6297 address1
= i
[ 'local' ]
6300 self
. assertFalse ( True )
6302 self
. assertRegex ( address1
, r
'^192.168.5.11[0-9]$' )
6304 print ( '## ip route show table main dev veth99' )
6305 output
= check_output ( 'ip route show table main dev veth99' )
6307 # no DHCP routes assigned to the main table
6308 self
. assertNotIn ( 'proto dhcp' , output
)
6310 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
6311 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
6312 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
6313 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
6315 print ( '## ip route show table 211 dev veth99' )
6316 output
= check_output ( 'ip route show table 211 dev veth99' )
6318 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24' )
6319 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24' )
6320 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24' )
6321 self
. assertRegex ( output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24' )
6322 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24' )
6323 self
. assertIn ( '10.0.0.0/8 via 192.168.5.1 proto dhcp' , output
)
6325 print ( '## link state file' )
6326 output
= read_link_state_file ( 'veth99' )
6328 # checking DNS server, SIP server, and Domains
6329 self
. assertIn ( 'DNS=192.168.5.6 192.168.5.7' , output
)
6330 self
. assertIn ( 'SIP=192.168.5.21 192.168.5.22' , output
)
6331 self
. assertIn ( 'DOMAINS=example.com' , output
)
6334 j
= json
. loads ( networkctl_json ( 'veth99' ))
6336 self
. assertEqual ( len ( j
[ 'DNS' ]), 2 )
6339 self
. assertEqual ( i
[ 'Family' ], 2 )
6340 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
6341 self
. assertRegex ( a
, '^192.168.5.[67]$' )
6342 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
6343 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
6344 self
. assertEqual ( '192.168.5.1' , a
)
6346 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
6349 self
. assertEqual ( i
[ 'Family' ], 2 )
6350 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
6351 self
. assertRegex ( a
, '^192.168.5.2[12]$' )
6352 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
6353 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
6354 self
. assertEqual ( '192.168.5.1' , a
)
6356 print ( '## dnsmasq log' )
6357 output
= read_dnsmasq_log_file ()
6359 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
6360 self
. assertIn ( 'DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc' , output
)
6361 self
. assertIn ( 'client provides name: test-hostname' , output
)
6362 self
. assertIn ( '26:mtu' , output
)
6364 # change address range, DNS servers, and Domains
6366 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8' ,
6367 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24' ,
6368 '--dhcp-option=option:domain-search,foo.example.com' ,
6369 '--dhcp-alternate-port=67,5555' ,
6370 ipv4_range
= '192.168.5.120,192.168.5.129' ,)
6372 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6373 print ( 'Wait for the DHCP lease to be expired' )
6374 self
. wait_address_dropped ( 'veth99' , f
'inet {address1} /24' , ipv
= '-4' , timeout_sec
= 120 )
6375 self
. wait_address ( 'veth99' , r
'inet 192.168.5.12[0-9]*/24' , ipv
= '-4' )
6377 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6379 print ( '## ip address show dev veth99 scope global' )
6380 output
= check_output ( 'ip address show dev veth99 scope global' )
6382 self
. assertIn ( 'mtu 1492' , output
)
6383 self
. assertIn ( 'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99' , output
)
6384 self
. assertNotIn ( f
' {address1} ' , output
)
6385 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' )
6386 self
. assertNotIn ( '2600::' , output
)
6388 output
= check_output ( 'ip -4 --json address show dev veth99' )
6389 for i
in json
. loads ( output
)[ 0 ][ 'addr_info' ]:
6390 if i
[ 'label' ] == 'test-label' :
6391 address2
= i
[ 'local' ]
6394 self
. assertFalse ( True )
6396 self
. assertRegex ( address2
, r
'^192.168.5.12[0-9]$' )
6398 print ( '## ip route show table main dev veth99' )
6399 output
= check_output ( 'ip route show table main dev veth99' )
6401 # no DHCP routes assigned to the main table
6402 self
. assertNotIn ( 'proto dhcp' , output
)
6404 self
. assertIn ( '192.168.5.0/24 proto kernel scope link src 192.168.5.250' , output
)
6405 self
. assertIn ( '192.168.5.0/24 proto static scope link' , output
)
6406 self
. assertIn ( '192.168.6.0/24 proto static scope link' , output
)
6407 self
. assertIn ( '192.168.7.0/24 proto static scope link' , output
)
6409 print ( '## ip route show table 211 dev veth99' )
6410 output
= check_output ( 'ip route show table 211 dev veth99' )
6412 self
. assertRegex ( output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24' )
6413 self
. assertRegex ( output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24' )
6414 self
. assertRegex ( output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24' )
6415 self
. assertNotIn ( '192.168.5.6' , output
)
6416 self
. assertRegex ( output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24' )
6417 self
. assertRegex ( output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24' )
6418 self
. assertIn ( '10.0.0.0/8 via 192.168.5.1 proto dhcp' , output
)
6420 print ( '## link state file' )
6421 output
= read_link_state_file ( 'veth99' )
6423 # checking DNS server, SIP server, and Domains
6424 self
. assertIn ( 'DNS=192.168.5.1 192.168.5.7 192.168.5.8' , output
)
6425 self
. assertIn ( 'SIP=192.168.5.23 192.168.5.24' , output
)
6426 self
. assertIn ( 'DOMAINS=foo.example.com' , output
)
6429 j
= json
. loads ( networkctl_json ( 'veth99' ))
6431 self
. assertEqual ( len ( j
[ 'DNS' ]), 3 )
6434 self
. assertEqual ( i
[ 'Family' ], 2 )
6435 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
6436 self
. assertRegex ( a
, '^192.168.5.[178]$' )
6437 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
6438 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
6439 self
. assertEqual ( '192.168.5.1' , a
)
6441 self
. assertEqual ( len ( j
[ 'SIP' ]), 2 )
6444 self
. assertEqual ( i
[ 'Family' ], 2 )
6445 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'Address' ]))
6446 self
. assertRegex ( a
, '^192.168.5.2[34]$' )
6447 self
. assertEqual ( i
[ 'ConfigSource' ], 'DHCPv4' )
6448 a
= socket
. inet_ntop ( socket
. AF_INET
, bytearray ( i
[ 'ConfigProvider' ]))
6449 self
. assertEqual ( '192.168.5.1' , a
)
6451 print ( '## dnsmasq log' )
6452 output
= read_dnsmasq_log_file ()
6454 self
. assertIn ( 'vendor class: FooBarVendorTest' , output
)
6455 self
. assertIn ( f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc' , output
)
6456 self
. assertIn ( 'client provides name: test-hostname' , output
)
6457 self
. assertIn ( '26:mtu' , output
)
6459 self
. check_netlabel ( 'veth99' , r
'192\.168\.5\.0/24' )
6461 self
. check_nftset ( 'addr4' , r
'192\.168\.5\.1' )
6462 self
. check_nftset ( 'network4' , r
'192\.168\.5\.0/24' )
6463 self
. check_nftset ( 'ifindex' , 'veth99' )
6465 self
. teardown_nftset ( 'addr4' , 'network4' , 'ifindex' )
6467 def test_dhcp_client_ipv4_dbus_status ( self
):
6468 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-ipv4-only.network' )
6470 self
. wait_online ( 'veth-peer:carrier' )
6472 state
= get_dhcp4_client_state ( 'veth99' )
6473 print ( f
"State = {state} " )
6474 self
. assertEqual ( state
, 'rebooting' )
6476 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7' ,
6477 '--dhcp-option=option:domain-search,example.com' ,
6478 '--dhcp-alternate-port=67,5555' ,
6479 ipv4_range
= '192.168.5.110,192.168.5.119' )
6480 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6481 self
. wait_address ( 'veth99' , r
'inet 192.168.5.11[0-9]*/24' , ipv
= '-4' )
6483 state
= get_dhcp4_client_state ( 'veth99' )
6484 print ( f
"State = {state} " )
6485 self
. assertEqual ( state
, 'bound' )
6487 def test_dhcp_client_allow_list ( self
):
6488 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-allow-list.network' , copy_dropins
= False )
6491 self
. wait_online ( 'veth-peer:carrier' )
6492 since
= datetime
. datetime
. now ()
6495 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6497 if expect
in read_networkd_log ( since
= since
):
6503 copy_network_unit ( '25-dhcp-client-allow-list.network.d/00-allow-list.conf' )
6504 since
= datetime
. datetime
. now ()
6507 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6509 if expect
in read_networkd_log ( since
= since
):
6515 copy_network_unit ( '25-dhcp-client-allow-list.network.d/10-deny-list.conf' )
6516 since
= datetime
. datetime
. now ()
6519 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6521 if expect
in read_networkd_log ( since
= since
):
6527 @unittest . skipUnless ( "--dhcp-rapid-commit" in run ( "dnsmasq --help" ). stdout
, reason
= "dnsmasq is missing dhcp-rapid-commit support" )
6528 def test_dhcp_client_rapid_commit ( self
):
6529 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
6531 self
. wait_online ( 'veth-peer:carrier' )
6533 start_dnsmasq ( '--dhcp-rapid-commit' )
6534 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6535 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
6537 state
= get_dhcp4_client_state ( 'veth99' )
6538 print ( f
"DHCPv4 client state = {state} " )
6539 self
. assertEqual ( state
, 'bound' )
6541 output
= read_dnsmasq_log_file ()
6542 self
. assertIn ( 'DHCPDISCOVER(veth-peer)' , output
)
6543 self
. assertNotIn ( 'DHCPOFFER(veth-peer)' , output
)
6544 self
. assertNotIn ( 'DHCPREQUEST(veth-peer)' , output
)
6545 self
. assertIn ( 'DHCPACK(veth-peer)' , output
)
6547 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity ( self
):
6548 copy_network_unit ( '25-veth.netdev' ,
6549 '25-dhcp-server-ipv6-only-mode.network' ,
6550 '25-dhcp-client-ipv6-only-mode.network' )
6552 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , timeout
= '40s' )
6553 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24' , ipv
= '-4' )
6555 state
= get_dhcp4_client_state ( 'veth99' )
6556 print ( f
"State = {state} " )
6557 self
. assertEqual ( state
, 'bound' )
6559 def test_dhcp_client_ipv4_use_routes_gateway ( self
):
6561 for ( routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
. product ([ True , False ], repeat
= 4 ):
6567 print ( f
'### test_dhcp_client_ipv4_use_routes_gateway(routes= {routes} , gateway= {gateway} , dns_and_ntp_routes= {dns_and_ntp_routes} , classless= {classless} )' )
6568 with self
. subTest ( routes
= routes
, gateway
= gateway
, dns_and_ntp_routes
= dns_and_ntp_routes
, classless
= classless
):
6569 self
._ test
_ dhcp
_ client
_ ipv
4_u se
_ routes
_ gateway
( routes
, gateway
, dns_and_ntp_routes
, classless
)
6571 def _test_dhcp_client_ipv4_use_routes_gateway ( self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6572 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6573 testunits
= [ '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , testunit
]
6574 testunits
. append ( f
' {testunit} .d/use-routes- {use_routes} .conf' )
6575 testunits
. append ( f
' {testunit} .d/use-gateway- {use_gateway} .conf' )
6576 testunits
. append ( f
' {testunit} .d/use-dns-and-ntp-routes- {dns_and_ntp_routes} .conf' )
6577 copy_network_unit (* testunits
, copy_dropins
= False )
6580 self
. wait_online ( 'veth-peer:carrier' )
6581 additional_options
= [
6582 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8' ,
6583 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9' ,
6584 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6587 additional_options
+= [
6588 '--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'
6590 start_dnsmasq (* additional_options
)
6591 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6593 output
= check_output ( 'ip -4 route show dev veth99' )
6599 self
. assertRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6600 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6601 self
. assertRegex ( output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6602 self
. assertRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6603 self
. assertRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6605 self
. assertRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6606 self
. assertRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6607 self
. assertRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6608 self
. assertRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6610 self
. assertNotRegex ( output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6611 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6612 self
. assertNotRegex ( output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6613 self
. assertNotRegex ( output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6614 self
. assertNotRegex ( output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6615 self
. assertNotRegex ( output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6616 self
. assertNotRegex ( output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6617 self
. assertNotRegex ( output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6620 if use_gateway
and ( not classless
or not use_routes
):
6621 self
. assertRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6623 self
. assertNotRegex ( output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6625 # Check route to gateway
6626 if ( use_gateway
or dns_and_ntp_routes
) and ( not classless
or not use_routes
):
6627 self
. assertRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6629 self
. assertNotRegex ( output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6631 # Check RoutesToDNS= and RoutesToNTP=
6632 if dns_and_ntp_routes
:
6633 self
. assertRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6634 self
. assertRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6637 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6638 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6640 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6641 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6643 self
. assertRegex ( output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6644 self
. assertRegex ( output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024' )
6646 self
. assertNotRegex ( output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6647 self
. assertNotRegex ( output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024' )
6648 self
. assertNotRegex ( output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
6649 self
. assertNotRegex ( output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024' )
6651 check_json ( networkctl_json ())
6653 def test_dhcp_client_settings_anonymize ( self
):
6654 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-anonymize.network' )
6656 self
. wait_online ( 'veth-peer:carrier' )
6658 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6660 print ( '## dnsmasq log' )
6661 output
= read_dnsmasq_log_file ()
6663 self
. assertNotIn ( 'VendorClassIdentifier=SusantVendorTest' , output
)
6664 self
. assertNotIn ( 'test-hostname' , output
)
6665 self
. assertNotIn ( '26:mtu' , output
)
6667 def test_dhcp_keep_configuration_dhcp ( self
):
6668 copy_network_unit ( '25-veth.netdev' ,
6669 '25-dhcp-server-veth-peer.network' ,
6670 '25-dhcp-client-keep-configuration-dhcp.network' )
6672 self
. wait_online ( 'veth-peer:carrier' )
6674 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6676 output
= check_output ( 'ip address show dev veth99 scope global' )
6678 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6679 'valid_lft forever preferred_lft forever' )
6681 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6684 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6685 print ( 'Wait for the DHCP lease to be expired' )
6688 # The lease address should be kept after the lease expired
6689 output
= check_output ( 'ip address show dev veth99 scope global' )
6691 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6692 'valid_lft forever preferred_lft forever' )
6696 # The lease address should be kept after networkd stopped
6697 output
= check_output ( 'ip address show dev veth99 scope global' )
6699 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6700 'valid_lft forever preferred_lft forever' )
6702 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network' ), mode
= 'a' , encoding
= 'utf-8' ) as f
:
6703 f
. write ( '[Network] \n DHCP=no \n ' )
6706 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6708 # Still the lease address should be kept after networkd restarted
6709 output
= check_output ( 'ip address show dev veth99 scope global' )
6711 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6712 'valid_lft forever preferred_lft forever' )
6714 def test_dhcp_keep_configuration_dhcp_on_stop ( self
):
6715 copy_network_unit ( '25-veth.netdev' ,
6716 '25-dhcp-server-veth-peer.network' ,
6717 '25-dhcp-client-keep-configuration-dhcp-on-stop.network' )
6719 self
. wait_online ( 'veth-peer:carrier' )
6721 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6723 output
= check_output ( 'ip address show dev veth99 scope global' )
6725 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
6730 output
= check_output ( 'ip address show dev veth99 scope global' )
6732 self
. assertRegex ( output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
6735 self
. wait_online ( 'veth-peer:routable' )
6737 output
= check_output ( 'ip address show dev veth99 scope global' )
6739 self
. assertNotIn ( '192.168.5.' , output
)
6741 def test_dhcp_client_reuse_address_as_static ( self
):
6742 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' )
6744 self
. wait_online ( 'veth-peer:carrier' )
6746 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6748 # link become 'routable' when at least one protocol provide an valid address.
6749 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
6750 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
6752 output
= check_output ( 'ip address show dev veth99 scope global' )
6753 ipv4_address
= re
. search ( r
'192.168.5.[0-9]*/24' , output
). group ()
6754 ipv6_address
= re
. search ( r
'2600::[0-9a-f:]*/128' , output
). group ()
6755 static_network
= ' \n ' . join ([ '[Match]' , 'Name=veth99' , '[Network]' , 'IPv6AcceptRA=no' , 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6756 print ( static_network
)
6758 remove_network_unit ( '25-dhcp-client.network' )
6760 with
open ( os
. path
. join ( network_unit_dir
, '25-static.network' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
6761 f
. write ( static_network
)
6764 self
. wait_online ( 'veth99:routable' )
6766 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
6768 self
. assertRegex ( output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99 \n *'
6769 'valid_lft forever preferred_lft forever' )
6771 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
6773 self
. assertRegex ( output
, f
'inet6 {ipv6_address} scope global * \n *'
6774 'valid_lft forever preferred_lft forever' )
6776 @expectedFailureIfModuleIsNotAvailable ( 'vrf' )
6777 def test_dhcp_client_vrf ( self
):
6778 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client-vrf.network' ,
6779 '25-vrf.netdev' , '25-vrf.network' )
6781 self
. wait_online ( 'veth-peer:carrier' )
6783 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'vrf99:carrier' )
6785 # link become 'routable' when at least one protocol provide an valid address.
6786 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
6787 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
6789 print ( '## ip -d link show dev vrf99' )
6790 output
= check_output ( 'ip -d link show dev vrf99' )
6792 self
. assertRegex ( output
, 'vrf table 42' )
6794 print ( '## ip address show vrf vrf99' )
6795 output
= check_output ( 'ip address show vrf vrf99' )
6797 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
6798 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
6799 self
. assertRegex ( output
, 'inet6 .* scope link' )
6801 print ( '## ip address show dev veth99' )
6802 output
= check_output ( 'ip address show dev veth99' )
6804 self
. assertRegex ( output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99' )
6805 self
. assertRegex ( output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
6806 self
. assertRegex ( output
, 'inet6 .* scope link' )
6808 print ( '## ip route show vrf vrf99' )
6809 output
= check_output ( 'ip route show vrf vrf99' )
6811 self
. assertRegex ( output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.' )
6812 self
. assertRegex ( output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5' )
6813 self
. assertRegex ( output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5' )
6815 print ( '## ip route show table main dev veth99' )
6816 output
= check_output ( 'ip route show table main dev veth99' )
6818 self
. assertEqual ( output
, '' )
6820 def test_dhcp_client_gateway_onlink_implicit ( self
):
6821 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
6822 '25-dhcp-client-gateway-onlink-implicit.network' )
6824 self
. wait_online ( 'veth-peer:carrier' )
6826 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' )
6828 output
= networkctl_status ( 'veth99' )
6830 self
. assertRegex ( output
, '192.168.5' )
6832 output
= check_output ( 'ip route list dev veth99 10.0.0.0/8' )
6834 self
. assertRegex ( output
, 'onlink' )
6835 output
= check_output ( 'ip route list dev veth99 192.168.100.0/24' )
6837 self
. assertRegex ( output
, 'onlink' )
6839 def test_dhcp_client_with_ipv4ll ( self
):
6840 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' ,
6841 '25-dhcp-client-with-ipv4ll.network' )
6843 # we need to increase timeout above default, as this will need to wait for
6844 # systemd-networkd to get the dhcpv4 transient failure event
6845 self
. wait_online ( 'veth99:degraded' , 'veth-peer:routable' , timeout
= '60s' )
6847 output
= check_output ( 'ip -4 address show dev veth99' )
6849 self
. assertNotIn ( '192.168.5.' , output
)
6850 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
6853 print ( 'Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped' )
6854 self
. wait_address ( 'veth99' , r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic' , ipv
= '-4' )
6855 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' )
6856 self
. wait_online ( 'veth99:routable' )
6858 output
= check_output ( 'ip -4 address show dev veth99' )
6860 self
. assertRegex ( output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99' )
6861 self
. assertNotIn ( '169.254.' , output
)
6862 self
. assertNotIn ( 'scope link' , output
)
6865 print ( 'Wait for the DHCP lease to be expired and an IPv4LL address to be acquired' )
6866 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 )
6867 self
. wait_address ( 'veth99' , r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link' , scope
= 'link' , ipv
= '-4' )
6869 output
= check_output ( 'ip -4 address show dev veth99' )
6871 self
. assertNotIn ( '192.168.5.' , output
)
6872 self
. assertIn ( 'inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link' , output
)
6874 def test_dhcp_client_use_dns ( self
):
6875 def check ( self
, ipv4
, ipv6
):
6876 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
6877 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
6878 f
. write ( '[DHCPv4] \n UseDNS=' )
6879 f
. write ( 'yes' if ipv4
else 'no' )
6880 f
. write ( ' \n [DHCPv6] \n UseDNS=' )
6881 f
. write ( 'yes' if ipv6
else 'no' )
6882 f
. write ( ' \n [IPv6AcceptRA] \n UseDNS=no' )
6885 self
. wait_online ( 'veth99:routable' )
6887 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6888 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
6889 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
6891 # make resolved re-read the link state file
6892 resolvectl ( 'revert' , 'veth99' )
6894 output
= resolvectl ( 'dns' , 'veth99' )
6897 self
. assertIn ( '192.168.5.1' , output
)
6899 self
. assertNotIn ( '192.168.5.1' , output
)
6901 self
. assertIn ( '2600::1' , output
)
6903 self
. assertNotIn ( '2600::1' , output
)
6905 check_json ( networkctl_json ())
6907 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
6910 self
. wait_online ( 'veth-peer:carrier' )
6911 start_dnsmasq ( '--dhcp-option=option:dns-server,192.168.5.1' ,
6912 '--dhcp-option=option6:dns-server,[2600::1]' )
6914 check ( self
, True , True )
6915 check ( self
, True , False )
6916 check ( self
, False , True )
6917 check ( self
, False , False )
6919 def test_dhcp_client_use_captive_portal ( self
):
6920 def check ( self
, ipv4
, ipv6
):
6921 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
6922 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
6923 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
6924 f
. write ( 'yes' if ipv4
else 'no' )
6925 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
6926 f
. write ( 'yes' if ipv6
else 'no' )
6927 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
6930 self
. wait_online ( 'veth99:routable' )
6932 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6933 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
6934 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
6936 output
= networkctl_status ( 'veth99' )
6939 self
. assertIn ( 'Captive Portal: http://systemd.io' , output
)
6941 self
. assertNotIn ( 'Captive Portal: http://systemd.io' , output
)
6943 check_json ( networkctl_json ())
6945 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
6948 self
. wait_online ( 'veth-peer:carrier' )
6949 start_dnsmasq ( '--dhcp-option=114,http://systemd.io' ,
6950 '--dhcp-option=option6:103,http://systemd.io' )
6952 check ( self
, True , True )
6953 check ( self
, True , False )
6954 check ( self
, False , True )
6955 check ( self
, False , False )
6957 def test_dhcp_client_reject_captive_portal ( self
):
6958 def check ( self
, ipv4
, ipv6
):
6959 os
. makedirs ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d' ), exist_ok
= True )
6960 with
open ( os
. path
. join ( network_unit_dir
, '25-dhcp-client.network.d/override.conf' ), mode
= 'w' , encoding
= 'utf-8' ) as f
:
6961 f
. write ( '[DHCPv4] \n UseCaptivePortal=' )
6962 f
. write ( 'yes' if ipv4
else 'no' )
6963 f
. write ( ' \n [DHCPv6] \n UseCaptivePortal=' )
6964 f
. write ( 'yes' if ipv6
else 'no' )
6965 f
. write ( ' \n [IPv6AcceptRA] \n UseCaptivePortal=no' )
6968 self
. wait_online ( 'veth99:routable' )
6970 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6971 self
. wait_address ( 'veth99' , r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic' , ipv
= '-4' )
6972 self
. wait_address ( 'veth99' , r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' , ipv
= '-6' )
6974 output
= networkctl_status ( 'veth99' )
6976 self
. assertNotIn ( 'Captive Portal: ' , output
)
6977 self
. assertNotIn ( 'invalid/url' , output
)
6979 check_json ( networkctl_json ())
6981 copy_network_unit ( '25-veth.netdev' , '25-dhcp-server-veth-peer.network' , '25-dhcp-client.network' , copy_dropins
= False )
6984 self
. wait_online ( 'veth-peer:carrier' )
6985 masq
= lambda bs
: ':' . join ( f
'{b:02x}' for b
in bs
)
6986 start_dnsmasq ( '--dhcp-option=114,' + masq ( b
'http:// \x00 invalid/url' ),
6987 '--dhcp-option=option6:103,' + masq ( b
'http:// \x00 /invalid/url' ))
6989 check ( self
, True , True )
6990 check ( self
, True , False )
6991 check ( self
, False , True )
6992 check ( self
, False , False )
6994 class NetworkdDHCPPDTests ( unittest
. TestCase
, Utilities
):
7002 def check_dhcp6_prefix ( self
, link
):
7003 description
= get_link_description ( link
)
7005 self
. assertIn ( 'DHCPv6Client' , description
. keys ())
7006 self
. assertIn ( 'Prefixes' , description
[ 'DHCPv6Client' ])
7008 prefixInfo
= description
[ 'DHCPv6Client' ][ 'Prefixes' ]
7010 self
. assertEqual ( len ( prefixInfo
), 1 )
7012 self
. assertIn ( 'Prefix' , prefixInfo
[ 0 ]. keys ())
7013 self
. assertIn ( 'PrefixLength' , prefixInfo
[ 0 ]. keys ())
7014 self
. assertIn ( 'PreferredLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
7015 self
. assertIn ( 'ValidLifetimeUSec' , prefixInfo
[ 0 ]. keys ())
7017 self
. assertEqual ( prefixInfo
[ 0 ][ 'Prefix' ][ 0 : 6 ], [ 63 , 254 , 5 , 1 , 255 , 255 ])
7018 self
. assertEqual ( prefixInfo
[ 0 ][ 'PrefixLength' ], 56 )
7019 self
. assertGreater ( prefixInfo
[ 0 ][ 'PreferredLifetimeUSec' ], 0 )
7020 self
. assertGreater ( prefixInfo
[ 0 ][ 'ValidLifetimeUSec' ], 0 )
7022 def test_dhcp6pd_no_address ( self
):
7024 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-address.network' )
7027 self
. wait_online ( 'veth-peer:routable' )
7028 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
7029 self
. wait_online ( 'veth99:degraded' )
7031 print ( '### ip -6 address show dev veth99 scope global' )
7032 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
7034 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
7036 self
. check_dhcp6_prefix ( 'veth99' )
7038 def test_dhcp6pd_no_assign ( self
):
7039 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
7040 # However, the server does not provide IA_NA. For issue #31349.
7041 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream-no-assign.network' )
7044 self
. wait_online ( 'veth-peer:routable' )
7045 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd-no-range.conf' , ipv
= '-6' )
7046 self
. wait_online ( 'veth99:degraded' )
7048 print ( '### ip -6 address show dev veth99 scope global' )
7049 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
7051 self
. assertNotIn ( 'inet6 3ffe:501:ffff' , output
)
7053 self
. check_dhcp6_prefix ( 'veth99' )
7055 def test_dhcp6pd ( self
):
7056 copy_network_unit ( '25-veth.netdev' , '25-dhcp6pd-server.network' , '25-dhcp6pd-upstream.network' ,
7057 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
7058 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
7059 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
7060 '25-dhcp-pd-downstream-dummy97.network' ,
7061 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
7062 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' )
7065 self
. wait_online ( 'veth-peer:routable' )
7066 start_isc_dhcpd ( conf_file
= 'isc-dhcpd-dhcp6pd.conf' , ipv
= '-6' )
7067 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
7068 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
7070 self
. setup_nftset ( 'addr6' , 'ipv6_addr' )
7071 self
. setup_nftset ( 'network6' , 'ipv6_addr' , 'flags interval;' )
7072 self
. setup_nftset ( 'ifindex' , 'iface_index' )
7074 # Check DBus assigned prefix information to veth99
7075 self
. check_dhcp6_prefix ( 'veth99' )
7077 print ( '### ip -6 address show dev veth-peer scope global' )
7078 output
= check_output ( 'ip -6 address show dev veth-peer scope global' )
7080 self
. assertIn ( 'inet6 3ffe:501:ffff:100::1/64 scope global' , output
)
7084 # dummy97: 0x01 (The link will appear later)
7086 # dummy99: auto -> 0x02 (No address assignment)
7091 print ( '### ip -6 address show dev veth99 scope global' )
7092 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
7095 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)' )
7096 # address in IA_PD (Token=static)
7097 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic' )
7098 # address in IA_PD (Token=eui64)
7099 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic' )
7100 # address in IA_PD (temporary)
7101 # Note that the temporary addresses may appear after the link enters configured state
7102 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' )
7104 print ( '### ip -6 address show dev test1 scope global' )
7105 output
= check_output ( 'ip -6 address show dev test1 scope global' )
7107 # address in IA_PD (Token=static)
7108 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7109 # address in IA_PD (temporary)
7110 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' )
7112 print ( '### ip -6 address show dev dummy98 scope global' )
7113 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
7115 # address in IA_PD (Token=static)
7116 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7117 # address in IA_PD (temporary)
7118 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' )
7120 print ( '### ip -6 address show dev dummy99 scope global' )
7121 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
7124 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
7126 print ( '### ip -6 address show dev veth97 scope global' )
7127 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
7129 # address in IA_PD (Token=static)
7130 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7131 # address in IA_PD (Token=eui64)
7132 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7133 # address in IA_PD (temporary)
7134 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' )
7136 print ( '### ip -6 address show dev veth97-peer scope global' )
7137 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
7139 # NDisc address (Token=static)
7140 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7141 # NDisc address (Token=eui64)
7142 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7143 # NDisc address (temporary)
7144 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' )
7146 print ( '### ip -6 address show dev veth98 scope global' )
7147 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
7149 # address in IA_PD (Token=static)
7150 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7151 # address in IA_PD (Token=eui64)
7152 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7153 # address in IA_PD (temporary)
7154 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' )
7156 print ( '### ip -6 address show dev veth98-peer scope global' )
7157 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
7159 # NDisc address (Token=static)
7160 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7161 # NDisc address (Token=eui64)
7162 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7163 # NDisc address (temporary)
7164 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' )
7166 print ( '### ip -6 route show type unreachable' )
7167 output
= check_output ( 'ip -6 route show type unreachable' )
7169 self
. assertRegex ( output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp' )
7171 print ( '### ip -6 route show dev veth99' )
7172 output
= check_output ( 'ip -6 route show dev veth99' )
7174 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires' )
7176 print ( '### ip -6 route show dev test1' )
7177 output
= check_output ( 'ip -6 route show dev test1' )
7179 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
7181 print ( '### ip -6 route show dev dummy98' )
7182 output
= check_output ( 'ip -6 route show dev dummy98' )
7184 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
7186 print ( '### ip -6 route show dev dummy99' )
7187 output
= check_output ( 'ip -6 route show dev dummy99' )
7189 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
7191 print ( '### ip -6 route show dev veth97' )
7192 output
= check_output ( 'ip -6 route show dev veth97' )
7194 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires' )
7196 print ( '### ip -6 route show dev veth97-peer' )
7197 output
= check_output ( 'ip -6 route show dev veth97-peer' )
7199 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires' )
7201 print ( '### ip -6 route show dev veth98' )
7202 output
= check_output ( 'ip -6 route show dev veth98' )
7204 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires' )
7206 print ( '### ip -6 route show dev veth98-peer' )
7207 output
= check_output ( 'ip -6 route show dev veth98-peer' )
7209 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires' )
7211 # Test case for a downstream which appears later
7212 check_output ( 'ip link add dummy97 type dummy' )
7213 self
. wait_online ( 'dummy97:routable' )
7215 print ( '### ip -6 address show dev dummy97 scope global' )
7216 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
7218 # address in IA_PD (Token=static)
7219 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7220 # address in IA_PD (temporary)
7221 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' )
7223 print ( '### ip -6 route show dev dummy97' )
7224 output
= check_output ( 'ip -6 route show dev dummy97' )
7226 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires' )
7228 # Test case for reconfigure
7229 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
7230 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
7232 print ( '### ip -6 address show dev dummy98 scope global' )
7233 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
7235 # address in IA_PD (Token=static)
7236 self
. assertRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7237 # address in IA_PD (temporary)
7238 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' )
7240 print ( '### ip -6 address show dev dummy99 scope global' )
7241 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
7244 self
. assertNotRegex ( output
, 'inet6 3ffe:501:ffff:[2-9a-f]02' )
7246 print ( '### ip -6 route show dev dummy98' )
7247 output
= check_output ( 'ip -6 route show dev dummy98' )
7249 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires' )
7251 print ( '### ip -6 route show dev dummy99' )
7252 output
= check_output ( 'ip -6 route show dev dummy99' )
7254 self
. assertRegex ( output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires' )
7256 self
. check_netlabel ( 'dummy98' , '3ffe:501:ffff:[2-9a-f]00::/64' )
7258 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d' )
7259 self
. check_nftset ( 'addr6' , '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*' )
7260 self
. check_nftset ( 'network6' , '3ffe:501:ffff:[2-9a-f]00::/64' )
7261 self
. check_nftset ( 'ifindex' , 'dummy98' )
7263 self
. teardown_nftset ( 'addr6' , 'network6' , 'ifindex' )
7265 def verify_dhcp4_6rd ( self
, tunnel_name
):
7266 print ( '### ip -4 address show dev veth-peer scope global' )
7267 output
= check_output ( 'ip -4 address show dev veth-peer scope global' )
7269 self
. assertIn ( 'inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer' , output
)
7273 # dummy97: 0x01 (The link will appear later)
7275 # dummy99: auto -> 0x0[23] (No address assignment)
7276 # 6rd-XXX: auto -> 0x0[23]
7281 print ( '### ip -4 address show dev veth99 scope global' )
7282 output
= check_output ( 'ip -4 address show dev veth99 scope global' )
7284 self
. assertRegex ( output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99' )
7286 print ( '### ip -6 address show dev veth99 scope global' )
7287 output
= check_output ( 'ip -6 address show dev veth99 scope global' )
7289 # address in IA_PD (Token=static)
7290 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7291 # address in IA_PD (Token=eui64)
7292 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7293 # address in IA_PD (temporary)
7294 # Note that the temporary addresses may appear after the link enters configured state
7295 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' )
7297 print ( '### ip -6 address show dev test1 scope global' )
7298 output
= check_output ( 'ip -6 address show dev test1 scope global' )
7300 # address in IA_PD (Token=static)
7301 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7302 # address in IA_PD (temporary)
7303 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' )
7305 print ( '### ip -6 address show dev dummy98 scope global' )
7306 output
= check_output ( 'ip -6 address show dev dummy98 scope global' )
7308 # address in IA_PD (Token=static)
7309 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7310 # address in IA_PD (temporary)
7311 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' )
7313 print ( '### ip -6 address show dev dummy99 scope global' )
7314 output
= check_output ( 'ip -6 address show dev dummy99 scope global' )
7317 self
. assertNotRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]' )
7319 print ( '### ip -6 address show dev veth97 scope global' )
7320 output
= check_output ( 'ip -6 address show dev veth97 scope global' )
7322 # address in IA_PD (Token=static)
7323 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7324 # address in IA_PD (Token=eui64)
7325 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7326 # address in IA_PD (temporary)
7327 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' )
7329 print ( '### ip -6 address show dev veth97-peer scope global' )
7330 output
= check_output ( 'ip -6 address show dev veth97-peer scope global' )
7332 # NDisc address (Token=static)
7333 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7334 # NDisc address (Token=eui64)
7335 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7336 # NDisc address (temporary)
7337 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' )
7339 print ( '### ip -6 address show dev veth98 scope global' )
7340 output
= check_output ( 'ip -6 address show dev veth98 scope global' )
7342 # address in IA_PD (Token=static)
7343 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7344 # address in IA_PD (Token=eui64)
7345 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7346 # address in IA_PD (temporary)
7347 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' )
7349 print ( '### ip -6 address show dev veth98-peer scope global' )
7350 output
= check_output ( 'ip -6 address show dev veth98-peer scope global' )
7352 # NDisc address (Token=static)
7353 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7354 # NDisc address (Token=eui64)
7355 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7356 # NDisc address (temporary)
7357 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' )
7359 print ( '### ip -6 route show type unreachable' )
7360 output
= check_output ( 'ip -6 route show type unreachable' )
7362 self
. assertRegex ( output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp' )
7364 print ( '### ip -6 route show dev veth99' )
7365 output
= check_output ( 'ip -6 route show dev veth99' )
7367 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires' )
7369 print ( '### ip -6 route show dev test1' )
7370 output
= check_output ( 'ip -6 route show dev test1' )
7372 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
7374 print ( '### ip -6 route show dev dummy98' )
7375 output
= check_output ( 'ip -6 route show dev dummy98' )
7377 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires' )
7379 print ( '### ip -6 route show dev dummy99' )
7380 output
= check_output ( 'ip -6 route show dev dummy99' )
7382 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires' )
7384 print ( '### ip -6 route show dev veth97' )
7385 output
= check_output ( 'ip -6 route show dev veth97' )
7387 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires' )
7389 print ( '### ip -6 route show dev veth97-peer' )
7390 output
= check_output ( 'ip -6 route show dev veth97-peer' )
7392 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires' )
7394 print ( '### ip -6 route show dev veth98' )
7395 output
= check_output ( 'ip -6 route show dev veth98' )
7397 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires' )
7399 print ( '### ip -6 route show dev veth98-peer' )
7400 output
= check_output ( 'ip -6 route show dev veth98-peer' )
7402 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires' )
7404 print ( '### ip -6 address show dev dummy97 scope global' )
7405 output
= check_output ( 'ip -6 address show dev dummy97 scope global' )
7407 # address in IA_PD (Token=static)
7408 self
. assertRegex ( output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr' )
7409 # address in IA_PD (temporary)
7410 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' )
7412 print ( '### ip -6 route show dev dummy97' )
7413 output
= check_output ( 'ip -6 route show dev dummy97' )
7415 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires' )
7417 print ( f
'### ip -d link show dev {tunnel_name} ' )
7418 output
= check_output ( f
'ip -d link show dev {tunnel_name} ' )
7420 self
. assertIn ( 'link/sit 10.100.100.' , output
)
7421 self
. assertIn ( 'local 10.100.100.' , output
)
7422 self
. assertIn ( 'ttl 64' , output
)
7423 self
. assertIn ( '6rd-prefix 2001:db8::/32' , output
)
7424 self
. assertIn ( '6rd-relay_prefix 10.0.0.0/8' , output
)
7426 print ( f
'### ip -6 address show dev {tunnel_name} ' )
7427 output
= check_output ( f
'ip -6 address show dev {tunnel_name} ' )
7429 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' )
7430 self
. assertRegex ( output
, 'inet6 ::10.100.100.[0-9]+/96 scope global' )
7432 print ( f
'### ip -6 route show dev {tunnel_name} ' )
7433 output
= check_output ( f
'ip -6 route show dev {tunnel_name} ' )
7435 self
. assertRegex ( output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires' )
7436 self
. assertRegex ( output
, '::/96 proto kernel metric [0-9]*' )
7438 print ( '### ip -6 route show default' )
7439 output
= check_output ( 'ip -6 route show default' )
7441 self
. assertIn ( 'default' , output
)
7442 self
. assertIn ( f
'via ::10.0.0.1 dev {tunnel_name} ' , output
)
7444 def test_dhcp4_6rd ( self
):
7445 def get_dhcp_6rd_prefix ( link
):
7446 description
= get_link_description ( link
)
7448 self
. assertIn ( 'DHCPv4Client' , description
. keys ())
7449 self
. assertIn ( '6rdPrefix' , description
[ 'DHCPv4Client' ]. keys ())
7451 prefixInfo
= description
[ 'DHCPv4Client' ][ '6rdPrefix' ]
7452 self
. assertIn ( 'Prefix' , prefixInfo
. keys ())
7453 self
. assertIn ( 'PrefixLength' , prefixInfo
. keys ())
7454 self
. assertIn ( 'IPv4MaskLength' , prefixInfo
. keys ())
7455 self
. assertIn ( 'BorderRouters' , prefixInfo
. keys ())
7459 copy_network_unit ( '25-veth.netdev' , '25-dhcp4-6rd-server.network' , '25-dhcp4-6rd-upstream.network' ,
7460 '25-veth-downstream-veth97.netdev' , '25-dhcp-pd-downstream-veth97.network' , '25-dhcp-pd-downstream-veth97-peer.network' ,
7461 '25-veth-downstream-veth98.netdev' , '25-dhcp-pd-downstream-veth98.network' , '25-dhcp-pd-downstream-veth98-peer.network' ,
7462 '11-dummy.netdev' , '25-dhcp-pd-downstream-test1.network' ,
7463 '25-dhcp-pd-downstream-dummy97.network' ,
7464 '12-dummy.netdev' , '25-dhcp-pd-downstream-dummy98.network' ,
7465 '13-dummy.netdev' , '25-dhcp-pd-downstream-dummy99.network' ,
7466 '80-6rd-tunnel.network' )
7469 self
. wait_online ( 'veth-peer:routable' )
7472 # 6rd-prefix: 2001:db8::/32
7473 # br-addresss: 10.0.0.1
7475 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' ,
7476 ipv4_range
= '10.100.100.100,10.100.100.200' ,
7477 ipv4_router
= '10.0.0.1' )
7478 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
7479 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
7481 # Check the DBus interface for assigned prefix information
7482 prefixInfo
= get_dhcp_6rd_prefix ( 'veth99' )
7484 self
. assertEqual ( prefixInfo
[ 'Prefix' ], [ 32 , 1 , 13 , 184 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]) # 2001:db8::
7485 self
. assertEqual ( prefixInfo
[ 'PrefixLength' ], 32 )
7486 self
. assertEqual ( prefixInfo
[ 'IPv4MaskLength' ], 8 )
7487 self
. assertEqual ( prefixInfo
[ 'BorderRouters' ], [[ 10 , 0 , 0 , 1 ]])
7489 # Test case for a downstream which appears later
7490 check_output ( 'ip link add dummy97 type dummy' )
7491 self
. wait_online ( 'dummy97:routable' )
7495 for name
in os
. listdir ( '/sys/class/net/' ):
7496 if name
. startswith ( '6rd-' ):
7500 self
. wait_online ( f
' {tunnel_name} :routable' )
7502 self
. verify_dhcp4_6rd ( tunnel_name
)
7504 # Test case for reconfigure
7505 networkctl_reconfigure ( 'dummy98' , 'dummy99' )
7506 self
. wait_online ( 'dummy98:routable' , 'dummy99:degraded' )
7508 self
. verify_dhcp4_6rd ( tunnel_name
)
7510 print ( 'Wait for the DHCP lease to be renewed/rebind' )
7513 self
. wait_online ( 'veth99:routable' , 'test1:routable' , 'dummy97:routable' , 'dummy98:routable' , 'dummy99:degraded' ,
7514 'veth97:routable' , 'veth97-peer:routable' , 'veth98:routable' , 'veth98-peer:routable' )
7516 self
. verify_dhcp4_6rd ( tunnel_name
)
7518 class NetworkdIPv6PrefixTests ( unittest
. TestCase
, Utilities
):
7526 def test_ipv6_route_prefix ( self
):
7527 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client.network' , '25-ipv6ra-prefix.network' ,
7528 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
7531 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
7533 output
= check_output ( 'ip address show dev veth-peer' )
7535 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
7536 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
7537 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
7539 output
= check_output ( 'ip -6 route show dev veth-peer' )
7541 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
7542 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
7543 self
. assertNotIn ( '2001:db8:0:3::/64 proto ra' , output
)
7544 self
. assertIn ( '2001:db0:fff::/64 via ' , output
)
7545 self
. assertNotIn ( '2001:db1:fff::/64 via ' , output
)
7546 self
. assertNotIn ( '2001:db2:fff::/64 via ' , output
)
7548 output
= check_output ( 'ip address show dev veth99' )
7550 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
7551 self
. assertIn ( 'inet6 2001:db8:0:2:1a:2b:3c:4d' , output
)
7552 self
. assertIn ( 'inet6 2001:db8:0:2:fa:de:ca:fe' , output
)
7553 self
. assertNotIn ( 'inet6 2001:db8:0:3:' , output
)
7555 output
= resolvectl ( 'dns' , 'veth-peer' )
7557 self
. assertRegex ( output
, '2001:db8:1:1::2' )
7559 output
= resolvectl ( 'domain' , 'veth-peer' )
7561 self
. assertIn ( 'example.com' , output
)
7563 check_json ( networkctl_json ())
7565 output
= networkctl_json ( 'veth-peer' )
7569 pref64
= json
. loads ( output
)[ 'NDisc' ][ 'PREF64' ][ 0 ]
7571 prefix
= socket
. inet_ntop ( socket
. AF_INET6
, bytearray ( pref64
[ 'Prefix' ]))
7572 self
. assertEqual ( prefix
, '64:ff9b::' )
7574 prefix_length
= pref64
[ 'PrefixLength' ]
7575 self
. assertEqual ( prefix_length
, 96 )
7577 def test_ipv6_route_prefix_deny_list ( self
):
7578 copy_network_unit ( '25-veth.netdev' , '25-ipv6ra-prefix-client-deny-list.network' , '25-ipv6ra-prefix.network' ,
7579 '12-dummy.netdev' , '25-ipv6ra-uplink.network' )
7582 self
. wait_online ( 'veth99:routable' , 'veth-peer:routable' , 'dummy98:routable' )
7584 output
= check_output ( 'ip address show dev veth-peer' )
7586 self
. assertIn ( 'inet6 2001:db8:0:1:' , output
)
7587 self
. assertNotIn ( 'inet6 2001:db8:0:2:' , output
)
7589 output
= check_output ( 'ip -6 route show dev veth-peer' )
7591 self
. assertIn ( '2001:db8:0:1::/64 proto ra' , output
)
7592 self
. assertNotIn ( '2001:db8:0:2::/64 proto ra' , output
)
7593 self
. assertIn ( '2001:db0:fff::/64 via ' , output
)
7594 self
. assertNotIn ( '2001:db1:fff::/64 via ' , output
)
7596 output
= check_output ( 'ip address show dev veth99' )
7598 self
. assertNotIn ( 'inet6 2001:db8:0:1:' , output
)
7599 self
. assertIn ( 'inet6 2001:db8:0:2:' , output
)
7601 output
= resolvectl ( 'dns' , 'veth-peer' )
7603 self
. assertRegex ( output
, '2001:db8:1:1::2' )
7605 output
= resolvectl ( 'domain' , 'veth-peer' )
7607 self
. assertIn ( 'example.com' , output
)
7609 class NetworkdMTUTests ( unittest
. TestCase
, Utilities
):
7617 def check_mtu ( self
, mtu
, ipv6_mtu
= None , reset
= True ):
7623 self
. wait_online ( 'dummy98:routable' )
7624 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
7625 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
7627 # test normal restart
7629 self
. wait_online ( 'dummy98:routable' )
7630 self
. check_link_attr ( 'dummy98' , 'mtu' , mtu
)
7631 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , ipv6_mtu
)
7634 self
. reset_check_mtu ( mtu
, ipv6_mtu
)
7636 def reset_check_mtu ( self
, mtu
, ipv6_mtu
= None ):
7637 ''' test setting mtu/ipv6_mtu with interface already up '''
7640 # note - changing the device mtu resets the ipv6 mtu
7641 check_output ( 'ip link set up mtu 1501 dev dummy98' )
7642 check_output ( 'ip link set up mtu 1500 dev dummy98' )
7643 self
. check_link_attr ( 'dummy98' , 'mtu' , '1500' )
7644 self
. check_ipv6_sysctl_attr ( 'dummy98' , 'mtu' , '1500' )
7646 self
. check_mtu ( mtu
, ipv6_mtu
, reset
= False )
7648 def test_mtu_network ( self
):
7649 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' )
7650 self
. check_mtu ( '1600' )
7652 def test_mtu_netdev ( self
):
7653 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network' , copy_dropins
= False )
7654 # note - MTU set by .netdev happens ONLY at device creation!
7655 self
. check_mtu ( '1600' , reset
= False )
7657 def test_mtu_link ( self
):
7658 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network' , copy_dropins
= False )
7659 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7660 self
. check_mtu ( '1600' , reset
= False )
7662 def test_ipv6_mtu ( self
):
7663 ''' set ipv6 mtu without setting device mtu '''
7664 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1400.conf' )
7665 self
. check_mtu ( '1500' , '1400' )
7667 def test_ipv6_mtu_toolarge ( self
):
7668 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7669 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
7670 self
. check_mtu ( '1500' , '1500' )
7672 def test_mtu_network_ipv6_mtu ( self
):
7673 ''' set ipv6 mtu and set device mtu via network file '''
7674 copy_network_unit ( '12-dummy.netdev' , '12-dummy.network.d/mtu.conf' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
7675 self
. check_mtu ( '1600' , '1550' )
7677 def test_mtu_netdev_ipv6_mtu ( self
):
7678 ''' set ipv6 mtu and set device mtu via netdev file '''
7679 copy_network_unit ( '12-dummy-mtu.netdev' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
7680 self
. check_mtu ( '1600' , '1550' , reset
= False )
7682 def test_mtu_link_ipv6_mtu ( self
):
7683 ''' set ipv6 mtu and set device mtu via link file '''
7684 copy_network_unit ( '12-dummy.netdev' , '12-dummy-mtu.link' , '12-dummy.network.d/ipv6-mtu-1550.conf' )
7685 self
. check_mtu ( '1600' , '1550' , reset
= False )
7688 if __name__
== '__main__' :
7689 parser
= argparse
. ArgumentParser ()
7690 parser
. add_argument ( '--build-dir' , help = 'Path to build dir' , dest
= 'build_dir' )
7691 parser
. add_argument ( '--source-dir' , help = 'Path to source dir/git tree' , dest
= 'source_dir' )
7692 parser
. add_argument ( '--valgrind' , help = 'Enable valgrind' , dest
= 'use_valgrind' , type = bool , nargs
= '?' , const
= True , default
= use_valgrind
)
7693 parser
. add_argument ( '--debug' , help = 'Generate debugging logs' , dest
= 'enable_debug' , type = bool , nargs
= '?' , const
= True , default
= enable_debug
)
7694 parser
. add_argument ( '--asan-options' , help = 'ASAN options' , dest
= 'asan_options' )
7695 parser
. add_argument ( '--lsan-options' , help = 'LSAN options' , dest
= 'lsan_options' )
7696 parser
. add_argument ( '--ubsan-options' , help = 'UBSAN options' , dest
= 'ubsan_options' )
7697 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
)
7698 ns
, unknown_args
= parser
. parse_known_args ( namespace
= unittest
)
7701 networkd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd' )
7702 resolved_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-resolved' )
7703 timesyncd_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-timesyncd' )
7704 wait_online_bin
= os
. path
. join ( ns
. build_dir
, 'systemd-networkd-wait-online' )
7705 networkctl_bin
= os
. path
. join ( ns
. build_dir
, 'networkctl' )
7706 resolvectl_bin
= os
. path
. join ( ns
. build_dir
, 'resolvectl' )
7707 timedatectl_bin
= os
. path
. join ( ns
. build_dir
, 'timedatectl' )
7708 udevadm_bin
= os
. path
. join ( ns
. build_dir
, 'udevadm' )
7709 build_dir
= ns
. build_dir
7712 source_dir
= ns
. source_dir
7714 source_dir
= os
. path
. normpath ( os
. path
. join ( os
. path
. dirname ( os
. path
. abspath ( __file__
)), "../../" ))
7715 assert os
. path
. exists ( os
. path
. join ( source_dir
, "meson_options.txt" )), f
" {source_dir} doesn't appear to be a systemd source tree."
7717 use_valgrind
= ns
. use_valgrind
7718 enable_debug
= ns
. enable_debug
7719 asan_options
= ns
. asan_options
7720 lsan_options
= ns
. lsan_options
7721 ubsan_options
= ns
. ubsan_options
7722 with_coverage
= ns
. with_coverage
7725 # Do not forget the trailing space.
7726 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7728 networkctl_cmd
= valgrind_cmd
. split () + [ networkctl_bin
]
7729 resolvectl_cmd
= valgrind_cmd
. split () + [ resolvectl_bin
]
7730 timedatectl_cmd
= valgrind_cmd
. split () + [ timedatectl_bin
]
7731 udevadm_cmd
= valgrind_cmd
. split () + [ udevadm_bin
]
7732 wait_online_cmd
= valgrind_cmd
. split () + [ wait_online_bin
]
7735 test_ndisc_send
= os
. path
. normpath ( os
. path
. join ( build_dir
, 'test-ndisc-send' ))
7737 test_ndisc_send
= '/usr/lib/tests/test-ndisc-send'
7740 env
. update ({ 'ASAN_OPTIONS' : asan_options
})
7742 env
. update ({ 'LSAN_OPTIONS' : lsan_options
})
7744 env
. update ({ 'UBSAN_OPTIONS' : ubsan_options
})
7746 env
. update ({ 'SYSTEMD_MEMPOOL' : '0' })
7748 wait_online_env
= env
. copy ()
7750 wait_online_env
. update ({ 'SYSTEMD_LOG_LEVEL' : 'debug' })
7752 sys
. argv
[ 1 :] = unknown_args
7753 unittest
. main ( verbosity
= 3 )