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(':'))
54 systemd_source_dir
= None
56 networkd_bin
= shutil
.which('systemd-networkd', path
=which_paths
)
57 resolved_bin
= shutil
.which('systemd-resolved', path
=which_paths
)
58 timesyncd_bin
= shutil
.which('systemd-timesyncd', path
=which_paths
)
59 udevd_bin
= shutil
.which('systemd-udevd', path
=which_paths
)
60 wait_online_bin
= shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
61 networkctl_bin
= shutil
.which('networkctl', path
=which_paths
)
62 resolvectl_bin
= shutil
.which('resolvectl', path
=which_paths
)
63 timedatectl_bin
= shutil
.which('timedatectl', path
=which_paths
)
64 udevadm_bin
= shutil
.which('udevadm', path
=which_paths
)
65 systemd_udev_rules_build_dir
= 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
)
287 check_output(*udevadm_cmd
, 'control', '--reload')
289 def copy_network_unit(*units
, copy_dropins
=True):
291 Copy networkd unit files into the testbed.
293 Any networkd unit file type can be specified, as well as drop-in files.
295 By default, all drop-ins for a specified unit file are copied in;
296 to avoid that specify dropins=False.
298 When a drop-in file is specified, its unit file is also copied in automatically.
301 mkdir_p(network_unit_dir
)
303 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
304 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
306 if unit
.endswith('.conf'):
308 unit
= os
.path
.dirname(dropin
).rstrip('.d')
309 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
311 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
313 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
315 if unit
.endswith('.link'):
321 def copy_credential(src
, target
):
322 mkdir_p(credstore_dir
)
323 cp(os
.path
.join(networkd_ci_temp_dir
, src
),
324 os
.path
.join(credstore_dir
, target
))
326 def remove_network_unit(*units
):
328 Remove previously copied unit files from the testbed.
330 Drop-ins will be removed automatically.
334 rm_f(os
.path
.join(network_unit_dir
, unit
))
335 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
337 if unit
.endswith('.link') or unit
.endswith('.link.d'):
343 def clear_network_units():
345 if os
.path
.exists(network_unit_dir
):
346 units
= os
.listdir(network_unit_dir
)
348 if unit
.endswith('.link') or unit
.endswith('.link.d'):
351 rm_rf(network_unit_dir
)
356 def copy_networkd_conf_dropin(*dropins
):
357 """Copy networkd.conf dropin files into the testbed."""
358 mkdir_p(networkd_conf_dropin_dir
)
359 for dropin
in dropins
:
360 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
362 def remove_networkd_conf_dropin(*dropins
):
363 """Remove previously copied networkd.conf dropin files from the testbed."""
364 for dropin
in dropins
:
365 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
367 def clear_networkd_conf_dropins():
368 rm_rf(networkd_conf_dropin_dir
)
370 def setup_systemd_udev_rules():
371 if not systemd_udev_rules_build_dir
:
374 mkdir_p(udev_rules_dir
)
376 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
377 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
379 for rule
in os
.listdir(path
):
380 if not rule
.endswith(".rules"):
382 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
384 def copy_udev_rule(*rules
):
385 """Copy udev rules"""
386 mkdir_p(udev_rules_dir
)
388 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
390 def remove_udev_rule(*rules
):
391 """Remove previously copied udev rules"""
393 rm_f(os
.path
.join(udev_rules_dir
, rule
))
395 def clear_udev_rules():
396 rm_rf(udev_rules_dir
)
398 def save_active_units():
399 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
400 'systemd-resolved.service', 'systemd-timesyncd.service',
401 'firewalld.service']:
402 if call(f
'systemctl is-active --quiet {u}') == 0:
403 call(f
'systemctl stop {u}')
404 active_units
.append(u
)
406 def restore_active_units():
407 if 'systemd-networkd.socket' in active_units
:
408 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
409 for u
in active_units
:
410 call(f
'systemctl restart {u}')
412 def create_unit_dropin(unit
, contents
):
413 mkdir_p(f
'/run/systemd/system/{unit}.d')
414 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
415 f
.write('\n'.join(contents
))
417 def create_service_dropin(service
, command
, additional_settings
=None):
421 f
'ExecStart=!!{valgrind_cmd}{command}',
424 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
426 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
428 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
430 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
431 if asan_options
or lsan_options
or ubsan_options
:
432 drop_in
+= ['SystemCallFilter=']
433 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
434 drop_in
+= ['MemoryDenyWriteExecute=no']
437 'Environment=SYSTEMD_MEMPOOL=0',
445 if additional_settings
:
446 drop_in
+= additional_settings
448 create_unit_dropin(f
'{service}.service', drop_in
)
450 def link_exists(link
):
451 return call_quiet(f
'ip link show {link}') == 0
453 def link_resolve(link
):
454 return check_output(f
'ip link show {link}').split(':')[1].strip()
456 def remove_link(*links
, protect
=False):
458 if protect
and link
in protected_links
:
460 if link_exists(link
):
461 call(f
'ip link del dev {link}')
463 def save_existing_links():
464 links
= os
.listdir('/sys/class/net')
466 if link_exists(link
):
467 protected_links
.add(link
)
469 print('### The following links will be protected:')
470 print(', '.join(sorted(list(protected_links
))))
473 links
= os
.listdir('/sys/class/net')
474 remove_link(*links
, protect
=True)
476 def flush_nexthops():
477 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
478 # Hence, we cannot restore nexthops in a simple way.
479 # Let's assume there is no nexthop used in the system
480 call_quiet('ip nexthop flush')
483 # pylint: disable=global-statement
485 saved_routes
= check_output('ip route show table all')
486 print('### The following routes will be protected:')
491 output
= check_output('ip route show table all')
492 for line
in output
.splitlines():
493 if line
in saved_routes
:
495 if 'proto kernel' in line
:
497 if ' dev ' in line
and not ' dev lo ' in line
:
501 print('### Removing routes that did not exist when the test started.')
503 call(f
'ip route del {line}')
505 def save_routing_policy_rules():
506 # pylint: disable=global-statement
507 global saved_ipv4_rules
, saved_ipv6_rules
509 output
= check_output(f
'ip -{ipv} rule show')
510 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
514 saved_ipv4_rules
= save(4)
515 saved_ipv6_rules
= save(6)
517 def flush_routing_policy_rules():
518 def flush(ipv
, saved_rules
):
520 output
= check_output(f
'ip -{ipv} rule show')
521 for line
in output
.splitlines():
522 if line
in saved_rules
:
526 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
528 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
529 priority
= words
[0].rstrip(':')
530 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
532 flush(4, saved_ipv4_rules
)
533 flush(6, saved_ipv6_rules
)
535 def flush_fou_ports():
536 ret
= run('ip fou show')
537 if ret
.returncode
!= 0:
538 return # fou may not be supported
539 for line
in ret
.stdout
.splitlines():
540 port
= line
.split()[1]
541 call(f
'ip fou del port {port}')
543 def flush_l2tp_tunnels():
545 ret
= run('ip l2tp show tunnel')
546 if ret
.returncode
!= 0:
547 return # l2tp may not be supported
548 for line
in ret
.stdout
.splitlines():
550 if words
[0] == 'Tunnel':
551 tid
= words
[1].rstrip(',')
552 call(f
'ip l2tp del tunnel tunnel_id {tid}')
555 # Removing L2TP tunnel is asynchronous and slightly takes a time.
558 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
559 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
563 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
566 # pylint: disable=global-statement
567 global saved_timezone
568 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
569 if r
.returncode
== 0:
570 saved_timezone
= r
.stdout
.rstrip()
571 print(f
'### Saved timezone: {saved_timezone}')
573 def restore_timezone():
575 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
577 def read_link_attr(*args
):
578 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
579 return f
.readline().strip()
581 def read_manager_state_file():
582 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
585 def read_link_state_file(link
):
586 ifindex
= read_link_attr(link
, 'ifindex')
587 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
588 with
open(path
, encoding
='utf-8') as f
:
591 def read_ip_sysctl_attr(link
, attribute
, ipv
):
592 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
593 return f
.readline().strip()
595 def read_ip_neigh_sysctl_attr(link
, attribute
, ipv
):
596 with
open(os
.path
.join('/proc/sys/net', ipv
, 'neigh', link
, attribute
), encoding
='utf-8') as f
:
597 return f
.readline().strip()
599 def read_ipv6_sysctl_attr(link
, attribute
):
600 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
602 def read_ipv6_neigh_sysctl_attr(link
, attribute
):
603 return read_ip_neigh_sysctl_attr(link
, attribute
, 'ipv6')
605 def read_ipv4_sysctl_attr(link
, attribute
):
606 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
608 def stop_by_pid_file(pid_file
):
609 if not os
.path
.exists(pid_file
):
611 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
612 pid
= f
.read().rstrip(' \t\r\n\0')
613 os
.kill(int(pid
), signal
.SIGTERM
)
617 print(f
"PID {pid} is still alive, waiting...")
620 if e
.errno
== errno
.ESRCH
:
622 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
625 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'):
627 ra_mode
= f
',{ra_mode}'
633 f
'--log-facility={dnsmasq_log_file}',
634 '--log-queries=extra',
636 f
'--pid-file={dnsmasq_pid_file}',
637 '--conf-file=/dev/null',
639 f
'--interface={interface}',
640 f
'--dhcp-leasefile={dnsmasq_lease_file}',
642 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
643 f
'--dhcp-range={ipv4_range},2m',
644 '--dhcp-option=option:mtu,1492',
645 f
'--dhcp-option=option:router,{ipv4_router}',
648 ) + additional_options
649 check_output(*command
)
652 stop_by_pid_file(dnsmasq_pid_file
)
653 rm_f(dnsmasq_lease_file
)
654 rm_f(dnsmasq_log_file
)
656 def read_dnsmasq_log_file():
657 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
660 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
661 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
662 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
663 touch(isc_dhcpd_lease_file
)
664 check_output(isc_dhcpd_command
)
666 def stop_isc_dhcpd():
667 stop_by_pid_file(isc_dhcpd_pid_file
)
668 rm_f(isc_dhcpd_lease_file
)
670 def get_dbus_link_path(link
):
671 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
672 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
673 'GetLinkByName', 's', link
])
675 assert out
.startswith(b
'io ')
677 assert out
.endswith(b
'"')
679 return out
[:-1].split('"')[1]
681 def get_dhcp_client_state(link
, family
):
682 link_path
= get_dbus_link_path(link
)
684 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
685 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
686 assert out
.startswith(b
's "')
688 assert out
.endswith(b
'"')
689 return out
[3:-1].decode()
691 def get_dhcp4_client_state(link
):
692 return get_dhcp_client_state(link
, '4')
694 def get_dhcp6_client_state(link
):
695 return get_dhcp_client_state(link
, '6')
697 def get_link_description(link
):
698 link_path
= get_dbus_link_path(link
)
700 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
701 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
702 assert out
.startswith(b
's "')
704 assert out
.endswith(b
'"')
705 json_raw
= out
[2:].decode()
707 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
708 check_json(description
)
709 return json
.loads(description
) # Now parse the json
711 def start_radvd(*additional_options
, config_file
):
712 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
715 f
'--pidfile={radvd_pid_file}',
716 f
'--config={config_file_path}',
717 '--logmethod=stderr',
718 ) + additional_options
719 check_output(*command
)
722 stop_by_pid_file(radvd_pid_file
)
724 def radvd_check_config(config_file
):
725 if not shutil
.which('radvd'):
726 print('radvd is not installed, assuming the config check failed')
729 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
730 # set up (one instance is @unittest.skipX())
731 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
732 return call(f
'radvd --config={config_file_path} --configtest') == 0
734 def networkd_invocation_id():
735 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
737 def read_networkd_log(invocation_id
=None, since
=None):
738 if not invocation_id
:
739 invocation_id
= networkd_invocation_id()
743 '--output=short-monotonic',
744 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
747 command
.append(f
'--since={since}')
748 check_output('journalctl --sync')
749 return check_output(*command
)
751 def networkd_is_failed():
752 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
754 def stop_networkd(show_logs
=True):
756 invocation_id
= networkd_invocation_id()
757 check_output('systemctl stop systemd-networkd.socket')
758 check_output('systemctl stop systemd-networkd.service')
760 print(read_networkd_log(invocation_id
))
761 # Check if networkd exits cleanly.
762 assert not networkd_is_failed()
764 def start_networkd():
765 check_output('systemctl start systemd-networkd')
767 def restart_networkd(show_logs
=True):
769 invocation_id
= networkd_invocation_id()
770 check_output('systemctl restart systemd-networkd.service')
772 print(read_networkd_log(invocation_id
))
775 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
777 def networkctl(*args
):
778 # Do not call networkctl if networkd is in failed state.
779 # Otherwise, networkd may be restarted and we may get wrong results.
780 assert not networkd_is_failed()
781 return check_output(*(networkctl_cmd
+ list(args
)), env
=env
)
783 def networkctl_status(*args
):
784 return networkctl('-n', '0', 'status', *args
)
786 def networkctl_json(*args
):
787 return networkctl('--json=short', 'status', *args
)
789 def networkctl_reconfigure(*links
):
790 networkctl('reconfigure', *links
)
792 def networkctl_reload():
795 def resolvectl(*args
):
796 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
798 def timedatectl(*args
):
799 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
804 def tear_down_common():
805 # 1. stop DHCP/RA servers
811 call_quiet('rmmod netdevsim')
812 call_quiet('rmmod sch_teql')
814 # 3. remove network namespace
815 call_quiet('ip netns del ns99')
825 clear_network_units()
826 clear_networkd_conf_dropins()
831 flush_routing_policy_rules()
835 rm_rf(networkd_ci_temp_dir
)
836 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
838 clear_network_units()
839 clear_networkd_conf_dropins()
842 setup_systemd_udev_rules()
843 copy_udev_rule('00-debug-net.rules')
847 save_existing_links()
849 save_routing_policy_rules()
852 create_service_dropin('systemd-networkd', networkd_bin
,
855 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
857 'StartLimitIntervalSec=0'])
858 create_service_dropin('systemd-resolved', resolved_bin
)
859 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
861 # TODO: also run udevd with sanitizers, valgrind, or coverage
862 #create_service_dropin('systemd-udevd', udevd_bin,
863 # f'{udevadm_bin} control --reload --timeout 0')
865 'systemd-udevd.service',
869 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
873 'systemd-networkd.socket',
876 'StartLimitIntervalSec=0',
880 check_output('systemctl daemon-reload')
881 print(check_output('systemctl cat systemd-networkd.service'))
882 print(check_output('systemctl cat systemd-resolved.service'))
883 print(check_output('systemctl cat systemd-timesyncd.service'))
884 print(check_output('systemctl cat systemd-udevd.service'))
885 check_output('systemctl restart systemd-resolved.service')
886 check_output('systemctl restart systemd-timesyncd.service')
887 check_output('systemctl restart systemd-udevd.service')
889 def tearDownModule():
890 rm_rf(networkd_ci_temp_dir
)
892 clear_network_units()
893 clear_networkd_conf_dropins()
897 rm_rf('/run/systemd/system/systemd-networkd.service.d')
898 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
899 rm_rf('/run/systemd/system/systemd-resolved.service.d')
900 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
901 rm_rf('/run/systemd/system/systemd-udevd.service.d')
902 check_output('systemctl daemon-reload')
903 check_output('systemctl restart systemd-udevd.service')
904 restore_active_units()
907 # pylint: disable=no-member
909 def check_link_exists(self
, link
, expected
=True):
911 self
.assertTrue(link_exists(link
))
913 self
.assertFalse(link_exists(link
))
915 def check_link_attr(self
, *args
):
916 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
918 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
919 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
920 if allow_enoent
and not os
.path
.exists(path
):
922 with
open(path
, encoding
='utf-8') as f
:
923 self
.assertEqual(f
.readline().strip(), expected
)
925 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
926 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
928 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
929 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
931 def check_ipv6_neigh_sysctl_attr(self
, link
, attribute
, expected
):
932 self
.assertEqual(read_ipv6_neigh_sysctl_attr(link
, attribute
), expected
)
934 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
935 def links_exist(*links
):
937 if not link_exists(link
):
941 for iteration
in range(timeout
+ 1):
945 if links_exist(*links
):
948 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
951 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
952 # wait for the interface is activated.
953 needle
= f
'{link}: Bringing link {state}'
955 for iteration
in range(timeout
+ 1):
958 if not link_exists(link
):
960 output
= read_networkd_log()
961 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
964 self
.fail(f
'Timed out waiting for {link} activated.')
967 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
968 """Wait for the link to reach the specified operstate and/or setup state.
970 Specify None or '' for either operstate or setup_state to ignore that state.
971 This will recheck until the state conditions are met or the timeout expires.
973 If the link successfully matches the requested state, this returns True.
974 If this times out waiting for the link to match, the behavior depends on the
975 'fail_assert' parameter; if True, this causes a test assertion failure,
976 otherwise this returns False. The default is to cause assertion failure.
978 Note that this function matches on *exactly* the given operstate and setup_state.
979 To wait for a link to reach *or exceed* a given operstate, use wait_online().
986 for secs
in range(setup_timeout
+ 1):
989 if not link_exists(link
):
991 output
= networkctl_status(link
)
992 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
996 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
999 def wait_online(self
, *links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
1000 """Wait for the links to reach the specified operstate and/or setup state.
1002 This is similar to wait_operstate() but can be used for multiple links,
1003 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1004 The operstate should be specified in the link name, like 'eth0:degraded'.
1005 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1007 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1008 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1010 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1011 If this is set, no setup_state checks are done.
1013 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1014 This is applied only for the operational state 'degraded' or above.
1016 Note that this function waits for the links to reach *or exceed* the given operstate.
1017 However, the setup_state, if specified, must be matched *exactly*.
1019 This returns if the links reached the requested operstate/setup_state; otherwise it
1020 raises CalledProcessError or fails test assertion.
1022 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1030 check_output(*args
, env
=wait_online_env
)
1031 except subprocess
.CalledProcessError
:
1032 if networkd_is_failed():
1033 print('!!!!! systemd-networkd.service is failed !!!!!')
1034 call('systemctl status systemd-networkd.service')
1036 # show detailed status on failure
1037 for link
in links_with_operstate
:
1038 name
= link
.split(':')[0]
1039 if link_exists(name
):
1040 print(networkctl_status(name
))
1042 print(f
'Interface {name} not found.')
1044 if not bool_any
and setup_state
:
1045 for link
in links_with_operstate
:
1046 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1048 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1049 for i
in range(timeout_sec
):
1052 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1053 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1056 self
.assertRegex(output
, address_regex
)
1058 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1059 for i
in range(timeout_sec
):
1062 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1063 if not re
.search(address_regex
, output
):
1066 self
.assertNotRegex(output
, address_regex
)
1068 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1069 for i
in range(timeout_sec
):
1072 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1073 if re
.search(route_regex
, output
):
1076 self
.assertRegex(output
, route_regex
)
1078 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1079 if not shutil
.which('selinuxenabled'):
1080 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1081 elif call_quiet('selinuxenabled') != 0:
1082 print('## Checking NetLabel skipped: SELinux disabled.')
1083 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1084 print('## Checking NetLabel skipped: netlabelctl command not found.')
1086 output
= check_output('netlabelctl unlbl list')
1088 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1090 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1091 if not shutil
.which('nft'):
1092 print('## Setting up NFT sets skipped: nft command not found.')
1094 if call(f
'nft add table inet sd_test') != 0:
1095 print('## Setting up NFT table failed.')
1097 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1098 print('## Setting up NFT sets failed.')
1101 def teardown_nftset(self
, *filters
):
1102 if not shutil
.which('nft'):
1103 print('## Tearing down NFT sets skipped: nft command not found.')
1105 for filter_name
in filters
:
1106 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1107 print('## Tearing down NFT sets failed.')
1109 if call(f
'nft delete table inet sd_test') != 0:
1110 print('## Tearing down NFT table failed.')
1113 def check_nftset(self
, filter_name
, contents
):
1114 if not shutil
.which('nft'):
1115 print('## Checking NFT sets skipped: nft command not found.')
1117 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1119 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1121 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1129 @expectedFailureIfAlternativeNameIsNotAvailable()
1130 def test_altname(self
):
1131 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1133 self
.wait_online('dummy98:degraded')
1135 output
= networkctl_status('dummy98')
1136 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1138 @expectedFailureIfAlternativeNameIsNotAvailable()
1139 def test_rename_to_altname(self
):
1140 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1141 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1143 self
.wait_online('dummyalt:degraded')
1145 output
= networkctl_status('dummyalt')
1146 self
.assertIn('hogehogehogehogehogehoge', output
)
1147 self
.assertNotIn('dummy98', output
)
1149 def test_reconfigure(self
):
1150 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1152 self
.wait_online('dummy98:routable')
1154 output
= check_output('ip -4 address show dev dummy98')
1156 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1157 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1158 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1160 check_output('ip address del 10.1.2.3/16 dev dummy98')
1161 check_output('ip address del 10.1.2.4/16 dev dummy98')
1162 check_output('ip address del 10.2.2.4/16 dev dummy98')
1164 networkctl_reconfigure('dummy98')
1165 self
.wait_online('dummy98:routable')
1167 output
= check_output('ip -4 address show dev dummy98')
1169 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1170 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1171 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1173 remove_network_unit('25-address-static.network')
1176 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1178 output
= check_output('ip -4 address show dev dummy98')
1180 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1181 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1182 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1184 copy_network_unit('25-address-static.network', copy_dropins
=False)
1186 self
.wait_online('dummy98:routable')
1188 output
= check_output('ip -4 address show dev dummy98')
1190 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1191 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1192 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1194 def test_renew(self
):
1196 self
.wait_online('veth99:routable', 'veth-peer:routable')
1197 output
= networkctl_status('veth99')
1199 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1200 self
.assertIn('Gateway: 192.168.5.3', output
)
1201 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1202 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1204 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1207 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1209 for verb
in ['renew', 'forcerenew']:
1210 networkctl(verb
, 'veth99')
1212 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1215 def test_up_down(self
):
1216 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1218 self
.wait_online('dummy98:routable')
1220 networkctl('down', 'dummy98')
1221 self
.wait_online('dummy98:off')
1222 networkctl('up', 'dummy98')
1223 self
.wait_online('dummy98:routable')
1224 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1225 self
.wait_online('dummy98:off')
1226 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1227 self
.wait_online('dummy98:routable')
1229 def test_reload(self
):
1232 copy_network_unit('11-dummy.netdev')
1234 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1236 copy_network_unit('11-dummy.network')
1238 self
.wait_online('test1:degraded')
1240 remove_network_unit('11-dummy.network')
1242 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1244 remove_network_unit('11-dummy.netdev')
1246 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1248 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1250 self
.wait_operstate('test1', 'degraded')
1252 def test_glob(self
):
1253 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1256 self
.wait_online('test1:degraded')
1258 output
= networkctl('list')
1259 self
.assertRegex(output
, '1 lo ')
1260 self
.assertRegex(output
, 'test1')
1262 output
= networkctl('list', 'test1')
1263 self
.assertNotRegex(output
, '1 lo ')
1264 self
.assertRegex(output
, 'test1')
1266 output
= networkctl('list', 'te*')
1267 self
.assertNotRegex(output
, '1 lo ')
1268 self
.assertRegex(output
, 'test1')
1270 output
= networkctl_status('te*')
1271 self
.assertNotRegex(output
, '1: lo ')
1272 self
.assertRegex(output
, 'test1')
1274 output
= networkctl_status('tes[a-z][0-9]')
1275 self
.assertNotRegex(output
, '1: lo ')
1276 self
.assertRegex(output
, 'test1')
1279 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1282 self
.wait_online('test1:degraded')
1284 output
= networkctl_status('test1')
1285 self
.assertRegex(output
, 'MTU: 1600')
1287 def test_type(self
):
1288 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1290 self
.wait_online('test1:degraded')
1292 output
= networkctl_status('test1')
1294 self
.assertRegex(output
, 'Type: ether')
1296 output
= networkctl_status('lo')
1298 self
.assertRegex(output
, 'Type: loopback')
1300 def test_unit_file(self
):
1301 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1303 self
.wait_online('test1:degraded')
1305 output
= networkctl_status('test1')
1307 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1308 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1309 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1310 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1312 output
= read_networkd_log()
1313 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
)
1315 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1316 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1317 # Let's reprocess the interface and drop the property.
1318 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1319 output
= networkctl_status('lo')
1321 self
.assertIn('Link File: n/a', output
)
1322 self
.assertIn('Network File: n/a', output
)
1324 def test_delete_links(self
):
1325 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1326 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1329 self
.wait_online('test1:degraded', 'veth99:degraded', 'veth-peer:degraded')
1331 networkctl('delete', 'test1', 'veth99')
1332 self
.check_link_exists('test1', expected
=False)
1333 self
.check_link_exists('veth99', expected
=False)
1334 self
.check_link_exists('veth-peer', expected
=False)
1336 def test_label(self
):
1339 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1347 @expectedFailureIfAlternativeNameIsNotAvailable()
1348 def test_match(self
):
1349 copy_network_unit('12-dummy-mac.netdev',
1350 '12-dummy-match-mac-01.network',
1351 '12-dummy-match-mac-02.network',
1352 '12-dummy-match-renamed.network',
1353 '12-dummy-match-altname.network',
1354 '12-dummy-altname.link')
1357 self
.wait_online('dummy98:routable')
1358 output
= networkctl_status('dummy98')
1359 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1360 output
= check_output('ip -4 address show dev dummy98')
1361 self
.assertIn('10.0.0.1/16', output
)
1363 check_output('ip link set dev dummy98 down')
1364 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1366 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1367 self
.wait_online('dummy98:routable')
1368 output
= networkctl_status('dummy98')
1369 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1371 check_output('ip link set dev dummy98 down')
1372 check_output('ip link set dev dummy98 name dummy98-1')
1374 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1375 self
.wait_online('dummy98-1:routable')
1376 output
= networkctl_status('dummy98-1')
1377 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1379 check_output('ip link set dev dummy98-1 down')
1380 check_output('ip link set dev dummy98-1 name dummy98-2')
1381 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1383 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1384 self
.wait_online('dummy98-2:routable')
1385 output
= networkctl_status('dummy98-2')
1386 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1388 def test_match_udev_property(self
):
1389 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1391 self
.wait_online('dummy98:routable')
1393 output
= networkctl_status('dummy98')
1395 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1397 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1405 def test_wait_online_any(self
):
1406 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1409 self
.wait_online('bridge99', 'test1:degraded', bool_any
=True)
1411 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1412 self
.wait_operstate('test1', 'degraded')
1414 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1422 def test_dropin_and_name_conflict(self
):
1423 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1426 self
.wait_online('dropin-test:off', setup_state
='unmanaged')
1428 output
= check_output('ip link show dropin-test')
1430 self
.assertRegex(output
, '00:50:56:c0:00:28')
1432 @expectedFailureIfModuleIsNotAvailable('bareudp')
1433 def test_bareudp(self
):
1434 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1437 self
.wait_online('bareudp99:degraded')
1439 output
= check_output('ip -d link show bareudp99')
1441 self
.assertRegex(output
, 'dstport 1000 ')
1442 self
.assertRegex(output
, 'ethertype ip ')
1444 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1445 def test_batadv(self
):
1446 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1449 self
.wait_online('batadv99:degraded')
1451 output
= check_output('ip -d link show batadv99')
1453 self
.assertRegex(output
, 'batadv')
1455 def test_bridge(self
):
1456 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1459 self
.wait_online('bridge99:no-carrier')
1461 tick
= os
.sysconf('SC_CLK_TCK')
1462 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1463 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1464 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1465 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1466 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1467 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1468 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1469 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1470 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1472 output
= networkctl_status('bridge99')
1474 self
.assertRegex(output
, 'Priority: 9')
1475 self
.assertRegex(output
, 'STP: yes')
1476 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1478 output
= check_output('ip -d link show bridge99')
1480 self
.assertIn('vlan_filtering 1 ', output
)
1481 self
.assertIn('vlan_protocol 802.1ad ', output
)
1482 self
.assertIn('vlan_default_pvid 9 ', output
)
1484 def test_bond(self
):
1485 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
1488 self
.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state
='unmanaged')
1490 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1491 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1492 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1493 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1494 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1495 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1496 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1497 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1498 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1499 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1500 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1502 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1503 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1505 output
= networkctl_status('bond99')
1507 self
.assertIn('Mode: 802.3ad', output
)
1508 self
.assertIn('Miimon: 1s', output
)
1509 self
.assertIn('Updelay: 2s', output
)
1510 self
.assertIn('Downdelay: 2s', output
)
1512 output
= networkctl_status('bond98')
1514 self
.assertIn('Mode: balance-tlb', output
)
1516 output
= networkctl_status('bond97')
1519 self
.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
1521 def test_vlan(self
):
1522 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1523 '21-vlan.network', '21-vlan-test1.network')
1526 self
.wait_online('test1:degraded', 'vlan99:routable')
1528 output
= check_output('ip -d link show test1')
1530 self
.assertRegex(output
, ' mtu 2000 ')
1532 output
= check_output('ip -d link show vlan99')
1534 self
.assertIn(' mtu 2000 ', output
)
1535 self
.assertIn('REORDER_HDR', output
)
1536 self
.assertIn('LOOSE_BINDING', output
)
1537 self
.assertIn('GVRP', output
)
1538 self
.assertIn('MVRP', output
)
1539 self
.assertIn(' id 99 ', output
)
1540 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1541 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1543 output
= check_output('ip -4 address show dev test1')
1545 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1546 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1548 output
= check_output('ip -4 address show dev vlan99')
1550 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1552 def test_vlan_on_bond(self
):
1553 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1554 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1556 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1557 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1559 self
.wait_online('bond99:off')
1560 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1562 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1563 # that the issue is fixed by the commit, let's allow to match both string.
1564 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1568 if log_re
.search(read_networkd_log()):
1573 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1575 self
.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1577 def test_macvtap(self
):
1579 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1585 print(f
'### test_macvtap(mode={mode})')
1586 with self
.subTest(mode
=mode
):
1587 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1588 '11-dummy.netdev', '25-macvtap.network')
1589 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1590 f
.write('[MACVTAP]\nMode=' + mode
)
1593 self
.wait_online('macvtap99:degraded',
1594 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1596 output
= check_output('ip -d link show macvtap99')
1598 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1600 def test_macvlan(self
):
1602 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1608 print(f
'### test_macvlan(mode={mode})')
1609 with self
.subTest(mode
=mode
):
1610 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1611 '11-dummy.netdev', '25-macvlan.network')
1612 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1613 f
.write('[MACVLAN]\nMode=' + mode
)
1616 self
.wait_online('macvlan99:degraded',
1617 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1619 output
= check_output('ip -d link show test1')
1621 self
.assertIn(' mtu 2000 ', output
)
1623 output
= check_output('ip -d link show macvlan99')
1625 self
.assertIn(' mtu 2000 ', output
)
1626 self
.assertIn(f
' macvlan mode {mode} ', output
)
1628 remove_link('test1')
1631 check_output("ip link add test1 type dummy")
1632 self
.wait_online('macvlan99:degraded',
1633 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1635 output
= check_output('ip -d link show test1')
1637 self
.assertIn(' mtu 2000 ', output
)
1639 output
= check_output('ip -d link show macvlan99')
1641 self
.assertIn(' mtu 2000 ', output
)
1642 self
.assertIn(f
' macvlan mode {mode} ', output
)
1643 self
.assertIn(' bcqueuelen 1234 ', output
)
1644 self
.assertIn(' bclim 2147483647 ', output
)
1646 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1647 def test_ipvlan(self
):
1649 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1655 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1656 with self
.subTest(mode
=mode
, flag
=flag
):
1657 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1658 '11-dummy.netdev', '25-ipvlan.network')
1659 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1660 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1663 self
.wait_online('ipvlan99:degraded', 'test1:degraded')
1665 output
= check_output('ip -d link show ipvlan99')
1667 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1669 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1670 def test_ipvtap(self
):
1672 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1678 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1679 with self
.subTest(mode
=mode
, flag
=flag
):
1680 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1681 '11-dummy.netdev', '25-ipvtap.network')
1682 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1683 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1686 self
.wait_online('ipvtap99:degraded', 'test1:degraded')
1688 output
= check_output('ip -d link show ipvtap99')
1690 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1692 def test_veth(self
):
1693 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1694 '25-veth-mtu.netdev')
1697 self
.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
1699 output
= check_output('ip -d link show veth99')
1701 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1702 output
= check_output('ip -d link show veth-peer')
1704 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1706 output
= check_output('ip -d link show veth-mtu')
1708 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1709 self
.assertRegex(output
, 'mtu 1800')
1710 output
= check_output('ip -d link show veth-mtu-peer')
1712 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1713 self
.assertRegex(output
, 'mtu 1800')
1715 def test_tuntap(self
):
1716 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1719 self
.wait_online('testtun99:degraded', 'testtap99:degraded')
1721 pid
= networkd_pid()
1722 name
= psutil
.Process(pid
).name()[:15]
1724 output
= check_output('ip -d tuntap show')
1726 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1727 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1729 output
= check_output('ip -d link show testtun99')
1731 # Old ip command does not support IFF_ flags
1732 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1733 self
.assertIn('UP,LOWER_UP', output
)
1735 output
= check_output('ip -d link show testtap99')
1737 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1738 self
.assertIn('UP,LOWER_UP', output
)
1740 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1743 self
.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state
='unmanaged')
1745 pid
= networkd_pid()
1746 name
= psutil
.Process(pid
).name()[:15]
1748 output
= check_output('ip -d tuntap show')
1750 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1751 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1753 output
= check_output('ip -d link show testtun99')
1755 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1756 self
.assertIn('UP,LOWER_UP', output
)
1758 output
= check_output('ip -d link show testtap99')
1760 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1761 self
.assertIn('UP,LOWER_UP', output
)
1763 clear_network_units()
1765 self
.wait_online('testtun99:off', 'testtap99:off', setup_state
='unmanaged')
1767 output
= check_output('ip -d tuntap show')
1769 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1770 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1775 output
= check_output('ip -d link show testtun99')
1777 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1778 if 'NO-CARRIER' in output
:
1786 output
= check_output('ip -d link show testtap99')
1788 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1789 if 'NO-CARRIER' in output
:
1794 @expectedFailureIfModuleIsNotAvailable('vrf')
1796 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1799 self
.wait_online('vrf99:carrier')
1801 @expectedFailureIfModuleIsNotAvailable('vcan')
1802 def test_vcan(self
):
1803 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1804 '25-vcan98.netdev', '25-vcan98.network')
1807 self
.wait_online('vcan99:carrier', 'vcan98:carrier')
1808 # For can devices, 'carrier' is the default required operational state.
1809 self
.wait_online('vcan99', 'vcan98')
1811 # https://github.com/systemd/systemd/issues/30140
1812 output
= check_output('ip -d link show vcan99')
1814 self
.assertIn('mtu 16 ', output
)
1816 output
= check_output('ip -d link show vcan98')
1818 self
.assertIn('mtu 16 ', output
)
1820 @expectedFailureIfModuleIsNotAvailable('vxcan')
1821 def test_vxcan(self
):
1822 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1825 self
.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
1826 # For can devices, 'carrier' is the default required operational state.
1827 self
.wait_online('vxcan99', 'vxcan-peer')
1829 @expectedFailureIfModuleIsNotAvailable('wireguard')
1830 def test_wireguard(self
):
1831 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1832 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1833 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1835 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1836 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1837 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1838 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1840 self
.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
1842 output
= check_output('ip -4 address show dev wg99')
1844 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1846 output
= check_output('ip -4 address show dev wg99')
1848 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1850 output
= check_output('ip -6 address show dev wg99')
1852 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1854 output
= check_output('ip -4 address show dev wg98')
1856 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1858 output
= check_output('ip -6 address show dev wg98')
1860 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1862 output
= check_output('ip -4 route show dev wg99 table 1234')
1864 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1866 output
= check_output('ip -6 route show dev wg99 table 1234')
1868 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1870 output
= check_output('ip -6 route show dev wg98 table 1234')
1872 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1873 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1909 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1910 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1911 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1912 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1913 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1914 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1915 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1916 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1917 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1919 if shutil
.which('wg'):
1922 output
= check_output('wg show wg99 listen-port')
1923 self
.assertEqual(output
, '51820')
1924 output
= check_output('wg show wg99 fwmark')
1925 self
.assertEqual(output
, '0x4d2')
1926 output
= check_output('wg show wg99 private-key')
1927 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1928 output
= check_output('wg show wg99 allowed-ips')
1929 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1930 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1931 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1932 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1933 output
= check_output('wg show wg99 persistent-keepalive')
1934 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1935 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1936 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1937 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1938 output
= check_output('wg show wg99 endpoints')
1939 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1940 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1941 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1942 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1943 output
= check_output('wg show wg99 preshared-keys')
1944 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1945 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1946 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1947 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1949 output
= check_output('wg show wg98 private-key')
1950 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1952 output
= check_output('wg show wg97 listen-port')
1953 self
.assertEqual(output
, '51821')
1954 output
= check_output('wg show wg97 fwmark')
1955 self
.assertEqual(output
, '0x4d3')
1957 def test_geneve(self
):
1958 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1961 self
.wait_online('geneve99:degraded')
1963 output
= check_output('ip -d link show geneve99')
1965 self
.assertRegex(output
, '192.168.22.1')
1966 self
.assertRegex(output
, '6082')
1967 self
.assertRegex(output
, 'udpcsum')
1968 self
.assertRegex(output
, 'udp6zerocsumrx')
1970 def test_ipip_tunnel(self
):
1971 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1972 '25-ipip-tunnel.netdev', '25-tunnel.network',
1973 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1974 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1975 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1977 self
.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
1979 output
= check_output('ip -d link show ipiptun99')
1981 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1982 output
= check_output('ip -d link show ipiptun98')
1984 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1985 output
= check_output('ip -d link show ipiptun97')
1987 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1988 output
= check_output('ip -d link show ipiptun96')
1990 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1992 def test_gre_tunnel(self
):
1993 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1994 '25-gre-tunnel.netdev', '25-tunnel.network',
1995 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1996 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1997 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1999 self
.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2001 output
= check_output('ip -d link show gretun99')
2003 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2004 self
.assertRegex(output
, 'ikey 1.2.3.103')
2005 self
.assertRegex(output
, 'okey 1.2.4.103')
2006 self
.assertRegex(output
, 'iseq')
2007 self
.assertRegex(output
, 'oseq')
2008 output
= check_output('ip -d link show gretun98')
2010 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2011 self
.assertRegex(output
, 'ikey 0.0.0.104')
2012 self
.assertRegex(output
, 'okey 0.0.0.104')
2013 self
.assertNotRegex(output
, 'iseq')
2014 self
.assertNotRegex(output
, 'oseq')
2015 output
= check_output('ip -d link show gretun97')
2017 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2018 self
.assertRegex(output
, 'ikey 0.0.0.105')
2019 self
.assertRegex(output
, 'okey 0.0.0.105')
2020 self
.assertNotRegex(output
, 'iseq')
2021 self
.assertNotRegex(output
, 'oseq')
2022 output
= check_output('ip -d link show gretun96')
2024 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2025 self
.assertRegex(output
, 'ikey 0.0.0.106')
2026 self
.assertRegex(output
, 'okey 0.0.0.106')
2027 self
.assertNotRegex(output
, 'iseq')
2028 self
.assertNotRegex(output
, 'oseq')
2030 def test_ip6gre_tunnel(self
):
2031 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2032 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2033 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2034 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2035 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2038 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2040 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2042 output
= check_output('ip -d link show ip6gretun99')
2044 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2045 output
= check_output('ip -d link show ip6gretun98')
2047 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2048 output
= check_output('ip -d link show ip6gretun97')
2050 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2051 output
= check_output('ip -d link show ip6gretun96')
2053 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2055 def test_gretap_tunnel(self
):
2056 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2057 '25-gretap-tunnel.netdev', '25-tunnel.network',
2058 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2060 self
.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2062 output
= check_output('ip -d link show gretap99')
2064 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2065 self
.assertRegex(output
, 'ikey 0.0.0.106')
2066 self
.assertRegex(output
, 'okey 0.0.0.106')
2067 self
.assertRegex(output
, 'iseq')
2068 self
.assertRegex(output
, 'oseq')
2069 self
.assertIn('nopmtudisc', output
)
2070 self
.assertIn('ignore-df', output
)
2071 output
= check_output('ip -d link show gretap98')
2073 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2074 self
.assertRegex(output
, 'ikey 0.0.0.107')
2075 self
.assertRegex(output
, 'okey 0.0.0.107')
2076 self
.assertRegex(output
, 'iseq')
2077 self
.assertRegex(output
, 'oseq')
2079 def test_ip6gretap_tunnel(self
):
2080 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2081 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2082 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2084 self
.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2086 output
= check_output('ip -d link show ip6gretap99')
2088 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2089 output
= check_output('ip -d link show ip6gretap98')
2091 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2093 def test_vti_tunnel(self
):
2094 copy_network_unit('12-dummy.netdev', '25-vti.network',
2095 '25-vti-tunnel.netdev', '25-tunnel.network',
2096 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2097 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2098 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2100 self
.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2102 output
= check_output('ip -d link show vtitun99')
2104 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2105 output
= check_output('ip -d link show vtitun98')
2107 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2108 output
= check_output('ip -d link show vtitun97')
2110 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2111 output
= check_output('ip -d link show vtitun96')
2113 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2115 def test_vti6_tunnel(self
):
2116 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2117 '25-vti6-tunnel.netdev', '25-tunnel.network',
2118 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2119 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2121 self
.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2123 output
= check_output('ip -d link show vti6tun99')
2125 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2126 output
= check_output('ip -d link show vti6tun98')
2128 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2129 output
= check_output('ip -d link show vti6tun97')
2131 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2133 def test_ip6tnl_tunnel(self
):
2134 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2135 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2136 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2137 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2138 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2139 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2140 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2142 self
.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2143 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2144 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2146 output
= check_output('ip -d link show ip6tnl99')
2148 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2149 output
= check_output('ip -d link show ip6tnl98')
2151 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2152 output
= check_output('ip -d link show ip6tnl97')
2154 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2155 output
= check_output('ip -d link show ip6tnl-external')
2157 self
.assertIn('ip6tnl-external@NONE:', output
)
2158 self
.assertIn('ip6tnl external ', output
)
2159 output
= check_output('ip -d link show ip6tnl-slaac')
2161 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2163 output
= check_output('ip -6 address show veth99')
2165 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2167 output
= check_output('ip -4 route show default')
2169 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2171 def test_sit_tunnel(self
):
2172 copy_network_unit('12-dummy.netdev', '25-sit.network',
2173 '25-sit-tunnel.netdev', '25-tunnel.network',
2174 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2175 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2176 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2178 self
.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2180 output
= check_output('ip -d link show sittun99')
2182 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2183 output
= check_output('ip -d link show sittun98')
2185 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2186 output
= check_output('ip -d link show sittun97')
2188 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2189 output
= check_output('ip -d link show sittun96')
2191 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2193 def test_isatap_tunnel(self
):
2194 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2195 '25-isatap-tunnel.netdev', '25-tunnel.network')
2197 self
.wait_online('isataptun99:routable', 'dummy98:degraded')
2199 output
= check_output('ip -d link show isataptun99')
2201 self
.assertRegex(output
, "isatap ")
2203 def test_6rd_tunnel(self
):
2204 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2205 '25-6rd-tunnel.netdev', '25-tunnel.network')
2207 self
.wait_online('sittun99:routable', 'dummy98:degraded')
2209 output
= check_output('ip -d link show sittun99')
2211 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2213 @expectedFailureIfERSPANv0IsNotSupported()
2214 def test_erspan_tunnel_v0(self
):
2215 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2216 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2217 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2219 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2221 output
= check_output('ip -d link show erspan99')
2223 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2224 self
.assertIn('erspan_ver 0', output
)
2225 self
.assertNotIn('erspan_index 123', output
)
2226 self
.assertNotIn('erspan_dir ingress', output
)
2227 self
.assertNotIn('erspan_hwid 1f', output
)
2228 self
.assertIn('ikey 0.0.0.101', output
)
2229 self
.assertIn('iseq', output
)
2230 self
.assertIn('nopmtudisc', output
)
2231 self
.assertIn('ignore-df', output
)
2232 output
= check_output('ip -d link show erspan98')
2234 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2235 self
.assertIn('erspan_ver 0', output
)
2236 self
.assertNotIn('erspan_index 124', output
)
2237 self
.assertNotIn('erspan_dir egress', output
)
2238 self
.assertNotIn('erspan_hwid 2f', output
)
2239 self
.assertIn('ikey 0.0.0.102', output
)
2240 self
.assertIn('iseq', output
)
2242 def test_erspan_tunnel_v1(self
):
2243 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2244 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2245 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2247 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2249 output
= check_output('ip -d link show erspan99')
2251 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2252 self
.assertIn('erspan_ver 1', output
)
2253 self
.assertIn('erspan_index 123', output
)
2254 self
.assertNotIn('erspan_dir ingress', output
)
2255 self
.assertNotIn('erspan_hwid 1f', output
)
2256 self
.assertIn('ikey 0.0.0.101', output
)
2257 self
.assertIn('okey 0.0.0.101', output
)
2258 self
.assertIn('iseq', output
)
2259 self
.assertIn('oseq', output
)
2260 output
= check_output('ip -d link show erspan98')
2262 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2263 self
.assertIn('erspan_ver 1', output
)
2264 self
.assertIn('erspan_index 124', output
)
2265 self
.assertNotIn('erspan_dir egress', output
)
2266 self
.assertNotIn('erspan_hwid 2f', output
)
2267 self
.assertIn('ikey 0.0.0.102', output
)
2268 self
.assertIn('okey 0.0.0.102', output
)
2269 self
.assertIn('iseq', output
)
2270 self
.assertIn('oseq', output
)
2272 @expectedFailureIfERSPANv2IsNotSupported()
2273 def test_erspan_tunnel_v2(self
):
2274 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2275 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2276 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2278 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2280 output
= check_output('ip -d link show erspan99')
2282 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2283 self
.assertIn('erspan_ver 2', output
)
2284 self
.assertNotIn('erspan_index 123', output
)
2285 self
.assertIn('erspan_dir ingress', output
)
2286 self
.assertIn('erspan_hwid 0x1f', output
)
2287 self
.assertIn('ikey 0.0.0.101', output
)
2288 self
.assertIn('okey 0.0.0.101', output
)
2289 self
.assertIn('iseq', output
)
2290 self
.assertIn('oseq', output
)
2291 output
= check_output('ip -d link show erspan98')
2293 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2294 self
.assertIn('erspan_ver 2', output
)
2295 self
.assertNotIn('erspan_index 124', output
)
2296 self
.assertIn('erspan_dir egress', output
)
2297 self
.assertIn('erspan_hwid 0x2f', output
)
2298 self
.assertIn('ikey 0.0.0.102', output
)
2299 self
.assertIn('okey 0.0.0.102', output
)
2300 self
.assertIn('iseq', output
)
2301 self
.assertIn('oseq', output
)
2303 def test_tunnel_independent(self
):
2304 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2307 self
.wait_online('ipiptun99:carrier')
2309 def test_tunnel_independent_loopback(self
):
2310 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2313 self
.wait_online('ipiptun99:carrier')
2315 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2316 def test_xfrm(self
):
2317 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2318 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2319 '26-netdev-link-local-addressing-yes.network')
2322 self
.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
2324 output
= check_output('ip -d link show dev xfrm98')
2326 self
.assertIn('xfrm98@dummy98:', output
)
2327 self
.assertIn('xfrm if_id 0x98 ', output
)
2329 output
= check_output('ip -d link show dev xfrm99')
2331 self
.assertIn('xfrm99@lo:', output
)
2332 self
.assertIn('xfrm if_id 0x99 ', output
)
2334 @expectedFailureIfModuleIsNotAvailable('fou')
2336 # The following redundant check is necessary for CentOS CI.
2337 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2338 self
.assertTrue(is_module_available('fou'))
2340 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2341 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2342 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2345 self
.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state
='unmanaged')
2347 output
= check_output('ip fou show')
2349 self
.assertRegex(output
, 'port 55555 ipproto 4')
2350 self
.assertRegex(output
, 'port 55556 ipproto 47')
2352 output
= check_output('ip -d link show ipiptun96')
2354 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2355 output
= check_output('ip -d link show sittun96')
2357 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2358 output
= check_output('ip -d link show gretun96')
2360 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2361 output
= check_output('ip -d link show gretap96')
2363 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2365 def test_vxlan(self
):
2366 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2367 '25-vxlan.netdev', '25-vxlan.network',
2368 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2369 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2370 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2371 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2374 self
.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2375 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
2377 output
= check_output('ip -d -d link show vxlan99')
2379 self
.assertIn('999', output
)
2380 self
.assertIn('5555', output
)
2381 self
.assertIn('l2miss', output
)
2382 self
.assertIn('l3miss', output
)
2383 self
.assertIn('gbp', output
)
2384 # Since [0] some of the options use slightly different names and some
2385 # options with default values are shown only if the -d(etails) setting
2387 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2388 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2389 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2390 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2391 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2392 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2394 output
= check_output('bridge fdb show dev vxlan99')
2396 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2397 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2398 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2400 output
= networkctl_status('vxlan99')
2402 self
.assertIn('VNI: 999', output
)
2403 self
.assertIn('Destination Port: 5555', output
)
2404 self
.assertIn('Underlying Device: test1', output
)
2406 output
= check_output('bridge fdb show dev vxlan97')
2408 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2409 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2410 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2412 output
= check_output('ip -d link show vxlan-slaac')
2414 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2416 output
= check_output('ip -6 address show veth99')
2418 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2420 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2421 def test_macsec(self
):
2422 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2423 '26-macsec.network', '12-dummy.netdev')
2426 self
.wait_online('dummy98:degraded', 'macsec99:routable')
2428 output
= check_output('ip -d link show macsec99')
2430 self
.assertRegex(output
, 'macsec99@dummy98')
2431 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2432 self
.assertRegex(output
, 'encrypt on')
2434 output
= check_output('ip macsec show macsec99')
2436 self
.assertRegex(output
, 'encrypt on')
2437 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2438 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2439 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2440 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2441 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2442 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2443 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2444 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2445 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2446 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2447 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2449 def test_nlmon(self
):
2450 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2453 self
.wait_online('nlmon99:carrier')
2455 @expectedFailureIfModuleIsNotAvailable('ifb')
2457 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2460 self
.wait_online('ifb99:degraded')
2462 def test_rps_cpu_0(self
):
2463 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link')
2466 self
.wait_links('dummy98')
2468 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2470 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2472 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2473 def test_rps_cpu_1(self
):
2474 copy_network_unit('12-dummy.netdev', '25-rps-cpu-1.link')
2477 self
.wait_links('dummy98')
2479 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2481 self
.assertEqual(int(output
.replace(',', ''), base
=16), 2)
2483 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2484 def test_rps_cpu_0_1(self
):
2485 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-1.link')
2488 self
.wait_links('dummy98')
2490 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2492 self
.assertEqual(int(output
.replace(',', ''), base
=16), 3)
2494 @unittest.skipUnless(os
.cpu_count() >= 4, reason
="CPU count should be >= 4 to pass this test")
2495 def test_rps_cpu_multi(self
):
2496 copy_network_unit('12-dummy.netdev', '25-rps-cpu-multi.link')
2499 self
.wait_links('dummy98')
2501 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2503 self
.assertEqual(int(output
.replace(',', ''), base
=16), 15)
2505 def test_rps_cpu_all(self
):
2506 cpu_count
= os
.cpu_count()
2508 copy_network_unit('12-dummy.netdev', '25-rps-cpu-all.link')
2511 self
.wait_links('dummy98')
2513 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2515 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2517 def test_rps_cpu_disable(self
):
2518 copy_network_unit('12-dummy.netdev', '25-rps-cpu-all.link', '24-rps-cpu-disable.link')
2521 self
.wait_links('dummy98')
2523 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2525 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2527 def test_rps_cpu_empty(self
):
2528 copy_network_unit('12-dummy.netdev', '24-rps-cpu-empty.link')
2531 self
.wait_links('dummy98')
2533 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2535 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2537 def test_rps_cpu_0_empty(self
):
2538 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-empty.link')
2541 self
.wait_links('dummy98')
2543 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2545 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2547 def test_rps_cpu_0_and_empty(self
):
2548 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link', '24-rps-cpu-empty.link')
2551 self
.wait_links('dummy98')
2553 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2555 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2557 def test_rps_cpu_invalid(self
):
2558 copy_network_unit('12-dummy.netdev', '24-rps-cpu-invalid.link')
2561 self
.wait_links('dummy98')
2563 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2565 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2567 def test_rps_cpu_0_invalid(self
):
2568 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-invalid.link')
2571 self
.wait_links('dummy98')
2573 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2575 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2577 def test_rps_cpu_0_and_invalid(self
):
2578 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link', '24-rps-cpu-invalid.link')
2581 self
.wait_links('dummy98')
2583 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2585 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2587 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2595 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2596 def test_l2tp_udp(self
):
2597 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2598 '25-l2tp-udp.netdev', '25-l2tp.network')
2601 self
.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
2603 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2605 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2606 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2607 self
.assertRegex(output
, "Peer tunnel 11")
2608 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2609 self
.assertRegex(output
, "UDP checksum: enabled")
2611 output
= check_output('ip l2tp show session tid 10 session_id 15')
2613 self
.assertRegex(output
, "Session 15 in tunnel 10")
2614 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2615 self
.assertRegex(output
, "interface name: l2tp-ses1")
2617 output
= check_output('ip l2tp show session tid 10 session_id 17')
2619 self
.assertRegex(output
, "Session 17 in tunnel 10")
2620 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2621 self
.assertRegex(output
, "interface name: l2tp-ses2")
2623 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2624 def test_l2tp_ip(self
):
2625 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2626 '25-l2tp-ip.netdev', '25-l2tp.network')
2629 self
.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
2631 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2633 self
.assertRegex(output
, "Tunnel 10, encap IP")
2634 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2635 self
.assertRegex(output
, "Peer tunnel 12")
2637 output
= check_output('ip l2tp show session tid 10 session_id 25')
2639 self
.assertRegex(output
, "Session 25 in tunnel 10")
2640 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2641 self
.assertRegex(output
, "interface name: l2tp-ses3")
2643 output
= check_output('ip l2tp show session tid 10 session_id 27')
2645 self
.assertRegex(output
, "Session 27 in tunnel 10")
2646 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2647 self
.assertRegex(output
, "interface name: l2tp-ses4")
2649 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2657 def verify_address_static(
2687 output
= check_output('ip address show dev dummy98')
2691 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2692 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2693 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2694 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2695 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2696 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2699 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2700 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2701 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2704 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2705 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2706 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2709 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2710 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2711 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2712 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2713 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2714 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2717 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2718 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2721 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2722 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2723 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2724 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2727 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2728 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2730 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2732 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2734 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2736 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2739 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2740 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2741 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2742 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2745 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2746 prefix16
= ip4_null_16
[:-len('.0.1')]
2747 self
.assertTrue(ip4_null_24
.endswith('.1'))
2748 prefix24
= ip4_null_24
[:-len('.1')]
2749 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2750 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2751 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2752 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2755 self
.assertNotIn('10.4.4.1', output
)
2756 self
.assertNotIn('10.5.4.1', output
)
2757 self
.assertNotIn('10.5.5.1', output
)
2758 self
.assertNotIn('10.8.2.1', output
)
2759 self
.assertNotIn('10.9.3.1', output
)
2760 self
.assertNotIn('2001:db8:0:f101::2', output
)
2761 self
.assertNotIn('2001:db8:0:f103::4', output
)
2764 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2766 check_json(networkctl_json())
2768 def test_address_static(self
):
2769 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2770 self
.setup_nftset('addr4', 'ipv4_addr')
2771 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2772 self
.setup_nftset('ifindex', 'iface_index')
2775 self
.wait_online('dummy98:routable')
2779 output
= check_output('ip -4 --json address show dev dummy98')
2780 for i
in json
.loads(output
)[0]['addr_info']:
2781 if i
['label'] == 'subnet16':
2782 ip4_null_16
= i
['local']
2783 elif i
['label'] == 'subnet24':
2784 ip4_null_24
= i
['local']
2785 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2786 self
.assertTrue(ip4_null_24
.endswith('.1'))
2790 output
= check_output('ip -6 --json address show dev dummy98')
2791 for i
in json
.loads(output
)[0]['addr_info']:
2792 if i
['prefixlen'] == 73:
2793 ip6_null_73
= i
['local']
2794 elif i
['prefixlen'] == 74:
2795 ip6_null_74
= i
['local']
2796 self
.assertTrue(ip6_null_73
.endswith(':1'))
2797 self
.assertTrue(ip6_null_74
.endswith(':1'))
2799 self
.verify_address_static(
2804 broadcast2
=' brd 10.4.2.255',
2805 broadcast3
=' brd 10.4.3.63',
2806 peer1
=' peer 10.5.1.101/24',
2807 peer2
=' peer 10.5.2.101/24',
2808 peer3
='/24 brd 10.5.3.255',
2809 peer4
=' peer 2001:db8:0:f103::101/128',
2810 peer5
=' peer 2001:db8:0:f103::102/128',
2815 deprecated2
=' deprecated',
2817 deprecated4
=' deprecated',
2819 flag1
=' noprefixroute',
2821 flag3
=' noprefixroute',
2822 flag4
=' home mngtmpaddr',
2823 ip4_null_16
=ip4_null_16
,
2824 ip4_null_24
=ip4_null_24
,
2825 ip6_null_73
=ip6_null_73
,
2826 ip6_null_74
=ip6_null_74
,
2829 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2830 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2831 self
.check_nftset('ifindex', 'dummy98')
2833 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2835 copy_network_unit('25-address-static.network.d/10-override.conf')
2837 self
.wait_online('dummy98:routable')
2838 self
.verify_address_static(
2839 label1
='new-label1',
2841 label3
='new-label3',
2842 broadcast1
=' brd 10.4.1.255',
2844 broadcast3
=' brd 10.4.3.31',
2845 peer1
=' peer 10.5.1.102/24',
2846 peer2
='/24 brd 10.5.2.255',
2847 peer3
=' peer 10.5.3.102/24',
2848 peer4
=' peer 2001:db8:0:f103::201/128',
2850 peer6
=' peer 2001:db8:0:f103::203/128',
2853 deprecated1
=' deprecated',
2855 deprecated3
=' deprecated',
2859 flag2
=' noprefixroute',
2860 flag3
=' home mngtmpaddr',
2861 flag4
=' noprefixroute',
2862 ip4_null_16
=ip4_null_16
,
2863 ip4_null_24
=ip4_null_24
,
2864 ip6_null_73
=ip6_null_73
,
2865 ip6_null_74
=ip6_null_74
,
2868 networkctl_reconfigure('dummy98')
2869 self
.wait_online('dummy98:routable')
2870 self
.verify_address_static(
2871 label1
='new-label1',
2873 label3
='new-label3',
2874 broadcast1
=' brd 10.4.1.255',
2876 broadcast3
=' brd 10.4.3.31',
2877 peer1
=' peer 10.5.1.102/24',
2878 peer2
='/24 brd 10.5.2.255',
2879 peer3
=' peer 10.5.3.102/24',
2880 peer4
=' peer 2001:db8:0:f103::201/128',
2882 peer6
=' peer 2001:db8:0:f103::203/128',
2885 deprecated1
=' deprecated',
2887 deprecated3
=' deprecated',
2891 flag2
=' noprefixroute',
2892 flag3
=' home mngtmpaddr',
2893 flag4
=' noprefixroute',
2894 ip4_null_16
=ip4_null_16
,
2895 ip4_null_24
=ip4_null_24
,
2896 ip6_null_73
=ip6_null_73
,
2897 ip6_null_74
=ip6_null_74
,
2901 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2902 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2903 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2904 output
= check_output('ip address show dev dummy98')
2906 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2907 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2909 # 2. reconfigure the interface, and check the deprecated flag is set again
2910 networkctl_reconfigure('dummy98')
2911 self
.wait_online('dummy98:routable')
2912 self
.verify_address_static(
2913 label1
='new-label1',
2915 label3
='new-label3',
2916 broadcast1
=' brd 10.4.1.255',
2918 broadcast3
=' brd 10.4.3.31',
2919 peer1
=' peer 10.5.1.102/24',
2920 peer2
='/24 brd 10.5.2.255',
2921 peer3
=' peer 10.5.3.102/24',
2922 peer4
=' peer 2001:db8:0:f103::201/128',
2924 peer6
=' peer 2001:db8:0:f103::203/128',
2927 deprecated1
=' deprecated',
2929 deprecated3
=' deprecated',
2933 flag2
=' noprefixroute',
2934 flag3
=' home mngtmpaddr',
2935 flag4
=' noprefixroute',
2936 ip4_null_16
=ip4_null_16
,
2937 ip4_null_24
=ip4_null_24
,
2938 ip6_null_73
=ip6_null_73
,
2939 ip6_null_74
=ip6_null_74
,
2942 # test for ENOBUFS issue #17012 (with reload)
2943 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2945 self
.wait_online('dummy98:routable')
2946 output
= check_output('ip -4 address show dev dummy98')
2947 for i
in range(1, 254):
2948 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2950 # (with reconfigure)
2951 networkctl_reconfigure('dummy98')
2952 self
.wait_online('dummy98:routable')
2953 output
= check_output('ip -4 address show dev dummy98')
2954 for i
in range(1, 254):
2955 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2957 # test for an empty string assignment for Address= in [Network]
2958 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2960 self
.wait_online('dummy98:routable')
2961 output
= check_output('ip -4 address show dev dummy98')
2962 for i
in range(1, 254):
2963 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2964 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2966 def test_address_ipv4acd(self
):
2967 check_output('ip netns add ns99')
2968 check_output('ip link add veth99 type veth peer veth-peer')
2969 check_output('ip link set veth-peer netns ns99')
2970 check_output('ip link set veth99 up')
2971 check_output('ip netns exec ns99 ip link set veth-peer up')
2972 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2974 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2976 self
.wait_online('veth99:routable')
2978 output
= check_output('ip -4 address show dev veth99')
2980 self
.assertNotIn('192.168.100.10/24', output
)
2981 self
.assertIn('192.168.100.11/24', output
)
2983 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2985 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2987 output
= check_output('ip -4 address show dev veth99')
2989 self
.assertNotIn('192.168.100.10/24', output
)
2990 self
.assertIn('192.168.100.11/24', output
)
2992 def test_address_peer_ipv4(self
):
2993 # test for issue #17304
2994 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2996 for trial
in range(2):
3002 self
.wait_online('dummy98:routable')
3004 output
= check_output('ip -4 address show dev dummy98')
3005 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
3007 @expectedFailureIfModuleIsNotAvailable('vrf')
3008 def test_prefix_route(self
):
3009 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3010 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3011 '25-vrf.netdev', '25-vrf.network')
3012 for trial
in range(2):
3018 self
.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
3020 output
= check_output('ip route show table 42 dev dummy98')
3021 print('### ip route show table 42 dev dummy98')
3023 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
3024 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3025 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3026 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3027 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
3028 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3029 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3030 output
= check_output('ip -6 route show table 42 dev dummy98')
3031 print('### ip -6 route show table 42 dev dummy98')
3035 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3036 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3037 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3038 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3039 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3040 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3041 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3042 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3046 output
= check_output('ip route show dev test1')
3047 print('### ip route show dev test1')
3049 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3050 output
= check_output('ip route show table local dev test1')
3051 print('### ip route show table local dev test1')
3053 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
3054 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3055 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3056 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
3057 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3058 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3059 output
= check_output('ip -6 route show dev test1')
3060 print('### ip -6 route show dev test1')
3062 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3063 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3064 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3065 output
= check_output('ip -6 route show table local dev test1')
3066 print('### ip -6 route show table local dev test1')
3068 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3069 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3070 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3071 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
3072 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3074 def test_configure_without_carrier(self
):
3075 copy_network_unit('11-dummy.netdev')
3077 self
.wait_operstate('test1', 'off', '')
3078 check_output('ip link set dev test1 up carrier off')
3080 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
3082 self
.wait_online('test1:no-carrier')
3084 carrier_map
= {'on': '1', 'off': '0'}
3085 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3086 for carrier
in ['off', 'on', 'off']:
3087 with self
.subTest(carrier
=carrier
):
3088 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3089 check_output(f
'ip link set dev test1 carrier {carrier}')
3090 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3092 output
= networkctl_status('test1')
3094 self
.assertRegex(output
, '192.168.0.15')
3095 self
.assertRegex(output
, '192.168.0.1')
3096 self
.assertRegex(output
, routable_map
[carrier
])
3098 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
3099 copy_network_unit('11-dummy.netdev')
3101 self
.wait_operstate('test1', 'off', '')
3102 check_output('ip link set dev test1 up carrier off')
3104 copy_network_unit('25-test1.network')
3106 self
.wait_online('test1:no-carrier')
3108 carrier_map
= {'on': '1', 'off': '0'}
3109 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3110 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
3111 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
3112 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3113 check_output(f
'ip link set dev test1 carrier {carrier}')
3114 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3116 output
= networkctl_status('test1')
3119 self
.assertRegex(output
, '192.168.0.15')
3120 self
.assertRegex(output
, '192.168.0.1')
3122 self
.assertNotRegex(output
, '192.168.0.15')
3123 self
.assertNotRegex(output
, '192.168.0.1')
3124 self
.assertRegex(output
, routable_map
[carrier
])
3126 def test_routing_policy_rule(self
):
3127 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
3129 self
.wait_online('test1:degraded')
3131 output
= check_output('ip rule list iif test1 priority 111')
3133 self
.assertRegex(output
, '111:')
3134 self
.assertRegex(output
, 'from 192.168.100.18')
3135 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3136 self
.assertRegex(output
, 'iif test1')
3137 self
.assertRegex(output
, 'oif test1')
3138 self
.assertRegex(output
, 'lookup 7')
3140 output
= check_output('ip rule list iif test1 priority 101')
3142 self
.assertRegex(output
, '101:')
3143 self
.assertRegex(output
, 'from all')
3144 self
.assertRegex(output
, 'iif test1')
3145 self
.assertRegex(output
, 'lookup 9')
3147 output
= check_output('ip -6 rule list iif test1 priority 100')
3149 self
.assertRegex(output
, '100:')
3150 self
.assertRegex(output
, 'from all')
3151 self
.assertRegex(output
, 'iif test1')
3152 self
.assertRegex(output
, 'lookup 8')
3154 output
= check_output('ip rule list iif test1 priority 102')
3156 self
.assertRegex(output
, '102:')
3157 self
.assertRegex(output
, 'from 0.0.0.0/8')
3158 self
.assertRegex(output
, 'iif test1')
3159 self
.assertRegex(output
, 'lookup 10')
3161 check_json(networkctl_json())
3163 def test_routing_policy_rule_issue_11280(self
):
3164 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3165 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3167 for trial
in range(3):
3168 restart_networkd(show_logs
=(trial
> 0))
3169 self
.wait_online('test1:degraded', 'dummy98:degraded')
3171 output
= check_output('ip rule list table 7')
3173 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3175 output
= check_output('ip rule list table 8')
3177 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3179 def test_routing_policy_rule_reconfigure(self
):
3180 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3182 self
.wait_online('test1:degraded')
3184 output
= check_output('ip rule list table 1011')
3186 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3187 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3188 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3189 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3191 output
= check_output('ip -6 rule list table 1011')
3193 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3195 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3197 self
.wait_online('test1:degraded')
3199 output
= check_output('ip rule list table 1011')
3201 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3202 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3203 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3204 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3206 output
= check_output('ip -6 rule list table 1011')
3208 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3209 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3211 call('ip rule delete priority 10111')
3212 call('ip rule delete priority 10112')
3213 call('ip rule delete priority 10113')
3214 call('ip rule delete priority 10114')
3215 call('ip -6 rule delete priority 10113')
3217 output
= check_output('ip rule list table 1011')
3219 self
.assertEqual(output
, '')
3221 output
= check_output('ip -6 rule list table 1011')
3223 self
.assertEqual(output
, '')
3225 networkctl_reconfigure('test1')
3226 self
.wait_online('test1:degraded')
3228 output
= check_output('ip rule list table 1011')
3230 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3231 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3232 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3233 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3235 output
= check_output('ip -6 rule list table 1011')
3237 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3239 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3240 def test_routing_policy_rule_port_range(self
):
3241 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3243 self
.wait_online('test1:degraded')
3245 output
= check_output('ip rule')
3247 self
.assertRegex(output
, '111')
3248 self
.assertRegex(output
, 'from 192.168.100.18')
3249 self
.assertRegex(output
, '1123-1150')
3250 self
.assertRegex(output
, '3224-3290')
3251 self
.assertRegex(output
, 'tcp')
3252 self
.assertRegex(output
, 'lookup 7')
3254 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3255 def test_routing_policy_rule_invert(self
):
3256 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3258 self
.wait_online('test1:degraded')
3260 output
= check_output('ip rule')
3262 self
.assertRegex(output
, '111')
3263 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3264 self
.assertRegex(output
, 'tcp')
3265 self
.assertRegex(output
, 'lookup 7')
3267 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3268 def test_routing_policy_rule_l3mdev(self
):
3269 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3271 self
.wait_online('test1:degraded')
3273 output
= check_output('ip rule')
3275 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3276 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3278 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3279 def test_routing_policy_rule_uidrange(self
):
3280 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3282 self
.wait_online('test1:degraded')
3284 output
= check_output('ip rule')
3286 self
.assertRegex(output
, '111')
3287 self
.assertRegex(output
, 'from 192.168.100.18')
3288 self
.assertRegex(output
, 'lookup 7')
3289 self
.assertRegex(output
, 'uidrange 100-200')
3291 def _test_route_static(self
, manage_foreign_routes
):
3292 if not manage_foreign_routes
:
3293 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3295 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3296 '25-route-static-test1.network', '11-dummy.netdev')
3298 self
.wait_online('dummy98:routable')
3300 output
= networkctl_status('dummy98')
3303 print('### ip -6 route show dev dummy98')
3304 output
= check_output('ip -6 route show dev dummy98')
3306 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3307 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3308 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3310 print('### ip -6 route show default')
3311 output
= check_output('ip -6 route show default')
3313 self
.assertIn('default', output
)
3314 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3316 print('### ip -4 route show dev dummy98')
3317 output
= check_output('ip -4 route show dev dummy98')
3319 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3320 self
.assertIn('149.10.124.64 proto static scope link', output
)
3321 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3322 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3323 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3324 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3325 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3326 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3328 print('### ip -4 route show dev dummy98 default')
3329 output
= check_output('ip -4 route show dev dummy98 default')
3331 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3332 self
.assertIn('default via 149.10.124.64 proto static', output
)
3333 self
.assertIn('default proto static', output
)
3334 self
.assertIn('default via 1.1.8.104 proto static', output
)
3336 print('### ip -4 route show table local dev dummy98')
3337 output
= check_output('ip -4 route show table local dev dummy98')
3339 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3340 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3341 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3343 print('### ip -4 route show type blackhole')
3344 output
= check_output('ip -4 route show type blackhole')
3346 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3348 print('### ip -4 route show type unreachable')
3349 output
= check_output('ip -4 route show type unreachable')
3351 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3353 print('### ip -4 route show type prohibit')
3354 output
= check_output('ip -4 route show type prohibit')
3356 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3358 print('### ip -6 route show type blackhole')
3359 output
= check_output('ip -6 route show type blackhole')
3361 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3363 print('### ip -6 route show type unreachable')
3364 output
= check_output('ip -6 route show type unreachable')
3366 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3368 print('### ip -6 route show type prohibit')
3369 output
= check_output('ip -6 route show type prohibit')
3371 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3373 print('### ip route show 192.168.10.1')
3374 output
= check_output('ip route show 192.168.10.1')
3376 self
.assertIn('192.168.10.1 proto static', output
)
3377 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3378 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3379 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3380 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3382 print('### ip route show 192.168.10.2')
3383 output
= check_output('ip route show 192.168.10.2')
3385 # old ip command does not show IPv6 gateways...
3386 self
.assertIn('192.168.10.2 proto static', output
)
3387 self
.assertIn('nexthop', output
)
3388 self
.assertIn('dev test1 weight 20', output
)
3389 self
.assertIn('dev test1 weight 30', output
)
3390 self
.assertIn('dev dummy98 weight 10', output
)
3391 self
.assertIn('dev dummy98 weight 5', output
)
3393 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3394 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3396 # old ip command does not show 'nexthop' keyword and weight...
3397 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3398 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3399 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3400 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3401 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3403 check_json(networkctl_json())
3405 copy_network_unit('25-address-static.network', copy_dropins
=False)
3407 self
.wait_online('dummy98:routable')
3409 # check all routes managed by Manager are removed
3410 print('### ip -4 route show type blackhole')
3411 output
= check_output('ip -4 route show type blackhole')
3413 self
.assertEqual(output
, '')
3415 print('### ip -4 route show type unreachable')
3416 output
= check_output('ip -4 route show type unreachable')
3418 self
.assertEqual(output
, '')
3420 print('### ip -4 route show type prohibit')
3421 output
= check_output('ip -4 route show type prohibit')
3423 self
.assertEqual(output
, '')
3425 print('### ip -6 route show type blackhole')
3426 output
= check_output('ip -6 route show type blackhole')
3428 self
.assertEqual(output
, '')
3430 print('### ip -6 route show type unreachable')
3431 output
= check_output('ip -6 route show type unreachable')
3433 self
.assertEqual(output
, '')
3435 print('### ip -6 route show type prohibit')
3436 output
= check_output('ip -6 route show type prohibit')
3438 self
.assertEqual(output
, '')
3440 remove_network_unit('25-address-static.network')
3442 self
.wait_online('dummy98:routable')
3444 # check all routes managed by Manager are reconfigured
3445 print('### ip -4 route show type blackhole')
3446 output
= check_output('ip -4 route show type blackhole')
3448 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3450 print('### ip -4 route show type unreachable')
3451 output
= check_output('ip -4 route show type unreachable')
3453 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3455 print('### ip -4 route show type prohibit')
3456 output
= check_output('ip -4 route show type prohibit')
3458 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3460 print('### ip -6 route show type blackhole')
3461 output
= check_output('ip -6 route show type blackhole')
3463 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3465 print('### ip -6 route show type unreachable')
3466 output
= check_output('ip -6 route show type unreachable')
3468 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3470 print('### ip -6 route show type prohibit')
3471 output
= check_output('ip -6 route show type prohibit')
3473 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3475 remove_link('dummy98')
3478 # check all routes managed by Manager are removed
3479 print('### ip -4 route show type blackhole')
3480 output
= check_output('ip -4 route show type blackhole')
3482 self
.assertEqual(output
, '')
3484 print('### ip -4 route show type unreachable')
3485 output
= check_output('ip -4 route show type unreachable')
3487 self
.assertEqual(output
, '')
3489 print('### ip -4 route show type prohibit')
3490 output
= check_output('ip -4 route show type prohibit')
3492 self
.assertEqual(output
, '')
3494 print('### ip -6 route show type blackhole')
3495 output
= check_output('ip -6 route show type blackhole')
3497 self
.assertEqual(output
, '')
3499 print('### ip -6 route show type unreachable')
3500 output
= check_output('ip -6 route show type unreachable')
3502 self
.assertEqual(output
, '')
3504 print('### ip -6 route show type prohibit')
3505 output
= check_output('ip -6 route show type prohibit')
3507 self
.assertEqual(output
, '')
3509 def test_route_static(self
):
3511 for manage_foreign_routes
in [True, False]:
3517 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3518 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3519 self
._test
_route
_static
(manage_foreign_routes
)
3521 @expectedFailureIfRTA_VIAIsNotSupported()
3522 def test_route_via_ipv6(self
):
3523 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3525 self
.wait_online('dummy98:routable')
3527 output
= networkctl_status('dummy98')
3530 print('### ip -6 route show dev dummy98')
3531 output
= check_output('ip -6 route show dev dummy98')
3533 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3534 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3536 print('### ip -4 route show dev dummy98')
3537 output
= check_output('ip -4 route show dev dummy98')
3539 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3540 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3542 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3543 def test_route_congctl(self
):
3544 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3546 self
.wait_online('dummy98:routable')
3548 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3549 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3551 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3552 self
.assertIn('congctl dctcp', output
)
3554 print('### ip -4 route show dev dummy98 149.10.124.66')
3555 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3557 self
.assertIn('149.10.124.66 proto static', output
)
3558 self
.assertIn('congctl dctcp', output
)
3559 self
.assertIn('rto_min 300s', output
)
3561 @expectedFailureIfModuleIsNotAvailable('vrf')
3562 def test_route_vrf(self
):
3563 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3564 '25-vrf.netdev', '25-vrf.network')
3566 self
.wait_online('dummy98:routable', 'vrf99:carrier')
3568 output
= check_output('ip route show vrf vrf99')
3570 self
.assertRegex(output
, 'default via 192.168.100.1')
3572 output
= check_output('ip route show')
3574 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3576 def test_gateway_reconfigure(self
):
3577 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3579 self
.wait_online('dummy98:routable')
3580 print('### ip -4 route show dev dummy98 default')
3581 output
= check_output('ip -4 route show dev dummy98 default')
3583 self
.assertIn('default via 149.10.124.59 proto static', output
)
3584 self
.assertNotIn('149.10.124.60', output
)
3586 remove_network_unit('25-gateway-static.network')
3587 copy_network_unit('25-gateway-next-static.network')
3589 self
.wait_online('dummy98:routable')
3590 print('### ip -4 route show dev dummy98 default')
3591 output
= check_output('ip -4 route show dev dummy98 default')
3593 self
.assertNotIn('149.10.124.59', output
)
3594 self
.assertIn('default via 149.10.124.60 proto static', output
)
3596 def test_ip_route_ipv6_src_route(self
):
3597 # a dummy device does not make the addresses go through tentative state, so we
3598 # reuse a bond from an earlier test, which does make the addresses go through
3599 # tentative state, and do our test on that
3600 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3602 self
.wait_online('dummy98:enslaved', 'bond199:routable')
3604 output
= check_output('ip -6 route list dev bond199')
3606 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3608 def test_route_preferred_source_with_existing_address(self
):
3610 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3615 networkctl_reconfigure('dummy98')
3617 self
.wait_online('dummy98:routable')
3619 output
= check_output('ip -6 route list dev dummy98')
3621 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3623 def test_ip_link_mac_address(self
):
3624 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3626 self
.wait_online('dummy98:degraded')
3628 output
= check_output('ip link show dummy98')
3630 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3632 def test_ip_link_unmanaged(self
):
3633 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3636 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3638 def test_ipv6_address_label(self
):
3639 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3641 self
.wait_online('dummy98:degraded')
3643 output
= check_output('ip addrlabel list')
3645 self
.assertRegex(output
, '2004:da8:1::/64')
3647 def test_ipv6_proxy_ndp(self
):
3648 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3651 self
.wait_online('dummy98:routable')
3653 output
= check_output('ip neighbor show proxy dev dummy98')
3655 for i
in range(1, 5):
3656 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3658 def test_ipv6_neigh_retrans_time(self
):
3660 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3663 self
.wait_online(f
'{link}:degraded')
3664 remove_network_unit('25-dummy.network')
3666 # expect retrans_time_ms updated
3667 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3669 self
.wait_online(f
'{link}:degraded')
3670 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3671 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3673 # expect retrans_time_ms unchanged
3674 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3676 self
.wait_online(f
'{link}:degraded')
3677 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3678 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3680 # expect retrans_time_ms unchanged
3681 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3683 self
.wait_online(f
'{link}:degraded')
3684 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3685 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3687 # expect retrans_time_ms unchanged
3688 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3690 self
.wait_online(f
'{link}:degraded')
3691 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3692 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3694 # expect retrans_time_ms unchanged
3695 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3697 self
.wait_online(f
'{link}:degraded')
3698 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3699 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3701 # expect retrans_time_ms updated
3702 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3704 self
.wait_online(f
'{link}:degraded')
3705 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3706 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3708 def test_neighbor(self
):
3709 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3710 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3711 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3714 self
.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
3716 print('### ip neigh list dev gretun97')
3717 output
= check_output('ip neigh list dev gretun97')
3719 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3720 self
.assertNotIn('10.0.0.23', output
)
3722 print('### ip neigh list dev ip6gretun97')
3723 output
= check_output('ip neigh list dev ip6gretun97')
3725 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3726 self
.assertNotIn('2001:db8:0:f102::18', output
)
3728 print('### ip neigh list dev dummy98')
3729 output
= check_output('ip neigh list dev dummy98')
3731 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3732 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3733 self
.assertNotIn('2004:da8:1:0::2', output
)
3734 self
.assertNotIn('192.168.10.2', output
)
3735 self
.assertNotIn('00:00:5e:00:02:67', output
)
3737 check_json(networkctl_json())
3739 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3740 # the valid configurations in 10-step1.conf.
3741 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3743 self
.wait_online('dummy98:degraded')
3745 print('### ip neigh list dev dummy98')
3746 output
= check_output('ip neigh list dev dummy98')
3748 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3749 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3750 self
.assertNotIn('2004:da8:1:0::2', output
)
3751 self
.assertNotIn('192.168.10.2', output
)
3752 self
.assertNotIn('00:00:5e:00:02:67', output
)
3754 check_json(networkctl_json())
3756 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3757 '25-neighbor-dummy.network.d/10-step2.conf')
3758 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3760 self
.wait_online('dummy98:degraded')
3762 print('### ip neigh list dev dummy98')
3763 output
= check_output('ip neigh list dev dummy98')
3765 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3766 self
.assertNotIn('00:00:5e:00:02:65', output
)
3767 self
.assertNotIn('00:00:5e:00:02:66', output
)
3768 self
.assertNotIn('00:00:5e:00:03:65', output
)
3769 self
.assertNotIn('2004:da8:1::1', output
)
3771 def test_link_local_addressing(self
):
3772 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3773 '25-link-local-addressing-no.network', '12-dummy.netdev')
3775 self
.wait_online('test1:degraded', 'dummy98:carrier')
3777 output
= check_output('ip address show dev test1')
3779 self
.assertRegex(output
, 'inet .* scope link')
3780 self
.assertRegex(output
, 'inet6 .* scope link')
3782 output
= check_output('ip address show dev dummy98')
3784 self
.assertNotRegex(output
, 'inet6* .* scope link')
3786 # Documentation/networking/ip-sysctl.txt
3788 # addr_gen_mode - INTEGER
3789 # Defines how link-local and autoconf addresses are generated.
3791 # 0: generate address based on EUI64 (default)
3792 # 1: do no generate a link-local address, use EUI64 for addresses generated
3794 # 2: generate stable privacy addresses, using the secret from
3795 # stable_secret (RFC7217)
3796 # 3: generate stable privacy addresses, using a random secret if unset
3798 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3799 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3800 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3802 def test_link_local_addressing_ipv6ll(self
):
3803 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3805 self
.wait_online('dummy98:degraded')
3807 # An IPv6LL address exists by default.
3808 output
= check_output('ip address show dev dummy98')
3810 self
.assertRegex(output
, 'inet6 .* scope link')
3812 copy_network_unit('25-link-local-addressing-no.network')
3814 self
.wait_online('dummy98:carrier')
3816 # Check if the IPv6LL address is removed.
3817 output
= check_output('ip address show dev dummy98')
3819 self
.assertNotRegex(output
, 'inet6 .* scope link')
3821 remove_network_unit('25-link-local-addressing-no.network')
3823 self
.wait_online('dummy98:degraded')
3825 # Check if a new IPv6LL address is assigned.
3826 output
= check_output('ip address show dev dummy98')
3828 self
.assertRegex(output
, 'inet6 .* scope link')
3830 def test_sysctl(self
):
3831 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3832 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3834 self
.wait_online('dummy98:degraded')
3836 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3837 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3838 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3839 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3840 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3841 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3842 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3843 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3844 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3845 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3847 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3849 self
.wait_online('dummy98:degraded')
3851 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3853 def test_sysctl_disable_ipv6(self
):
3854 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3856 print('## Disable ipv6')
3857 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3858 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3861 self
.wait_online('dummy98:routable')
3863 output
= check_output('ip -4 address show dummy98')
3865 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3866 output
= check_output('ip -6 address show dummy98')
3868 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3869 self
.assertRegex(output
, 'inet6 .* scope link')
3870 output
= check_output('ip -4 route show dev dummy98')
3872 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3873 output
= check_output('ip -6 route show default')
3875 self
.assertRegex(output
, 'default')
3876 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3878 remove_link('dummy98')
3880 print('## Enable ipv6')
3881 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3882 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3885 self
.wait_online('dummy98:routable')
3887 output
= check_output('ip -4 address show dummy98')
3889 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3890 output
= check_output('ip -6 address show dummy98')
3892 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3893 self
.assertRegex(output
, 'inet6 .* scope link')
3894 output
= check_output('ip -4 route show dev dummy98')
3896 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3897 output
= check_output('ip -6 route show default')
3899 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3901 def test_bind_carrier(self
):
3902 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3905 # no bound interface.
3906 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3907 output
= check_output('ip address show test1')
3909 self
.assertNotIn('UP,LOWER_UP', output
)
3910 self
.assertIn('DOWN', output
)
3911 self
.assertNotIn('192.168.10', output
)
3913 # add one bound interface. The interface will be up.
3914 check_output('ip link add dummy98 type dummy')
3915 check_output('ip link set dummy98 up')
3916 self
.wait_online('test1:routable')
3917 output
= check_output('ip address show test1')
3919 self
.assertIn('UP,LOWER_UP', output
)
3920 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3922 # add another bound interface. The interface is still up.
3923 check_output('ip link add dummy99 type dummy')
3924 check_output('ip link set dummy99 up')
3925 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3926 output
= check_output('ip address show test1')
3928 self
.assertIn('UP,LOWER_UP', output
)
3929 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3931 # remove one of the bound interfaces. The interface is still up
3932 remove_link('dummy98')
3933 output
= check_output('ip address show test1')
3935 self
.assertIn('UP,LOWER_UP', output
)
3936 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3938 # bring down the remaining bound interface. The interface will be down.
3939 check_output('ip link set dummy99 down')
3940 self
.wait_operstate('test1', 'off')
3941 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3942 output
= check_output('ip address show test1')
3944 self
.assertNotIn('UP,LOWER_UP', output
)
3945 self
.assertIn('DOWN', output
)
3946 self
.assertNotIn('192.168.10', output
)
3948 # bring up the bound interface. The interface will be up.
3949 check_output('ip link set dummy99 up')
3950 self
.wait_online('test1:routable')
3951 output
= check_output('ip address show test1')
3953 self
.assertIn('UP,LOWER_UP', output
)
3954 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3956 # remove the remaining bound interface. The interface will be down.
3957 remove_link('dummy99')
3958 self
.wait_operstate('test1', 'off')
3959 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3960 output
= check_output('ip address show test1')
3962 self
.assertNotIn('UP,LOWER_UP', output
)
3963 self
.assertIn('DOWN', output
)
3964 self
.assertNotIn('192.168.10', output
)
3966 # re-add one bound interface. The interface will be up.
3967 check_output('ip link add dummy98 type dummy')
3968 check_output('ip link set dummy98 up')
3969 self
.wait_online('test1:routable')
3970 output
= check_output('ip address show test1')
3972 self
.assertIn('UP,LOWER_UP', output
)
3973 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3975 def _test_activation_policy(self
, interface
, test
):
3976 conffile
= '25-activation-policy.network'
3978 conffile
= f
'{conffile}.d/{test}.conf'
3979 if interface
== 'vlan99':
3980 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3981 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3984 always
= test
.startswith('always')
3985 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3986 expect_up
= initial_up
3987 next_up
= not expect_up
3989 if test
.endswith('down'):
3990 self
.wait_activated(interface
)
3992 for iteration
in range(4):
3993 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3994 operstate
= 'routable' if expect_up
else 'off'
3995 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3996 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3999 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
4000 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
4001 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
4003 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
4006 check_output(f
'ip link set dev {interface} up')
4008 check_output(f
'ip link set dev {interface} down')
4009 expect_up
= initial_up
if always
else next_up
4010 next_up
= not next_up
4014 def test_activation_policy(self
):
4016 for interface
in ['test1', 'vlan99']:
4017 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
4023 print(f
'### test_activation_policy(interface={interface}, test={test})')
4024 with self
.subTest(interface
=interface
, test
=test
):
4025 self
._test
_activation
_policy
(interface
, test
)
4027 def _test_activation_policy_required_for_online(self
, policy
, required
):
4028 conffile
= '25-activation-policy.network'
4029 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
4031 units
+= [f
'{conffile}.d/{policy}.conf']
4033 units
+= [f
'{conffile}.d/required-{required}.conf']
4034 copy_network_unit(*units
, copy_dropins
=False)
4037 if policy
.endswith('down'):
4038 self
.wait_activated('test1')
4040 if policy
.endswith('down') or policy
== 'manual':
4041 self
.wait_operstate('test1', 'off', setup_state
='configuring')
4043 self
.wait_online('test1')
4045 if policy
== 'always-down':
4046 # if always-down, required for online is forced to no
4049 # otherwise if required for online is specified, it should match that
4050 expected
= required
== 'yes'
4052 # otherwise if only policy specified, required for online defaults to
4053 # true if policy is up, always-up, or bound
4054 expected
= policy
.endswith('up') or policy
== 'bound'
4056 # default is true, if neither are specified
4059 output
= networkctl_status('test1')
4062 yesno
= 'yes' if expected
else 'no'
4063 self
.assertRegex(output
, f
'Required For Online: {yesno}')
4065 def test_activation_policy_required_for_online(self
):
4067 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
4068 for required
in ['yes', 'no', '']:
4074 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
4075 with self
.subTest(policy
=policy
, required
=required
):
4076 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
4078 def test_domain(self
):
4079 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
4081 self
.wait_online('dummy98:routable')
4083 output
= networkctl_status('dummy98')
4085 self
.assertRegex(output
, 'Address: 192.168.42.100')
4086 self
.assertRegex(output
, 'DNS: 192.168.42.1')
4087 self
.assertRegex(output
, 'Search Domains: one')
4089 def test_keep_configuration_static(self
):
4090 check_output('ip link add name dummy98 type dummy')
4091 check_output('ip address add 10.1.2.3/16 dev dummy98')
4092 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
4093 output
= check_output('ip address show dummy98')
4095 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4096 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4097 output
= check_output('ip route show dev dummy98')
4100 copy_network_unit('24-keep-configuration-static.network')
4102 self
.wait_online('dummy98:routable')
4104 output
= check_output('ip address show dummy98')
4106 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4107 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4109 def check_nexthop(self
, manage_foreign_nexthops
, first
):
4110 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
4112 output
= check_output('ip nexthop list dev veth99')
4115 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
4116 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
4118 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
4119 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
4120 self
.assertIn('id 3 dev veth99', output
)
4121 self
.assertIn('id 4 dev veth99', output
)
4123 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
4125 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
4126 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
4127 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
4128 if manage_foreign_nexthops
:
4129 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
4131 output
= check_output('ip nexthop list dev dummy98')
4134 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4136 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4137 if manage_foreign_nexthops
:
4138 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4140 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4142 # kernel manages blackhole nexthops on lo
4143 output
= check_output('ip nexthop list dev lo')
4146 self
.assertIn('id 6 blackhole', output
)
4147 self
.assertIn('id 7 blackhole', output
)
4149 self
.assertIn('id 1 blackhole', output
)
4150 self
.assertIn('id 2 blackhole', output
)
4152 # group nexthops are shown with -0 option
4154 output
= check_output('ip -0 nexthop list id 21')
4156 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4158 output
= check_output('ip -0 nexthop list id 20')
4160 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4162 output
= check_output('ip route show dev veth99 10.10.10.10')
4165 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4167 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4169 output
= check_output('ip route show dev veth99 10.10.10.11')
4172 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4174 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4176 output
= check_output('ip route show dev veth99 10.10.10.12')
4179 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4181 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4183 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4186 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4188 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4190 output
= check_output('ip route show 10.10.10.13')
4193 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4195 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4197 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4200 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4202 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4204 output
= check_output('ip route show 10.10.10.14')
4207 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4208 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4210 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4211 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4212 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4214 output
= networkctl_json()
4216 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4218 def _test_nexthop(self
, manage_foreign_nexthops
):
4219 if not manage_foreign_nexthops
:
4220 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4222 check_output('ip link add dummy98 type dummy')
4223 check_output('ip link set dummy98 up')
4224 check_output('ip address add 192.168.20.20/24 dev dummy98')
4225 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4227 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4228 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4231 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4233 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4234 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4236 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4238 remove_network_unit('25-nexthop-2.network')
4239 copy_network_unit('25-nexthop-nothing.network')
4241 self
.wait_online('veth99:routable', 'veth-peer:routable')
4243 output
= check_output('ip nexthop list dev veth99')
4245 self
.assertEqual(output
, '')
4246 output
= check_output('ip nexthop list dev lo')
4248 self
.assertEqual(output
, '')
4250 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4251 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4252 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4253 # here to test reconfiguring with different .network files does not trigger race.
4254 # See also comments in link_drop_requests().
4255 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4256 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4258 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4260 # Remove nexthop with ID 20
4261 check_output('ip nexthop del id 20')
4262 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4265 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4266 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4267 # hence test1 should be stuck in the configuring state.
4268 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4270 # Wait for a while, and check if the interface is still in the configuring state.
4272 output
= networkctl_status('test1')
4273 self
.assertIn('State: routable (configuring)', output
)
4275 # Check if the route which needs nexthop 20 and 21 are forgotten.
4276 output
= networkctl_json()
4278 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4280 # Reconfigure the interface that has nexthop with ID 20 and 21,
4281 # then the route requested by test1 can be configured.
4282 networkctl_reconfigure('dummy98')
4283 self
.wait_online('test1:routable')
4285 # Check if the requested route actually configured.
4286 output
= check_output('ip route show 10.10.11.10')
4288 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4289 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4290 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4292 remove_link('veth99')
4295 output
= check_output('ip nexthop list dev lo')
4297 self
.assertEqual(output
, '')
4299 @expectedFailureIfNexthopIsNotAvailable()
4300 def test_nexthop(self
):
4302 for manage_foreign_nexthops
in [True, False]:
4308 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4309 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4310 self
._test
_nexthop
(manage_foreign_nexthops
)
4312 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4320 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4321 def test_qdisc_cake(self
):
4322 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4324 self
.wait_online('dummy98:routable')
4326 output
= check_output('tc qdisc show dev dummy98')
4328 self
.assertIn('qdisc cake 3a: root', output
)
4329 self
.assertIn('bandwidth 500Mbit', output
)
4330 self
.assertIn('autorate-ingress', output
)
4331 self
.assertIn('diffserv8', output
)
4332 self
.assertIn('dual-dsthost', output
)
4333 self
.assertIn(' nat', output
)
4334 self
.assertIn(' wash', output
)
4335 self
.assertIn(' split-gso', output
)
4336 self
.assertIn(' raw', output
)
4337 self
.assertIn(' atm', output
)
4338 self
.assertIn('overhead 128', output
)
4339 self
.assertIn('mpu 20', output
)
4340 self
.assertIn('fwmark 0xff00', output
)
4341 self
.assertIn('rtt 1s', output
)
4342 self
.assertIn('ack-filter-aggressive', output
)
4344 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4345 def test_qdisc_codel(self
):
4346 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4348 self
.wait_online('dummy98:routable')
4350 output
= check_output('tc qdisc show dev dummy98')
4352 self
.assertRegex(output
, 'qdisc codel 33: root')
4353 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4355 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4356 def test_qdisc_drr(self
):
4357 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4359 self
.wait_online('dummy98:routable')
4361 output
= check_output('tc qdisc show dev dummy98')
4363 self
.assertRegex(output
, 'qdisc drr 2: root')
4364 output
= check_output('tc class show dev dummy98')
4366 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4368 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4369 def test_qdisc_ets(self
):
4370 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4372 self
.wait_online('dummy98:routable')
4374 output
= check_output('tc qdisc show dev dummy98')
4377 self
.assertRegex(output
, 'qdisc ets 3a: root')
4378 self
.assertRegex(output
, 'bands 10 strict 3')
4379 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4380 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4382 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4383 def test_qdisc_fq(self
):
4384 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4386 self
.wait_online('dummy98:routable')
4388 output
= check_output('tc qdisc show dev dummy98')
4390 self
.assertRegex(output
, 'qdisc fq 32: root')
4391 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4392 self
.assertRegex(output
, 'quantum 1500')
4393 self
.assertRegex(output
, 'initial_quantum 13000')
4394 self
.assertRegex(output
, 'maxrate 1Mbit')
4396 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4397 def test_qdisc_fq_codel(self
):
4398 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4400 self
.wait_online('dummy98:routable')
4402 output
= check_output('tc qdisc show dev dummy98')
4404 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4405 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')
4407 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4408 def test_qdisc_fq_pie(self
):
4409 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4411 self
.wait_online('dummy98:routable')
4413 output
= check_output('tc qdisc show dev dummy98')
4416 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4417 self
.assertRegex(output
, 'limit 200000p')
4419 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4420 def test_qdisc_gred(self
):
4421 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4423 self
.wait_online('dummy98:routable')
4425 output
= check_output('tc qdisc show dev dummy98')
4427 self
.assertRegex(output
, 'qdisc gred 38: root')
4428 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4430 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4431 def test_qdisc_hhf(self
):
4432 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4434 self
.wait_online('dummy98:routable')
4436 output
= check_output('tc qdisc show dev dummy98')
4438 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4439 self
.assertRegex(output
, 'limit 1022p')
4441 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4442 def test_qdisc_htb_fifo(self
):
4443 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4445 self
.wait_online('dummy98:routable')
4447 output
= check_output('tc qdisc show dev dummy98')
4449 self
.assertRegex(output
, 'qdisc htb 2: root')
4450 self
.assertRegex(output
, r
'default (0x30|30)')
4452 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4453 self
.assertRegex(output
, 'limit 100000p')
4455 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4456 self
.assertRegex(output
, 'limit 1000000')
4458 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4459 self
.assertRegex(output
, 'limit 1023p')
4461 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4463 output
= check_output('tc -d class show dev dummy98')
4465 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4466 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4467 # which is fixed in v6.3.0 by
4468 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4469 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4470 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4471 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4472 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4473 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4474 self
.assertRegex(output
, 'burst 123456')
4475 self
.assertRegex(output
, 'cburst 123457')
4477 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4478 def test_qdisc_ingress(self
):
4479 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4480 '25-qdisc-ingress.network', '11-dummy.netdev')
4482 self
.wait_online('dummy98:routable', 'test1:routable')
4484 output
= check_output('tc qdisc show dev dummy98')
4486 self
.assertRegex(output
, 'qdisc clsact')
4488 output
= check_output('tc qdisc show dev test1')
4490 self
.assertRegex(output
, 'qdisc ingress')
4492 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4493 def test_qdisc_netem(self
):
4494 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4495 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4497 self
.wait_online('dummy98:routable', 'test1:routable')
4499 output
= check_output('tc qdisc show dev dummy98')
4501 self
.assertRegex(output
, 'qdisc netem 30: root')
4502 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4504 output
= check_output('tc qdisc show dev test1')
4506 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4507 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4509 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4510 def test_qdisc_pie(self
):
4511 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4513 self
.wait_online('dummy98:routable')
4515 output
= check_output('tc qdisc show dev dummy98')
4517 self
.assertRegex(output
, 'qdisc pie 3a: root')
4518 self
.assertRegex(output
, 'limit 200000')
4520 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4521 def test_qdisc_qfq(self
):
4522 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4524 self
.wait_online('dummy98:routable')
4526 output
= check_output('tc qdisc show dev dummy98')
4528 self
.assertRegex(output
, 'qdisc qfq 2: root')
4529 output
= check_output('tc class show dev dummy98')
4531 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4532 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4534 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4535 def test_qdisc_sfb(self
):
4536 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4538 self
.wait_online('dummy98:routable')
4540 output
= check_output('tc qdisc show dev dummy98')
4542 self
.assertRegex(output
, 'qdisc sfb 39: root')
4543 self
.assertRegex(output
, 'limit 200000')
4545 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4546 def test_qdisc_sfq(self
):
4547 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4549 self
.wait_online('dummy98:routable')
4551 output
= check_output('tc qdisc show dev dummy98')
4553 self
.assertRegex(output
, 'qdisc sfq 36: root')
4554 self
.assertRegex(output
, 'perturb 5sec')
4556 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4557 def test_qdisc_tbf(self
):
4558 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4560 self
.wait_online('dummy98:routable')
4562 output
= check_output('tc qdisc show dev dummy98')
4564 self
.assertRegex(output
, 'qdisc tbf 35: root')
4565 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4567 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4568 def test_qdisc_teql(self
):
4569 call_quiet('rmmod sch_teql')
4571 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4573 self
.wait_links('dummy98')
4574 check_output('modprobe sch_teql max_equalizers=2')
4575 self
.wait_online('dummy98:routable')
4577 output
= check_output('tc qdisc show dev dummy98')
4579 self
.assertRegex(output
, 'qdisc teql1 31: root')
4581 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4589 def test_state_file(self
):
4590 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4592 self
.wait_online('dummy98:routable')
4594 # make link state file updated
4595 resolvectl('revert', 'dummy98')
4597 check_json(networkctl_json())
4599 output
= read_link_state_file('dummy98')
4601 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4602 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4603 self
.assertIn('ADMIN_STATE=configured', output
)
4604 self
.assertIn('OPER_STATE=routable', output
)
4605 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4606 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4607 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4608 self
.assertIn('ACTIVATION_POLICY=up', output
)
4609 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4610 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4611 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4612 self
.assertIn('DOMAINS=hogehoge', output
)
4613 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4614 self
.assertIn('LLMNR=no', output
)
4615 self
.assertIn('MDNS=yes', output
)
4616 self
.assertIn('DNSSEC=no', output
)
4618 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4619 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4620 resolvectl('llmnr', 'dummy98', 'yes')
4621 resolvectl('mdns', 'dummy98', 'no')
4622 resolvectl('dnssec', 'dummy98', 'yes')
4623 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4625 check_json(networkctl_json())
4627 output
= read_link_state_file('dummy98')
4629 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4630 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4631 self
.assertIn('DOMAINS=hogehogehoge', output
)
4632 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4633 self
.assertIn('LLMNR=yes', output
)
4634 self
.assertIn('MDNS=no', output
)
4635 self
.assertIn('DNSSEC=yes', output
)
4637 timedatectl('revert', 'dummy98')
4639 check_json(networkctl_json())
4641 output
= read_link_state_file('dummy98')
4643 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4644 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4645 self
.assertIn('DOMAINS=hogehogehoge', output
)
4646 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4647 self
.assertIn('LLMNR=yes', output
)
4648 self
.assertIn('MDNS=no', output
)
4649 self
.assertIn('DNSSEC=yes', output
)
4651 resolvectl('revert', 'dummy98')
4653 check_json(networkctl_json())
4655 output
= read_link_state_file('dummy98')
4657 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4658 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4659 self
.assertIn('DOMAINS=hogehoge', output
)
4660 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4661 self
.assertIn('LLMNR=no', output
)
4662 self
.assertIn('MDNS=yes', output
)
4663 self
.assertIn('DNSSEC=no', output
)
4665 def test_address_state(self
):
4666 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4669 self
.wait_online('dummy98:degraded')
4671 output
= read_link_state_file('dummy98')
4672 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4673 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4675 # with a routable IPv4 address
4676 check_output('ip address add 10.1.2.3/16 dev dummy98')
4677 self
.wait_online('dummy98:routable', ipv4
=True)
4678 self
.wait_online('dummy98:routable')
4680 output
= read_link_state_file('dummy98')
4681 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4682 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4684 check_output('ip address del 10.1.2.3/16 dev dummy98')
4686 # with a routable IPv6 address
4687 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4688 self
.wait_online('dummy98:routable', ipv6
=True)
4689 self
.wait_online('dummy98:routable')
4691 output
= read_link_state_file('dummy98')
4692 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4693 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4695 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4703 def test_bond_keep_master(self
):
4704 check_output('ip link add bond199 type bond mode active-backup')
4705 check_output('ip link add dummy98 type dummy')
4706 check_output('ip link set dummy98 master bond199')
4708 copy_network_unit('23-keep-master.network')
4710 self
.wait_online('dummy98:enslaved')
4712 output
= check_output('ip -d link show bond199')
4714 self
.assertRegex(output
, 'active_slave dummy98')
4716 output
= check_output('ip -d link show dummy98')
4718 self
.assertRegex(output
, 'master bond199')
4720 def test_bond_active_slave(self
):
4721 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4723 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4725 output
= check_output('ip -d link show bond199')
4727 self
.assertIn('active_slave dummy98', output
)
4729 # test case for issue #31165.
4730 since
= datetime
.datetime
.now()
4731 networkctl_reconfigure('dummy98')
4732 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4733 self
.assertNotIn('dummy98: Bringing link down', read_networkd_log(since
=since
))
4735 def test_bond_primary_slave(self
):
4736 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4738 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4740 output
= check_output('ip -d link show bond199')
4742 self
.assertIn('primary dummy98', output
)
4745 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4746 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4747 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4748 f
.write(f
'[Link]\nMACAddress={mac}\n')
4751 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4753 output
= check_output('ip -d link show bond199')
4755 self
.assertIn(f
'link/ether {mac}', output
)
4757 def test_bond_operstate(self
):
4758 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4759 '25-bond99.network', '25-bond-slave.network')
4761 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
4763 output
= check_output('ip -d link show dummy98')
4765 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4767 output
= check_output('ip -d link show test1')
4769 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4771 output
= check_output('ip -d link show bond99')
4773 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4775 self
.wait_operstate('dummy98', 'enslaved')
4776 self
.wait_operstate('test1', 'enslaved')
4777 self
.wait_operstate('bond99', 'routable')
4779 check_output('ip link set dummy98 down')
4781 self
.wait_operstate('dummy98', 'off')
4782 self
.wait_operstate('test1', 'enslaved')
4783 self
.wait_operstate('bond99', 'routable')
4785 check_output('ip link set dummy98 up')
4787 self
.wait_operstate('dummy98', 'enslaved')
4788 self
.wait_operstate('test1', 'enslaved')
4789 self
.wait_operstate('bond99', 'routable')
4791 check_output('ip link set dummy98 down')
4792 check_output('ip link set test1 down')
4794 self
.wait_operstate('dummy98', 'off')
4795 self
.wait_operstate('test1', 'off')
4797 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4798 # Huh? Kernel does not recognize that all slave interfaces are down?
4799 # Let's confirm that networkd's operstate is consistent with ip's result.
4800 self
.assertNotRegex(output
, 'NO-CARRIER')
4802 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4810 def test_bridge_mac_none(self
):
4811 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4812 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4814 self
.wait_online('dummy98:enslaved', 'bridge99:degraded')
4816 output
= check_output('ip link show dev dummy98')
4818 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4820 output
= check_output('ip link show dev bridge99')
4822 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4824 def test_bridge_vlan(self
):
4825 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4826 '26-bridge.netdev', '26-bridge-vlan-master.network',
4829 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4831 output
= check_output('bridge vlan show dev test1')
4833 # check if the default VID is removed
4834 self
.assertNotIn('1 Egress Untagged', output
)
4835 for i
in range(1000, 3000):
4837 self
.assertIn(f
'{i} PVID', output
)
4838 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4839 self
.assertIn(f
'{i} Egress Untagged', output
)
4840 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4841 self
.assertIn(f
'{i}', output
)
4843 self
.assertNotIn(f
'{i}', output
)
4845 output
= check_output('bridge vlan show dev bridge99')
4847 # check if the default VID is removed
4848 self
.assertNotIn('1 Egress Untagged', output
)
4849 for i
in range(1000, 3000):
4851 self
.assertIn(f
'{i} PVID', output
)
4852 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4853 self
.assertIn(f
'{i} Egress Untagged', output
)
4854 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4855 self
.assertIn(f
'{i}', output
)
4857 self
.assertNotIn(f
'{i}', output
)
4860 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4861 '26-bridge-vlan-master.network.d/10-override.conf')
4863 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4865 output
= check_output('bridge vlan show dev test1')
4867 for i
in range(1000, 3000):
4869 self
.assertIn(f
'{i} PVID', output
)
4870 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4871 self
.assertIn(f
'{i} Egress Untagged', output
)
4872 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4873 self
.assertIn(f
'{i}', output
)
4875 self
.assertNotIn(f
'{i}', output
)
4877 output
= check_output('bridge vlan show dev bridge99')
4879 for i
in range(1000, 3000):
4881 self
.assertIn(f
'{i} PVID', output
)
4882 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4883 self
.assertIn(f
'{i} Egress Untagged', output
)
4884 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4885 self
.assertIn(f
'{i}', output
)
4887 self
.assertNotIn(f
'{i}', output
)
4889 # Remove several vlan IDs
4890 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4891 '26-bridge-vlan-master.network.d/20-override.conf')
4893 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4895 output
= check_output('bridge vlan show dev test1')
4897 for i
in range(1000, 3000):
4899 self
.assertIn(f
'{i} PVID', output
)
4900 elif i
in range(2012, 2016):
4901 self
.assertIn(f
'{i} Egress Untagged', output
)
4902 elif i
in range(2008, 2014):
4903 self
.assertIn(f
'{i}', output
)
4905 self
.assertNotIn(f
'{i}', output
)
4907 output
= check_output('bridge vlan show dev bridge99')
4909 for i
in range(1000, 3000):
4911 self
.assertIn(f
'{i} PVID', output
)
4912 elif i
in range(2022, 2026):
4913 self
.assertIn(f
'{i} Egress Untagged', output
)
4914 elif i
in range(2018, 2024):
4915 self
.assertIn(f
'{i}', output
)
4917 self
.assertNotIn(f
'{i}', output
)
4919 # Remove all vlan IDs
4920 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4921 '26-bridge-vlan-master.network.d/30-override.conf')
4923 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4925 output
= check_output('bridge vlan show dev test1')
4927 self
.assertNotIn('PVID', output
)
4928 for i
in range(1000, 3000):
4929 self
.assertNotIn(f
'{i}', output
)
4931 output
= check_output('bridge vlan show dev bridge99')
4933 self
.assertNotIn('PVID', output
)
4934 for i
in range(1000, 3000):
4935 self
.assertNotIn(f
'{i}', output
)
4937 def test_bridge_vlan_issue_20373(self
):
4938 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4939 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4940 '21-vlan.netdev', '21-vlan.network')
4942 self
.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
4944 output
= check_output('bridge vlan show dev test1')
4946 self
.assertIn('100 PVID Egress Untagged', output
)
4947 self
.assertIn('560', output
)
4948 self
.assertIn('600', output
)
4950 output
= check_output('bridge vlan show dev bridge99')
4952 self
.assertIn('1 PVID Egress Untagged', output
)
4953 self
.assertIn('100', output
)
4954 self
.assertIn('600', output
)
4956 def test_bridge_mdb(self
):
4957 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4958 '26-bridge.netdev', '26-bridge-mdb-master.network')
4960 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4962 output
= check_output('bridge mdb show dev bridge99')
4964 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4965 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4967 # Old kernel may not support bridge MDB entries on bridge master
4968 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4969 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4970 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4972 def test_bridge_keep_master(self
):
4973 check_output('ip link add bridge99 type bridge')
4974 check_output('ip link set bridge99 up')
4975 check_output('ip link add dummy98 type dummy')
4976 check_output('ip link set dummy98 master bridge99')
4978 copy_network_unit('23-keep-master.network')
4980 self
.wait_online('dummy98:enslaved')
4982 output
= check_output('ip -d link show dummy98')
4984 self
.assertRegex(output
, 'master bridge99')
4985 self
.assertRegex(output
, 'bridge')
4987 output
= check_output('bridge -d link show dummy98')
4989 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4990 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4991 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4992 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4993 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4994 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4995 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4996 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4997 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4998 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4999 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5000 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
5002 def test_bridge_property(self
):
5003 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5004 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5005 '25-bridge99.network')
5007 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5009 output
= check_output('ip -d link show bridge99')
5011 self
.assertIn('mtu 9000 ', output
)
5013 output
= check_output('ip -d link show test1')
5015 self
.assertIn('master bridge99 ', output
)
5016 self
.assertIn('bridge_slave', output
)
5017 self
.assertIn('mtu 9000 ', output
)
5019 output
= check_output('ip -d link show dummy98')
5021 self
.assertIn('master bridge99 ', output
)
5022 self
.assertIn('bridge_slave', output
)
5023 self
.assertIn('mtu 9000 ', output
)
5025 output
= check_output('ip addr show bridge99')
5027 self
.assertIn('192.168.0.15/24', output
)
5029 output
= check_output('bridge -d link show dummy98')
5031 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5032 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5033 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
5034 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5035 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5036 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5037 # CONFIG_BRIDGE_IGMP_SNOOPING=y
5038 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
5039 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
5040 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5041 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
5042 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5043 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
5045 output
= check_output('bridge -d link show test1')
5047 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
5049 check_output('ip address add 192.168.0.16/24 dev bridge99')
5050 output
= check_output('ip addr show bridge99')
5052 self
.assertIn('192.168.0.16/24', output
)
5055 print('### ip -6 route list table all dev bridge99')
5056 output
= check_output('ip -6 route list table all dev bridge99')
5058 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5060 remove_link('test1')
5061 self
.wait_operstate('bridge99', 'routable')
5063 output
= check_output('ip -d link show bridge99')
5065 self
.assertIn('mtu 9000 ', output
)
5067 output
= check_output('ip -d link show dummy98')
5069 self
.assertIn('master bridge99 ', output
)
5070 self
.assertIn('bridge_slave', output
)
5071 self
.assertIn('mtu 9000 ', output
)
5073 remove_link('dummy98')
5074 self
.wait_operstate('bridge99', 'no-carrier')
5076 output
= check_output('ip -d link show bridge99')
5078 # When no carrier, the kernel may reset the MTU
5079 self
.assertIn('NO-CARRIER', output
)
5081 output
= check_output('ip address show bridge99')
5083 self
.assertNotIn('192.168.0.15/24', output
)
5084 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
5086 print('### ip -6 route list table all dev bridge99')
5087 output
= check_output('ip -6 route list table all dev bridge99')
5089 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5091 check_output('ip link add dummy98 type dummy')
5092 self
.wait_online('dummy98:enslaved', 'bridge99:routable')
5094 output
= check_output('ip -d link show bridge99')
5096 self
.assertIn('mtu 9000 ', output
)
5098 output
= check_output('ip -d link show dummy98')
5100 self
.assertIn('master bridge99 ', output
)
5101 self
.assertIn('bridge_slave', output
)
5102 self
.assertIn('mtu 9000 ', output
)
5104 def test_bridge_configure_without_carrier(self
):
5105 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
5109 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5110 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
5111 with self
.subTest(test
=test
):
5112 if test
== 'no-slave':
5113 # bridge has no slaves; it's up but *might* not have carrier
5114 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
5115 # due to a bug in the kernel, newly-created bridges are brought up
5116 # *with* carrier, unless they have had any setting changed; e.g.
5117 # their mac set, priority set, etc. Then, they will lose carrier
5118 # as soon as a (down) slave interface is added, and regain carrier
5119 # again once the slave interface is brought up.
5120 #self.check_link_attr('bridge99', 'carrier', '0')
5121 elif test
== 'add-slave':
5122 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5123 self
.check_link_attr('test1', 'operstate', 'down')
5124 check_output('ip link set dev test1 master bridge99')
5125 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
5126 self
.check_link_attr('bridge99', 'carrier', '0')
5127 elif test
== 'slave-up':
5128 # bring up slave, which will have carrier; bridge gains carrier
5129 check_output('ip link set dev test1 up')
5130 self
.wait_online('bridge99:routable')
5131 self
.check_link_attr('bridge99', 'carrier', '1')
5132 elif test
== 'slave-no-carrier':
5133 # drop slave carrier; bridge loses carrier
5134 check_output('ip link set dev test1 carrier off')
5135 self
.wait_online('bridge99:no-carrier:no-carrier')
5136 self
.check_link_attr('bridge99', 'carrier', '0')
5137 elif test
== 'slave-carrier':
5138 # restore slave carrier; bridge gains carrier
5139 check_output('ip link set dev test1 carrier on')
5140 self
.wait_online('bridge99:routable')
5141 self
.check_link_attr('bridge99', 'carrier', '1')
5142 elif test
== 'slave-down':
5143 # bring down slave; bridge loses carrier
5144 check_output('ip link set dev test1 down')
5145 self
.wait_online('bridge99:no-carrier:no-carrier')
5146 self
.check_link_attr('bridge99', 'carrier', '0')
5148 output
= networkctl_status('bridge99')
5149 self
.assertRegex(output
, '10.1.2.3')
5150 self
.assertRegex(output
, '10.1.2.1')
5152 def test_bridge_ignore_carrier_loss(self
):
5153 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5154 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5155 '25-bridge99-ignore-carrier-loss.network')
5157 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5159 check_output('ip address add 192.168.0.16/24 dev bridge99')
5160 remove_link('test1', 'dummy98')
5163 output
= check_output('ip address show bridge99')
5165 self
.assertRegex(output
, 'NO-CARRIER')
5166 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5167 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5169 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5170 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5171 '25-bridge99-ignore-carrier-loss.network')
5173 self
.wait_online('bridge99:no-carrier')
5175 for trial
in range(4):
5176 check_output('ip link add dummy98 type dummy')
5177 check_output('ip link set dummy98 up')
5179 remove_link('dummy98')
5181 self
.wait_online('bridge99:routable', 'dummy98:enslaved')
5183 output
= check_output('ip address show bridge99')
5185 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5187 output
= check_output('ip rule list table 100')
5189 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5191 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5199 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5200 def test_sriov(self
):
5201 copy_network_unit('25-default.link', '25-sriov.network')
5203 call('modprobe netdevsim')
5205 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5208 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5212 self
.wait_online('eni99np1:routable')
5214 output
= check_output('ip link show dev eni99np1')
5216 self
.assertRegex(output
,
5217 '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 *'
5218 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5219 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5222 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5223 def test_sriov_udev(self
):
5224 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5226 call('modprobe netdevsim')
5228 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5232 self
.wait_online('eni99np1:routable')
5234 # the name eni99np1 may be an alternative name.
5235 ifname
= link_resolve('eni99np1')
5237 output
= check_output('ip link show dev eni99np1')
5239 self
.assertRegex(output
,
5240 '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 *'
5241 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5242 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5244 self
.assertNotIn('vf 3', output
)
5245 self
.assertNotIn('vf 4', output
)
5247 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5248 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5251 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5253 output
= check_output('ip link show dev eni99np1')
5255 self
.assertRegex(output
,
5256 '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 *'
5257 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5258 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5261 self
.assertNotIn('vf 4', output
)
5263 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5264 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5267 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5269 output
= check_output('ip link show dev eni99np1')
5271 self
.assertRegex(output
,
5272 '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 *'
5273 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5274 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5277 self
.assertNotIn('vf 4', output
)
5279 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5280 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5283 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5285 output
= check_output('ip link show dev eni99np1')
5287 self
.assertRegex(output
,
5288 '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 *'
5289 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5291 self
.assertNotIn('vf 2', output
)
5292 self
.assertNotIn('vf 3', output
)
5293 self
.assertNotIn('vf 4', output
)
5295 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5296 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5299 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5301 output
= check_output('ip link show dev eni99np1')
5303 self
.assertRegex(output
,
5304 '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 *'
5305 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5306 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5308 self
.assertNotIn('vf 3', output
)
5309 self
.assertNotIn('vf 4', output
)
5311 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5319 def test_lldp(self
):
5320 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5322 self
.wait_online('veth99:degraded', 'veth-peer:degraded')
5324 for trial
in range(10):
5328 output
= networkctl('lldp')
5330 if re
.search(r
'veth99 .* veth-peer', output
):
5335 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5343 def test_ipv6_prefix_delegation(self
):
5344 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5345 self
.setup_nftset('addr6', 'ipv6_addr')
5346 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5347 self
.setup_nftset('ifindex', 'iface_index')
5349 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5351 # IPv6SendRA=yes implies IPv6Forwarding.
5352 self
.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5354 output
= resolvectl('dns', 'veth99')
5356 self
.assertRegex(output
, 'fe80::')
5357 self
.assertRegex(output
, '2002:da8:1::1')
5359 output
= resolvectl('domain', 'veth99')
5361 self
.assertIn('hogehoge.test', output
)
5363 output
= networkctl_status('veth99')
5365 self
.assertRegex(output
, '2002:da8:1:0')
5367 self
.check_netlabel('veth99', '2002:da8:1::/64')
5368 self
.check_netlabel('veth99', '2002:da8:2::/64')
5370 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5371 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5372 self
.check_nftset('network6', '2002:da8:1::/64')
5373 self
.check_nftset('network6', '2002:da8:2::/64')
5374 self
.check_nftset('ifindex', 'veth99')
5376 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5378 def check_ipv6_token_static(self
):
5379 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5381 output
= networkctl_status('veth99')
5383 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5384 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5385 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5386 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5388 def test_ipv6_token_static(self
):
5389 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5392 self
.check_ipv6_token_static()
5395 check_output('ip link set veth99 down')
5396 check_output('ip link set veth99 up')
5398 self
.check_ipv6_token_static()
5401 check_output('ip link set veth99 down')
5402 time
.sleep(random
.uniform(0, 0.1))
5403 check_output('ip link set veth99 up')
5404 time
.sleep(random
.uniform(0, 0.1))
5406 self
.check_ipv6_token_static()
5408 def test_ipv6_token_prefixstable(self
):
5409 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5411 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5413 output
= networkctl_status('veth99')
5415 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5416 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5418 def test_ipv6_token_prefixstable_without_address(self
):
5419 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5421 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5423 output
= networkctl_status('veth99')
5425 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5426 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5428 def check_router_hop_limit(self
, hop_limit
):
5429 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5431 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5433 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5435 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5437 def test_router_hop_limit(self
):
5438 copy_network_unit('25-veth-client.netdev',
5439 '25-veth-router.netdev',
5441 '25-veth-bridge.network',
5442 '25-veth-client.network',
5443 '25-veth-router-hop-limit.network',
5444 '25-bridge99.network')
5446 self
.wait_online('client-p:enslaved',
5447 'router:degraded', 'router-p:enslaved',
5448 'bridge99:routable')
5450 self
.check_router_hop_limit(42)
5452 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5453 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5457 self
.check_router_hop_limit(43)
5459 def test_router_preference(self
):
5460 copy_network_unit('25-veth-client.netdev',
5461 '25-veth-router-high.netdev',
5462 '25-veth-router-low.netdev',
5464 '25-veth-bridge.network',
5465 '25-veth-client.network',
5466 '25-veth-router-high.network',
5467 '25-veth-router-low.network',
5468 '25-bridge99.network')
5470 self
.wait_online('client-p:enslaved',
5471 'router-high:degraded', 'router-high-p:enslaved',
5472 'router-low:degraded', 'router-low-p:enslaved',
5473 'bridge99:routable')
5475 networkctl_reconfigure('client')
5476 self
.wait_online('client:routable')
5478 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5479 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5480 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5481 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5483 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5485 self
.assertIn('metric 512', output
)
5486 self
.assertIn('pref high', output
)
5487 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5489 self
.assertIn('metric 2048', output
)
5490 self
.assertIn('pref low', output
)
5492 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5493 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5496 self
.wait_online('client:routable')
5498 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5499 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5500 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5501 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5503 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5505 self
.assertIn('metric 100', output
)
5506 self
.assertNotIn('metric 512', output
)
5507 self
.assertIn('pref high', output
)
5508 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5510 self
.assertIn('metric 300', output
)
5511 self
.assertNotIn('metric 2048', output
)
5512 self
.assertIn('pref low', output
)
5514 # swap the preference (for issue #28439)
5515 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5516 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5518 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5519 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5521 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5523 self
.assertIn('metric 300', output
)
5524 self
.assertNotIn('metric 100', output
)
5525 self
.assertIn('pref low', output
)
5526 self
.assertNotIn('pref high', output
)
5527 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5529 self
.assertIn('metric 100', output
)
5530 self
.assertNotIn('metric 300', output
)
5531 self
.assertIn('pref high', output
)
5532 self
.assertNotIn('pref low', output
)
5534 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5535 def test_captive_portal(self
):
5536 copy_network_unit('25-veth-client.netdev',
5537 '25-veth-router-captive.netdev',
5539 '25-veth-client-captive.network',
5540 '25-veth-router-captive.network',
5541 '25-veth-bridge-captive.network',
5542 '25-bridge99.network')
5544 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5545 'router-captive:degraded', 'router-captivep:enslaved')
5547 start_radvd(config_file
='captive-portal.conf')
5548 networkctl_reconfigure('client')
5549 self
.wait_online('client:routable')
5551 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5552 output
= networkctl_status('client')
5554 self
.assertIn('Captive Portal: http://systemd.io', output
)
5556 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5557 def test_invalid_captive_portal(self
):
5558 def radvd_write_config(captive_portal_uri
):
5559 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5560 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5562 captive_portal_uris
= [
5563 "42ěščěškd ěšč ě s",
5568 copy_network_unit('25-veth-client.netdev',
5569 '25-veth-router-captive.netdev',
5571 '25-veth-client-captive.network',
5572 '25-veth-router-captive.network',
5573 '25-veth-bridge-captive.network',
5574 '25-bridge99.network')
5576 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5577 'router-captive:degraded', 'router-captivep:enslaved')
5579 for uri
in captive_portal_uris
:
5580 print(f
"Captive portal: {uri}")
5581 radvd_write_config(uri
)
5583 start_radvd(config_file
='bogus-captive-portal.conf')
5584 networkctl_reconfigure('client')
5585 self
.wait_online('client:routable')
5587 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5588 output
= networkctl_status('client')
5590 self
.assertNotIn('Captive Portal:', output
)
5592 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5600 def test_dhcp_server(self
):
5601 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5603 self
.wait_online('veth99:routable', 'veth-peer:routable')
5605 output
= networkctl_status('veth99')
5607 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5608 self
.assertIn('Gateway: 192.168.5.3', output
)
5609 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5610 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5612 output
= networkctl_status('veth-peer')
5613 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5615 def test_dhcp_server_null_server_address(self
):
5616 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5618 self
.wait_online('veth99:routable', 'veth-peer:routable')
5620 output
= check_output('ip --json address show dev veth-peer')
5621 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5622 print(server_address
)
5624 output
= check_output('ip --json address show dev veth99')
5625 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5626 print(client_address
)
5628 output
= networkctl_status('veth99')
5630 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5631 self
.assertIn(f
'Gateway: {server_address}', output
)
5632 self
.assertIn(f
'DNS: {server_address}', output
)
5633 self
.assertIn(f
'NTP: {server_address}', output
)
5635 output
= networkctl_status('veth-peer')
5636 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5638 def test_dhcp_server_with_uplink(self
):
5639 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5640 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5642 self
.wait_online('veth99:routable', 'veth-peer:routable')
5644 output
= networkctl_status('veth99')
5646 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5647 self
.assertIn('Gateway: 192.168.5.3', output
)
5648 self
.assertIn('DNS: 192.168.5.1', output
)
5649 self
.assertIn('NTP: 192.168.5.1', output
)
5651 def test_emit_router_timezone(self
):
5652 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5654 self
.wait_online('veth99:routable', 'veth-peer:routable')
5656 output
= networkctl_status('veth99')
5658 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5659 self
.assertIn('Gateway: 192.168.5.1', output
)
5660 self
.assertIn('Time Zone: Europe/Berlin', output
)
5662 def test_dhcp_server_static_lease(self
):
5663 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5665 self
.wait_online('veth99:routable', 'veth-peer:routable')
5667 output
= networkctl_status('veth99')
5669 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5670 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5672 def test_dhcp_server_static_lease_default_client_id(self
):
5673 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5675 self
.wait_online('veth99:routable', 'veth-peer:routable')
5677 output
= networkctl_status('veth99')
5679 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5680 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5682 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5690 def test_relay_agent(self
):
5691 copy_network_unit('25-agent-veth-client.netdev',
5692 '25-agent-veth-server.netdev',
5693 '25-agent-client.network',
5694 '25-agent-server.network',
5695 '25-agent-client-peer.network',
5696 '25-agent-server-peer.network')
5699 self
.wait_online('client:routable')
5701 output
= networkctl_status('client')
5703 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5705 def test_relay_agent_on_bridge(self
):
5706 copy_network_unit('25-agent-bridge.netdev',
5707 '25-agent-veth-client.netdev',
5708 '25-agent-bridge.network',
5709 '25-agent-bridge-port.network',
5710 '25-agent-client.network')
5712 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5715 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5717 if expect
in read_networkd_log():
5723 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5731 def test_dhcp_client_ipv6_only(self
):
5732 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5735 self
.wait_online('veth-peer:carrier')
5737 # information request mode
5738 # The name ipv6-only option may not be supported by older dnsmasq
5739 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5740 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5741 '--dhcp-option=option6:dns-server,[2600::ee]',
5742 '--dhcp-option=option6:ntp-server,[2600::ff]',
5743 ra_mode
='ra-stateless')
5744 self
.wait_online('veth99:routable', 'veth-peer:routable')
5746 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5747 # Let's wait for the expected DNS server being listed in the state file.
5748 for _
in range(100):
5749 output
= read_link_state_file('veth99')
5750 if 'DNS=2600::ee' in output
:
5754 # Check link state file
5755 print('## link state file')
5756 output
= read_link_state_file('veth99')
5758 self
.assertIn('DNS=2600::ee', output
)
5759 self
.assertIn('NTP=2600::ff', output
)
5761 # Check manager state file
5762 print('## manager state file')
5763 output
= read_manager_state_file()
5765 self
.assertRegex(output
, 'DNS=.*2600::ee')
5766 self
.assertRegex(output
, 'NTP=.*2600::ff')
5768 print('## dnsmasq log')
5769 output
= read_dnsmasq_log_file()
5771 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5772 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5773 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5774 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5775 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5778 check_json(networkctl_json('veth99'))
5782 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5783 '--dhcp-option=option6:dns-server,[2600::ee]',
5784 '--dhcp-option=option6:ntp-server,[2600::ff]')
5785 networkctl_reconfigure('veth99')
5786 self
.wait_online('veth99:routable', 'veth-peer:routable')
5789 output
= check_output('ip address show dev veth99 scope global')
5791 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5792 self
.assertNotIn('192.168.5', output
)
5794 # checking semi-static route
5795 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5797 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5799 # Confirm that ipv6 token is not set in the kernel
5800 output
= check_output('ip token show dev veth99')
5802 self
.assertRegex(output
, 'token :: dev veth99')
5804 # Make manager and link state file updated
5805 resolvectl('revert', 'veth99')
5807 # Check link state file
5808 print('## link state file')
5809 output
= read_link_state_file('veth99')
5811 self
.assertIn('DNS=2600::ee', output
)
5812 self
.assertIn('NTP=2600::ff', output
)
5814 # Check manager state file
5815 print('## manager state file')
5816 output
= read_manager_state_file()
5818 self
.assertRegex(output
, 'DNS=.*2600::ee')
5819 self
.assertRegex(output
, 'NTP=.*2600::ff')
5821 print('## dnsmasq log')
5822 output
= read_dnsmasq_log_file()
5824 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5825 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5826 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5827 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5828 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5829 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5832 check_json(networkctl_json('veth99'))
5834 # Testing without rapid commit support
5835 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5836 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5839 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5840 '--dhcp-option=option6:dns-server,[2600::ee]',
5841 '--dhcp-option=option6:ntp-server,[2600::ff]')
5844 self
.wait_online('veth99:routable', 'veth-peer:routable')
5847 output
= check_output('ip address show dev veth99 scope global')
5849 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5850 self
.assertNotIn('192.168.5', output
)
5852 # checking semi-static route
5853 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5855 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5857 # Make manager and link state file updated
5858 resolvectl('revert', 'veth99')
5860 # Check link state file
5861 print('## link state file')
5862 output
= read_link_state_file('veth99')
5864 self
.assertIn('DNS=2600::ee', output
)
5865 self
.assertIn('NTP=2600::ff', output
)
5867 # Check manager state file
5868 print('## manager state file')
5869 output
= read_manager_state_file()
5871 self
.assertRegex(output
, 'DNS=.*2600::ee')
5872 self
.assertRegex(output
, 'NTP=.*2600::ff')
5874 print('## dnsmasq log')
5875 output
= read_dnsmasq_log_file()
5877 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5878 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5879 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5880 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5881 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5882 self
.assertNotIn('rapid-commit', output
)
5885 check_json(networkctl_json('veth99'))
5887 def test_dhcp_client_ipv6_dbus_status(self
):
5888 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5890 self
.wait_online('veth-peer:carrier')
5892 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5893 # bit set) has yet been received and the configuration does not include WithoutRA=true
5894 state
= get_dhcp6_client_state('veth99')
5895 print(f
"DHCPv6 client state = {state}")
5896 self
.assertEqual(state
, 'stopped')
5898 state
= get_dhcp4_client_state('veth99')
5899 print(f
"DHCPv4 client state = {state}")
5900 self
.assertEqual(state
, 'selecting')
5902 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5903 self
.wait_online('veth99:routable', 'veth-peer:routable')
5905 state
= get_dhcp6_client_state('veth99')
5906 print(f
"DHCPv6 client state = {state}")
5907 self
.assertEqual(state
, 'bound')
5909 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5910 for _
in range(100):
5911 state
= get_dhcp4_client_state('veth99')
5912 if state
== 'stopped':
5916 print(f
"DHCPv4 client state = {state}")
5917 self
.assertEqual(state
, 'stopped')
5919 # restart dnsmasq to clear log
5921 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5923 # Test renew command
5924 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5925 networkctl('renew', 'veth99')
5927 for _
in range(100):
5928 state
= get_dhcp4_client_state('veth99')
5929 if state
== 'stopped':
5933 print(f
"DHCPv4 client state = {state}")
5934 self
.assertEqual(state
, 'stopped')
5936 print('## dnsmasq log')
5937 output
= read_dnsmasq_log_file()
5939 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5940 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5941 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5942 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5944 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5945 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5948 self
.wait_online('veth-peer:carrier')
5950 self
.wait_online('veth99:routable', 'veth-peer:routable')
5953 output
= check_output('ip address show dev veth99 scope global')
5955 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5956 self
.assertNotIn('192.168.5', output
)
5958 print('## dnsmasq log')
5959 output
= read_dnsmasq_log_file()
5961 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5962 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5963 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5964 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5965 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5967 def test_dhcp_client_ipv4_only(self
):
5968 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5970 self
.setup_nftset('addr4', 'ipv4_addr')
5971 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5972 self
.setup_nftset('ifindex', 'iface_index')
5975 self
.wait_online('veth-peer:carrier')
5976 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5977 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5978 '--dhcp-option=option:domain-search,example.com',
5979 '--dhcp-alternate-port=67,5555',
5980 ipv4_range
='192.168.5.110,192.168.5.119')
5981 self
.wait_online('veth99:routable', 'veth-peer:routable')
5982 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5984 print('## ip address show dev veth99 scope global')
5985 output
= check_output('ip address show dev veth99 scope global')
5987 self
.assertIn('mtu 1492', output
)
5988 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5989 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')
5990 self
.assertNotIn('2600::', output
)
5992 output
= check_output('ip -4 --json address show dev veth99')
5993 for i
in json
.loads(output
)[0]['addr_info']:
5994 if i
['label'] == 'test-label':
5995 address1
= i
['local']
5998 self
.assertFalse(True)
6000 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
6002 print('## ip route show table main dev veth99')
6003 output
= check_output('ip route show table main dev veth99')
6005 # no DHCP routes assigned to the main table
6006 self
.assertNotIn('proto dhcp', output
)
6008 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6009 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6010 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6011 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6013 print('## ip route show table 211 dev veth99')
6014 output
= check_output('ip route show table 211 dev veth99')
6016 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6017 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6018 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6019 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6020 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6021 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6023 print('## link state file')
6024 output
= read_link_state_file('veth99')
6026 # checking DNS server, SIP server, and Domains
6027 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
6028 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
6029 self
.assertIn('DOMAINS=example.com', output
)
6032 j
= json
.loads(networkctl_json('veth99'))
6034 self
.assertEqual(len(j
['DNS']), 2)
6037 self
.assertEqual(i
['Family'], 2)
6038 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6039 self
.assertRegex(a
, '^192.168.5.[67]$')
6040 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6041 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6042 self
.assertEqual('192.168.5.1', a
)
6044 self
.assertEqual(len(j
['SIP']), 2)
6047 self
.assertEqual(i
['Family'], 2)
6048 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6049 self
.assertRegex(a
, '^192.168.5.2[12]$')
6050 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6051 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6052 self
.assertEqual('192.168.5.1', a
)
6054 print('## dnsmasq log')
6055 output
= read_dnsmasq_log_file()
6057 self
.assertIn('vendor class: FooBarVendorTest', output
)
6058 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
6059 self
.assertIn('client provides name: test-hostname', output
)
6060 self
.assertIn('26:mtu', output
)
6062 # change address range, DNS servers, and Domains
6064 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6065 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6066 '--dhcp-option=option:domain-search,foo.example.com',
6067 '--dhcp-alternate-port=67,5555',
6068 ipv4_range
='192.168.5.120,192.168.5.129',)
6070 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6071 print('Wait for the DHCP lease to be expired')
6072 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
6073 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
6075 self
.wait_online('veth99:routable', 'veth-peer:routable')
6077 print('## ip address show dev veth99 scope global')
6078 output
= check_output('ip address show dev veth99 scope global')
6080 self
.assertIn('mtu 1492', output
)
6081 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6082 self
.assertNotIn(f
'{address1}', output
)
6083 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')
6084 self
.assertNotIn('2600::', output
)
6086 output
= check_output('ip -4 --json address show dev veth99')
6087 for i
in json
.loads(output
)[0]['addr_info']:
6088 if i
['label'] == 'test-label':
6089 address2
= i
['local']
6092 self
.assertFalse(True)
6094 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
6096 print('## ip route show table main dev veth99')
6097 output
= check_output('ip route show table main dev veth99')
6099 # no DHCP routes assigned to the main table
6100 self
.assertNotIn('proto dhcp', output
)
6102 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6103 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6104 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6105 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6107 print('## ip route show table 211 dev veth99')
6108 output
= check_output('ip route show table 211 dev veth99')
6110 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6111 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6112 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6113 self
.assertNotIn('192.168.5.6', output
)
6114 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6115 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6116 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6118 print('## link state file')
6119 output
= read_link_state_file('veth99')
6121 # checking DNS server, SIP server, and Domains
6122 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
6123 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
6124 self
.assertIn('DOMAINS=foo.example.com', output
)
6127 j
= json
.loads(networkctl_json('veth99'))
6129 self
.assertEqual(len(j
['DNS']), 3)
6132 self
.assertEqual(i
['Family'], 2)
6133 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6134 self
.assertRegex(a
, '^192.168.5.[178]$')
6135 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6136 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6137 self
.assertEqual('192.168.5.1', a
)
6139 self
.assertEqual(len(j
['SIP']), 2)
6142 self
.assertEqual(i
['Family'], 2)
6143 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6144 self
.assertRegex(a
, '^192.168.5.2[34]$')
6145 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6146 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6147 self
.assertEqual('192.168.5.1', a
)
6149 print('## dnsmasq log')
6150 output
= read_dnsmasq_log_file()
6152 self
.assertIn('vendor class: FooBarVendorTest', output
)
6153 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6154 self
.assertIn('client provides name: test-hostname', output
)
6155 self
.assertIn('26:mtu', output
)
6157 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6159 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6160 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6161 self
.check_nftset('ifindex', 'veth99')
6163 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6165 def test_dhcp_client_ipv4_dbus_status(self
):
6166 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6168 self
.wait_online('veth-peer:carrier')
6170 state
= get_dhcp4_client_state('veth99')
6171 print(f
"State = {state}")
6172 self
.assertEqual(state
, 'rebooting')
6174 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6175 '--dhcp-option=option:domain-search,example.com',
6176 '--dhcp-alternate-port=67,5555',
6177 ipv4_range
='192.168.5.110,192.168.5.119')
6178 self
.wait_online('veth99:routable', 'veth-peer:routable')
6179 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6181 state
= get_dhcp4_client_state('veth99')
6182 print(f
"State = {state}")
6183 self
.assertEqual(state
, 'bound')
6185 def test_dhcp_client_allow_list(self
):
6186 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6189 self
.wait_online('veth-peer:carrier')
6190 since
= datetime
.datetime
.now()
6193 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6195 if expect
in read_networkd_log(since
=since
):
6201 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6202 since
= datetime
.datetime
.now()
6205 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6207 if expect
in read_networkd_log(since
=since
):
6213 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6214 since
= datetime
.datetime
.now()
6217 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6219 if expect
in read_networkd_log(since
=since
):
6225 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6226 def test_dhcp_client_rapid_commit(self
):
6227 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6229 self
.wait_online('veth-peer:carrier')
6231 start_dnsmasq('--dhcp-rapid-commit')
6232 self
.wait_online('veth99:routable', 'veth-peer:routable')
6233 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6235 state
= get_dhcp4_client_state('veth99')
6236 print(f
"DHCPv4 client state = {state}")
6237 self
.assertEqual(state
, 'bound')
6239 output
= read_dnsmasq_log_file()
6240 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6241 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6242 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6243 self
.assertIn('DHCPACK(veth-peer)', output
)
6245 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6246 copy_network_unit('25-veth.netdev',
6247 '25-dhcp-server-ipv6-only-mode.network',
6248 '25-dhcp-client-ipv6-only-mode.network')
6250 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6251 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6253 state
= get_dhcp4_client_state('veth99')
6254 print(f
"State = {state}")
6255 self
.assertEqual(state
, 'bound')
6257 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6259 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6265 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6266 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6267 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6269 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6270 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6271 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6272 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6273 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6274 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6275 copy_network_unit(*testunits
, copy_dropins
=False)
6278 self
.wait_online('veth-peer:carrier')
6279 additional_options
= [
6280 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6281 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6282 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6285 additional_options
+= [
6286 '--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'
6288 start_dnsmasq(*additional_options
)
6289 self
.wait_online('veth99:routable', 'veth-peer:routable')
6291 output
= check_output('ip -4 route show dev veth99')
6297 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6298 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6299 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6300 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6301 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6303 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6304 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6305 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6306 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6308 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6309 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6310 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6311 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6312 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6313 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6314 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6315 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6318 if use_gateway
and (not classless
or not use_routes
):
6319 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6321 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6323 # Check route to gateway
6324 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6325 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6327 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6329 # Check RoutesToDNS= and RoutesToNTP=
6330 if dns_and_ntp_routes
:
6331 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6332 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6335 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6336 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6338 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6339 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6341 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6342 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6344 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6345 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6346 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6347 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6349 check_json(networkctl_json())
6351 def test_dhcp_client_settings_anonymize(self
):
6352 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6354 self
.wait_online('veth-peer:carrier')
6356 self
.wait_online('veth99:routable', 'veth-peer:routable')
6358 print('## dnsmasq log')
6359 output
= read_dnsmasq_log_file()
6361 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6362 self
.assertNotIn('test-hostname', output
)
6363 self
.assertNotIn('26:mtu', output
)
6365 def test_dhcp_keep_configuration_dhcp(self
):
6366 copy_network_unit('25-veth.netdev',
6367 '25-dhcp-server-veth-peer.network',
6368 '25-dhcp-client-keep-configuration-dhcp.network')
6370 self
.wait_online('veth-peer:carrier')
6372 self
.wait_online('veth99:routable', 'veth-peer:routable')
6374 output
= check_output('ip address show dev veth99 scope global')
6376 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6377 'valid_lft forever preferred_lft forever')
6379 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6382 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6383 print('Wait for the DHCP lease to be expired')
6386 # The lease address should be kept after the lease expired
6387 output
= check_output('ip address show dev veth99 scope global')
6389 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6390 'valid_lft forever preferred_lft forever')
6394 # The lease address should be kept after networkd stopped
6395 output
= check_output('ip address show dev veth99 scope global')
6397 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6398 'valid_lft forever preferred_lft forever')
6400 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6401 f
.write('[Network]\nDHCP=no\n')
6404 self
.wait_online('veth99:routable', 'veth-peer:routable')
6406 # Still the lease address should be kept after networkd restarted
6407 output
= check_output('ip address show dev veth99 scope global')
6409 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6410 'valid_lft forever preferred_lft forever')
6412 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6413 copy_network_unit('25-veth.netdev',
6414 '25-dhcp-server-veth-peer.network',
6415 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6417 self
.wait_online('veth-peer:carrier')
6419 self
.wait_online('veth99:routable', 'veth-peer:routable')
6421 output
= check_output('ip address show dev veth99 scope global')
6423 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6428 output
= check_output('ip address show dev veth99 scope global')
6430 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6433 self
.wait_online('veth-peer:routable')
6435 output
= check_output('ip address show dev veth99 scope global')
6437 self
.assertNotIn('192.168.5.', output
)
6439 def test_dhcp_client_reuse_address_as_static(self
):
6440 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6442 self
.wait_online('veth-peer:carrier')
6444 self
.wait_online('veth99:routable', 'veth-peer:routable')
6446 # link become 'routable' when at least one protocol provide an valid address.
6447 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6448 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6450 output
= check_output('ip address show dev veth99 scope global')
6451 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6452 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6453 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6454 print(static_network
)
6456 remove_network_unit('25-dhcp-client.network')
6458 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6459 f
.write(static_network
)
6462 self
.wait_online('veth99:routable')
6464 output
= check_output('ip -4 address show dev veth99 scope global')
6466 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6467 'valid_lft forever preferred_lft forever')
6469 output
= check_output('ip -6 address show dev veth99 scope global')
6471 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6472 'valid_lft forever preferred_lft forever')
6474 @expectedFailureIfModuleIsNotAvailable('vrf')
6475 def test_dhcp_client_vrf(self
):
6476 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6477 '25-vrf.netdev', '25-vrf.network')
6479 self
.wait_online('veth-peer:carrier')
6481 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6483 # link become 'routable' when at least one protocol provide an valid address.
6484 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6485 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6487 print('## ip -d link show dev vrf99')
6488 output
= check_output('ip -d link show dev vrf99')
6490 self
.assertRegex(output
, 'vrf table 42')
6492 print('## ip address show vrf vrf99')
6493 output
= check_output('ip address show vrf vrf99')
6495 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6496 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6497 self
.assertRegex(output
, 'inet6 .* scope link')
6499 print('## ip address show dev veth99')
6500 output
= check_output('ip address show dev veth99')
6502 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6503 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6504 self
.assertRegex(output
, 'inet6 .* scope link')
6506 print('## ip route show vrf vrf99')
6507 output
= check_output('ip route show vrf vrf99')
6509 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6510 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6511 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6513 print('## ip route show table main dev veth99')
6514 output
= check_output('ip route show table main dev veth99')
6516 self
.assertEqual(output
, '')
6518 def test_dhcp_client_gateway_onlink_implicit(self
):
6519 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6520 '25-dhcp-client-gateway-onlink-implicit.network')
6522 self
.wait_online('veth-peer:carrier')
6524 self
.wait_online('veth99:routable', 'veth-peer:routable')
6526 output
= networkctl_status('veth99')
6528 self
.assertRegex(output
, '192.168.5')
6530 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6532 self
.assertRegex(output
, 'onlink')
6533 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6535 self
.assertRegex(output
, 'onlink')
6537 def test_dhcp_client_with_ipv4ll(self
):
6538 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6539 '25-dhcp-client-with-ipv4ll.network')
6541 # we need to increase timeout above default, as this will need to wait for
6542 # systemd-networkd to get the dhcpv4 transient failure event
6543 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6545 output
= check_output('ip -4 address show dev veth99')
6547 self
.assertNotIn('192.168.5.', output
)
6548 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6551 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6552 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6553 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')
6554 self
.wait_online('veth99:routable')
6556 output
= check_output('ip -4 address show dev veth99')
6558 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6559 self
.assertNotIn('169.254.', output
)
6560 self
.assertNotIn('scope link', output
)
6563 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6564 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)
6565 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6567 output
= check_output('ip -4 address show dev veth99')
6569 self
.assertNotIn('192.168.5.', output
)
6570 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6572 def test_dhcp_client_use_dns(self
):
6573 def check(self
, ipv4
, ipv6
):
6574 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6575 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6576 f
.write('[DHCPv4]\nUseDNS=')
6577 f
.write('yes' if ipv4
else 'no')
6578 f
.write('\n[DHCPv6]\nUseDNS=')
6579 f
.write('yes' if ipv6
else 'no')
6580 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6583 self
.wait_online('veth99:routable')
6585 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6586 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6587 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6589 # make resolved re-read the link state file
6590 resolvectl('revert', 'veth99')
6592 output
= resolvectl('dns', 'veth99')
6595 self
.assertIn('192.168.5.1', output
)
6597 self
.assertNotIn('192.168.5.1', output
)
6599 self
.assertIn('2600::1', output
)
6601 self
.assertNotIn('2600::1', output
)
6603 check_json(networkctl_json())
6605 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6608 self
.wait_online('veth-peer:carrier')
6609 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6610 '--dhcp-option=option6:dns-server,[2600::1]')
6612 check(self
, True, True)
6613 check(self
, True, False)
6614 check(self
, False, True)
6615 check(self
, False, False)
6617 def test_dhcp_client_use_captive_portal(self
):
6618 def check(self
, ipv4
, ipv6
):
6619 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6620 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6621 f
.write('[DHCPv4]\nUseCaptivePortal=')
6622 f
.write('yes' if ipv4
else 'no')
6623 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6624 f
.write('yes' if ipv6
else 'no')
6625 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6628 self
.wait_online('veth99:routable')
6630 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6631 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6632 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6634 output
= networkctl_status('veth99')
6637 self
.assertIn('Captive Portal: http://systemd.io', output
)
6639 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6641 check_json(networkctl_json())
6643 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6646 self
.wait_online('veth-peer:carrier')
6647 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6648 '--dhcp-option=option6:103,http://systemd.io')
6650 check(self
, True, True)
6651 check(self
, True, False)
6652 check(self
, False, True)
6653 check(self
, False, False)
6655 def test_dhcp_client_reject_captive_portal(self
):
6656 def check(self
, ipv4
, ipv6
):
6657 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6658 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6659 f
.write('[DHCPv4]\nUseCaptivePortal=')
6660 f
.write('yes' if ipv4
else 'no')
6661 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6662 f
.write('yes' if ipv6
else 'no')
6663 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6666 self
.wait_online('veth99:routable')
6668 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6669 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6670 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6672 output
= networkctl_status('veth99')
6674 self
.assertNotIn('Captive Portal: ', output
)
6675 self
.assertNotIn('invalid/url', output
)
6677 check_json(networkctl_json())
6679 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6682 self
.wait_online('veth-peer:carrier')
6683 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6684 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6685 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6687 check(self
, True, True)
6688 check(self
, True, False)
6689 check(self
, False, True)
6690 check(self
, False, False)
6692 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6700 def check_dhcp6_prefix(self
, link
):
6701 description
= get_link_description(link
)
6703 self
.assertIn('DHCPv6Client', description
.keys())
6704 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6706 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6708 self
.assertEqual(len(prefixInfo
), 1)
6710 self
.assertIn('Prefix', prefixInfo
[0].keys())
6711 self
.assertIn('PrefixLength', prefixInfo
[0].keys())
6712 self
.assertIn('PreferredLifetimeUSec', prefixInfo
[0].keys())
6713 self
.assertIn('ValidLifetimeUSec', prefixInfo
[0].keys())
6715 self
.assertEqual(prefixInfo
[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6716 self
.assertEqual(prefixInfo
[0]['PrefixLength'], 56)
6717 self
.assertGreater(prefixInfo
[0]['PreferredLifetimeUSec'], 0)
6718 self
.assertGreater(prefixInfo
[0]['ValidLifetimeUSec'], 0)
6720 def test_dhcp6pd_no_address(self
):
6722 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6725 self
.wait_online('veth-peer:routable')
6726 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6727 self
.wait_online('veth99:degraded')
6729 print('### ip -6 address show dev veth99 scope global')
6730 output
= check_output('ip -6 address show dev veth99 scope global')
6732 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6734 self
.check_dhcp6_prefix('veth99')
6736 def test_dhcp6pd_no_assign(self
):
6737 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6738 # However, the server does not provide IA_NA. For issue #31349.
6739 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6742 self
.wait_online('veth-peer:routable')
6743 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd-no-range.conf', ipv
='-6')
6744 self
.wait_online('veth99:degraded')
6746 print('### ip -6 address show dev veth99 scope global')
6747 output
= check_output('ip -6 address show dev veth99 scope global')
6749 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6751 self
.check_dhcp6_prefix('veth99')
6753 def test_dhcp6pd(self
):
6754 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6755 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6756 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6757 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6758 '25-dhcp-pd-downstream-dummy97.network',
6759 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6760 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6763 self
.wait_online('veth-peer:routable')
6764 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6765 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6766 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6768 self
.setup_nftset('addr6', 'ipv6_addr')
6769 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6770 self
.setup_nftset('ifindex', 'iface_index')
6772 # Check DBus assigned prefix information to veth99
6773 self
.check_dhcp6_prefix('veth99')
6775 print('### ip -6 address show dev veth-peer scope global')
6776 output
= check_output('ip -6 address show dev veth-peer scope global')
6778 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6782 # dummy97: 0x01 (The link will appear later)
6784 # dummy99: auto -> 0x02 (No address assignment)
6789 print('### ip -6 address show dev veth99 scope global')
6790 output
= check_output('ip -6 address show dev veth99 scope global')
6793 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6794 # address in IA_PD (Token=static)
6795 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6796 # address in IA_PD (Token=eui64)
6797 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6798 # address in IA_PD (temporary)
6799 # Note that the temporary addresses may appear after the link enters configured state
6800 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')
6802 print('### ip -6 address show dev test1 scope global')
6803 output
= check_output('ip -6 address show dev test1 scope global')
6805 # address in IA_PD (Token=static)
6806 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6807 # address in IA_PD (temporary)
6808 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')
6810 print('### ip -6 address show dev dummy98 scope global')
6811 output
= check_output('ip -6 address show dev dummy98 scope global')
6813 # address in IA_PD (Token=static)
6814 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6815 # address in IA_PD (temporary)
6816 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')
6818 print('### ip -6 address show dev dummy99 scope global')
6819 output
= check_output('ip -6 address show dev dummy99 scope global')
6822 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6824 print('### ip -6 address show dev veth97 scope global')
6825 output
= check_output('ip -6 address show dev veth97 scope global')
6827 # address in IA_PD (Token=static)
6828 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6829 # address in IA_PD (Token=eui64)
6830 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6831 # address in IA_PD (temporary)
6832 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')
6834 print('### ip -6 address show dev veth97-peer scope global')
6835 output
= check_output('ip -6 address show dev veth97-peer scope global')
6837 # NDisc address (Token=static)
6838 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6839 # NDisc address (Token=eui64)
6840 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6841 # NDisc address (temporary)
6842 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')
6844 print('### ip -6 address show dev veth98 scope global')
6845 output
= check_output('ip -6 address show dev veth98 scope global')
6847 # address in IA_PD (Token=static)
6848 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6849 # address in IA_PD (Token=eui64)
6850 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6851 # address in IA_PD (temporary)
6852 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')
6854 print('### ip -6 address show dev veth98-peer scope global')
6855 output
= check_output('ip -6 address show dev veth98-peer scope global')
6857 # NDisc address (Token=static)
6858 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6859 # NDisc address (Token=eui64)
6860 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6861 # NDisc address (temporary)
6862 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')
6864 print('### ip -6 route show type unreachable')
6865 output
= check_output('ip -6 route show type unreachable')
6867 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6869 print('### ip -6 route show dev veth99')
6870 output
= check_output('ip -6 route show dev veth99')
6872 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6874 print('### ip -6 route show dev test1')
6875 output
= check_output('ip -6 route show dev test1')
6877 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6879 print('### ip -6 route show dev dummy98')
6880 output
= check_output('ip -6 route show dev dummy98')
6882 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6884 print('### ip -6 route show dev dummy99')
6885 output
= check_output('ip -6 route show dev dummy99')
6887 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6889 print('### ip -6 route show dev veth97')
6890 output
= check_output('ip -6 route show dev veth97')
6892 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6894 print('### ip -6 route show dev veth97-peer')
6895 output
= check_output('ip -6 route show dev veth97-peer')
6897 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6899 print('### ip -6 route show dev veth98')
6900 output
= check_output('ip -6 route show dev veth98')
6902 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6904 print('### ip -6 route show dev veth98-peer')
6905 output
= check_output('ip -6 route show dev veth98-peer')
6907 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6909 # Test case for a downstream which appears later
6910 check_output('ip link add dummy97 type dummy')
6911 self
.wait_online('dummy97:routable')
6913 print('### ip -6 address show dev dummy97 scope global')
6914 output
= check_output('ip -6 address show dev dummy97 scope global')
6916 # address in IA_PD (Token=static)
6917 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6918 # address in IA_PD (temporary)
6919 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')
6921 print('### ip -6 route show dev dummy97')
6922 output
= check_output('ip -6 route show dev dummy97')
6924 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6926 # Test case for reconfigure
6927 networkctl_reconfigure('dummy98', 'dummy99')
6928 self
.wait_online('dummy98:routable', 'dummy99:degraded')
6930 print('### ip -6 address show dev dummy98 scope global')
6931 output
= check_output('ip -6 address show dev dummy98 scope global')
6933 # address in IA_PD (Token=static)
6934 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6935 # address in IA_PD (temporary)
6936 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')
6938 print('### ip -6 address show dev dummy99 scope global')
6939 output
= check_output('ip -6 address show dev dummy99 scope global')
6942 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6944 print('### ip -6 route show dev dummy98')
6945 output
= check_output('ip -6 route show dev dummy98')
6947 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6949 print('### ip -6 route show dev dummy99')
6950 output
= check_output('ip -6 route show dev dummy99')
6952 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6954 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6956 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6957 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6958 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6959 self
.check_nftset('ifindex', 'dummy98')
6961 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6963 def verify_dhcp4_6rd(self
, tunnel_name
):
6964 print('### ip -4 address show dev veth-peer scope global')
6965 output
= check_output('ip -4 address show dev veth-peer scope global')
6967 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6971 # dummy97: 0x01 (The link will appear later)
6973 # dummy99: auto -> 0x0[23] (No address assignment)
6974 # 6rd-XXX: auto -> 0x0[23]
6979 print('### ip -4 address show dev veth99 scope global')
6980 output
= check_output('ip -4 address show dev veth99 scope global')
6982 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6984 print('### ip -6 address show dev veth99 scope global')
6985 output
= check_output('ip -6 address show dev veth99 scope global')
6987 # address in IA_PD (Token=static)
6988 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6989 # address in IA_PD (Token=eui64)
6990 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6991 # address in IA_PD (temporary)
6992 # Note that the temporary addresses may appear after the link enters configured state
6993 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')
6995 print('### ip -6 address show dev test1 scope global')
6996 output
= check_output('ip -6 address show dev test1 scope global')
6998 # address in IA_PD (Token=static)
6999 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7000 # address in IA_PD (temporary)
7001 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')
7003 print('### ip -6 address show dev dummy98 scope global')
7004 output
= check_output('ip -6 address show dev dummy98 scope global')
7006 # address in IA_PD (Token=static)
7007 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7008 # address in IA_PD (temporary)
7009 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')
7011 print('### ip -6 address show dev dummy99 scope global')
7012 output
= check_output('ip -6 address show dev dummy99 scope global')
7015 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7017 print('### ip -6 address show dev veth97 scope global')
7018 output
= check_output('ip -6 address show dev veth97 scope global')
7020 # address in IA_PD (Token=static)
7021 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7022 # address in IA_PD (Token=eui64)
7023 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7024 # address in IA_PD (temporary)
7025 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')
7027 print('### ip -6 address show dev veth97-peer scope global')
7028 output
= check_output('ip -6 address show dev veth97-peer scope global')
7030 # NDisc address (Token=static)
7031 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7032 # NDisc address (Token=eui64)
7033 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7034 # NDisc address (temporary)
7035 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')
7037 print('### ip -6 address show dev veth98 scope global')
7038 output
= check_output('ip -6 address show dev veth98 scope global')
7040 # address in IA_PD (Token=static)
7041 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7042 # address in IA_PD (Token=eui64)
7043 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7044 # address in IA_PD (temporary)
7045 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')
7047 print('### ip -6 address show dev veth98-peer scope global')
7048 output
= check_output('ip -6 address show dev veth98-peer scope global')
7050 # NDisc address (Token=static)
7051 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7052 # NDisc address (Token=eui64)
7053 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7054 # NDisc address (temporary)
7055 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')
7057 print('### ip -6 route show type unreachable')
7058 output
= check_output('ip -6 route show type unreachable')
7060 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7062 print('### ip -6 route show dev veth99')
7063 output
= check_output('ip -6 route show dev veth99')
7065 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7067 print('### ip -6 route show dev test1')
7068 output
= check_output('ip -6 route show dev test1')
7070 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7072 print('### ip -6 route show dev dummy98')
7073 output
= check_output('ip -6 route show dev dummy98')
7075 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7077 print('### ip -6 route show dev dummy99')
7078 output
= check_output('ip -6 route show dev dummy99')
7080 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7082 print('### ip -6 route show dev veth97')
7083 output
= check_output('ip -6 route show dev veth97')
7085 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7087 print('### ip -6 route show dev veth97-peer')
7088 output
= check_output('ip -6 route show dev veth97-peer')
7090 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7092 print('### ip -6 route show dev veth98')
7093 output
= check_output('ip -6 route show dev veth98')
7095 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7097 print('### ip -6 route show dev veth98-peer')
7098 output
= check_output('ip -6 route show dev veth98-peer')
7100 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7102 print('### ip -6 address show dev dummy97 scope global')
7103 output
= check_output('ip -6 address show dev dummy97 scope global')
7105 # address in IA_PD (Token=static)
7106 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7107 # address in IA_PD (temporary)
7108 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')
7110 print('### ip -6 route show dev dummy97')
7111 output
= check_output('ip -6 route show dev dummy97')
7113 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7115 print(f
'### ip -d link show dev {tunnel_name}')
7116 output
= check_output(f
'ip -d link show dev {tunnel_name}')
7118 self
.assertIn('link/sit 10.100.100.', output
)
7119 self
.assertIn('local 10.100.100.', output
)
7120 self
.assertIn('ttl 64', output
)
7121 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
7122 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
7124 print(f
'### ip -6 address show dev {tunnel_name}')
7125 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
7127 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')
7128 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7130 print(f
'### ip -6 route show dev {tunnel_name}')
7131 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
7133 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7134 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
7136 print('### ip -6 route show default')
7137 output
= check_output('ip -6 route show default')
7139 self
.assertIn('default', output
)
7140 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
7142 def test_dhcp4_6rd(self
):
7143 def get_dhcp_6rd_prefix(link
):
7144 description
= get_link_description(link
)
7146 self
.assertIn('DHCPv4Client', description
.keys())
7147 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
7149 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
7150 self
.assertIn('Prefix', prefixInfo
.keys())
7151 self
.assertIn('PrefixLength', prefixInfo
.keys())
7152 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
7153 self
.assertIn('BorderRouters', prefixInfo
.keys())
7157 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7158 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7159 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7160 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7161 '25-dhcp-pd-downstream-dummy97.network',
7162 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7163 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7164 '80-6rd-tunnel.network')
7167 self
.wait_online('veth-peer:routable')
7170 # 6rd-prefix: 2001:db8::/32
7171 # br-addresss: 10.0.0.1
7173 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',
7174 ipv4_range
='10.100.100.100,10.100.100.200',
7175 ipv4_router
='10.0.0.1')
7176 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7177 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7179 # Check the DBus interface for assigned prefix information
7180 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7182 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7183 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7184 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7185 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7187 # Test case for a downstream which appears later
7188 check_output('ip link add dummy97 type dummy')
7189 self
.wait_online('dummy97:routable')
7193 for name
in os
.listdir('/sys/class/net/'):
7194 if name
.startswith('6rd-'):
7198 self
.wait_online(f
'{tunnel_name}:routable')
7200 self
.verify_dhcp4_6rd(tunnel_name
)
7202 # Test case for reconfigure
7203 networkctl_reconfigure('dummy98', 'dummy99')
7204 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7206 self
.verify_dhcp4_6rd(tunnel_name
)
7208 print('Wait for the DHCP lease to be renewed/rebind')
7211 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7212 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7214 self
.verify_dhcp4_6rd(tunnel_name
)
7216 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7224 def test_ipv6_route_prefix(self
):
7225 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7226 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7229 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7231 output
= check_output('ip address show dev veth-peer')
7233 self
.assertIn('inet6 2001:db8:0:1:', output
)
7234 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7235 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7237 output
= check_output('ip -6 route show dev veth-peer')
7239 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7240 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7241 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7242 self
.assertIn('2001:db0:fff::/64 via ', output
)
7243 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7244 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7246 output
= check_output('ip address show dev veth99')
7248 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7249 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7250 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7251 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7253 output
= resolvectl('dns', 'veth-peer')
7255 self
.assertRegex(output
, '2001:db8:1:1::2')
7257 output
= resolvectl('domain', 'veth-peer')
7259 self
.assertIn('example.com', output
)
7261 check_json(networkctl_json())
7263 output
= networkctl_json('veth-peer')
7267 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7269 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7270 self
.assertEqual(prefix
, '64:ff9b::')
7272 prefix_length
= pref64
['PrefixLength']
7273 self
.assertEqual(prefix_length
, 96)
7275 def test_ipv6_route_prefix_deny_list(self
):
7276 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7277 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7280 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7282 output
= check_output('ip address show dev veth-peer')
7284 self
.assertIn('inet6 2001:db8:0:1:', output
)
7285 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7287 output
= check_output('ip -6 route show dev veth-peer')
7289 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7290 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7291 self
.assertIn('2001:db0:fff::/64 via ', output
)
7292 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7294 output
= check_output('ip address show dev veth99')
7296 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7297 self
.assertIn('inet6 2001:db8:0:2:', output
)
7299 output
= resolvectl('dns', 'veth-peer')
7301 self
.assertRegex(output
, '2001:db8:1:1::2')
7303 output
= resolvectl('domain', 'veth-peer')
7305 self
.assertIn('example.com', output
)
7307 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7315 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7321 self
.wait_online('dummy98:routable')
7322 self
.check_link_attr('dummy98', 'mtu', mtu
)
7323 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7325 # test normal restart
7327 self
.wait_online('dummy98:routable')
7328 self
.check_link_attr('dummy98', 'mtu', mtu
)
7329 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7332 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7334 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7335 ''' test setting mtu/ipv6_mtu with interface already up '''
7338 # note - changing the device mtu resets the ipv6 mtu
7339 check_output('ip link set up mtu 1501 dev dummy98')
7340 check_output('ip link set up mtu 1500 dev dummy98')
7341 self
.check_link_attr('dummy98', 'mtu', '1500')
7342 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7344 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7346 def test_mtu_network(self
):
7347 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7348 self
.check_mtu('1600')
7350 def test_mtu_netdev(self
):
7351 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7352 # note - MTU set by .netdev happens ONLY at device creation!
7353 self
.check_mtu('1600', reset
=False)
7355 def test_mtu_link(self
):
7356 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7357 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7358 self
.check_mtu('1600', reset
=False)
7360 def test_ipv6_mtu(self
):
7361 ''' set ipv6 mtu without setting device mtu '''
7362 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7363 self
.check_mtu('1500', '1400')
7365 def test_ipv6_mtu_toolarge(self
):
7366 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7367 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7368 self
.check_mtu('1500', '1500')
7370 def test_mtu_network_ipv6_mtu(self
):
7371 ''' set ipv6 mtu and set device mtu via network file '''
7372 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7373 self
.check_mtu('1600', '1550')
7375 def test_mtu_netdev_ipv6_mtu(self
):
7376 ''' set ipv6 mtu and set device mtu via netdev file '''
7377 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7378 self
.check_mtu('1600', '1550', reset
=False)
7380 def test_mtu_link_ipv6_mtu(self
):
7381 ''' set ipv6 mtu and set device mtu via link file '''
7382 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7383 self
.check_mtu('1600', '1550', reset
=False)
7386 if __name__
== '__main__':
7387 parser
= argparse
.ArgumentParser()
7388 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7389 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7390 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7391 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7392 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7393 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7394 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7395 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7396 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7397 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7398 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7399 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7400 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7401 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7402 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7403 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7404 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
)
7405 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7408 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7409 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7410 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7411 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7412 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7413 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7414 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7415 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7416 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7417 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7418 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7419 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7420 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7423 networkd_bin
= ns
.networkd_bin
7425 resolved_bin
= ns
.resolved_bin
7426 if ns
.timesyncd_bin
:
7427 timesyncd_bin
= ns
.timesyncd_bin
7429 udevd_bin
= ns
.udevd_bin
7430 if ns
.wait_online_bin
:
7431 wait_online_bin
= ns
.wait_online_bin
7432 if ns
.networkctl_bin
:
7433 networkctl_bin
= ns
.networkctl_bin
7434 if ns
.resolvectl_bin
:
7435 resolvectl_bin
= ns
.resolvectl_bin
7436 if ns
.timedatectl_bin
:
7437 timedatectl_bin
= ns
.timedatectl_bin
7439 udevadm_bin
= ns
.udevadm_bin
7442 systemd_source_dir
= ns
.source_dir
7444 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7445 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7446 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7448 use_valgrind
= ns
.use_valgrind
7449 enable_debug
= ns
.enable_debug
7450 asan_options
= ns
.asan_options
7451 lsan_options
= ns
.lsan_options
7452 ubsan_options
= ns
.ubsan_options
7453 with_coverage
= ns
.with_coverage
7456 # Do not forget the trailing space.
7457 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7459 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7460 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7461 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7462 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7463 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7466 env
.update({'ASAN_OPTIONS': asan_options
})
7468 env
.update({'LSAN_OPTIONS': lsan_options
})
7470 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7472 env
.update({'SYSTEMD_MEMPOOL': '0'})
7474 wait_online_env
= env
.copy()
7476 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7478 sys
.argv
[1:] = unknown_args
7479 unittest
.main(verbosity
=3)