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')
1520 self
.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
1522 def test_vlan(self
):
1523 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1524 '21-vlan.network', '21-vlan-test1.network')
1527 self
.wait_online('test1:degraded', 'vlan99:routable')
1529 output
= check_output('ip -d link show test1')
1531 self
.assertRegex(output
, ' mtu 2000 ')
1533 output
= check_output('ip -d link show vlan99')
1535 self
.assertIn(' mtu 2000 ', output
)
1536 self
.assertIn('REORDER_HDR', output
)
1537 self
.assertIn('LOOSE_BINDING', output
)
1538 self
.assertIn('GVRP', output
)
1539 self
.assertIn('MVRP', output
)
1540 self
.assertIn(' id 99 ', output
)
1541 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1542 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1544 output
= check_output('ip -4 address show dev test1')
1546 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1547 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1549 output
= check_output('ip -4 address show dev vlan99')
1551 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1553 def test_vlan_on_bond(self
):
1554 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1555 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1557 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1558 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1560 self
.wait_online('bond99:off')
1561 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1563 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1564 # that the issue is fixed by the commit, let's allow to match both string.
1565 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1569 if log_re
.search(read_networkd_log()):
1574 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1576 self
.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1578 def test_macvtap(self
):
1580 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1586 print(f
'### test_macvtap(mode={mode})')
1587 with self
.subTest(mode
=mode
):
1588 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1589 '11-dummy.netdev', '25-macvtap.network')
1590 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1591 f
.write('[MACVTAP]\nMode=' + mode
)
1594 self
.wait_online('macvtap99:degraded',
1595 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1597 output
= check_output('ip -d link show macvtap99')
1599 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1601 def test_macvlan(self
):
1603 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1609 print(f
'### test_macvlan(mode={mode})')
1610 with self
.subTest(mode
=mode
):
1611 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1612 '11-dummy.netdev', '25-macvlan.network')
1613 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1614 f
.write('[MACVLAN]\nMode=' + mode
)
1617 self
.wait_online('macvlan99:degraded',
1618 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1620 output
= check_output('ip -d link show test1')
1622 self
.assertIn(' mtu 2000 ', output
)
1624 output
= check_output('ip -d link show macvlan99')
1626 self
.assertIn(' mtu 2000 ', output
)
1627 self
.assertIn(f
' macvlan mode {mode} ', output
)
1629 remove_link('test1')
1632 check_output("ip link add test1 type dummy")
1633 self
.wait_online('macvlan99:degraded',
1634 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1636 output
= check_output('ip -d link show test1')
1638 self
.assertIn(' mtu 2000 ', output
)
1640 output
= check_output('ip -d link show macvlan99')
1642 self
.assertIn(' mtu 2000 ', output
)
1643 self
.assertIn(f
' macvlan mode {mode} ', output
)
1644 self
.assertIn(' bcqueuelen 1234 ', output
)
1645 self
.assertIn(' bclim 2147483647 ', output
)
1647 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1648 def test_ipvlan(self
):
1650 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1656 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1657 with self
.subTest(mode
=mode
, flag
=flag
):
1658 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1659 '11-dummy.netdev', '25-ipvlan.network')
1660 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1661 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1664 self
.wait_online('ipvlan99:degraded', 'test1:degraded')
1666 output
= check_output('ip -d link show ipvlan99')
1668 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1670 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1671 def test_ipvtap(self
):
1673 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1679 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1680 with self
.subTest(mode
=mode
, flag
=flag
):
1681 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1682 '11-dummy.netdev', '25-ipvtap.network')
1683 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1684 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1687 self
.wait_online('ipvtap99:degraded', 'test1:degraded')
1689 output
= check_output('ip -d link show ipvtap99')
1691 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1693 def test_veth(self
):
1694 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1695 '25-veth-mtu.netdev')
1698 self
.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
1700 output
= check_output('ip -d link show veth99')
1702 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1703 output
= check_output('ip -d link show veth-peer')
1705 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1707 output
= check_output('ip -d link show veth-mtu')
1709 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1710 self
.assertRegex(output
, 'mtu 1800')
1711 output
= check_output('ip -d link show veth-mtu-peer')
1713 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1714 self
.assertRegex(output
, 'mtu 1800')
1716 def test_tuntap(self
):
1717 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1720 self
.wait_online('testtun99:degraded', 'testtap99:degraded')
1722 pid
= networkd_pid()
1723 name
= psutil
.Process(pid
).name()[:15]
1725 output
= check_output('ip -d tuntap show')
1727 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1728 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1730 output
= check_output('ip -d link show testtun99')
1732 # Old ip command does not support IFF_ flags
1733 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1734 self
.assertIn('UP,LOWER_UP', output
)
1736 output
= check_output('ip -d link show testtap99')
1738 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1739 self
.assertIn('UP,LOWER_UP', output
)
1741 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1744 self
.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state
='unmanaged')
1746 pid
= networkd_pid()
1747 name
= psutil
.Process(pid
).name()[:15]
1749 output
= check_output('ip -d tuntap show')
1751 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1752 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1754 output
= check_output('ip -d link show testtun99')
1756 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1757 self
.assertIn('UP,LOWER_UP', output
)
1759 output
= check_output('ip -d link show testtap99')
1761 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1762 self
.assertIn('UP,LOWER_UP', output
)
1764 clear_network_units()
1766 self
.wait_online('testtun99:off', 'testtap99:off', setup_state
='unmanaged')
1768 output
= check_output('ip -d tuntap show')
1770 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1771 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1776 output
= check_output('ip -d link show testtun99')
1778 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1779 if 'NO-CARRIER' in output
:
1787 output
= check_output('ip -d link show testtap99')
1789 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1790 if 'NO-CARRIER' in output
:
1795 @expectedFailureIfModuleIsNotAvailable('vrf')
1797 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1800 self
.wait_online('vrf99:carrier')
1802 @expectedFailureIfModuleIsNotAvailable('vcan')
1803 def test_vcan(self
):
1804 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1805 '25-vcan98.netdev', '25-vcan98.network')
1808 self
.wait_online('vcan99:carrier', 'vcan98:carrier')
1809 # For can devices, 'carrier' is the default required operational state.
1810 self
.wait_online('vcan99', 'vcan98')
1812 # https://github.com/systemd/systemd/issues/30140
1813 output
= check_output('ip -d link show vcan99')
1815 self
.assertIn('mtu 16 ', output
)
1817 output
= check_output('ip -d link show vcan98')
1819 self
.assertIn('mtu 16 ', output
)
1821 @expectedFailureIfModuleIsNotAvailable('vxcan')
1822 def test_vxcan(self
):
1823 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1826 self
.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
1827 # For can devices, 'carrier' is the default required operational state.
1828 self
.wait_online('vxcan99', 'vxcan-peer')
1830 @expectedFailureIfModuleIsNotAvailable('wireguard')
1831 def test_wireguard(self
):
1832 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1833 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1834 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1836 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1837 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1838 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1839 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1841 self
.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
1843 output
= check_output('ip -4 address show dev wg99')
1845 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1847 output
= check_output('ip -4 address show dev wg99')
1849 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1851 output
= check_output('ip -6 address show dev wg99')
1853 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1855 output
= check_output('ip -4 address show dev wg98')
1857 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1859 output
= check_output('ip -6 address show dev wg98')
1861 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1863 output
= check_output('ip -4 route show dev wg99 table 1234')
1865 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1867 output
= check_output('ip -6 route show dev wg99 table 1234')
1869 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1871 output
= check_output('ip -6 route show dev wg98 table 1234')
1873 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1909 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1910 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1911 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1912 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1913 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1914 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1915 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1916 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1917 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1918 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1920 if shutil
.which('wg'):
1923 output
= check_output('wg show wg99 listen-port')
1924 self
.assertEqual(output
, '51820')
1925 output
= check_output('wg show wg99 fwmark')
1926 self
.assertEqual(output
, '0x4d2')
1927 output
= check_output('wg show wg99 private-key')
1928 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1929 output
= check_output('wg show wg99 allowed-ips')
1930 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1931 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1932 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1933 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1934 output
= check_output('wg show wg99 persistent-keepalive')
1935 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1936 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1937 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1938 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1939 output
= check_output('wg show wg99 endpoints')
1940 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1941 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1942 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1943 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1944 output
= check_output('wg show wg99 preshared-keys')
1945 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1946 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1947 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1948 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1950 output
= check_output('wg show wg98 private-key')
1951 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1953 output
= check_output('wg show wg97 listen-port')
1954 self
.assertEqual(output
, '51821')
1955 output
= check_output('wg show wg97 fwmark')
1956 self
.assertEqual(output
, '0x4d3')
1958 def test_geneve(self
):
1959 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1962 self
.wait_online('geneve99:degraded')
1964 output
= check_output('ip -d link show geneve99')
1966 self
.assertRegex(output
, '192.168.22.1')
1967 self
.assertRegex(output
, '6082')
1968 self
.assertRegex(output
, 'udpcsum')
1969 self
.assertRegex(output
, 'udp6zerocsumrx')
1971 def test_ipip_tunnel(self
):
1972 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1973 '25-ipip-tunnel.netdev', '25-tunnel.network',
1974 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1975 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1976 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1978 self
.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
1980 output
= check_output('ip -d link show ipiptun99')
1982 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1983 output
= check_output('ip -d link show ipiptun98')
1985 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1986 output
= check_output('ip -d link show ipiptun97')
1988 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1989 output
= check_output('ip -d link show ipiptun96')
1991 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1993 def test_gre_tunnel(self
):
1994 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1995 '25-gre-tunnel.netdev', '25-tunnel.network',
1996 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1997 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1998 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2000 self
.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2002 output
= check_output('ip -d link show gretun99')
2004 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2005 self
.assertRegex(output
, 'ikey 1.2.3.103')
2006 self
.assertRegex(output
, 'okey 1.2.4.103')
2007 self
.assertRegex(output
, 'iseq')
2008 self
.assertRegex(output
, 'oseq')
2009 output
= check_output('ip -d link show gretun98')
2011 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2012 self
.assertRegex(output
, 'ikey 0.0.0.104')
2013 self
.assertRegex(output
, 'okey 0.0.0.104')
2014 self
.assertNotRegex(output
, 'iseq')
2015 self
.assertNotRegex(output
, 'oseq')
2016 output
= check_output('ip -d link show gretun97')
2018 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2019 self
.assertRegex(output
, 'ikey 0.0.0.105')
2020 self
.assertRegex(output
, 'okey 0.0.0.105')
2021 self
.assertNotRegex(output
, 'iseq')
2022 self
.assertNotRegex(output
, 'oseq')
2023 output
= check_output('ip -d link show gretun96')
2025 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2026 self
.assertRegex(output
, 'ikey 0.0.0.106')
2027 self
.assertRegex(output
, 'okey 0.0.0.106')
2028 self
.assertNotRegex(output
, 'iseq')
2029 self
.assertNotRegex(output
, 'oseq')
2031 def test_ip6gre_tunnel(self
):
2032 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2033 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2034 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2035 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2036 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2039 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2041 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2043 output
= check_output('ip -d link show ip6gretun99')
2045 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2046 output
= check_output('ip -d link show ip6gretun98')
2048 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2049 output
= check_output('ip -d link show ip6gretun97')
2051 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2052 output
= check_output('ip -d link show ip6gretun96')
2054 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2056 def test_gretap_tunnel(self
):
2057 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2058 '25-gretap-tunnel.netdev', '25-tunnel.network',
2059 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2061 self
.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2063 output
= check_output('ip -d link show gretap99')
2065 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2066 self
.assertRegex(output
, 'ikey 0.0.0.106')
2067 self
.assertRegex(output
, 'okey 0.0.0.106')
2068 self
.assertRegex(output
, 'iseq')
2069 self
.assertRegex(output
, 'oseq')
2070 self
.assertIn('nopmtudisc', output
)
2071 self
.assertIn('ignore-df', output
)
2072 output
= check_output('ip -d link show gretap98')
2074 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2075 self
.assertRegex(output
, 'ikey 0.0.0.107')
2076 self
.assertRegex(output
, 'okey 0.0.0.107')
2077 self
.assertRegex(output
, 'iseq')
2078 self
.assertRegex(output
, 'oseq')
2080 def test_ip6gretap_tunnel(self
):
2081 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2082 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2083 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2085 self
.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2087 output
= check_output('ip -d link show ip6gretap99')
2089 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2090 output
= check_output('ip -d link show ip6gretap98')
2092 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2094 def test_vti_tunnel(self
):
2095 copy_network_unit('12-dummy.netdev', '25-vti.network',
2096 '25-vti-tunnel.netdev', '25-tunnel.network',
2097 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2098 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2099 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2101 self
.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2103 output
= check_output('ip -d link show vtitun99')
2105 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2106 output
= check_output('ip -d link show vtitun98')
2108 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2109 output
= check_output('ip -d link show vtitun97')
2111 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2112 output
= check_output('ip -d link show vtitun96')
2114 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2116 def test_vti6_tunnel(self
):
2117 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2118 '25-vti6-tunnel.netdev', '25-tunnel.network',
2119 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2120 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2122 self
.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2124 output
= check_output('ip -d link show vti6tun99')
2126 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2127 output
= check_output('ip -d link show vti6tun98')
2129 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2130 output
= check_output('ip -d link show vti6tun97')
2132 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2134 def test_ip6tnl_tunnel(self
):
2135 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2136 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2137 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2138 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2139 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2140 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2141 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2143 self
.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2144 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2145 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2147 output
= check_output('ip -d link show ip6tnl99')
2149 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2150 output
= check_output('ip -d link show ip6tnl98')
2152 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2153 output
= check_output('ip -d link show ip6tnl97')
2155 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2156 output
= check_output('ip -d link show ip6tnl-external')
2158 self
.assertIn('ip6tnl-external@NONE:', output
)
2159 self
.assertIn('ip6tnl external ', output
)
2160 output
= check_output('ip -d link show ip6tnl-slaac')
2162 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2164 output
= check_output('ip -6 address show veth99')
2166 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2168 output
= check_output('ip -4 route show default')
2170 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2172 def test_sit_tunnel(self
):
2173 copy_network_unit('12-dummy.netdev', '25-sit.network',
2174 '25-sit-tunnel.netdev', '25-tunnel.network',
2175 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2176 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2177 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2179 self
.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2181 output
= check_output('ip -d link show sittun99')
2183 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2184 output
= check_output('ip -d link show sittun98')
2186 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2187 output
= check_output('ip -d link show sittun97')
2189 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2190 output
= check_output('ip -d link show sittun96')
2192 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2194 def test_isatap_tunnel(self
):
2195 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2196 '25-isatap-tunnel.netdev', '25-tunnel.network')
2198 self
.wait_online('isataptun99:routable', 'dummy98:degraded')
2200 output
= check_output('ip -d link show isataptun99')
2202 self
.assertRegex(output
, "isatap ")
2204 def test_6rd_tunnel(self
):
2205 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2206 '25-6rd-tunnel.netdev', '25-tunnel.network')
2208 self
.wait_online('sittun99:routable', 'dummy98:degraded')
2210 output
= check_output('ip -d link show sittun99')
2212 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2214 @expectedFailureIfERSPANv0IsNotSupported()
2215 def test_erspan_tunnel_v0(self
):
2216 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2217 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2218 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2220 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2222 output
= check_output('ip -d link show erspan99')
2224 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2225 self
.assertIn('erspan_ver 0', output
)
2226 self
.assertNotIn('erspan_index 123', output
)
2227 self
.assertNotIn('erspan_dir ingress', output
)
2228 self
.assertNotIn('erspan_hwid 1f', output
)
2229 self
.assertIn('ikey 0.0.0.101', output
)
2230 self
.assertIn('iseq', output
)
2231 self
.assertIn('nopmtudisc', output
)
2232 self
.assertIn('ignore-df', output
)
2233 output
= check_output('ip -d link show erspan98')
2235 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2236 self
.assertIn('erspan_ver 0', output
)
2237 self
.assertNotIn('erspan_index 124', output
)
2238 self
.assertNotIn('erspan_dir egress', output
)
2239 self
.assertNotIn('erspan_hwid 2f', output
)
2240 self
.assertIn('ikey 0.0.0.102', output
)
2241 self
.assertIn('iseq', output
)
2243 def test_erspan_tunnel_v1(self
):
2244 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2245 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2246 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2248 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2250 output
= check_output('ip -d link show erspan99')
2252 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2253 self
.assertIn('erspan_ver 1', output
)
2254 self
.assertIn('erspan_index 123', output
)
2255 self
.assertNotIn('erspan_dir ingress', output
)
2256 self
.assertNotIn('erspan_hwid 1f', output
)
2257 self
.assertIn('ikey 0.0.0.101', output
)
2258 self
.assertIn('okey 0.0.0.101', output
)
2259 self
.assertIn('iseq', output
)
2260 self
.assertIn('oseq', output
)
2261 output
= check_output('ip -d link show erspan98')
2263 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2264 self
.assertIn('erspan_ver 1', output
)
2265 self
.assertIn('erspan_index 124', output
)
2266 self
.assertNotIn('erspan_dir egress', output
)
2267 self
.assertNotIn('erspan_hwid 2f', output
)
2268 self
.assertIn('ikey 0.0.0.102', output
)
2269 self
.assertIn('okey 0.0.0.102', output
)
2270 self
.assertIn('iseq', output
)
2271 self
.assertIn('oseq', output
)
2273 @expectedFailureIfERSPANv2IsNotSupported()
2274 def test_erspan_tunnel_v2(self
):
2275 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2276 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2277 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2279 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2281 output
= check_output('ip -d link show erspan99')
2283 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2284 self
.assertIn('erspan_ver 2', output
)
2285 self
.assertNotIn('erspan_index 123', output
)
2286 self
.assertIn('erspan_dir ingress', output
)
2287 self
.assertIn('erspan_hwid 0x1f', output
)
2288 self
.assertIn('ikey 0.0.0.101', output
)
2289 self
.assertIn('okey 0.0.0.101', output
)
2290 self
.assertIn('iseq', output
)
2291 self
.assertIn('oseq', output
)
2292 output
= check_output('ip -d link show erspan98')
2294 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2295 self
.assertIn('erspan_ver 2', output
)
2296 self
.assertNotIn('erspan_index 124', output
)
2297 self
.assertIn('erspan_dir egress', output
)
2298 self
.assertIn('erspan_hwid 0x2f', output
)
2299 self
.assertIn('ikey 0.0.0.102', output
)
2300 self
.assertIn('okey 0.0.0.102', output
)
2301 self
.assertIn('iseq', output
)
2302 self
.assertIn('oseq', output
)
2304 def test_tunnel_independent(self
):
2305 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2308 self
.wait_online('ipiptun99:carrier')
2310 def test_tunnel_independent_loopback(self
):
2311 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2314 self
.wait_online('ipiptun99:carrier')
2316 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2317 def test_xfrm(self
):
2318 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2319 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2320 '26-netdev-link-local-addressing-yes.network')
2323 self
.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
2325 output
= check_output('ip -d link show dev xfrm98')
2327 self
.assertIn('xfrm98@dummy98:', output
)
2328 self
.assertIn('xfrm if_id 0x98 ', output
)
2330 output
= check_output('ip -d link show dev xfrm99')
2332 self
.assertIn('xfrm99@lo:', output
)
2333 self
.assertIn('xfrm if_id 0x99 ', output
)
2335 @expectedFailureIfModuleIsNotAvailable('fou')
2337 # The following redundant check is necessary for CentOS CI.
2338 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2339 self
.assertTrue(is_module_available('fou'))
2341 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2342 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2343 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2346 self
.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state
='unmanaged')
2348 output
= check_output('ip fou show')
2350 self
.assertRegex(output
, 'port 55555 ipproto 4')
2351 self
.assertRegex(output
, 'port 55556 ipproto 47')
2353 output
= check_output('ip -d link show ipiptun96')
2355 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2356 output
= check_output('ip -d link show sittun96')
2358 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2359 output
= check_output('ip -d link show gretun96')
2361 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2362 output
= check_output('ip -d link show gretap96')
2364 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2366 def test_vxlan(self
):
2367 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2368 '25-vxlan.netdev', '25-vxlan.network',
2369 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2370 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2371 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2372 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2375 self
.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2376 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
2378 output
= check_output('ip -d -d link show vxlan99')
2380 self
.assertIn('999', output
)
2381 self
.assertIn('5555', output
)
2382 self
.assertIn('l2miss', output
)
2383 self
.assertIn('l3miss', output
)
2384 self
.assertIn('gbp', output
)
2385 # Since [0] some of the options use slightly different names and some
2386 # options with default values are shown only if the -d(etails) setting
2388 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2389 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2390 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2391 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2392 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2393 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2395 output
= check_output('bridge fdb show dev vxlan99')
2397 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2398 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2399 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2401 output
= networkctl_status('vxlan99')
2403 self
.assertIn('VNI: 999', output
)
2404 self
.assertIn('Destination Port: 5555', output
)
2405 self
.assertIn('Underlying Device: test1', output
)
2407 output
= check_output('bridge fdb show dev vxlan97')
2409 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2410 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2411 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2413 output
= check_output('ip -d link show vxlan-slaac')
2415 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2417 output
= check_output('ip -6 address show veth99')
2419 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2421 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2422 def test_macsec(self
):
2423 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2424 '26-macsec.network', '12-dummy.netdev')
2427 self
.wait_online('dummy98:degraded', 'macsec99:routable')
2429 output
= check_output('ip -d link show macsec99')
2431 self
.assertRegex(output
, 'macsec99@dummy98')
2432 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2433 self
.assertRegex(output
, 'encrypt on')
2435 output
= check_output('ip macsec show macsec99')
2437 self
.assertRegex(output
, 'encrypt on')
2438 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2439 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2440 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2441 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2442 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2443 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2444 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2445 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2446 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2447 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2448 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2450 def test_nlmon(self
):
2451 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2454 self
.wait_online('nlmon99:carrier')
2456 @expectedFailureIfModuleIsNotAvailable('ifb')
2458 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2461 self
.wait_online('ifb99:degraded')
2463 def test_rps_cpu_0(self
):
2464 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link')
2467 self
.wait_links('dummy98')
2469 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2471 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2473 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2474 def test_rps_cpu_1(self
):
2475 copy_network_unit('12-dummy.netdev', '25-rps-cpu-1.link')
2478 self
.wait_links('dummy98')
2480 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2482 self
.assertEqual(int(output
.replace(',', ''), base
=16), 2)
2484 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2485 def test_rps_cpu_0_1(self
):
2486 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-1.link')
2489 self
.wait_links('dummy98')
2491 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2493 self
.assertEqual(int(output
.replace(',', ''), base
=16), 3)
2495 @unittest.skipUnless(os
.cpu_count() >= 4, reason
="CPU count should be >= 4 to pass this test")
2496 def test_rps_cpu_multi(self
):
2497 copy_network_unit('12-dummy.netdev', '25-rps-cpu-multi.link')
2500 self
.wait_links('dummy98')
2502 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2504 self
.assertEqual(int(output
.replace(',', ''), base
=16), 15)
2506 def test_rps_cpu_all(self
):
2507 cpu_count
= os
.cpu_count()
2509 copy_network_unit('12-dummy.netdev', '25-rps-cpu-all.link')
2512 self
.wait_links('dummy98')
2514 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2516 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2518 def test_rps_cpu_disable(self
):
2519 copy_network_unit('12-dummy.netdev', '25-rps-cpu-all.link', '24-rps-cpu-disable.link')
2522 self
.wait_links('dummy98')
2524 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2526 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2528 def test_rps_cpu_empty(self
):
2529 copy_network_unit('12-dummy.netdev', '24-rps-cpu-empty.link')
2532 self
.wait_links('dummy98')
2534 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2536 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2538 def test_rps_cpu_0_empty(self
):
2539 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-empty.link')
2542 self
.wait_links('dummy98')
2544 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2546 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2548 def test_rps_cpu_0_and_empty(self
):
2549 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link', '24-rps-cpu-empty.link')
2552 self
.wait_links('dummy98')
2554 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2556 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2558 def test_rps_cpu_invalid(self
):
2559 copy_network_unit('12-dummy.netdev', '24-rps-cpu-invalid.link')
2562 self
.wait_links('dummy98')
2564 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2566 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2568 def test_rps_cpu_0_invalid(self
):
2569 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0-invalid.link')
2572 self
.wait_links('dummy98')
2574 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2576 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2578 def test_rps_cpu_0_and_invalid(self
):
2579 copy_network_unit('12-dummy.netdev', '25-rps-cpu-0.link', '24-rps-cpu-invalid.link')
2582 self
.wait_links('dummy98')
2584 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2586 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2588 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2596 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2597 def test_l2tp_udp(self
):
2598 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2599 '25-l2tp-udp.netdev', '25-l2tp.network')
2602 self
.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
2604 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2606 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2607 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2608 self
.assertRegex(output
, "Peer tunnel 11")
2609 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2610 self
.assertRegex(output
, "UDP checksum: enabled")
2612 output
= check_output('ip l2tp show session tid 10 session_id 15')
2614 self
.assertRegex(output
, "Session 15 in tunnel 10")
2615 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2616 self
.assertRegex(output
, "interface name: l2tp-ses1")
2618 output
= check_output('ip l2tp show session tid 10 session_id 17')
2620 self
.assertRegex(output
, "Session 17 in tunnel 10")
2621 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2622 self
.assertRegex(output
, "interface name: l2tp-ses2")
2624 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2625 def test_l2tp_ip(self
):
2626 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2627 '25-l2tp-ip.netdev', '25-l2tp.network')
2630 self
.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
2632 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2634 self
.assertRegex(output
, "Tunnel 10, encap IP")
2635 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2636 self
.assertRegex(output
, "Peer tunnel 12")
2638 output
= check_output('ip l2tp show session tid 10 session_id 25')
2640 self
.assertRegex(output
, "Session 25 in tunnel 10")
2641 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2642 self
.assertRegex(output
, "interface name: l2tp-ses3")
2644 output
= check_output('ip l2tp show session tid 10 session_id 27')
2646 self
.assertRegex(output
, "Session 27 in tunnel 10")
2647 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2648 self
.assertRegex(output
, "interface name: l2tp-ses4")
2650 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2658 def verify_address_static(
2688 output
= check_output('ip address show dev dummy98')
2692 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2693 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2694 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2695 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2696 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2697 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2700 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2701 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2702 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2705 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2706 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2707 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2710 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2711 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2712 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2713 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2714 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2715 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2718 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2719 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2722 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2723 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2724 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2725 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2728 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2729 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2731 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2733 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2735 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2737 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2740 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2741 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2742 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2743 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2746 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2747 prefix16
= ip4_null_16
[:-len('.0.1')]
2748 self
.assertTrue(ip4_null_24
.endswith('.1'))
2749 prefix24
= ip4_null_24
[:-len('.1')]
2750 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2751 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2752 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2753 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2756 self
.assertNotIn('10.4.4.1', output
)
2757 self
.assertNotIn('10.5.4.1', output
)
2758 self
.assertNotIn('10.5.5.1', output
)
2759 self
.assertNotIn('10.8.2.1', output
)
2760 self
.assertNotIn('10.9.3.1', output
)
2761 self
.assertNotIn('2001:db8:0:f101::2', output
)
2762 self
.assertNotIn('2001:db8:0:f103::4', output
)
2765 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2767 check_json(networkctl_json())
2769 def test_address_static(self
):
2770 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2771 self
.setup_nftset('addr4', 'ipv4_addr')
2772 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2773 self
.setup_nftset('ifindex', 'iface_index')
2776 self
.wait_online('dummy98:routable')
2780 output
= check_output('ip -4 --json address show dev dummy98')
2781 for i
in json
.loads(output
)[0]['addr_info']:
2782 if i
['label'] == 'subnet16':
2783 ip4_null_16
= i
['local']
2784 elif i
['label'] == 'subnet24':
2785 ip4_null_24
= i
['local']
2786 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2787 self
.assertTrue(ip4_null_24
.endswith('.1'))
2791 output
= check_output('ip -6 --json address show dev dummy98')
2792 for i
in json
.loads(output
)[0]['addr_info']:
2793 if i
['prefixlen'] == 73:
2794 ip6_null_73
= i
['local']
2795 elif i
['prefixlen'] == 74:
2796 ip6_null_74
= i
['local']
2797 self
.assertTrue(ip6_null_73
.endswith(':1'))
2798 self
.assertTrue(ip6_null_74
.endswith(':1'))
2800 self
.verify_address_static(
2805 broadcast2
=' brd 10.4.2.255',
2806 broadcast3
=' brd 10.4.3.63',
2807 peer1
=' peer 10.5.1.101/24',
2808 peer2
=' peer 10.5.2.101/24',
2809 peer3
='/24 brd 10.5.3.255',
2810 peer4
=' peer 2001:db8:0:f103::101/128',
2811 peer5
=' peer 2001:db8:0:f103::102/128',
2816 deprecated2
=' deprecated',
2818 deprecated4
=' deprecated',
2820 flag1
=' noprefixroute',
2822 flag3
=' noprefixroute',
2823 flag4
=' home mngtmpaddr',
2824 ip4_null_16
=ip4_null_16
,
2825 ip4_null_24
=ip4_null_24
,
2826 ip6_null_73
=ip6_null_73
,
2827 ip6_null_74
=ip6_null_74
,
2830 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2831 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2832 self
.check_nftset('ifindex', 'dummy98')
2834 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2836 copy_network_unit('25-address-static.network.d/10-override.conf')
2838 self
.wait_online('dummy98:routable')
2839 self
.verify_address_static(
2840 label1
='new-label1',
2842 label3
='new-label3',
2843 broadcast1
=' brd 10.4.1.255',
2845 broadcast3
=' brd 10.4.3.31',
2846 peer1
=' peer 10.5.1.102/24',
2847 peer2
='/24 brd 10.5.2.255',
2848 peer3
=' peer 10.5.3.102/24',
2849 peer4
=' peer 2001:db8:0:f103::201/128',
2851 peer6
=' peer 2001:db8:0:f103::203/128',
2854 deprecated1
=' deprecated',
2856 deprecated3
=' deprecated',
2860 flag2
=' noprefixroute',
2861 flag3
=' home mngtmpaddr',
2862 flag4
=' noprefixroute',
2863 ip4_null_16
=ip4_null_16
,
2864 ip4_null_24
=ip4_null_24
,
2865 ip6_null_73
=ip6_null_73
,
2866 ip6_null_74
=ip6_null_74
,
2869 networkctl_reconfigure('dummy98')
2870 self
.wait_online('dummy98:routable')
2871 self
.verify_address_static(
2872 label1
='new-label1',
2874 label3
='new-label3',
2875 broadcast1
=' brd 10.4.1.255',
2877 broadcast3
=' brd 10.4.3.31',
2878 peer1
=' peer 10.5.1.102/24',
2879 peer2
='/24 brd 10.5.2.255',
2880 peer3
=' peer 10.5.3.102/24',
2881 peer4
=' peer 2001:db8:0:f103::201/128',
2883 peer6
=' peer 2001:db8:0:f103::203/128',
2886 deprecated1
=' deprecated',
2888 deprecated3
=' deprecated',
2892 flag2
=' noprefixroute',
2893 flag3
=' home mngtmpaddr',
2894 flag4
=' noprefixroute',
2895 ip4_null_16
=ip4_null_16
,
2896 ip4_null_24
=ip4_null_24
,
2897 ip6_null_73
=ip6_null_73
,
2898 ip6_null_74
=ip6_null_74
,
2902 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2903 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2904 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2905 output
= check_output('ip address show dev dummy98')
2907 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2908 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2910 # 2. reconfigure the interface, and check the deprecated flag is set again
2911 networkctl_reconfigure('dummy98')
2912 self
.wait_online('dummy98:routable')
2913 self
.verify_address_static(
2914 label1
='new-label1',
2916 label3
='new-label3',
2917 broadcast1
=' brd 10.4.1.255',
2919 broadcast3
=' brd 10.4.3.31',
2920 peer1
=' peer 10.5.1.102/24',
2921 peer2
='/24 brd 10.5.2.255',
2922 peer3
=' peer 10.5.3.102/24',
2923 peer4
=' peer 2001:db8:0:f103::201/128',
2925 peer6
=' peer 2001:db8:0:f103::203/128',
2928 deprecated1
=' deprecated',
2930 deprecated3
=' deprecated',
2934 flag2
=' noprefixroute',
2935 flag3
=' home mngtmpaddr',
2936 flag4
=' noprefixroute',
2937 ip4_null_16
=ip4_null_16
,
2938 ip4_null_24
=ip4_null_24
,
2939 ip6_null_73
=ip6_null_73
,
2940 ip6_null_74
=ip6_null_74
,
2943 # test for ENOBUFS issue #17012 (with reload)
2944 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2946 self
.wait_online('dummy98:routable')
2947 output
= check_output('ip -4 address show dev dummy98')
2948 for i
in range(1, 254):
2949 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2951 # (with reconfigure)
2952 networkctl_reconfigure('dummy98')
2953 self
.wait_online('dummy98:routable')
2954 output
= check_output('ip -4 address show dev dummy98')
2955 for i
in range(1, 254):
2956 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2958 # test for an empty string assignment for Address= in [Network]
2959 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2961 self
.wait_online('dummy98:routable')
2962 output
= check_output('ip -4 address show dev dummy98')
2963 for i
in range(1, 254):
2964 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2965 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2967 def test_address_ipv4acd(self
):
2968 check_output('ip netns add ns99')
2969 check_output('ip link add veth99 type veth peer veth-peer')
2970 check_output('ip link set veth-peer netns ns99')
2971 check_output('ip link set veth99 up')
2972 check_output('ip netns exec ns99 ip link set veth-peer up')
2973 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2975 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2977 self
.wait_online('veth99:routable')
2979 output
= check_output('ip -4 address show dev veth99')
2981 self
.assertNotIn('192.168.100.10/24', output
)
2982 self
.assertIn('192.168.100.11/24', output
)
2984 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2986 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2988 output
= check_output('ip -4 address show dev veth99')
2990 self
.assertNotIn('192.168.100.10/24', output
)
2991 self
.assertIn('192.168.100.11/24', output
)
2993 def test_address_peer_ipv4(self
):
2994 # test for issue #17304
2995 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2997 for trial
in range(2):
3003 self
.wait_online('dummy98:routable')
3005 output
= check_output('ip -4 address show dev dummy98')
3006 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
3008 @expectedFailureIfModuleIsNotAvailable('vrf')
3009 def test_prefix_route(self
):
3010 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
3011 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
3012 '25-vrf.netdev', '25-vrf.network')
3013 for trial
in range(2):
3019 self
.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
3021 output
= check_output('ip route show table 42 dev dummy98')
3022 print('### ip route show table 42 dev dummy98')
3024 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
3025 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3026 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3027 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3028 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
3029 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3030 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3031 output
= check_output('ip -6 route show table 42 dev dummy98')
3032 print('### ip -6 route show table 42 dev dummy98')
3036 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3037 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3038 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3039 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3040 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3041 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3042 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3043 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3047 output
= check_output('ip route show dev test1')
3048 print('### ip route show dev test1')
3050 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3051 output
= check_output('ip route show table local dev test1')
3052 print('### ip route show table local dev test1')
3054 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
3055 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3056 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3057 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
3058 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3059 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3060 output
= check_output('ip -6 route show dev test1')
3061 print('### ip -6 route show dev test1')
3063 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3064 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3065 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3066 output
= check_output('ip -6 route show table local dev test1')
3067 print('### ip -6 route show table local dev test1')
3069 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3070 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3071 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3072 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
3073 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3075 def test_configure_without_carrier(self
):
3076 copy_network_unit('11-dummy.netdev')
3078 self
.wait_operstate('test1', 'off', '')
3079 check_output('ip link set dev test1 up carrier off')
3081 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
3083 self
.wait_online('test1:no-carrier')
3085 carrier_map
= {'on': '1', 'off': '0'}
3086 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3087 for carrier
in ['off', 'on', 'off']:
3088 with self
.subTest(carrier
=carrier
):
3089 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3090 check_output(f
'ip link set dev test1 carrier {carrier}')
3091 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3093 output
= networkctl_status('test1')
3095 self
.assertRegex(output
, '192.168.0.15')
3096 self
.assertRegex(output
, '192.168.0.1')
3097 self
.assertRegex(output
, routable_map
[carrier
])
3099 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
3100 copy_network_unit('11-dummy.netdev')
3102 self
.wait_operstate('test1', 'off', '')
3103 check_output('ip link set dev test1 up carrier off')
3105 copy_network_unit('25-test1.network')
3107 self
.wait_online('test1:no-carrier')
3109 carrier_map
= {'on': '1', 'off': '0'}
3110 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3111 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
3112 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
3113 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3114 check_output(f
'ip link set dev test1 carrier {carrier}')
3115 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3117 output
= networkctl_status('test1')
3120 self
.assertRegex(output
, '192.168.0.15')
3121 self
.assertRegex(output
, '192.168.0.1')
3123 self
.assertNotRegex(output
, '192.168.0.15')
3124 self
.assertNotRegex(output
, '192.168.0.1')
3125 self
.assertRegex(output
, routable_map
[carrier
])
3127 def test_routing_policy_rule(self
):
3128 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
3130 self
.wait_online('test1:degraded')
3132 output
= check_output('ip rule list iif test1 priority 111')
3134 self
.assertRegex(output
, '111:')
3135 self
.assertRegex(output
, 'from 192.168.100.18')
3136 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3137 self
.assertRegex(output
, 'iif test1')
3138 self
.assertRegex(output
, 'oif test1')
3139 self
.assertRegex(output
, 'lookup 7')
3141 output
= check_output('ip rule list iif test1 priority 101')
3143 self
.assertRegex(output
, '101:')
3144 self
.assertRegex(output
, 'from all')
3145 self
.assertRegex(output
, 'iif test1')
3146 self
.assertRegex(output
, 'lookup 9')
3148 output
= check_output('ip -6 rule list iif test1 priority 100')
3150 self
.assertRegex(output
, '100:')
3151 self
.assertRegex(output
, 'from all')
3152 self
.assertRegex(output
, 'iif test1')
3153 self
.assertRegex(output
, 'lookup 8')
3155 output
= check_output('ip rule list iif test1 priority 102')
3157 self
.assertRegex(output
, '102:')
3158 self
.assertRegex(output
, 'from 0.0.0.0/8')
3159 self
.assertRegex(output
, 'iif test1')
3160 self
.assertRegex(output
, 'lookup 10')
3162 check_json(networkctl_json())
3164 def test_routing_policy_rule_issue_11280(self
):
3165 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3166 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3168 for trial
in range(3):
3169 restart_networkd(show_logs
=(trial
> 0))
3170 self
.wait_online('test1:degraded', 'dummy98:degraded')
3172 output
= check_output('ip rule list table 7')
3174 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3176 output
= check_output('ip rule list table 8')
3178 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3180 def test_routing_policy_rule_reconfigure(self
):
3181 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3183 self
.wait_online('test1:degraded')
3185 output
= check_output('ip rule list table 1011')
3187 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3188 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3189 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3190 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3192 output
= check_output('ip -6 rule list table 1011')
3194 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3196 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3198 self
.wait_online('test1:degraded')
3200 output
= check_output('ip rule list table 1011')
3202 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3203 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3204 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3205 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3207 output
= check_output('ip -6 rule list table 1011')
3209 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3210 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3212 call('ip rule delete priority 10111')
3213 call('ip rule delete priority 10112')
3214 call('ip rule delete priority 10113')
3215 call('ip rule delete priority 10114')
3216 call('ip -6 rule delete priority 10113')
3218 output
= check_output('ip rule list table 1011')
3220 self
.assertEqual(output
, '')
3222 output
= check_output('ip -6 rule list table 1011')
3224 self
.assertEqual(output
, '')
3226 networkctl_reconfigure('test1')
3227 self
.wait_online('test1:degraded')
3229 output
= check_output('ip rule list table 1011')
3231 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3232 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3233 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3234 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3236 output
= check_output('ip -6 rule list table 1011')
3238 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3240 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3241 def test_routing_policy_rule_port_range(self
):
3242 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3244 self
.wait_online('test1:degraded')
3246 output
= check_output('ip rule')
3248 self
.assertRegex(output
, '111')
3249 self
.assertRegex(output
, 'from 192.168.100.18')
3250 self
.assertRegex(output
, '1123-1150')
3251 self
.assertRegex(output
, '3224-3290')
3252 self
.assertRegex(output
, 'tcp')
3253 self
.assertRegex(output
, 'lookup 7')
3255 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3256 def test_routing_policy_rule_invert(self
):
3257 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3259 self
.wait_online('test1:degraded')
3261 output
= check_output('ip rule')
3263 self
.assertRegex(output
, '111')
3264 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3265 self
.assertRegex(output
, 'tcp')
3266 self
.assertRegex(output
, 'lookup 7')
3268 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3269 def test_routing_policy_rule_l3mdev(self
):
3270 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3272 self
.wait_online('test1:degraded')
3274 output
= check_output('ip rule')
3276 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3277 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3279 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3280 def test_routing_policy_rule_uidrange(self
):
3281 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3283 self
.wait_online('test1:degraded')
3285 output
= check_output('ip rule')
3287 self
.assertRegex(output
, '111')
3288 self
.assertRegex(output
, 'from 192.168.100.18')
3289 self
.assertRegex(output
, 'lookup 7')
3290 self
.assertRegex(output
, 'uidrange 100-200')
3292 def _test_route_static(self
, manage_foreign_routes
):
3293 if not manage_foreign_routes
:
3294 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3296 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3297 '25-route-static-test1.network', '11-dummy.netdev')
3299 self
.wait_online('dummy98:routable')
3301 output
= networkctl_status('dummy98')
3304 print('### ip -6 route show dev dummy98')
3305 output
= check_output('ip -6 route show dev dummy98')
3307 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3308 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3309 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3311 print('### ip -6 route show default')
3312 output
= check_output('ip -6 route show default')
3314 self
.assertIn('default', output
)
3315 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3317 print('### ip -4 route show dev dummy98')
3318 output
= check_output('ip -4 route show dev dummy98')
3320 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3321 self
.assertIn('149.10.124.64 proto static scope link', output
)
3322 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3323 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3324 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3325 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3326 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3327 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3329 print('### ip -4 route show dev dummy98 default')
3330 output
= check_output('ip -4 route show dev dummy98 default')
3332 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3333 self
.assertIn('default via 149.10.124.64 proto static', output
)
3334 self
.assertIn('default proto static', output
)
3335 self
.assertIn('default via 1.1.8.104 proto static', output
)
3337 print('### ip -4 route show table local dev dummy98')
3338 output
= check_output('ip -4 route show table local dev dummy98')
3340 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3341 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3342 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3344 print('### ip -4 route show type blackhole')
3345 output
= check_output('ip -4 route show type blackhole')
3347 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3349 print('### ip -4 route show type unreachable')
3350 output
= check_output('ip -4 route show type unreachable')
3352 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3354 print('### ip -4 route show type prohibit')
3355 output
= check_output('ip -4 route show type prohibit')
3357 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3359 print('### ip -6 route show type blackhole')
3360 output
= check_output('ip -6 route show type blackhole')
3362 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3364 print('### ip -6 route show type unreachable')
3365 output
= check_output('ip -6 route show type unreachable')
3367 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3369 print('### ip -6 route show type prohibit')
3370 output
= check_output('ip -6 route show type prohibit')
3372 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3374 print('### ip route show 192.168.10.1')
3375 output
= check_output('ip route show 192.168.10.1')
3377 self
.assertIn('192.168.10.1 proto static', output
)
3378 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3379 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3380 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3381 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3383 print('### ip route show 192.168.10.2')
3384 output
= check_output('ip route show 192.168.10.2')
3386 # old ip command does not show IPv6 gateways...
3387 self
.assertIn('192.168.10.2 proto static', output
)
3388 self
.assertIn('nexthop', output
)
3389 self
.assertIn('dev test1 weight 20', output
)
3390 self
.assertIn('dev test1 weight 30', output
)
3391 self
.assertIn('dev dummy98 weight 10', output
)
3392 self
.assertIn('dev dummy98 weight 5', output
)
3394 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3395 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3397 # old ip command does not show 'nexthop' keyword and weight...
3398 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3399 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3400 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3401 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3402 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3404 check_json(networkctl_json())
3406 copy_network_unit('25-address-static.network', copy_dropins
=False)
3408 self
.wait_online('dummy98:routable')
3410 # check all routes managed by Manager are removed
3411 print('### ip -4 route show type blackhole')
3412 output
= check_output('ip -4 route show type blackhole')
3414 self
.assertEqual(output
, '')
3416 print('### ip -4 route show type unreachable')
3417 output
= check_output('ip -4 route show type unreachable')
3419 self
.assertEqual(output
, '')
3421 print('### ip -4 route show type prohibit')
3422 output
= check_output('ip -4 route show type prohibit')
3424 self
.assertEqual(output
, '')
3426 print('### ip -6 route show type blackhole')
3427 output
= check_output('ip -6 route show type blackhole')
3429 self
.assertEqual(output
, '')
3431 print('### ip -6 route show type unreachable')
3432 output
= check_output('ip -6 route show type unreachable')
3434 self
.assertEqual(output
, '')
3436 print('### ip -6 route show type prohibit')
3437 output
= check_output('ip -6 route show type prohibit')
3439 self
.assertEqual(output
, '')
3441 remove_network_unit('25-address-static.network')
3443 self
.wait_online('dummy98:routable')
3445 # check all routes managed by Manager are reconfigured
3446 print('### ip -4 route show type blackhole')
3447 output
= check_output('ip -4 route show type blackhole')
3449 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3451 print('### ip -4 route show type unreachable')
3452 output
= check_output('ip -4 route show type unreachable')
3454 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3456 print('### ip -4 route show type prohibit')
3457 output
= check_output('ip -4 route show type prohibit')
3459 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3461 print('### ip -6 route show type blackhole')
3462 output
= check_output('ip -6 route show type blackhole')
3464 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3466 print('### ip -6 route show type unreachable')
3467 output
= check_output('ip -6 route show type unreachable')
3469 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3471 print('### ip -6 route show type prohibit')
3472 output
= check_output('ip -6 route show type prohibit')
3474 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3476 remove_link('dummy98')
3479 # check all routes managed by Manager are removed
3480 print('### ip -4 route show type blackhole')
3481 output
= check_output('ip -4 route show type blackhole')
3483 self
.assertEqual(output
, '')
3485 print('### ip -4 route show type unreachable')
3486 output
= check_output('ip -4 route show type unreachable')
3488 self
.assertEqual(output
, '')
3490 print('### ip -4 route show type prohibit')
3491 output
= check_output('ip -4 route show type prohibit')
3493 self
.assertEqual(output
, '')
3495 print('### ip -6 route show type blackhole')
3496 output
= check_output('ip -6 route show type blackhole')
3498 self
.assertEqual(output
, '')
3500 print('### ip -6 route show type unreachable')
3501 output
= check_output('ip -6 route show type unreachable')
3503 self
.assertEqual(output
, '')
3505 print('### ip -6 route show type prohibit')
3506 output
= check_output('ip -6 route show type prohibit')
3508 self
.assertEqual(output
, '')
3510 def test_route_static(self
):
3512 for manage_foreign_routes
in [True, False]:
3518 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3519 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3520 self
._test
_route
_static
(manage_foreign_routes
)
3522 @expectedFailureIfRTA_VIAIsNotSupported()
3523 def test_route_via_ipv6(self
):
3524 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3526 self
.wait_online('dummy98:routable')
3528 output
= networkctl_status('dummy98')
3531 print('### ip -6 route show dev dummy98')
3532 output
= check_output('ip -6 route show dev dummy98')
3534 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3535 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3537 print('### ip -4 route show dev dummy98')
3538 output
= check_output('ip -4 route show dev dummy98')
3540 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3541 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3543 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3544 def test_route_congctl(self
):
3545 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3547 self
.wait_online('dummy98:routable')
3549 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3550 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3552 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3553 self
.assertIn('congctl dctcp', output
)
3555 print('### ip -4 route show dev dummy98 149.10.124.66')
3556 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3558 self
.assertIn('149.10.124.66 proto static', output
)
3559 self
.assertIn('congctl dctcp', output
)
3560 self
.assertIn('rto_min 300s', output
)
3562 @expectedFailureIfModuleIsNotAvailable('vrf')
3563 def test_route_vrf(self
):
3564 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3565 '25-vrf.netdev', '25-vrf.network')
3567 self
.wait_online('dummy98:routable', 'vrf99:carrier')
3569 output
= check_output('ip route show vrf vrf99')
3571 self
.assertRegex(output
, 'default via 192.168.100.1')
3573 output
= check_output('ip route show')
3575 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3577 def test_gateway_reconfigure(self
):
3578 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3580 self
.wait_online('dummy98:routable')
3581 print('### ip -4 route show dev dummy98 default')
3582 output
= check_output('ip -4 route show dev dummy98 default')
3584 self
.assertIn('default via 149.10.124.59 proto static', output
)
3585 self
.assertNotIn('149.10.124.60', output
)
3587 remove_network_unit('25-gateway-static.network')
3588 copy_network_unit('25-gateway-next-static.network')
3590 self
.wait_online('dummy98:routable')
3591 print('### ip -4 route show dev dummy98 default')
3592 output
= check_output('ip -4 route show dev dummy98 default')
3594 self
.assertNotIn('149.10.124.59', output
)
3595 self
.assertIn('default via 149.10.124.60 proto static', output
)
3597 def test_ip_route_ipv6_src_route(self
):
3598 # a dummy device does not make the addresses go through tentative state, so we
3599 # reuse a bond from an earlier test, which does make the addresses go through
3600 # tentative state, and do our test on that
3601 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3603 self
.wait_online('dummy98:enslaved', 'bond199:routable')
3605 output
= check_output('ip -6 route list dev bond199')
3607 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3609 def test_route_preferred_source_with_existing_address(self
):
3611 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3616 networkctl_reconfigure('dummy98')
3618 self
.wait_online('dummy98:routable')
3620 output
= check_output('ip -6 route list dev dummy98')
3622 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3624 def test_ip_link_mac_address(self
):
3625 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3627 self
.wait_online('dummy98:degraded')
3629 output
= check_output('ip link show dummy98')
3631 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3633 def test_ip_link_unmanaged(self
):
3634 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3637 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3639 def test_ipv6_address_label(self
):
3640 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3642 self
.wait_online('dummy98:degraded')
3644 output
= check_output('ip addrlabel list')
3646 self
.assertRegex(output
, '2004:da8:1::/64')
3648 def test_ipv6_proxy_ndp(self
):
3649 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3652 self
.wait_online('dummy98:routable')
3654 output
= check_output('ip neighbor show proxy dev dummy98')
3656 for i
in range(1, 5):
3657 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3659 def test_ipv6_neigh_retrans_time(self
):
3661 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3664 self
.wait_online(f
'{link}:degraded')
3665 remove_network_unit('25-dummy.network')
3667 # expect retrans_time_ms updated
3668 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3670 self
.wait_online(f
'{link}:degraded')
3671 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3672 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3674 # expect retrans_time_ms unchanged
3675 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3677 self
.wait_online(f
'{link}:degraded')
3678 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3679 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3681 # expect retrans_time_ms unchanged
3682 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3684 self
.wait_online(f
'{link}:degraded')
3685 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3686 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3688 # expect retrans_time_ms unchanged
3689 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3691 self
.wait_online(f
'{link}:degraded')
3692 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3693 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3695 # expect retrans_time_ms unchanged
3696 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3698 self
.wait_online(f
'{link}:degraded')
3699 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3700 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3702 # expect retrans_time_ms updated
3703 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3705 self
.wait_online(f
'{link}:degraded')
3706 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3707 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3709 def test_neighbor(self
):
3710 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3711 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3712 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3715 self
.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
3717 print('### ip neigh list dev gretun97')
3718 output
= check_output('ip neigh list dev gretun97')
3720 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3721 self
.assertNotIn('10.0.0.23', output
)
3723 print('### ip neigh list dev ip6gretun97')
3724 output
= check_output('ip neigh list dev ip6gretun97')
3726 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3727 self
.assertNotIn('2001:db8:0:f102::18', output
)
3729 print('### ip neigh list dev dummy98')
3730 output
= check_output('ip neigh list dev dummy98')
3732 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3733 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3734 self
.assertNotIn('2004:da8:1:0::2', output
)
3735 self
.assertNotIn('192.168.10.2', output
)
3736 self
.assertNotIn('00:00:5e:00:02:67', output
)
3738 check_json(networkctl_json())
3740 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3741 # the valid configurations in 10-step1.conf.
3742 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3744 self
.wait_online('dummy98:degraded')
3746 print('### ip neigh list dev dummy98')
3747 output
= check_output('ip neigh list dev dummy98')
3749 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3750 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3751 self
.assertNotIn('2004:da8:1:0::2', output
)
3752 self
.assertNotIn('192.168.10.2', output
)
3753 self
.assertNotIn('00:00:5e:00:02:67', output
)
3755 check_json(networkctl_json())
3757 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3758 '25-neighbor-dummy.network.d/10-step2.conf')
3759 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3761 self
.wait_online('dummy98:degraded')
3763 print('### ip neigh list dev dummy98')
3764 output
= check_output('ip neigh list dev dummy98')
3766 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3767 self
.assertNotIn('00:00:5e:00:02:65', output
)
3768 self
.assertNotIn('00:00:5e:00:02:66', output
)
3769 self
.assertNotIn('00:00:5e:00:03:65', output
)
3770 self
.assertNotIn('2004:da8:1::1', output
)
3772 def test_link_local_addressing(self
):
3773 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3774 '25-link-local-addressing-no.network', '12-dummy.netdev')
3776 self
.wait_online('test1:degraded', 'dummy98:carrier')
3778 output
= check_output('ip address show dev test1')
3780 self
.assertRegex(output
, 'inet .* scope link')
3781 self
.assertRegex(output
, 'inet6 .* scope link')
3783 output
= check_output('ip address show dev dummy98')
3785 self
.assertNotRegex(output
, 'inet6* .* scope link')
3787 # Documentation/networking/ip-sysctl.txt
3789 # addr_gen_mode - INTEGER
3790 # Defines how link-local and autoconf addresses are generated.
3792 # 0: generate address based on EUI64 (default)
3793 # 1: do no generate a link-local address, use EUI64 for addresses generated
3795 # 2: generate stable privacy addresses, using the secret from
3796 # stable_secret (RFC7217)
3797 # 3: generate stable privacy addresses, using a random secret if unset
3799 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3800 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3801 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3803 def test_link_local_addressing_ipv6ll(self
):
3804 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3806 self
.wait_online('dummy98:degraded')
3808 # An IPv6LL address exists by default.
3809 output
= check_output('ip address show dev dummy98')
3811 self
.assertRegex(output
, 'inet6 .* scope link')
3813 copy_network_unit('25-link-local-addressing-no.network')
3815 self
.wait_online('dummy98:carrier')
3817 # Check if the IPv6LL address is removed.
3818 output
= check_output('ip address show dev dummy98')
3820 self
.assertNotRegex(output
, 'inet6 .* scope link')
3822 remove_network_unit('25-link-local-addressing-no.network')
3824 self
.wait_online('dummy98:degraded')
3826 # Check if a new IPv6LL address is assigned.
3827 output
= check_output('ip address show dev dummy98')
3829 self
.assertRegex(output
, 'inet6 .* scope link')
3831 def test_sysctl(self
):
3832 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3833 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3835 self
.wait_online('dummy98:degraded')
3837 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3838 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3839 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3840 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3841 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3842 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3843 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3844 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3845 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3846 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3848 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3850 self
.wait_online('dummy98:degraded')
3852 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3854 def test_sysctl_disable_ipv6(self
):
3855 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3857 print('## Disable ipv6')
3858 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3859 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3862 self
.wait_online('dummy98:routable')
3864 output
= check_output('ip -4 address show dummy98')
3866 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3867 output
= check_output('ip -6 address show dummy98')
3869 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3870 self
.assertRegex(output
, 'inet6 .* scope link')
3871 output
= check_output('ip -4 route show dev dummy98')
3873 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3874 output
= check_output('ip -6 route show default')
3876 self
.assertRegex(output
, 'default')
3877 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3879 remove_link('dummy98')
3881 print('## Enable ipv6')
3882 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3883 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3886 self
.wait_online('dummy98:routable')
3888 output
= check_output('ip -4 address show dummy98')
3890 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3891 output
= check_output('ip -6 address show dummy98')
3893 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3894 self
.assertRegex(output
, 'inet6 .* scope link')
3895 output
= check_output('ip -4 route show dev dummy98')
3897 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3898 output
= check_output('ip -6 route show default')
3900 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3902 def test_bind_carrier(self
):
3903 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3906 # no bound interface.
3907 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3908 output
= check_output('ip address show test1')
3910 self
.assertNotIn('UP,LOWER_UP', output
)
3911 self
.assertIn('DOWN', output
)
3912 self
.assertNotIn('192.168.10', output
)
3914 # add one bound interface. The interface will be up.
3915 check_output('ip link add dummy98 type dummy')
3916 check_output('ip link set dummy98 up')
3917 self
.wait_online('test1:routable')
3918 output
= check_output('ip address show test1')
3920 self
.assertIn('UP,LOWER_UP', output
)
3921 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3923 # add another bound interface. The interface is still up.
3924 check_output('ip link add dummy99 type dummy')
3925 check_output('ip link set dummy99 up')
3926 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3927 output
= check_output('ip address show test1')
3929 self
.assertIn('UP,LOWER_UP', output
)
3930 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3932 # remove one of the bound interfaces. The interface is still up
3933 remove_link('dummy98')
3934 output
= check_output('ip address show test1')
3936 self
.assertIn('UP,LOWER_UP', output
)
3937 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3939 # bring down the remaining bound interface. The interface will be down.
3940 check_output('ip link set dummy99 down')
3941 self
.wait_operstate('test1', 'off')
3942 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3943 output
= check_output('ip address show test1')
3945 self
.assertNotIn('UP,LOWER_UP', output
)
3946 self
.assertIn('DOWN', output
)
3947 self
.assertNotIn('192.168.10', output
)
3949 # bring up the bound interface. The interface will be up.
3950 check_output('ip link set dummy99 up')
3951 self
.wait_online('test1:routable')
3952 output
= check_output('ip address show test1')
3954 self
.assertIn('UP,LOWER_UP', output
)
3955 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3957 # remove the remaining bound interface. The interface will be down.
3958 remove_link('dummy99')
3959 self
.wait_operstate('test1', 'off')
3960 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3961 output
= check_output('ip address show test1')
3963 self
.assertNotIn('UP,LOWER_UP', output
)
3964 self
.assertIn('DOWN', output
)
3965 self
.assertNotIn('192.168.10', output
)
3967 # re-add one bound interface. The interface will be up.
3968 check_output('ip link add dummy98 type dummy')
3969 check_output('ip link set dummy98 up')
3970 self
.wait_online('test1:routable')
3971 output
= check_output('ip address show test1')
3973 self
.assertIn('UP,LOWER_UP', output
)
3974 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3976 def _test_activation_policy(self
, interface
, test
):
3977 conffile
= '25-activation-policy.network'
3979 conffile
= f
'{conffile}.d/{test}.conf'
3980 if interface
== 'vlan99':
3981 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3982 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3985 always
= test
.startswith('always')
3986 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3987 expect_up
= initial_up
3988 next_up
= not expect_up
3990 if test
.endswith('down'):
3991 self
.wait_activated(interface
)
3993 for iteration
in range(4):
3994 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3995 operstate
= 'routable' if expect_up
else 'off'
3996 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3997 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
4000 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
4001 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
4002 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
4004 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
4007 check_output(f
'ip link set dev {interface} up')
4009 check_output(f
'ip link set dev {interface} down')
4010 expect_up
= initial_up
if always
else next_up
4011 next_up
= not next_up
4015 def test_activation_policy(self
):
4017 for interface
in ['test1', 'vlan99']:
4018 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
4024 print(f
'### test_activation_policy(interface={interface}, test={test})')
4025 with self
.subTest(interface
=interface
, test
=test
):
4026 self
._test
_activation
_policy
(interface
, test
)
4028 def _test_activation_policy_required_for_online(self
, policy
, required
):
4029 conffile
= '25-activation-policy.network'
4030 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
4032 units
+= [f
'{conffile}.d/{policy}.conf']
4034 units
+= [f
'{conffile}.d/required-{required}.conf']
4035 copy_network_unit(*units
, copy_dropins
=False)
4038 if policy
.endswith('down'):
4039 self
.wait_activated('test1')
4041 if policy
.endswith('down') or policy
== 'manual':
4042 self
.wait_operstate('test1', 'off', setup_state
='configuring')
4044 self
.wait_online('test1')
4046 if policy
== 'always-down':
4047 # if always-down, required for online is forced to no
4050 # otherwise if required for online is specified, it should match that
4051 expected
= required
== 'yes'
4053 # otherwise if only policy specified, required for online defaults to
4054 # true if policy is up, always-up, or bound
4055 expected
= policy
.endswith('up') or policy
== 'bound'
4057 # default is true, if neither are specified
4060 output
= networkctl_status('test1')
4063 yesno
= 'yes' if expected
else 'no'
4064 self
.assertRegex(output
, f
'Required For Online: {yesno}')
4066 def test_activation_policy_required_for_online(self
):
4068 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
4069 for required
in ['yes', 'no', '']:
4075 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
4076 with self
.subTest(policy
=policy
, required
=required
):
4077 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
4079 def test_domain(self
):
4080 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
4082 self
.wait_online('dummy98:routable')
4084 output
= networkctl_status('dummy98')
4086 self
.assertRegex(output
, 'Address: 192.168.42.100')
4087 self
.assertRegex(output
, 'DNS: 192.168.42.1')
4088 self
.assertRegex(output
, 'Search Domains: one')
4090 def test_keep_configuration_static(self
):
4091 check_output('ip link add name dummy98 type dummy')
4092 check_output('ip address add 10.1.2.3/16 dev dummy98')
4093 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
4094 output
= check_output('ip address show dummy98')
4096 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4097 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4098 output
= check_output('ip route show dev dummy98')
4101 copy_network_unit('24-keep-configuration-static.network')
4103 self
.wait_online('dummy98:routable')
4105 output
= check_output('ip address show dummy98')
4107 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4108 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4110 def check_nexthop(self
, manage_foreign_nexthops
, first
):
4111 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
4113 output
= check_output('ip nexthop list dev veth99')
4116 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
4117 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
4119 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
4120 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
4121 self
.assertIn('id 3 dev veth99', output
)
4122 self
.assertIn('id 4 dev veth99', output
)
4124 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
4126 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
4127 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
4128 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
4129 if manage_foreign_nexthops
:
4130 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
4132 output
= check_output('ip nexthop list dev dummy98')
4135 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4137 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4138 if manage_foreign_nexthops
:
4139 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4141 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4143 # kernel manages blackhole nexthops on lo
4144 output
= check_output('ip nexthop list dev lo')
4147 self
.assertIn('id 6 blackhole', output
)
4148 self
.assertIn('id 7 blackhole', output
)
4150 self
.assertIn('id 1 blackhole', output
)
4151 self
.assertIn('id 2 blackhole', output
)
4153 # group nexthops are shown with -0 option
4155 output
= check_output('ip -0 nexthop list id 21')
4157 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4159 output
= check_output('ip -0 nexthop list id 20')
4161 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4163 output
= check_output('ip route show dev veth99 10.10.10.10')
4166 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4168 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4170 output
= check_output('ip route show dev veth99 10.10.10.11')
4173 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4175 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4177 output
= check_output('ip route show dev veth99 10.10.10.12')
4180 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4182 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4184 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4187 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4189 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4191 output
= check_output('ip route show 10.10.10.13')
4194 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4196 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4198 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4201 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4203 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4205 output
= check_output('ip route show 10.10.10.14')
4208 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4209 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4211 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4212 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4213 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4215 output
= networkctl_json()
4217 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4219 def _test_nexthop(self
, manage_foreign_nexthops
):
4220 if not manage_foreign_nexthops
:
4221 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4223 check_output('ip link add dummy98 type dummy')
4224 check_output('ip link set dummy98 up')
4225 check_output('ip address add 192.168.20.20/24 dev dummy98')
4226 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4228 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4229 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4232 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4234 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4235 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4237 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4239 remove_network_unit('25-nexthop-2.network')
4240 copy_network_unit('25-nexthop-nothing.network')
4242 self
.wait_online('veth99:routable', 'veth-peer:routable')
4244 output
= check_output('ip nexthop list dev veth99')
4246 self
.assertEqual(output
, '')
4247 output
= check_output('ip nexthop list dev lo')
4249 self
.assertEqual(output
, '')
4251 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4252 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4253 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4254 # here to test reconfiguring with different .network files does not trigger race.
4255 # See also comments in link_drop_requests().
4256 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4257 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4259 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4261 # Remove nexthop with ID 20
4262 check_output('ip nexthop del id 20')
4263 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4266 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4267 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4268 # hence test1 should be stuck in the configuring state.
4269 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4271 # Wait for a while, and check if the interface is still in the configuring state.
4273 output
= networkctl_status('test1')
4274 self
.assertIn('State: routable (configuring)', output
)
4276 # Check if the route which needs nexthop 20 and 21 are forgotten.
4277 output
= networkctl_json()
4279 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4281 # Reconfigure the interface that has nexthop with ID 20 and 21,
4282 # then the route requested by test1 can be configured.
4283 networkctl_reconfigure('dummy98')
4284 self
.wait_online('test1:routable')
4286 # Check if the requested route actually configured.
4287 output
= check_output('ip route show 10.10.11.10')
4289 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4290 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4291 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4293 remove_link('veth99')
4296 output
= check_output('ip nexthop list dev lo')
4298 self
.assertEqual(output
, '')
4300 @expectedFailureIfNexthopIsNotAvailable()
4301 def test_nexthop(self
):
4303 for manage_foreign_nexthops
in [True, False]:
4309 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4310 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4311 self
._test
_nexthop
(manage_foreign_nexthops
)
4313 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4321 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4322 def test_qdisc_cake(self
):
4323 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4325 self
.wait_online('dummy98:routable')
4327 output
= check_output('tc qdisc show dev dummy98')
4329 self
.assertIn('qdisc cake 3a: root', output
)
4330 self
.assertIn('bandwidth 500Mbit', output
)
4331 self
.assertIn('autorate-ingress', output
)
4332 self
.assertIn('diffserv8', output
)
4333 self
.assertIn('dual-dsthost', output
)
4334 self
.assertIn(' nat', output
)
4335 self
.assertIn(' wash', output
)
4336 self
.assertIn(' split-gso', output
)
4337 self
.assertIn(' raw', output
)
4338 self
.assertIn(' atm', output
)
4339 self
.assertIn('overhead 128', output
)
4340 self
.assertIn('mpu 20', output
)
4341 self
.assertIn('fwmark 0xff00', output
)
4342 self
.assertIn('rtt 1s', output
)
4343 self
.assertIn('ack-filter-aggressive', output
)
4345 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4346 def test_qdisc_codel(self
):
4347 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4349 self
.wait_online('dummy98:routable')
4351 output
= check_output('tc qdisc show dev dummy98')
4353 self
.assertRegex(output
, 'qdisc codel 33: root')
4354 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4356 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4357 def test_qdisc_drr(self
):
4358 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4360 self
.wait_online('dummy98:routable')
4362 output
= check_output('tc qdisc show dev dummy98')
4364 self
.assertRegex(output
, 'qdisc drr 2: root')
4365 output
= check_output('tc class show dev dummy98')
4367 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4369 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4370 def test_qdisc_ets(self
):
4371 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4373 self
.wait_online('dummy98:routable')
4375 output
= check_output('tc qdisc show dev dummy98')
4378 self
.assertRegex(output
, 'qdisc ets 3a: root')
4379 self
.assertRegex(output
, 'bands 10 strict 3')
4380 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4381 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4383 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4384 def test_qdisc_fq(self
):
4385 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4387 self
.wait_online('dummy98:routable')
4389 output
= check_output('tc qdisc show dev dummy98')
4391 self
.assertRegex(output
, 'qdisc fq 32: root')
4392 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4393 self
.assertRegex(output
, 'quantum 1500')
4394 self
.assertRegex(output
, 'initial_quantum 13000')
4395 self
.assertRegex(output
, 'maxrate 1Mbit')
4397 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4398 def test_qdisc_fq_codel(self
):
4399 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4401 self
.wait_online('dummy98:routable')
4403 output
= check_output('tc qdisc show dev dummy98')
4405 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4406 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')
4408 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4409 def test_qdisc_fq_pie(self
):
4410 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4412 self
.wait_online('dummy98:routable')
4414 output
= check_output('tc qdisc show dev dummy98')
4417 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4418 self
.assertRegex(output
, 'limit 200000p')
4420 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4421 def test_qdisc_gred(self
):
4422 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4424 self
.wait_online('dummy98:routable')
4426 output
= check_output('tc qdisc show dev dummy98')
4428 self
.assertRegex(output
, 'qdisc gred 38: root')
4429 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4431 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4432 def test_qdisc_hhf(self
):
4433 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4435 self
.wait_online('dummy98:routable')
4437 output
= check_output('tc qdisc show dev dummy98')
4439 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4440 self
.assertRegex(output
, 'limit 1022p')
4442 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4443 def test_qdisc_htb_fifo(self
):
4444 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4446 self
.wait_online('dummy98:routable')
4448 output
= check_output('tc qdisc show dev dummy98')
4450 self
.assertRegex(output
, 'qdisc htb 2: root')
4451 self
.assertRegex(output
, r
'default (0x30|30)')
4453 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4454 self
.assertRegex(output
, 'limit 100000p')
4456 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4457 self
.assertRegex(output
, 'limit 1000000')
4459 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4460 self
.assertRegex(output
, 'limit 1023p')
4462 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4464 output
= check_output('tc -d class show dev dummy98')
4466 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4467 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4468 # which is fixed in v6.3.0 by
4469 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4470 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4471 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4472 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4473 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4474 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4475 self
.assertRegex(output
, 'burst 123456')
4476 self
.assertRegex(output
, 'cburst 123457')
4478 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4479 def test_qdisc_ingress(self
):
4480 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4481 '25-qdisc-ingress.network', '11-dummy.netdev')
4483 self
.wait_online('dummy98:routable', 'test1:routable')
4485 output
= check_output('tc qdisc show dev dummy98')
4487 self
.assertRegex(output
, 'qdisc clsact')
4489 output
= check_output('tc qdisc show dev test1')
4491 self
.assertRegex(output
, 'qdisc ingress')
4493 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4494 def test_qdisc_netem(self
):
4495 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4496 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4498 self
.wait_online('dummy98:routable', 'test1:routable')
4500 output
= check_output('tc qdisc show dev dummy98')
4502 self
.assertRegex(output
, 'qdisc netem 30: root')
4503 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4505 output
= check_output('tc qdisc show dev test1')
4507 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4508 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4510 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4511 def test_qdisc_pie(self
):
4512 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4514 self
.wait_online('dummy98:routable')
4516 output
= check_output('tc qdisc show dev dummy98')
4518 self
.assertRegex(output
, 'qdisc pie 3a: root')
4519 self
.assertRegex(output
, 'limit 200000')
4521 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4522 def test_qdisc_qfq(self
):
4523 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4525 self
.wait_online('dummy98:routable')
4527 output
= check_output('tc qdisc show dev dummy98')
4529 self
.assertRegex(output
, 'qdisc qfq 2: root')
4530 output
= check_output('tc class show dev dummy98')
4532 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4533 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4535 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4536 def test_qdisc_sfb(self
):
4537 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4539 self
.wait_online('dummy98:routable')
4541 output
= check_output('tc qdisc show dev dummy98')
4543 self
.assertRegex(output
, 'qdisc sfb 39: root')
4544 self
.assertRegex(output
, 'limit 200000')
4546 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4547 def test_qdisc_sfq(self
):
4548 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4550 self
.wait_online('dummy98:routable')
4552 output
= check_output('tc qdisc show dev dummy98')
4554 self
.assertRegex(output
, 'qdisc sfq 36: root')
4555 self
.assertRegex(output
, 'perturb 5sec')
4557 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4558 def test_qdisc_tbf(self
):
4559 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4561 self
.wait_online('dummy98:routable')
4563 output
= check_output('tc qdisc show dev dummy98')
4565 self
.assertRegex(output
, 'qdisc tbf 35: root')
4566 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4568 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4569 def test_qdisc_teql(self
):
4570 call_quiet('rmmod sch_teql')
4572 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4574 self
.wait_links('dummy98')
4575 check_output('modprobe sch_teql max_equalizers=2')
4576 self
.wait_online('dummy98:routable')
4578 output
= check_output('tc qdisc show dev dummy98')
4580 self
.assertRegex(output
, 'qdisc teql1 31: root')
4582 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4590 def test_state_file(self
):
4591 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4593 self
.wait_online('dummy98:routable')
4595 # make link state file updated
4596 resolvectl('revert', 'dummy98')
4598 check_json(networkctl_json())
4600 output
= read_link_state_file('dummy98')
4602 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4603 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4604 self
.assertIn('ADMIN_STATE=configured', output
)
4605 self
.assertIn('OPER_STATE=routable', output
)
4606 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4607 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4608 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4609 self
.assertIn('ACTIVATION_POLICY=up', output
)
4610 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4611 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4612 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4613 self
.assertIn('DOMAINS=hogehoge', output
)
4614 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4615 self
.assertIn('LLMNR=no', output
)
4616 self
.assertIn('MDNS=yes', output
)
4617 self
.assertIn('DNSSEC=no', output
)
4619 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4620 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4621 resolvectl('llmnr', 'dummy98', 'yes')
4622 resolvectl('mdns', 'dummy98', 'no')
4623 resolvectl('dnssec', 'dummy98', 'yes')
4624 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4626 check_json(networkctl_json())
4628 output
= read_link_state_file('dummy98')
4630 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4631 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4632 self
.assertIn('DOMAINS=hogehogehoge', output
)
4633 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4634 self
.assertIn('LLMNR=yes', output
)
4635 self
.assertIn('MDNS=no', output
)
4636 self
.assertIn('DNSSEC=yes', output
)
4638 timedatectl('revert', 'dummy98')
4640 check_json(networkctl_json())
4642 output
= read_link_state_file('dummy98')
4644 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4645 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4646 self
.assertIn('DOMAINS=hogehogehoge', output
)
4647 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4648 self
.assertIn('LLMNR=yes', output
)
4649 self
.assertIn('MDNS=no', output
)
4650 self
.assertIn('DNSSEC=yes', output
)
4652 resolvectl('revert', 'dummy98')
4654 check_json(networkctl_json())
4656 output
= read_link_state_file('dummy98')
4658 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4659 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4660 self
.assertIn('DOMAINS=hogehoge', output
)
4661 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4662 self
.assertIn('LLMNR=no', output
)
4663 self
.assertIn('MDNS=yes', output
)
4664 self
.assertIn('DNSSEC=no', output
)
4666 def test_address_state(self
):
4667 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4670 self
.wait_online('dummy98:degraded')
4672 output
= read_link_state_file('dummy98')
4673 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4674 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4676 # with a routable IPv4 address
4677 check_output('ip address add 10.1.2.3/16 dev dummy98')
4678 self
.wait_online('dummy98:routable', ipv4
=True)
4679 self
.wait_online('dummy98:routable')
4681 output
= read_link_state_file('dummy98')
4682 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4683 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4685 check_output('ip address del 10.1.2.3/16 dev dummy98')
4687 # with a routable IPv6 address
4688 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4689 self
.wait_online('dummy98:routable', ipv6
=True)
4690 self
.wait_online('dummy98:routable')
4692 output
= read_link_state_file('dummy98')
4693 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4694 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4696 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4704 def test_bond_keep_master(self
):
4705 check_output('ip link add bond199 type bond mode active-backup')
4706 check_output('ip link add dummy98 type dummy')
4707 check_output('ip link set dummy98 master bond199')
4709 copy_network_unit('23-keep-master.network')
4711 self
.wait_online('dummy98:enslaved')
4713 output
= check_output('ip -d link show bond199')
4715 self
.assertRegex(output
, 'active_slave dummy98')
4717 output
= check_output('ip -d link show dummy98')
4719 self
.assertRegex(output
, 'master bond199')
4721 def test_bond_active_slave(self
):
4722 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4724 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4726 output
= check_output('ip -d link show bond199')
4728 self
.assertIn('active_slave dummy98', output
)
4730 # test case for issue #31165.
4731 since
= datetime
.datetime
.now()
4732 networkctl_reconfigure('dummy98')
4733 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4734 self
.assertNotIn('dummy98: Bringing link down', read_networkd_log(since
=since
))
4736 def test_bond_primary_slave(self
):
4737 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4739 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4741 output
= check_output('ip -d link show bond199')
4743 self
.assertIn('primary dummy98', output
)
4746 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4747 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4748 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4749 f
.write(f
'[Link]\nMACAddress={mac}\n')
4752 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4754 output
= check_output('ip -d link show bond199')
4756 self
.assertIn(f
'link/ether {mac}', output
)
4758 def test_bond_operstate(self
):
4759 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4760 '25-bond99.network', '25-bond-slave.network')
4762 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
4764 output
= check_output('ip -d link show dummy98')
4766 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4768 output
= check_output('ip -d link show test1')
4770 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4772 output
= check_output('ip -d link show bond99')
4774 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4776 self
.wait_operstate('dummy98', 'enslaved')
4777 self
.wait_operstate('test1', 'enslaved')
4778 self
.wait_operstate('bond99', 'routable')
4780 check_output('ip link set dummy98 down')
4782 self
.wait_operstate('dummy98', 'off')
4783 self
.wait_operstate('test1', 'enslaved')
4784 self
.wait_operstate('bond99', 'routable')
4786 check_output('ip link set dummy98 up')
4788 self
.wait_operstate('dummy98', 'enslaved')
4789 self
.wait_operstate('test1', 'enslaved')
4790 self
.wait_operstate('bond99', 'routable')
4792 check_output('ip link set dummy98 down')
4793 check_output('ip link set test1 down')
4795 self
.wait_operstate('dummy98', 'off')
4796 self
.wait_operstate('test1', 'off')
4798 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4799 # Huh? Kernel does not recognize that all slave interfaces are down?
4800 # Let's confirm that networkd's operstate is consistent with ip's result.
4801 self
.assertNotRegex(output
, 'NO-CARRIER')
4803 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4811 def test_bridge_mac_none(self
):
4812 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4813 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4815 self
.wait_online('dummy98:enslaved', 'bridge99:degraded')
4817 output
= check_output('ip link show dev dummy98')
4819 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4821 output
= check_output('ip link show dev bridge99')
4823 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4825 def test_bridge_vlan(self
):
4826 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4827 '26-bridge.netdev', '26-bridge-vlan-master.network',
4830 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4832 output
= check_output('bridge vlan show dev test1')
4834 # check if the default VID is removed
4835 self
.assertNotIn('1 Egress Untagged', output
)
4836 for i
in range(1000, 3000):
4838 self
.assertIn(f
'{i} PVID', output
)
4839 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4840 self
.assertIn(f
'{i} Egress Untagged', output
)
4841 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4842 self
.assertIn(f
'{i}', output
)
4844 self
.assertNotIn(f
'{i}', output
)
4846 output
= check_output('bridge vlan show dev bridge99')
4848 # check if the default VID is removed
4849 self
.assertNotIn('1 Egress Untagged', output
)
4850 for i
in range(1000, 3000):
4852 self
.assertIn(f
'{i} PVID', output
)
4853 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4854 self
.assertIn(f
'{i} Egress Untagged', output
)
4855 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4856 self
.assertIn(f
'{i}', output
)
4858 self
.assertNotIn(f
'{i}', output
)
4861 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4862 '26-bridge-vlan-master.network.d/10-override.conf')
4864 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4866 output
= check_output('bridge vlan show dev test1')
4868 for i
in range(1000, 3000):
4870 self
.assertIn(f
'{i} PVID', output
)
4871 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4872 self
.assertIn(f
'{i} Egress Untagged', output
)
4873 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4874 self
.assertIn(f
'{i}', output
)
4876 self
.assertNotIn(f
'{i}', output
)
4878 output
= check_output('bridge vlan show dev bridge99')
4880 for i
in range(1000, 3000):
4882 self
.assertIn(f
'{i} PVID', output
)
4883 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4884 self
.assertIn(f
'{i} Egress Untagged', output
)
4885 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4886 self
.assertIn(f
'{i}', output
)
4888 self
.assertNotIn(f
'{i}', output
)
4890 # Remove several vlan IDs
4891 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4892 '26-bridge-vlan-master.network.d/20-override.conf')
4894 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4896 output
= check_output('bridge vlan show dev test1')
4898 for i
in range(1000, 3000):
4900 self
.assertIn(f
'{i} PVID', output
)
4901 elif i
in range(2012, 2016):
4902 self
.assertIn(f
'{i} Egress Untagged', output
)
4903 elif i
in range(2008, 2014):
4904 self
.assertIn(f
'{i}', output
)
4906 self
.assertNotIn(f
'{i}', output
)
4908 output
= check_output('bridge vlan show dev bridge99')
4910 for i
in range(1000, 3000):
4912 self
.assertIn(f
'{i} PVID', output
)
4913 elif i
in range(2022, 2026):
4914 self
.assertIn(f
'{i} Egress Untagged', output
)
4915 elif i
in range(2018, 2024):
4916 self
.assertIn(f
'{i}', output
)
4918 self
.assertNotIn(f
'{i}', output
)
4920 # Remove all vlan IDs
4921 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4922 '26-bridge-vlan-master.network.d/30-override.conf')
4924 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4926 output
= check_output('bridge vlan show dev test1')
4928 self
.assertNotIn('PVID', output
)
4929 for i
in range(1000, 3000):
4930 self
.assertNotIn(f
'{i}', output
)
4932 output
= check_output('bridge vlan show dev bridge99')
4934 self
.assertNotIn('PVID', output
)
4935 for i
in range(1000, 3000):
4936 self
.assertNotIn(f
'{i}', output
)
4938 def test_bridge_vlan_issue_20373(self
):
4939 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4940 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4941 '21-vlan.netdev', '21-vlan.network')
4943 self
.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
4945 output
= check_output('bridge vlan show dev test1')
4947 self
.assertIn('100 PVID Egress Untagged', output
)
4948 self
.assertIn('560', output
)
4949 self
.assertIn('600', output
)
4951 output
= check_output('bridge vlan show dev bridge99')
4953 self
.assertIn('1 PVID Egress Untagged', output
)
4954 self
.assertIn('100', output
)
4955 self
.assertIn('600', output
)
4957 def test_bridge_mdb(self
):
4958 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4959 '26-bridge.netdev', '26-bridge-mdb-master.network')
4961 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4963 output
= check_output('bridge mdb show dev bridge99')
4965 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4966 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4968 # Old kernel may not support bridge MDB entries on bridge master
4969 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4970 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4971 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4973 def test_bridge_keep_master(self
):
4974 check_output('ip link add bridge99 type bridge')
4975 check_output('ip link set bridge99 up')
4976 check_output('ip link add dummy98 type dummy')
4977 check_output('ip link set dummy98 master bridge99')
4979 copy_network_unit('23-keep-master.network')
4981 self
.wait_online('dummy98:enslaved')
4983 output
= check_output('ip -d link show dummy98')
4985 self
.assertRegex(output
, 'master bridge99')
4986 self
.assertRegex(output
, 'bridge')
4988 output
= check_output('bridge -d link show dummy98')
4990 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4991 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4992 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4993 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4994 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4995 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4996 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4997 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4998 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4999 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
5000 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5001 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
5003 def test_bridge_property(self
):
5004 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5005 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5006 '25-bridge99.network')
5008 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5010 output
= check_output('ip -d link show bridge99')
5012 self
.assertIn('mtu 9000 ', output
)
5014 output
= check_output('ip -d link show test1')
5016 self
.assertIn('master bridge99 ', output
)
5017 self
.assertIn('bridge_slave', output
)
5018 self
.assertIn('mtu 9000 ', output
)
5020 output
= check_output('ip -d link show dummy98')
5022 self
.assertIn('master bridge99 ', output
)
5023 self
.assertIn('bridge_slave', output
)
5024 self
.assertIn('mtu 9000 ', output
)
5026 output
= check_output('ip addr show bridge99')
5028 self
.assertIn('192.168.0.15/24', output
)
5030 output
= check_output('bridge -d link show dummy98')
5032 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5033 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5034 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
5035 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5036 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5037 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5038 # CONFIG_BRIDGE_IGMP_SNOOPING=y
5039 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
5040 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
5041 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5042 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
5043 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5044 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
5046 output
= check_output('bridge -d link show test1')
5048 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
5050 check_output('ip address add 192.168.0.16/24 dev bridge99')
5051 output
= check_output('ip addr show bridge99')
5053 self
.assertIn('192.168.0.16/24', output
)
5056 print('### ip -6 route list table all dev bridge99')
5057 output
= check_output('ip -6 route list table all dev bridge99')
5059 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5061 remove_link('test1')
5062 self
.wait_operstate('bridge99', 'routable')
5064 output
= check_output('ip -d link show bridge99')
5066 self
.assertIn('mtu 9000 ', output
)
5068 output
= check_output('ip -d link show dummy98')
5070 self
.assertIn('master bridge99 ', output
)
5071 self
.assertIn('bridge_slave', output
)
5072 self
.assertIn('mtu 9000 ', output
)
5074 remove_link('dummy98')
5075 self
.wait_operstate('bridge99', 'no-carrier')
5077 output
= check_output('ip -d link show bridge99')
5079 # When no carrier, the kernel may reset the MTU
5080 self
.assertIn('NO-CARRIER', output
)
5082 output
= check_output('ip address show bridge99')
5084 self
.assertNotIn('192.168.0.15/24', output
)
5085 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
5087 print('### ip -6 route list table all dev bridge99')
5088 output
= check_output('ip -6 route list table all dev bridge99')
5090 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5092 check_output('ip link add dummy98 type dummy')
5093 self
.wait_online('dummy98:enslaved', 'bridge99:routable')
5095 output
= check_output('ip -d link show bridge99')
5097 self
.assertIn('mtu 9000 ', output
)
5099 output
= check_output('ip -d link show dummy98')
5101 self
.assertIn('master bridge99 ', output
)
5102 self
.assertIn('bridge_slave', output
)
5103 self
.assertIn('mtu 9000 ', output
)
5105 def test_bridge_configure_without_carrier(self
):
5106 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
5110 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5111 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
5112 with self
.subTest(test
=test
):
5113 if test
== 'no-slave':
5114 # bridge has no slaves; it's up but *might* not have carrier
5115 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
5116 # due to a bug in the kernel, newly-created bridges are brought up
5117 # *with* carrier, unless they have had any setting changed; e.g.
5118 # their mac set, priority set, etc. Then, they will lose carrier
5119 # as soon as a (down) slave interface is added, and regain carrier
5120 # again once the slave interface is brought up.
5121 #self.check_link_attr('bridge99', 'carrier', '0')
5122 elif test
== 'add-slave':
5123 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5124 self
.check_link_attr('test1', 'operstate', 'down')
5125 check_output('ip link set dev test1 master bridge99')
5126 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
5127 self
.check_link_attr('bridge99', 'carrier', '0')
5128 elif test
== 'slave-up':
5129 # bring up slave, which will have carrier; bridge gains carrier
5130 check_output('ip link set dev test1 up')
5131 self
.wait_online('bridge99:routable')
5132 self
.check_link_attr('bridge99', 'carrier', '1')
5133 elif test
== 'slave-no-carrier':
5134 # drop slave carrier; bridge loses carrier
5135 check_output('ip link set dev test1 carrier off')
5136 self
.wait_online('bridge99:no-carrier:no-carrier')
5137 self
.check_link_attr('bridge99', 'carrier', '0')
5138 elif test
== 'slave-carrier':
5139 # restore slave carrier; bridge gains carrier
5140 check_output('ip link set dev test1 carrier on')
5141 self
.wait_online('bridge99:routable')
5142 self
.check_link_attr('bridge99', 'carrier', '1')
5143 elif test
== 'slave-down':
5144 # bring down slave; bridge loses carrier
5145 check_output('ip link set dev test1 down')
5146 self
.wait_online('bridge99:no-carrier:no-carrier')
5147 self
.check_link_attr('bridge99', 'carrier', '0')
5149 output
= networkctl_status('bridge99')
5150 self
.assertRegex(output
, '10.1.2.3')
5151 self
.assertRegex(output
, '10.1.2.1')
5153 def test_bridge_ignore_carrier_loss(self
):
5154 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5155 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5156 '25-bridge99-ignore-carrier-loss.network')
5158 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5160 check_output('ip address add 192.168.0.16/24 dev bridge99')
5161 remove_link('test1', 'dummy98')
5164 output
= check_output('ip address show bridge99')
5166 self
.assertRegex(output
, 'NO-CARRIER')
5167 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5168 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5170 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5171 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5172 '25-bridge99-ignore-carrier-loss.network')
5174 self
.wait_online('bridge99:no-carrier')
5176 for trial
in range(4):
5177 check_output('ip link add dummy98 type dummy')
5178 check_output('ip link set dummy98 up')
5180 remove_link('dummy98')
5182 self
.wait_online('bridge99:routable', 'dummy98:enslaved')
5184 output
= check_output('ip address show bridge99')
5186 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5188 output
= check_output('ip rule list table 100')
5190 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5192 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5200 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5201 def test_sriov(self
):
5202 copy_network_unit('25-default.link', '25-sriov.network')
5204 call('modprobe netdevsim')
5206 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5209 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5213 self
.wait_online('eni99np1:routable')
5215 output
= check_output('ip link show dev eni99np1')
5217 self
.assertRegex(output
,
5218 '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 *'
5219 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5220 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5223 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5224 def test_sriov_udev(self
):
5225 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5227 call('modprobe netdevsim')
5229 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5233 self
.wait_online('eni99np1:routable')
5235 # the name eni99np1 may be an alternative name.
5236 ifname
= link_resolve('eni99np1')
5238 output
= check_output('ip link show dev eni99np1')
5240 self
.assertRegex(output
,
5241 '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 *'
5242 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5243 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5245 self
.assertNotIn('vf 3', output
)
5246 self
.assertNotIn('vf 4', output
)
5248 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5249 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5252 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5254 output
= check_output('ip link show dev eni99np1')
5256 self
.assertRegex(output
,
5257 '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 *'
5258 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5259 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5262 self
.assertNotIn('vf 4', output
)
5264 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5265 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5268 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5270 output
= check_output('ip link show dev eni99np1')
5272 self
.assertRegex(output
,
5273 '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 *'
5274 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5275 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5278 self
.assertNotIn('vf 4', output
)
5280 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5281 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5284 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5286 output
= check_output('ip link show dev eni99np1')
5288 self
.assertRegex(output
,
5289 '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 *'
5290 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5292 self
.assertNotIn('vf 2', output
)
5293 self
.assertNotIn('vf 3', output
)
5294 self
.assertNotIn('vf 4', output
)
5296 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5297 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5300 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5302 output
= check_output('ip link show dev eni99np1')
5304 self
.assertRegex(output
,
5305 '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 *'
5306 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5307 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5309 self
.assertNotIn('vf 3', output
)
5310 self
.assertNotIn('vf 4', output
)
5312 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5320 def test_lldp(self
):
5321 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5323 self
.wait_online('veth99:degraded', 'veth-peer:degraded')
5325 for trial
in range(10):
5329 output
= networkctl('lldp')
5331 if re
.search(r
'veth99 .* veth-peer', output
):
5336 # With interface name
5337 output
= networkctl('lldp', 'veth99');
5339 self
.assertRegex(output
, r
'veth99 .* veth-peer')
5341 # With interface name pattern
5342 output
= networkctl('lldp', 've*9');
5344 self
.assertRegex(output
, r
'veth99 .* veth-peer')
5347 output
= networkctl('--json=short', 'lldp')
5349 self
.assertIn('"InterfaceName":"veth99"', output
)
5350 self
.assertIn('"PortID":"veth-peer"', output
)
5352 # json format with interface name
5353 output
= networkctl('--json=short', 'lldp', 'veth99')
5355 self
.assertIn('"InterfaceName":"veth99"', output
)
5356 self
.assertIn('"PortID":"veth-peer"', output
)
5358 # json format with interface name pattern
5359 output
= networkctl('--json=short', 'lldp', 've*9')
5361 self
.assertIn('"InterfaceName":"veth99"', output
)
5362 self
.assertIn('"PortID":"veth-peer"', output
)
5364 # LLDP neighbors in status
5365 output
= networkctl_status('veth99')
5367 self
.assertRegex(output
, r
'Connected To: .* on port veth-peer')
5369 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5377 def test_ipv6_prefix_delegation(self
):
5378 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5379 self
.setup_nftset('addr6', 'ipv6_addr')
5380 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5381 self
.setup_nftset('ifindex', 'iface_index')
5383 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5385 # IPv6SendRA=yes implies IPv6Forwarding.
5386 self
.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5388 output
= resolvectl('dns', 'veth99')
5390 self
.assertRegex(output
, 'fe80::')
5391 self
.assertRegex(output
, '2002:da8:1::1')
5393 output
= resolvectl('domain', 'veth99')
5395 self
.assertIn('hogehoge.test', output
)
5397 output
= networkctl_status('veth99')
5399 self
.assertRegex(output
, '2002:da8:1:0')
5401 self
.check_netlabel('veth99', '2002:da8:1::/64')
5402 self
.check_netlabel('veth99', '2002:da8:2::/64')
5404 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5405 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5406 self
.check_nftset('network6', '2002:da8:1::/64')
5407 self
.check_nftset('network6', '2002:da8:2::/64')
5408 self
.check_nftset('ifindex', 'veth99')
5410 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5412 def check_ipv6_token_static(self
):
5413 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5415 output
= networkctl_status('veth99')
5417 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5418 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5419 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5420 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5422 def test_ipv6_token_static(self
):
5423 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5426 self
.check_ipv6_token_static()
5429 check_output('ip link set veth99 down')
5430 check_output('ip link set veth99 up')
5432 self
.check_ipv6_token_static()
5435 check_output('ip link set veth99 down')
5436 time
.sleep(random
.uniform(0, 0.1))
5437 check_output('ip link set veth99 up')
5438 time
.sleep(random
.uniform(0, 0.1))
5440 self
.check_ipv6_token_static()
5442 def test_ipv6_token_prefixstable(self
):
5443 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5445 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5447 output
= networkctl_status('veth99')
5449 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5450 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5452 def test_ipv6_token_prefixstable_without_address(self
):
5453 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5455 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5457 output
= networkctl_status('veth99')
5459 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5460 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5462 def check_router_hop_limit(self
, hop_limit
):
5463 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5465 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5467 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5469 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5471 def test_router_hop_limit(self
):
5472 copy_network_unit('25-veth-client.netdev',
5473 '25-veth-router.netdev',
5475 '25-veth-bridge.network',
5476 '25-veth-client.network',
5477 '25-veth-router-hop-limit.network',
5478 '25-bridge99.network')
5480 self
.wait_online('client-p:enslaved',
5481 'router:degraded', 'router-p:enslaved',
5482 'bridge99:routable')
5484 self
.check_router_hop_limit(42)
5486 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5487 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5491 self
.check_router_hop_limit(43)
5493 def test_router_preference(self
):
5494 copy_network_unit('25-veth-client.netdev',
5495 '25-veth-router-high.netdev',
5496 '25-veth-router-low.netdev',
5498 '25-veth-bridge.network',
5499 '25-veth-client.network',
5500 '25-veth-router-high.network',
5501 '25-veth-router-low.network',
5502 '25-bridge99.network')
5504 self
.wait_online('client-p:enslaved',
5505 'router-high:degraded', 'router-high-p:enslaved',
5506 'router-low:degraded', 'router-low-p:enslaved',
5507 'bridge99:routable')
5509 networkctl_reconfigure('client')
5510 self
.wait_online('client:routable')
5512 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5513 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5514 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5515 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5517 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5519 self
.assertIn('metric 512', output
)
5520 self
.assertIn('pref high', output
)
5521 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5523 self
.assertIn('metric 2048', output
)
5524 self
.assertIn('pref low', output
)
5526 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5527 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5530 self
.wait_online('client:routable')
5532 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5533 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5534 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5535 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5537 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5539 self
.assertIn('metric 100', output
)
5540 self
.assertNotIn('metric 512', output
)
5541 self
.assertIn('pref high', output
)
5542 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5544 self
.assertIn('metric 300', output
)
5545 self
.assertNotIn('metric 2048', output
)
5546 self
.assertIn('pref low', output
)
5548 # swap the preference (for issue #28439)
5549 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5550 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5552 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5553 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5555 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5557 self
.assertIn('metric 300', output
)
5558 self
.assertNotIn('metric 100', output
)
5559 self
.assertIn('pref low', output
)
5560 self
.assertNotIn('pref high', output
)
5561 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5563 self
.assertIn('metric 100', output
)
5564 self
.assertNotIn('metric 300', output
)
5565 self
.assertIn('pref high', output
)
5566 self
.assertNotIn('pref low', output
)
5568 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5569 def test_captive_portal(self
):
5570 copy_network_unit('25-veth-client.netdev',
5571 '25-veth-router-captive.netdev',
5573 '25-veth-client-captive.network',
5574 '25-veth-router-captive.network',
5575 '25-veth-bridge-captive.network',
5576 '25-bridge99.network')
5578 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5579 'router-captive:degraded', 'router-captivep:enslaved')
5581 start_radvd(config_file
='captive-portal.conf')
5582 networkctl_reconfigure('client')
5583 self
.wait_online('client:routable')
5585 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5586 output
= networkctl_status('client')
5588 self
.assertIn('Captive Portal: http://systemd.io', output
)
5590 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5591 def test_invalid_captive_portal(self
):
5592 def radvd_write_config(captive_portal_uri
):
5593 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5594 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5596 captive_portal_uris
= [
5597 "42ěščěškd ěšč ě s",
5602 copy_network_unit('25-veth-client.netdev',
5603 '25-veth-router-captive.netdev',
5605 '25-veth-client-captive.network',
5606 '25-veth-router-captive.network',
5607 '25-veth-bridge-captive.network',
5608 '25-bridge99.network')
5610 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5611 'router-captive:degraded', 'router-captivep:enslaved')
5613 for uri
in captive_portal_uris
:
5614 print(f
"Captive portal: {uri}")
5615 radvd_write_config(uri
)
5617 start_radvd(config_file
='bogus-captive-portal.conf')
5618 networkctl_reconfigure('client')
5619 self
.wait_online('client:routable')
5621 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5622 output
= networkctl_status('client')
5624 self
.assertNotIn('Captive Portal:', output
)
5626 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5634 def test_dhcp_server(self
):
5635 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5637 self
.wait_online('veth99:routable', 'veth-peer:routable')
5639 output
= networkctl_status('veth99')
5641 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5642 self
.assertIn('Gateway: 192.168.5.3', output
)
5643 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5644 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5646 output
= networkctl_status('veth-peer')
5647 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5649 def test_dhcp_server_null_server_address(self
):
5650 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5652 self
.wait_online('veth99:routable', 'veth-peer:routable')
5654 output
= check_output('ip --json address show dev veth-peer')
5655 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5656 print(server_address
)
5658 output
= check_output('ip --json address show dev veth99')
5659 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5660 print(client_address
)
5662 output
= networkctl_status('veth99')
5664 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5665 self
.assertIn(f
'Gateway: {server_address}', output
)
5666 self
.assertIn(f
'DNS: {server_address}', output
)
5667 self
.assertIn(f
'NTP: {server_address}', output
)
5669 output
= networkctl_status('veth-peer')
5670 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5672 def test_dhcp_server_with_uplink(self
):
5673 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5674 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5676 self
.wait_online('veth99:routable', 'veth-peer:routable')
5678 output
= networkctl_status('veth99')
5680 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5681 self
.assertIn('Gateway: 192.168.5.3', output
)
5682 self
.assertIn('DNS: 192.168.5.1', output
)
5683 self
.assertIn('NTP: 192.168.5.1', output
)
5685 def test_emit_router_timezone(self
):
5686 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5688 self
.wait_online('veth99:routable', 'veth-peer:routable')
5690 output
= networkctl_status('veth99')
5692 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5693 self
.assertIn('Gateway: 192.168.5.1', output
)
5694 self
.assertIn('Time Zone: Europe/Berlin', output
)
5696 def test_dhcp_server_static_lease(self
):
5697 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5699 self
.wait_online('veth99:routable', 'veth-peer:routable')
5701 output
= networkctl_status('veth99')
5703 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5704 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5706 def test_dhcp_server_static_lease_default_client_id(self
):
5707 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5709 self
.wait_online('veth99:routable', 'veth-peer:routable')
5711 output
= networkctl_status('veth99')
5713 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5714 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5716 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5724 def test_relay_agent(self
):
5725 copy_network_unit('25-agent-veth-client.netdev',
5726 '25-agent-veth-server.netdev',
5727 '25-agent-client.network',
5728 '25-agent-server.network',
5729 '25-agent-client-peer.network',
5730 '25-agent-server-peer.network')
5733 self
.wait_online('client:routable')
5735 output
= networkctl_status('client')
5737 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5739 def test_relay_agent_on_bridge(self
):
5740 copy_network_unit('25-agent-bridge.netdev',
5741 '25-agent-veth-client.netdev',
5742 '25-agent-bridge.network',
5743 '25-agent-bridge-port.network',
5744 '25-agent-client.network')
5746 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5749 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5751 if expect
in read_networkd_log():
5757 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5765 def test_dhcp_client_ipv6_only(self
):
5766 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5769 self
.wait_online('veth-peer:carrier')
5771 # information request mode
5772 # The name ipv6-only option may not be supported by older dnsmasq
5773 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5774 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5775 '--dhcp-option=option6:dns-server,[2600::ee]',
5776 '--dhcp-option=option6:ntp-server,[2600::ff]',
5777 ra_mode
='ra-stateless')
5778 self
.wait_online('veth99:routable', 'veth-peer:routable')
5780 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5781 # Let's wait for the expected DNS server being listed in the state file.
5782 for _
in range(100):
5783 output
= read_link_state_file('veth99')
5784 if 'DNS=2600::ee' in output
:
5788 # Check link state file
5789 print('## link state file')
5790 output
= read_link_state_file('veth99')
5792 self
.assertIn('DNS=2600::ee', output
)
5793 self
.assertIn('NTP=2600::ff', output
)
5795 # Check manager state file
5796 print('## manager state file')
5797 output
= read_manager_state_file()
5799 self
.assertRegex(output
, 'DNS=.*2600::ee')
5800 self
.assertRegex(output
, 'NTP=.*2600::ff')
5802 print('## dnsmasq log')
5803 output
= read_dnsmasq_log_file()
5805 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5806 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5807 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5808 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5809 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5812 check_json(networkctl_json('veth99'))
5816 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5817 '--dhcp-option=option6:dns-server,[2600::ee]',
5818 '--dhcp-option=option6:ntp-server,[2600::ff]')
5819 networkctl_reconfigure('veth99')
5820 self
.wait_online('veth99:routable', 'veth-peer:routable')
5823 output
= check_output('ip address show dev veth99 scope global')
5825 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5826 self
.assertNotIn('192.168.5', output
)
5828 # checking semi-static route
5829 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5831 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5833 # Confirm that ipv6 token is not set in the kernel
5834 output
= check_output('ip token show dev veth99')
5836 self
.assertRegex(output
, 'token :: dev veth99')
5838 # Make manager and link state file updated
5839 resolvectl('revert', 'veth99')
5841 # Check link state file
5842 print('## link state file')
5843 output
= read_link_state_file('veth99')
5845 self
.assertIn('DNS=2600::ee', output
)
5846 self
.assertIn('NTP=2600::ff', output
)
5848 # Check manager state file
5849 print('## manager state file')
5850 output
= read_manager_state_file()
5852 self
.assertRegex(output
, 'DNS=.*2600::ee')
5853 self
.assertRegex(output
, 'NTP=.*2600::ff')
5855 print('## dnsmasq log')
5856 output
= read_dnsmasq_log_file()
5858 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5859 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5860 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5861 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5862 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5863 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5866 check_json(networkctl_json('veth99'))
5868 # Testing without rapid commit support
5869 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5870 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5873 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5874 '--dhcp-option=option6:dns-server,[2600::ee]',
5875 '--dhcp-option=option6:ntp-server,[2600::ff]')
5878 self
.wait_online('veth99:routable', 'veth-peer:routable')
5881 output
= check_output('ip address show dev veth99 scope global')
5883 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5884 self
.assertNotIn('192.168.5', output
)
5886 # checking semi-static route
5887 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5889 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5891 # Make manager and link state file updated
5892 resolvectl('revert', 'veth99')
5894 # Check link state file
5895 print('## link state file')
5896 output
= read_link_state_file('veth99')
5898 self
.assertIn('DNS=2600::ee', output
)
5899 self
.assertIn('NTP=2600::ff', output
)
5901 # Check manager state file
5902 print('## manager state file')
5903 output
= read_manager_state_file()
5905 self
.assertRegex(output
, 'DNS=.*2600::ee')
5906 self
.assertRegex(output
, 'NTP=.*2600::ff')
5908 print('## dnsmasq log')
5909 output
= read_dnsmasq_log_file()
5911 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5912 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5913 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5914 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5915 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5916 self
.assertNotIn('rapid-commit', output
)
5919 check_json(networkctl_json('veth99'))
5921 def test_dhcp_client_ipv6_dbus_status(self
):
5922 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5924 self
.wait_online('veth-peer:carrier')
5926 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5927 # bit set) has yet been received and the configuration does not include WithoutRA=true
5928 state
= get_dhcp6_client_state('veth99')
5929 print(f
"DHCPv6 client state = {state}")
5930 self
.assertEqual(state
, 'stopped')
5932 state
= get_dhcp4_client_state('veth99')
5933 print(f
"DHCPv4 client state = {state}")
5934 self
.assertEqual(state
, 'selecting')
5936 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5937 self
.wait_online('veth99:routable', 'veth-peer:routable')
5939 state
= get_dhcp6_client_state('veth99')
5940 print(f
"DHCPv6 client state = {state}")
5941 self
.assertEqual(state
, 'bound')
5943 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5944 for _
in range(100):
5945 state
= get_dhcp4_client_state('veth99')
5946 if state
== 'stopped':
5950 print(f
"DHCPv4 client state = {state}")
5951 self
.assertEqual(state
, 'stopped')
5953 # restart dnsmasq to clear log
5955 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5957 # Test renew command
5958 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5959 networkctl('renew', 'veth99')
5961 for _
in range(100):
5962 state
= get_dhcp4_client_state('veth99')
5963 if state
== 'stopped':
5967 print(f
"DHCPv4 client state = {state}")
5968 self
.assertEqual(state
, 'stopped')
5970 print('## dnsmasq log')
5971 output
= read_dnsmasq_log_file()
5973 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5974 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5975 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5976 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5978 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5979 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5982 self
.wait_online('veth-peer:carrier')
5984 self
.wait_online('veth99:routable', 'veth-peer:routable')
5987 output
= check_output('ip address show dev veth99 scope global')
5989 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5990 self
.assertNotIn('192.168.5', output
)
5992 print('## dnsmasq log')
5993 output
= read_dnsmasq_log_file()
5995 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5996 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5997 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5998 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5999 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
6001 def test_dhcp_client_ipv4_only(self
):
6002 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6004 self
.setup_nftset('addr4', 'ipv4_addr')
6005 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6006 self
.setup_nftset('ifindex', 'iface_index')
6009 self
.wait_online('veth-peer:carrier')
6010 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6011 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
6012 '--dhcp-option=option:domain-search,example.com',
6013 '--dhcp-alternate-port=67,5555',
6014 ipv4_range
='192.168.5.110,192.168.5.119')
6015 self
.wait_online('veth99:routable', 'veth-peer:routable')
6016 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6018 print('## ip address show dev veth99 scope global')
6019 output
= check_output('ip address show dev veth99 scope global')
6021 self
.assertIn('mtu 1492', output
)
6022 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6023 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')
6024 self
.assertNotIn('2600::', output
)
6026 output
= check_output('ip -4 --json address show dev veth99')
6027 for i
in json
.loads(output
)[0]['addr_info']:
6028 if i
['label'] == 'test-label':
6029 address1
= i
['local']
6032 self
.assertFalse(True)
6034 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
6036 print('## ip route show table main dev veth99')
6037 output
= check_output('ip route show table main dev veth99')
6039 # no DHCP routes assigned to the main table
6040 self
.assertNotIn('proto dhcp', output
)
6042 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6043 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6044 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6045 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6047 print('## ip route show table 211 dev veth99')
6048 output
= check_output('ip route show table 211 dev veth99')
6050 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6051 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6052 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6053 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6054 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6055 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6057 print('## link state file')
6058 output
= read_link_state_file('veth99')
6060 # checking DNS server, SIP server, and Domains
6061 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
6062 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
6063 self
.assertIn('DOMAINS=example.com', output
)
6066 j
= json
.loads(networkctl_json('veth99'))
6068 self
.assertEqual(len(j
['DNS']), 2)
6071 self
.assertEqual(i
['Family'], 2)
6072 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6073 self
.assertRegex(a
, '^192.168.5.[67]$')
6074 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6075 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6076 self
.assertEqual('192.168.5.1', a
)
6078 self
.assertEqual(len(j
['SIP']), 2)
6081 self
.assertEqual(i
['Family'], 2)
6082 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6083 self
.assertRegex(a
, '^192.168.5.2[12]$')
6084 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6085 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6086 self
.assertEqual('192.168.5.1', a
)
6088 print('## dnsmasq log')
6089 output
= read_dnsmasq_log_file()
6091 self
.assertIn('vendor class: FooBarVendorTest', output
)
6092 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
6093 self
.assertIn('client provides name: test-hostname', output
)
6094 self
.assertIn('26:mtu', output
)
6096 # change address range, DNS servers, and Domains
6098 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6099 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6100 '--dhcp-option=option:domain-search,foo.example.com',
6101 '--dhcp-alternate-port=67,5555',
6102 ipv4_range
='192.168.5.120,192.168.5.129',)
6104 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6105 print('Wait for the DHCP lease to be expired')
6106 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
6107 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
6109 self
.wait_online('veth99:routable', 'veth-peer:routable')
6111 print('## ip address show dev veth99 scope global')
6112 output
= check_output('ip address show dev veth99 scope global')
6114 self
.assertIn('mtu 1492', output
)
6115 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6116 self
.assertNotIn(f
'{address1}', output
)
6117 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')
6118 self
.assertNotIn('2600::', output
)
6120 output
= check_output('ip -4 --json address show dev veth99')
6121 for i
in json
.loads(output
)[0]['addr_info']:
6122 if i
['label'] == 'test-label':
6123 address2
= i
['local']
6126 self
.assertFalse(True)
6128 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
6130 print('## ip route show table main dev veth99')
6131 output
= check_output('ip route show table main dev veth99')
6133 # no DHCP routes assigned to the main table
6134 self
.assertNotIn('proto dhcp', output
)
6136 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6137 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6138 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6139 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6141 print('## ip route show table 211 dev veth99')
6142 output
= check_output('ip route show table 211 dev veth99')
6144 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6145 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6146 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6147 self
.assertNotIn('192.168.5.6', output
)
6148 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6149 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6150 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6152 print('## link state file')
6153 output
= read_link_state_file('veth99')
6155 # checking DNS server, SIP server, and Domains
6156 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
6157 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
6158 self
.assertIn('DOMAINS=foo.example.com', output
)
6161 j
= json
.loads(networkctl_json('veth99'))
6163 self
.assertEqual(len(j
['DNS']), 3)
6166 self
.assertEqual(i
['Family'], 2)
6167 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6168 self
.assertRegex(a
, '^192.168.5.[178]$')
6169 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6170 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6171 self
.assertEqual('192.168.5.1', a
)
6173 self
.assertEqual(len(j
['SIP']), 2)
6176 self
.assertEqual(i
['Family'], 2)
6177 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6178 self
.assertRegex(a
, '^192.168.5.2[34]$')
6179 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6180 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6181 self
.assertEqual('192.168.5.1', a
)
6183 print('## dnsmasq log')
6184 output
= read_dnsmasq_log_file()
6186 self
.assertIn('vendor class: FooBarVendorTest', output
)
6187 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6188 self
.assertIn('client provides name: test-hostname', output
)
6189 self
.assertIn('26:mtu', output
)
6191 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6193 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6194 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6195 self
.check_nftset('ifindex', 'veth99')
6197 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6199 def test_dhcp_client_ipv4_dbus_status(self
):
6200 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6202 self
.wait_online('veth-peer:carrier')
6204 state
= get_dhcp4_client_state('veth99')
6205 print(f
"State = {state}")
6206 self
.assertEqual(state
, 'rebooting')
6208 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6209 '--dhcp-option=option:domain-search,example.com',
6210 '--dhcp-alternate-port=67,5555',
6211 ipv4_range
='192.168.5.110,192.168.5.119')
6212 self
.wait_online('veth99:routable', 'veth-peer:routable')
6213 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6215 state
= get_dhcp4_client_state('veth99')
6216 print(f
"State = {state}")
6217 self
.assertEqual(state
, 'bound')
6219 def test_dhcp_client_allow_list(self
):
6220 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6223 self
.wait_online('veth-peer:carrier')
6224 since
= datetime
.datetime
.now()
6227 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6229 if expect
in read_networkd_log(since
=since
):
6235 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6236 since
= datetime
.datetime
.now()
6239 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6241 if expect
in read_networkd_log(since
=since
):
6247 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6248 since
= datetime
.datetime
.now()
6251 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6253 if expect
in read_networkd_log(since
=since
):
6259 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6260 def test_dhcp_client_rapid_commit(self
):
6261 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6263 self
.wait_online('veth-peer:carrier')
6265 start_dnsmasq('--dhcp-rapid-commit')
6266 self
.wait_online('veth99:routable', 'veth-peer:routable')
6267 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6269 state
= get_dhcp4_client_state('veth99')
6270 print(f
"DHCPv4 client state = {state}")
6271 self
.assertEqual(state
, 'bound')
6273 output
= read_dnsmasq_log_file()
6274 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6275 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6276 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6277 self
.assertIn('DHCPACK(veth-peer)', output
)
6279 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6280 copy_network_unit('25-veth.netdev',
6281 '25-dhcp-server-ipv6-only-mode.network',
6282 '25-dhcp-client-ipv6-only-mode.network')
6284 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6285 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6287 state
= get_dhcp4_client_state('veth99')
6288 print(f
"State = {state}")
6289 self
.assertEqual(state
, 'bound')
6291 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6293 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6299 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6300 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6301 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6303 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6304 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6305 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6306 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6307 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6308 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6309 copy_network_unit(*testunits
, copy_dropins
=False)
6312 self
.wait_online('veth-peer:carrier')
6313 additional_options
= [
6314 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6315 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6316 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6319 additional_options
+= [
6320 '--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'
6322 start_dnsmasq(*additional_options
)
6323 self
.wait_online('veth99:routable', 'veth-peer:routable')
6325 output
= check_output('ip -4 route show dev veth99')
6331 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6332 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6333 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6334 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6335 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6337 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6338 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6339 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6340 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6342 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6343 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6344 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6345 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6346 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6347 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6348 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6349 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6352 if use_gateway
and (not classless
or not use_routes
):
6353 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6355 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6357 # Check route to gateway
6358 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6359 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6361 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6363 # Check RoutesToDNS= and RoutesToNTP=
6364 if dns_and_ntp_routes
:
6365 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6366 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6369 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6370 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6372 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6373 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6375 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6376 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6378 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6379 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6380 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6381 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6383 check_json(networkctl_json())
6385 def test_dhcp_client_settings_anonymize(self
):
6386 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6388 self
.wait_online('veth-peer:carrier')
6390 self
.wait_online('veth99:routable', 'veth-peer:routable')
6392 print('## dnsmasq log')
6393 output
= read_dnsmasq_log_file()
6395 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6396 self
.assertNotIn('test-hostname', output
)
6397 self
.assertNotIn('26:mtu', output
)
6399 def test_dhcp_keep_configuration_dhcp(self
):
6400 copy_network_unit('25-veth.netdev',
6401 '25-dhcp-server-veth-peer.network',
6402 '25-dhcp-client-keep-configuration-dhcp.network')
6404 self
.wait_online('veth-peer:carrier')
6406 self
.wait_online('veth99:routable', 'veth-peer:routable')
6408 output
= check_output('ip address show dev veth99 scope global')
6410 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6411 'valid_lft forever preferred_lft forever')
6413 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6416 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6417 print('Wait for the DHCP lease to be expired')
6420 # The lease address should be kept after the lease expired
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 veth99\n *'
6424 'valid_lft forever preferred_lft forever')
6428 # The lease address should be kept after networkd stopped
6429 output
= check_output('ip address show dev veth99 scope global')
6431 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6432 'valid_lft forever preferred_lft forever')
6434 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6435 f
.write('[Network]\nDHCP=no\n')
6438 self
.wait_online('veth99:routable', 'veth-peer:routable')
6440 # Still the lease address should be kept after networkd restarted
6441 output
= check_output('ip address show dev veth99 scope global')
6443 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6444 'valid_lft forever preferred_lft forever')
6446 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6447 copy_network_unit('25-veth.netdev',
6448 '25-dhcp-server-veth-peer.network',
6449 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6451 self
.wait_online('veth-peer:carrier')
6453 self
.wait_online('veth99:routable', 'veth-peer:routable')
6455 output
= check_output('ip address show dev veth99 scope global')
6457 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6462 output
= check_output('ip address show dev veth99 scope global')
6464 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6467 self
.wait_online('veth-peer:routable')
6469 output
= check_output('ip address show dev veth99 scope global')
6471 self
.assertNotIn('192.168.5.', output
)
6473 def test_dhcp_client_reuse_address_as_static(self
):
6474 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6476 self
.wait_online('veth-peer:carrier')
6478 self
.wait_online('veth99:routable', 'veth-peer:routable')
6480 # link become 'routable' when at least one protocol provide an valid address.
6481 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6482 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6484 output
= check_output('ip address show dev veth99 scope global')
6485 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6486 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6487 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6488 print(static_network
)
6490 remove_network_unit('25-dhcp-client.network')
6492 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6493 f
.write(static_network
)
6496 self
.wait_online('veth99:routable')
6498 output
= check_output('ip -4 address show dev veth99 scope global')
6500 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6501 'valid_lft forever preferred_lft forever')
6503 output
= check_output('ip -6 address show dev veth99 scope global')
6505 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6506 'valid_lft forever preferred_lft forever')
6508 @expectedFailureIfModuleIsNotAvailable('vrf')
6509 def test_dhcp_client_vrf(self
):
6510 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6511 '25-vrf.netdev', '25-vrf.network')
6513 self
.wait_online('veth-peer:carrier')
6515 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6517 # link become 'routable' when at least one protocol provide an valid address.
6518 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6519 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6521 print('## ip -d link show dev vrf99')
6522 output
= check_output('ip -d link show dev vrf99')
6524 self
.assertRegex(output
, 'vrf table 42')
6526 print('## ip address show vrf vrf99')
6527 output
= check_output('ip address show vrf vrf99')
6529 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6530 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6531 self
.assertRegex(output
, 'inet6 .* scope link')
6533 print('## ip address show dev veth99')
6534 output
= check_output('ip address show dev veth99')
6536 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6537 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6538 self
.assertRegex(output
, 'inet6 .* scope link')
6540 print('## ip route show vrf vrf99')
6541 output
= check_output('ip route show vrf vrf99')
6543 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6544 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6545 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6547 print('## ip route show table main dev veth99')
6548 output
= check_output('ip route show table main dev veth99')
6550 self
.assertEqual(output
, '')
6552 def test_dhcp_client_gateway_onlink_implicit(self
):
6553 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6554 '25-dhcp-client-gateway-onlink-implicit.network')
6556 self
.wait_online('veth-peer:carrier')
6558 self
.wait_online('veth99:routable', 'veth-peer:routable')
6560 output
= networkctl_status('veth99')
6562 self
.assertRegex(output
, '192.168.5')
6564 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6566 self
.assertRegex(output
, 'onlink')
6567 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6569 self
.assertRegex(output
, 'onlink')
6571 def test_dhcp_client_with_ipv4ll(self
):
6572 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6573 '25-dhcp-client-with-ipv4ll.network')
6575 # we need to increase timeout above default, as this will need to wait for
6576 # systemd-networkd to get the dhcpv4 transient failure event
6577 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6579 output
= check_output('ip -4 address show dev veth99')
6581 self
.assertNotIn('192.168.5.', output
)
6582 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6585 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6586 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6587 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')
6588 self
.wait_online('veth99:routable')
6590 output
= check_output('ip -4 address show dev veth99')
6592 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6593 self
.assertNotIn('169.254.', output
)
6594 self
.assertNotIn('scope link', output
)
6597 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6598 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)
6599 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6601 output
= check_output('ip -4 address show dev veth99')
6603 self
.assertNotIn('192.168.5.', output
)
6604 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6606 def test_dhcp_client_use_dns(self
):
6607 def check(self
, ipv4
, ipv6
):
6608 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6609 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6610 f
.write('[DHCPv4]\nUseDNS=')
6611 f
.write('yes' if ipv4
else 'no')
6612 f
.write('\n[DHCPv6]\nUseDNS=')
6613 f
.write('yes' if ipv6
else 'no')
6614 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6617 self
.wait_online('veth99:routable')
6619 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6620 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6621 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6623 # make resolved re-read the link state file
6624 resolvectl('revert', 'veth99')
6626 output
= resolvectl('dns', 'veth99')
6629 self
.assertIn('192.168.5.1', output
)
6631 self
.assertNotIn('192.168.5.1', output
)
6633 self
.assertIn('2600::1', output
)
6635 self
.assertNotIn('2600::1', output
)
6637 check_json(networkctl_json())
6639 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6642 self
.wait_online('veth-peer:carrier')
6643 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6644 '--dhcp-option=option6:dns-server,[2600::1]')
6646 check(self
, True, True)
6647 check(self
, True, False)
6648 check(self
, False, True)
6649 check(self
, False, False)
6651 def test_dhcp_client_use_captive_portal(self
):
6652 def check(self
, ipv4
, ipv6
):
6653 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6654 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6655 f
.write('[DHCPv4]\nUseCaptivePortal=')
6656 f
.write('yes' if ipv4
else 'no')
6657 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6658 f
.write('yes' if ipv6
else 'no')
6659 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6662 self
.wait_online('veth99:routable')
6664 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6665 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6666 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6668 output
= networkctl_status('veth99')
6671 self
.assertIn('Captive Portal: http://systemd.io', output
)
6673 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6675 check_json(networkctl_json())
6677 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6680 self
.wait_online('veth-peer:carrier')
6681 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6682 '--dhcp-option=option6:103,http://systemd.io')
6684 check(self
, True, True)
6685 check(self
, True, False)
6686 check(self
, False, True)
6687 check(self
, False, False)
6689 def test_dhcp_client_reject_captive_portal(self
):
6690 def check(self
, ipv4
, ipv6
):
6691 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6692 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6693 f
.write('[DHCPv4]\nUseCaptivePortal=')
6694 f
.write('yes' if ipv4
else 'no')
6695 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6696 f
.write('yes' if ipv6
else 'no')
6697 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6700 self
.wait_online('veth99:routable')
6702 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6703 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6704 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6706 output
= networkctl_status('veth99')
6708 self
.assertNotIn('Captive Portal: ', output
)
6709 self
.assertNotIn('invalid/url', output
)
6711 check_json(networkctl_json())
6713 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6716 self
.wait_online('veth-peer:carrier')
6717 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6718 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6719 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6721 check(self
, True, True)
6722 check(self
, True, False)
6723 check(self
, False, True)
6724 check(self
, False, False)
6726 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6734 def check_dhcp6_prefix(self
, link
):
6735 description
= get_link_description(link
)
6737 self
.assertIn('DHCPv6Client', description
.keys())
6738 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6740 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6742 self
.assertEqual(len(prefixInfo
), 1)
6744 self
.assertIn('Prefix', prefixInfo
[0].keys())
6745 self
.assertIn('PrefixLength', prefixInfo
[0].keys())
6746 self
.assertIn('PreferredLifetimeUSec', prefixInfo
[0].keys())
6747 self
.assertIn('ValidLifetimeUSec', prefixInfo
[0].keys())
6749 self
.assertEqual(prefixInfo
[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6750 self
.assertEqual(prefixInfo
[0]['PrefixLength'], 56)
6751 self
.assertGreater(prefixInfo
[0]['PreferredLifetimeUSec'], 0)
6752 self
.assertGreater(prefixInfo
[0]['ValidLifetimeUSec'], 0)
6754 def test_dhcp6pd_no_address(self
):
6756 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6759 self
.wait_online('veth-peer:routable')
6760 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6761 self
.wait_online('veth99:degraded')
6763 print('### ip -6 address show dev veth99 scope global')
6764 output
= check_output('ip -6 address show dev veth99 scope global')
6766 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6768 self
.check_dhcp6_prefix('veth99')
6770 def test_dhcp6pd_no_assign(self
):
6771 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6772 # However, the server does not provide IA_NA. For issue #31349.
6773 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6776 self
.wait_online('veth-peer:routable')
6777 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd-no-range.conf', ipv
='-6')
6778 self
.wait_online('veth99:degraded')
6780 print('### ip -6 address show dev veth99 scope global')
6781 output
= check_output('ip -6 address show dev veth99 scope global')
6783 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6785 self
.check_dhcp6_prefix('veth99')
6787 def test_dhcp6pd(self
):
6788 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6789 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6790 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6791 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6792 '25-dhcp-pd-downstream-dummy97.network',
6793 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6794 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6797 self
.wait_online('veth-peer:routable')
6798 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6799 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6800 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6802 self
.setup_nftset('addr6', 'ipv6_addr')
6803 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6804 self
.setup_nftset('ifindex', 'iface_index')
6806 # Check DBus assigned prefix information to veth99
6807 self
.check_dhcp6_prefix('veth99')
6809 print('### ip -6 address show dev veth-peer scope global')
6810 output
= check_output('ip -6 address show dev veth-peer scope global')
6812 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6816 # dummy97: 0x01 (The link will appear later)
6818 # dummy99: auto -> 0x02 (No address assignment)
6823 print('### ip -6 address show dev veth99 scope global')
6824 output
= check_output('ip -6 address show dev veth99 scope global')
6827 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6828 # address in IA_PD (Token=static)
6829 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6830 # address in IA_PD (Token=eui64)
6831 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6832 # address in IA_PD (temporary)
6833 # Note that the temporary addresses may appear after the link enters configured state
6834 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')
6836 print('### ip -6 address show dev test1 scope global')
6837 output
= check_output('ip -6 address show dev test1 scope global')
6839 # address in IA_PD (Token=static)
6840 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6841 # address in IA_PD (temporary)
6842 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')
6844 print('### ip -6 address show dev dummy98 scope global')
6845 output
= check_output('ip -6 address show dev dummy98 scope global')
6847 # address in IA_PD (Token=static)
6848 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6849 # address in IA_PD (temporary)
6850 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')
6852 print('### ip -6 address show dev dummy99 scope global')
6853 output
= check_output('ip -6 address show dev dummy99 scope global')
6856 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6858 print('### ip -6 address show dev veth97 scope global')
6859 output
= check_output('ip -6 address show dev veth97 scope global')
6861 # address in IA_PD (Token=static)
6862 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6863 # address in IA_PD (Token=eui64)
6864 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6865 # address in IA_PD (temporary)
6866 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')
6868 print('### ip -6 address show dev veth97-peer scope global')
6869 output
= check_output('ip -6 address show dev veth97-peer scope global')
6871 # NDisc address (Token=static)
6872 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6873 # NDisc address (Token=eui64)
6874 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6875 # NDisc address (temporary)
6876 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')
6878 print('### ip -6 address show dev veth98 scope global')
6879 output
= check_output('ip -6 address show dev veth98 scope global')
6881 # address in IA_PD (Token=static)
6882 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6883 # address in IA_PD (Token=eui64)
6884 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6885 # address in IA_PD (temporary)
6886 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')
6888 print('### ip -6 address show dev veth98-peer scope global')
6889 output
= check_output('ip -6 address show dev veth98-peer scope global')
6891 # NDisc address (Token=static)
6892 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6893 # NDisc address (Token=eui64)
6894 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6895 # NDisc address (temporary)
6896 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')
6898 print('### ip -6 route show type unreachable')
6899 output
= check_output('ip -6 route show type unreachable')
6901 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6903 print('### ip -6 route show dev veth99')
6904 output
= check_output('ip -6 route show dev veth99')
6906 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6908 print('### ip -6 route show dev test1')
6909 output
= check_output('ip -6 route show dev test1')
6911 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6913 print('### ip -6 route show dev dummy98')
6914 output
= check_output('ip -6 route show dev dummy98')
6916 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6918 print('### ip -6 route show dev dummy99')
6919 output
= check_output('ip -6 route show dev dummy99')
6921 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6923 print('### ip -6 route show dev veth97')
6924 output
= check_output('ip -6 route show dev veth97')
6926 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6928 print('### ip -6 route show dev veth97-peer')
6929 output
= check_output('ip -6 route show dev veth97-peer')
6931 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6933 print('### ip -6 route show dev veth98')
6934 output
= check_output('ip -6 route show dev veth98')
6936 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6938 print('### ip -6 route show dev veth98-peer')
6939 output
= check_output('ip -6 route show dev veth98-peer')
6941 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6943 # Test case for a downstream which appears later
6944 check_output('ip link add dummy97 type dummy')
6945 self
.wait_online('dummy97:routable')
6947 print('### ip -6 address show dev dummy97 scope global')
6948 output
= check_output('ip -6 address show dev dummy97 scope global')
6950 # address in IA_PD (Token=static)
6951 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6952 # address in IA_PD (temporary)
6953 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')
6955 print('### ip -6 route show dev dummy97')
6956 output
= check_output('ip -6 route show dev dummy97')
6958 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6960 # Test case for reconfigure
6961 networkctl_reconfigure('dummy98', 'dummy99')
6962 self
.wait_online('dummy98:routable', 'dummy99:degraded')
6964 print('### ip -6 address show dev dummy98 scope global')
6965 output
= check_output('ip -6 address show dev dummy98 scope global')
6967 # address in IA_PD (Token=static)
6968 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6969 # address in IA_PD (temporary)
6970 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')
6972 print('### ip -6 address show dev dummy99 scope global')
6973 output
= check_output('ip -6 address show dev dummy99 scope global')
6976 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6978 print('### ip -6 route show dev dummy98')
6979 output
= check_output('ip -6 route show dev dummy98')
6981 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6983 print('### ip -6 route show dev dummy99')
6984 output
= check_output('ip -6 route show dev dummy99')
6986 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6988 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6990 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6991 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6992 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6993 self
.check_nftset('ifindex', 'dummy98')
6995 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6997 def verify_dhcp4_6rd(self
, tunnel_name
):
6998 print('### ip -4 address show dev veth-peer scope global')
6999 output
= check_output('ip -4 address show dev veth-peer scope global')
7001 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
7005 # dummy97: 0x01 (The link will appear later)
7007 # dummy99: auto -> 0x0[23] (No address assignment)
7008 # 6rd-XXX: auto -> 0x0[23]
7013 print('### ip -4 address show dev veth99 scope global')
7014 output
= check_output('ip -4 address show dev veth99 scope global')
7016 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7018 print('### ip -6 address show dev veth99 scope global')
7019 output
= check_output('ip -6 address show dev veth99 scope global')
7021 # address in IA_PD (Token=static)
7022 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7023 # address in IA_PD (Token=eui64)
7024 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7025 # address in IA_PD (temporary)
7026 # Note that the temporary addresses may appear after the link enters configured state
7027 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')
7029 print('### ip -6 address show dev test1 scope global')
7030 output
= check_output('ip -6 address show dev test1 scope global')
7032 # address in IA_PD (Token=static)
7033 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7034 # address in IA_PD (temporary)
7035 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')
7037 print('### ip -6 address show dev dummy98 scope global')
7038 output
= check_output('ip -6 address show dev dummy98 scope global')
7040 # address in IA_PD (Token=static)
7041 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7042 # address in IA_PD (temporary)
7043 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')
7045 print('### ip -6 address show dev dummy99 scope global')
7046 output
= check_output('ip -6 address show dev dummy99 scope global')
7049 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7051 print('### ip -6 address show dev veth97 scope global')
7052 output
= check_output('ip -6 address show dev veth97 scope global')
7054 # address in IA_PD (Token=static)
7055 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7056 # address in IA_PD (Token=eui64)
7057 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7058 # address in IA_PD (temporary)
7059 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')
7061 print('### ip -6 address show dev veth97-peer scope global')
7062 output
= check_output('ip -6 address show dev veth97-peer scope global')
7064 # NDisc address (Token=static)
7065 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7066 # NDisc address (Token=eui64)
7067 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7068 # NDisc address (temporary)
7069 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')
7071 print('### ip -6 address show dev veth98 scope global')
7072 output
= check_output('ip -6 address show dev veth98 scope global')
7074 # address in IA_PD (Token=static)
7075 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7076 # address in IA_PD (Token=eui64)
7077 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7078 # address in IA_PD (temporary)
7079 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')
7081 print('### ip -6 address show dev veth98-peer scope global')
7082 output
= check_output('ip -6 address show dev veth98-peer scope global')
7084 # NDisc address (Token=static)
7085 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7086 # NDisc address (Token=eui64)
7087 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7088 # NDisc address (temporary)
7089 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')
7091 print('### ip -6 route show type unreachable')
7092 output
= check_output('ip -6 route show type unreachable')
7094 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7096 print('### ip -6 route show dev veth99')
7097 output
= check_output('ip -6 route show dev veth99')
7099 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7101 print('### ip -6 route show dev test1')
7102 output
= check_output('ip -6 route show dev test1')
7104 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7106 print('### ip -6 route show dev dummy98')
7107 output
= check_output('ip -6 route show dev dummy98')
7109 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7111 print('### ip -6 route show dev dummy99')
7112 output
= check_output('ip -6 route show dev dummy99')
7114 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7116 print('### ip -6 route show dev veth97')
7117 output
= check_output('ip -6 route show dev veth97')
7119 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7121 print('### ip -6 route show dev veth97-peer')
7122 output
= check_output('ip -6 route show dev veth97-peer')
7124 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7126 print('### ip -6 route show dev veth98')
7127 output
= check_output('ip -6 route show dev veth98')
7129 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7131 print('### ip -6 route show dev veth98-peer')
7132 output
= check_output('ip -6 route show dev veth98-peer')
7134 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7136 print('### ip -6 address show dev dummy97 scope global')
7137 output
= check_output('ip -6 address show dev dummy97 scope global')
7139 # address in IA_PD (Token=static)
7140 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7141 # address in IA_PD (temporary)
7142 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')
7144 print('### ip -6 route show dev dummy97')
7145 output
= check_output('ip -6 route show dev dummy97')
7147 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7149 print(f
'### ip -d link show dev {tunnel_name}')
7150 output
= check_output(f
'ip -d link show dev {tunnel_name}')
7152 self
.assertIn('link/sit 10.100.100.', output
)
7153 self
.assertIn('local 10.100.100.', output
)
7154 self
.assertIn('ttl 64', output
)
7155 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
7156 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
7158 print(f
'### ip -6 address show dev {tunnel_name}')
7159 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
7161 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')
7162 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7164 print(f
'### ip -6 route show dev {tunnel_name}')
7165 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
7167 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7168 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
7170 print('### ip -6 route show default')
7171 output
= check_output('ip -6 route show default')
7173 self
.assertIn('default', output
)
7174 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
7176 def test_dhcp4_6rd(self
):
7177 def get_dhcp_6rd_prefix(link
):
7178 description
= get_link_description(link
)
7180 self
.assertIn('DHCPv4Client', description
.keys())
7181 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
7183 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
7184 self
.assertIn('Prefix', prefixInfo
.keys())
7185 self
.assertIn('PrefixLength', prefixInfo
.keys())
7186 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
7187 self
.assertIn('BorderRouters', prefixInfo
.keys())
7191 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7192 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7193 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7194 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7195 '25-dhcp-pd-downstream-dummy97.network',
7196 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7197 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7198 '80-6rd-tunnel.network')
7201 self
.wait_online('veth-peer:routable')
7204 # 6rd-prefix: 2001:db8::/32
7205 # br-addresss: 10.0.0.1
7207 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',
7208 ipv4_range
='10.100.100.100,10.100.100.200',
7209 ipv4_router
='10.0.0.1')
7210 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7211 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7213 # Check the DBus interface for assigned prefix information
7214 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7216 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7217 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7218 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7219 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7221 # Test case for a downstream which appears later
7222 check_output('ip link add dummy97 type dummy')
7223 self
.wait_online('dummy97:routable')
7227 for name
in os
.listdir('/sys/class/net/'):
7228 if name
.startswith('6rd-'):
7232 self
.wait_online(f
'{tunnel_name}:routable')
7234 self
.verify_dhcp4_6rd(tunnel_name
)
7236 # Test case for reconfigure
7237 networkctl_reconfigure('dummy98', 'dummy99')
7238 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7240 self
.verify_dhcp4_6rd(tunnel_name
)
7242 print('Wait for the DHCP lease to be renewed/rebind')
7245 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7246 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7248 self
.verify_dhcp4_6rd(tunnel_name
)
7250 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7258 def test_ipv6_route_prefix(self
):
7259 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7260 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7263 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7265 output
= check_output('ip address show dev veth-peer')
7267 self
.assertIn('inet6 2001:db8:0:1:', output
)
7268 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7269 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7271 output
= check_output('ip -6 route show dev veth-peer')
7273 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7274 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7275 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7276 self
.assertIn('2001:db0:fff::/64 via ', output
)
7277 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7278 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7280 output
= check_output('ip address show dev veth99')
7282 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7283 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7284 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7285 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7287 output
= resolvectl('dns', 'veth-peer')
7289 self
.assertRegex(output
, '2001:db8:1:1::2')
7291 output
= resolvectl('domain', 'veth-peer')
7293 self
.assertIn('example.com', output
)
7295 check_json(networkctl_json())
7297 output
= networkctl_json('veth-peer')
7301 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7303 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7304 self
.assertEqual(prefix
, '64:ff9b::')
7306 prefix_length
= pref64
['PrefixLength']
7307 self
.assertEqual(prefix_length
, 96)
7309 def test_ipv6_route_prefix_deny_list(self
):
7310 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7311 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7314 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7316 output
= check_output('ip address show dev veth-peer')
7318 self
.assertIn('inet6 2001:db8:0:1:', output
)
7319 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7321 output
= check_output('ip -6 route show dev veth-peer')
7323 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7324 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7325 self
.assertIn('2001:db0:fff::/64 via ', output
)
7326 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7328 output
= check_output('ip address show dev veth99')
7330 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7331 self
.assertIn('inet6 2001:db8:0:2:', output
)
7333 output
= resolvectl('dns', 'veth-peer')
7335 self
.assertRegex(output
, '2001:db8:1:1::2')
7337 output
= resolvectl('domain', 'veth-peer')
7339 self
.assertIn('example.com', output
)
7341 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7349 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7355 self
.wait_online('dummy98:routable')
7356 self
.check_link_attr('dummy98', 'mtu', mtu
)
7357 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7359 # test normal restart
7361 self
.wait_online('dummy98:routable')
7362 self
.check_link_attr('dummy98', 'mtu', mtu
)
7363 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7366 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7368 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7369 ''' test setting mtu/ipv6_mtu with interface already up '''
7372 # note - changing the device mtu resets the ipv6 mtu
7373 check_output('ip link set up mtu 1501 dev dummy98')
7374 check_output('ip link set up mtu 1500 dev dummy98')
7375 self
.check_link_attr('dummy98', 'mtu', '1500')
7376 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7378 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7380 def test_mtu_network(self
):
7381 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7382 self
.check_mtu('1600')
7384 def test_mtu_netdev(self
):
7385 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7386 # note - MTU set by .netdev happens ONLY at device creation!
7387 self
.check_mtu('1600', reset
=False)
7389 def test_mtu_link(self
):
7390 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7391 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7392 self
.check_mtu('1600', reset
=False)
7394 def test_ipv6_mtu(self
):
7395 ''' set ipv6 mtu without setting device mtu '''
7396 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7397 self
.check_mtu('1500', '1400')
7399 def test_ipv6_mtu_toolarge(self
):
7400 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7401 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7402 self
.check_mtu('1500', '1500')
7404 def test_mtu_network_ipv6_mtu(self
):
7405 ''' set ipv6 mtu and set device mtu via network file '''
7406 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7407 self
.check_mtu('1600', '1550')
7409 def test_mtu_netdev_ipv6_mtu(self
):
7410 ''' set ipv6 mtu and set device mtu via netdev file '''
7411 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7412 self
.check_mtu('1600', '1550', reset
=False)
7414 def test_mtu_link_ipv6_mtu(self
):
7415 ''' set ipv6 mtu and set device mtu via link file '''
7416 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7417 self
.check_mtu('1600', '1550', reset
=False)
7420 if __name__
== '__main__':
7421 parser
= argparse
.ArgumentParser()
7422 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7423 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7424 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7425 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7426 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7427 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7428 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7429 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7430 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7431 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7432 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7433 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7434 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7435 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7436 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7437 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7438 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
)
7439 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7442 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7443 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7444 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7445 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7446 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7447 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7448 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7449 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7450 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7451 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7452 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7453 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7454 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7457 networkd_bin
= ns
.networkd_bin
7459 resolved_bin
= ns
.resolved_bin
7460 if ns
.timesyncd_bin
:
7461 timesyncd_bin
= ns
.timesyncd_bin
7463 udevd_bin
= ns
.udevd_bin
7464 if ns
.wait_online_bin
:
7465 wait_online_bin
= ns
.wait_online_bin
7466 if ns
.networkctl_bin
:
7467 networkctl_bin
= ns
.networkctl_bin
7468 if ns
.resolvectl_bin
:
7469 resolvectl_bin
= ns
.resolvectl_bin
7470 if ns
.timedatectl_bin
:
7471 timedatectl_bin
= ns
.timedatectl_bin
7473 udevadm_bin
= ns
.udevadm_bin
7476 systemd_source_dir
= ns
.source_dir
7478 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7479 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7480 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7482 use_valgrind
= ns
.use_valgrind
7483 enable_debug
= ns
.enable_debug
7484 asan_options
= ns
.asan_options
7485 lsan_options
= ns
.lsan_options
7486 ubsan_options
= ns
.ubsan_options
7487 with_coverage
= ns
.with_coverage
7490 # Do not forget the trailing space.
7491 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7493 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7494 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7495 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7496 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7497 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7500 env
.update({'ASAN_OPTIONS': asan_options
})
7502 env
.update({'LSAN_OPTIONS': lsan_options
})
7504 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7506 env
.update({'SYSTEMD_MEMPOOL': '0'})
7508 wait_online_env
= env
.copy()
7510 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7512 sys
.argv
[1:] = unknown_args
7513 unittest
.main(verbosity
=3)