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
)
286 def copy_network_unit(*units
, copy_dropins
=True):
288 Copy networkd unit files into the testbed.
290 Any networkd unit file type can be specified, as well as drop-in files.
292 By default, all drop-ins for a specified unit file are copied in;
293 to avoid that specify dropins=False.
295 When a drop-in file is specified, its unit file is also copied in automatically.
298 mkdir_p(network_unit_dir
)
300 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
301 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
303 if unit
.endswith('.conf'):
305 unit
= os
.path
.dirname(dropin
).rstrip('.d')
306 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
308 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
310 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
312 if unit
.endswith('.link'):
318 def copy_credential(src
, target
):
319 mkdir_p(credstore_dir
)
320 cp(os
.path
.join(networkd_ci_temp_dir
, src
),
321 os
.path
.join(credstore_dir
, target
))
323 def remove_network_unit(*units
):
325 Remove previously copied unit files from the testbed.
327 Drop-ins will be removed automatically.
331 rm_f(os
.path
.join(network_unit_dir
, unit
))
332 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
334 if unit
.endswith('.link') or unit
.endswith('.link.d'):
340 def clear_network_units():
342 if os
.path
.exists(network_unit_dir
):
343 units
= os
.listdir(network_unit_dir
)
345 if unit
.endswith('.link') or unit
.endswith('.link.d'):
348 rm_rf(network_unit_dir
)
353 def copy_networkd_conf_dropin(*dropins
):
354 """Copy networkd.conf dropin files into the testbed."""
355 mkdir_p(networkd_conf_dropin_dir
)
356 for dropin
in dropins
:
357 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
359 def remove_networkd_conf_dropin(*dropins
):
360 """Remove previously copied networkd.conf dropin files from the testbed."""
361 for dropin
in dropins
:
362 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
364 def clear_networkd_conf_dropins():
365 rm_rf(networkd_conf_dropin_dir
)
367 def setup_systemd_udev_rules():
368 if not systemd_udev_rules_build_dir
:
371 mkdir_p(udev_rules_dir
)
373 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
374 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
376 for rule
in os
.listdir(path
):
377 if not rule
.endswith(".rules"):
379 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
381 def copy_udev_rule(*rules
):
382 """Copy udev rules"""
383 mkdir_p(udev_rules_dir
)
385 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
387 def remove_udev_rule(*rules
):
388 """Remove previously copied udev rules"""
390 rm_f(os
.path
.join(udev_rules_dir
, rule
))
392 def clear_udev_rules():
393 rm_rf(udev_rules_dir
)
395 def save_active_units():
396 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
397 'systemd-resolved.service', 'systemd-timesyncd.service',
398 'firewalld.service']:
399 if call(f
'systemctl is-active --quiet {u}') == 0:
400 call(f
'systemctl stop {u}')
401 active_units
.append(u
)
403 def restore_active_units():
404 if 'systemd-networkd.socket' in active_units
:
405 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
406 for u
in active_units
:
407 call(f
'systemctl restart {u}')
409 def create_unit_dropin(unit
, contents
):
410 mkdir_p(f
'/run/systemd/system/{unit}.d')
411 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
412 f
.write('\n'.join(contents
))
414 def create_service_dropin(service
, command
, additional_settings
=None):
418 f
'ExecStart=!!{valgrind_cmd}{command}',
421 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
423 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
425 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
427 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
428 if asan_options
or lsan_options
or ubsan_options
:
429 drop_in
+= ['SystemCallFilter=']
430 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
431 drop_in
+= ['MemoryDenyWriteExecute=no']
434 'Environment=SYSTEMD_MEMPOOL=0',
442 if additional_settings
:
443 drop_in
+= additional_settings
445 create_unit_dropin(f
'{service}.service', drop_in
)
447 def link_exists(link
):
448 return call_quiet(f
'ip link show {link}') == 0
450 def link_resolve(link
):
451 return check_output(f
'ip link show {link}').split(':')[1].strip()
453 def remove_link(*links
, protect
=False):
455 if protect
and link
in protected_links
:
457 if link_exists(link
):
458 call(f
'ip link del dev {link}')
460 def save_existing_links():
461 links
= os
.listdir('/sys/class/net')
463 if link_exists(link
):
464 protected_links
.add(link
)
466 print('### The following links will be protected:')
467 print(', '.join(sorted(list(protected_links
))))
470 links
= os
.listdir('/sys/class/net')
471 remove_link(*links
, protect
=True)
473 def flush_nexthops():
474 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
475 # Hence, we cannot restore nexthops in a simple way.
476 # Let's assume there is no nexthop used in the system
477 call_quiet('ip nexthop flush')
480 # pylint: disable=global-statement
482 saved_routes
= check_output('ip route show table all')
483 print('### The following routes will be protected:')
488 output
= check_output('ip route show table all')
489 for line
in output
.splitlines():
490 if line
in saved_routes
:
492 if 'proto kernel' in line
:
494 if ' dev ' in line
and not ' dev lo ' in line
:
498 print('### Removing routes that did not exist when the test started.')
500 call(f
'ip route del {line}')
502 def save_routing_policy_rules():
503 # pylint: disable=global-statement
504 global saved_ipv4_rules
, saved_ipv6_rules
506 output
= check_output(f
'ip -{ipv} rule show')
507 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
511 saved_ipv4_rules
= save(4)
512 saved_ipv6_rules
= save(6)
514 def flush_routing_policy_rules():
515 def flush(ipv
, saved_rules
):
517 output
= check_output(f
'ip -{ipv} rule show')
518 for line
in output
.splitlines():
519 if line
in saved_rules
:
523 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
525 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
526 priority
= words
[0].rstrip(':')
527 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
529 flush(4, saved_ipv4_rules
)
530 flush(6, saved_ipv6_rules
)
532 def flush_fou_ports():
533 ret
= run('ip fou show')
534 if ret
.returncode
!= 0:
535 return # fou may not be supported
536 for line
in ret
.stdout
.splitlines():
537 port
= line
.split()[1]
538 call(f
'ip fou del port {port}')
540 def flush_l2tp_tunnels():
542 ret
= run('ip l2tp show tunnel')
543 if ret
.returncode
!= 0:
544 return # l2tp may not be supported
545 for line
in ret
.stdout
.splitlines():
547 if words
[0] == 'Tunnel':
548 tid
= words
[1].rstrip(',')
549 call(f
'ip l2tp del tunnel tunnel_id {tid}')
552 # Removing L2TP tunnel is asynchronous and slightly takes a time.
555 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
556 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
560 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
563 # pylint: disable=global-statement
564 global saved_timezone
565 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
566 if r
.returncode
== 0:
567 saved_timezone
= r
.stdout
.rstrip()
568 print(f
'### Saved timezone: {saved_timezone}')
570 def restore_timezone():
572 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
574 def read_link_attr(*args
):
575 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
576 return f
.readline().strip()
578 def read_manager_state_file():
579 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
582 def read_link_state_file(link
):
583 ifindex
= read_link_attr(link
, 'ifindex')
584 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
585 with
open(path
, encoding
='utf-8') as f
:
588 def read_ip_sysctl_attr(link
, attribute
, ipv
):
589 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
590 return f
.readline().strip()
592 def read_ip_neigh_sysctl_attr(link
, attribute
, ipv
):
593 with
open(os
.path
.join('/proc/sys/net', ipv
, 'neigh', link
, attribute
), encoding
='utf-8') as f
:
594 return f
.readline().strip()
596 def read_ipv6_sysctl_attr(link
, attribute
):
597 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
599 def read_ipv6_neigh_sysctl_attr(link
, attribute
):
600 return read_ip_neigh_sysctl_attr(link
, attribute
, 'ipv6')
602 def read_ipv4_sysctl_attr(link
, attribute
):
603 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
605 def stop_by_pid_file(pid_file
):
606 if not os
.path
.exists(pid_file
):
608 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
609 pid
= f
.read().rstrip(' \t\r\n\0')
610 os
.kill(int(pid
), signal
.SIGTERM
)
614 print(f
"PID {pid} is still alive, waiting...")
617 if e
.errno
== errno
.ESRCH
:
619 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
622 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'):
624 ra_mode
= f
',{ra_mode}'
630 f
'--log-facility={dnsmasq_log_file}',
631 '--log-queries=extra',
633 f
'--pid-file={dnsmasq_pid_file}',
634 '--conf-file=/dev/null',
636 f
'--interface={interface}',
637 f
'--dhcp-leasefile={dnsmasq_lease_file}',
639 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
640 f
'--dhcp-range={ipv4_range},2m',
641 '--dhcp-option=option:mtu,1492',
642 f
'--dhcp-option=option:router,{ipv4_router}',
645 ) + additional_options
646 check_output(*command
)
649 stop_by_pid_file(dnsmasq_pid_file
)
650 rm_f(dnsmasq_lease_file
)
651 rm_f(dnsmasq_log_file
)
653 def read_dnsmasq_log_file():
654 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
657 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
658 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
659 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
660 touch(isc_dhcpd_lease_file
)
661 check_output(isc_dhcpd_command
)
663 def stop_isc_dhcpd():
664 stop_by_pid_file(isc_dhcpd_pid_file
)
665 rm_f(isc_dhcpd_lease_file
)
667 def get_dbus_link_path(link
):
668 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
669 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
670 'GetLinkByName', 's', link
])
672 assert out
.startswith(b
'io ')
674 assert out
.endswith(b
'"')
676 return out
[:-1].split('"')[1]
678 def get_dhcp_client_state(link
, family
):
679 link_path
= get_dbus_link_path(link
)
681 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
682 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
683 assert out
.startswith(b
's "')
685 assert out
.endswith(b
'"')
686 return out
[3:-1].decode()
688 def get_dhcp4_client_state(link
):
689 return get_dhcp_client_state(link
, '4')
691 def get_dhcp6_client_state(link
):
692 return get_dhcp_client_state(link
, '6')
694 def get_link_description(link
):
695 link_path
= get_dbus_link_path(link
)
697 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
698 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
699 assert out
.startswith(b
's "')
701 assert out
.endswith(b
'"')
702 json_raw
= out
[2:].decode()
704 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
705 check_json(description
)
706 return json
.loads(description
) # Now parse the json
708 def start_radvd(*additional_options
, config_file
):
709 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
712 f
'--pidfile={radvd_pid_file}',
713 f
'--config={config_file_path}',
714 '--logmethod=stderr',
715 ) + additional_options
716 check_output(*command
)
719 stop_by_pid_file(radvd_pid_file
)
721 def radvd_check_config(config_file
):
722 if not shutil
.which('radvd'):
723 print('radvd is not installed, assuming the config check failed')
726 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
727 # set up (one instance is @unittest.skipX())
728 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
729 return call(f
'radvd --config={config_file_path} --configtest') == 0
731 def networkd_invocation_id():
732 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
734 def read_networkd_log(invocation_id
=None, since
=None):
735 if not invocation_id
:
736 invocation_id
= networkd_invocation_id()
740 '--output=short-monotonic',
741 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
744 command
.append(f
'--since={since}')
745 check_output('journalctl --sync')
746 return check_output(*command
)
748 def networkd_is_failed():
749 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
751 def stop_networkd(show_logs
=True):
753 invocation_id
= networkd_invocation_id()
754 check_output('systemctl stop systemd-networkd.socket')
755 check_output('systemctl stop systemd-networkd.service')
757 print(read_networkd_log(invocation_id
))
758 # Check if networkd exits cleanly.
759 assert not networkd_is_failed()
761 def start_networkd():
762 check_output('systemctl start systemd-networkd')
764 def restart_networkd(show_logs
=True):
766 invocation_id
= networkd_invocation_id()
767 check_output('systemctl restart systemd-networkd.service')
769 print(read_networkd_log(invocation_id
))
772 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
774 def networkctl(*args
):
775 # Do not call networkctl if networkd is in failed state.
776 # Otherwise, networkd may be restarted and we may get wrong results.
777 assert not networkd_is_failed()
778 return check_output(*(networkctl_cmd
+ list(args
)), env
=env
)
780 def networkctl_status(*args
):
781 return networkctl('-n', '0', 'status', *args
)
783 def networkctl_json(*args
):
784 return networkctl('--json=short', 'status', *args
)
786 def networkctl_reconfigure(*links
):
787 networkctl('reconfigure', *links
)
789 def networkctl_reload():
792 def resolvectl(*args
):
793 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
795 def timedatectl(*args
):
796 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
799 return check_output(*(udevadm_cmd
+ list(args
)))
801 def udevadm_reload():
802 udevadm('control', '--reload')
804 def udevadm_trigger(*args
, action
='add'):
805 udevadm('trigger', '--settle', f
'--action={action}', *args
)
810 def tear_down_common():
811 # 1. stop DHCP/RA servers
817 call_quiet('rmmod netdevsim')
818 call_quiet('rmmod sch_teql')
820 # 3. remove network namespace
821 call_quiet('ip netns del ns99')
831 clear_network_units()
832 clear_networkd_conf_dropins()
837 flush_routing_policy_rules()
841 rm_rf(networkd_ci_temp_dir
)
842 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
844 clear_network_units()
845 clear_networkd_conf_dropins()
848 setup_systemd_udev_rules()
849 copy_udev_rule('00-debug-net.rules')
853 save_existing_links()
855 save_routing_policy_rules()
858 create_service_dropin('systemd-networkd', networkd_bin
,
861 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
863 'StartLimitIntervalSec=0'])
864 create_service_dropin('systemd-resolved', resolved_bin
)
865 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
867 # TODO: also run udevd with sanitizers, valgrind, or coverage
868 #create_service_dropin('systemd-udevd', udevd_bin,
869 # f'{udevadm_bin} control --reload --timeout 0')
871 'systemd-udevd.service',
875 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
879 'systemd-networkd.socket',
882 'StartLimitIntervalSec=0',
886 check_output('systemctl daemon-reload')
887 print(check_output('systemctl cat systemd-networkd.service'))
888 print(check_output('systemctl cat systemd-resolved.service'))
889 print(check_output('systemctl cat systemd-timesyncd.service'))
890 print(check_output('systemctl cat systemd-udevd.service'))
891 check_output('systemctl restart systemd-resolved.service')
892 check_output('systemctl restart systemd-timesyncd.service')
893 check_output('systemctl restart systemd-udevd.service')
895 def tearDownModule():
896 rm_rf(networkd_ci_temp_dir
)
898 clear_network_units()
899 clear_networkd_conf_dropins()
903 rm_rf('/run/systemd/system/systemd-networkd.service.d')
904 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
905 rm_rf('/run/systemd/system/systemd-resolved.service.d')
906 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
907 rm_rf('/run/systemd/system/systemd-udevd.service.d')
908 check_output('systemctl daemon-reload')
909 check_output('systemctl restart systemd-udevd.service')
910 restore_active_units()
913 # pylint: disable=no-member
915 def check_link_exists(self
, link
, expected
=True):
917 self
.assertTrue(link_exists(link
))
919 self
.assertFalse(link_exists(link
))
921 def check_link_attr(self
, *args
):
922 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
924 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
925 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
926 if allow_enoent
and not os
.path
.exists(path
):
928 with
open(path
, encoding
='utf-8') as f
:
929 self
.assertEqual(f
.readline().strip(), expected
)
931 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
932 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
934 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
935 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
937 def check_ipv6_neigh_sysctl_attr(self
, link
, attribute
, expected
):
938 self
.assertEqual(read_ipv6_neigh_sysctl_attr(link
, attribute
), expected
)
940 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
941 def links_exist(*links
):
943 if not link_exists(link
):
947 for iteration
in range(timeout
+ 1):
951 if links_exist(*links
):
954 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
957 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
958 # wait for the interface is activated.
959 needle
= f
'{link}: Bringing link {state}'
961 for iteration
in range(timeout
+ 1):
964 if not link_exists(link
):
966 output
= read_networkd_log()
967 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
970 self
.fail(f
'Timed out waiting for {link} activated.')
973 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
974 """Wait for the link to reach the specified operstate and/or setup state.
976 Specify None or '' for either operstate or setup_state to ignore that state.
977 This will recheck until the state conditions are met or the timeout expires.
979 If the link successfully matches the requested state, this returns True.
980 If this times out waiting for the link to match, the behavior depends on the
981 'fail_assert' parameter; if True, this causes a test assertion failure,
982 otherwise this returns False. The default is to cause assertion failure.
984 Note that this function matches on *exactly* the given operstate and setup_state.
985 To wait for a link to reach *or exceed* a given operstate, use wait_online().
992 for secs
in range(setup_timeout
+ 1):
995 if not link_exists(link
):
997 output
= networkctl_status(link
)
998 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
1002 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
1005 def wait_online(self
, *links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
1006 """Wait for the links to reach the specified operstate and/or setup state.
1008 This is similar to wait_operstate() but can be used for multiple links,
1009 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1010 The operstate should be specified in the link name, like 'eth0:degraded'.
1011 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1013 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1014 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1016 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1017 If this is set, no setup_state checks are done.
1019 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1020 This is applied only for the operational state 'degraded' or above.
1022 Note that this function waits for the links to reach *or exceed* the given operstate.
1023 However, the setup_state, if specified, must be matched *exactly*.
1025 This returns if the links reached the requested operstate/setup_state; otherwise it
1026 raises CalledProcessError or fails test assertion.
1028 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1036 check_output(*args
, env
=wait_online_env
)
1037 except subprocess
.CalledProcessError
:
1038 if networkd_is_failed():
1039 print('!!!!! systemd-networkd.service is failed !!!!!')
1040 call('systemctl status systemd-networkd.service')
1042 # show detailed status on failure
1043 for link
in links_with_operstate
:
1044 name
= link
.split(':')[0]
1045 if link_exists(name
):
1046 print(networkctl_status(name
))
1048 print(f
'Interface {name} not found.')
1050 if not bool_any
and setup_state
:
1051 for link
in links_with_operstate
:
1052 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1054 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1055 for i
in range(timeout_sec
):
1058 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1059 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1062 self
.assertRegex(output
, address_regex
)
1064 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1065 for i
in range(timeout_sec
):
1068 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1069 if not re
.search(address_regex
, output
):
1072 self
.assertNotRegex(output
, address_regex
)
1074 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1075 for i
in range(timeout_sec
):
1078 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1079 if re
.search(route_regex
, output
):
1082 self
.assertRegex(output
, route_regex
)
1084 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1085 if not shutil
.which('selinuxenabled'):
1086 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1087 elif call_quiet('selinuxenabled') != 0:
1088 print('## Checking NetLabel skipped: SELinux disabled.')
1089 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1090 print('## Checking NetLabel skipped: netlabelctl command not found.')
1092 output
= check_output('netlabelctl unlbl list')
1094 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1096 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1097 if not shutil
.which('nft'):
1098 print('## Setting up NFT sets skipped: nft command not found.')
1100 if call(f
'nft add table inet sd_test') != 0:
1101 print('## Setting up NFT table failed.')
1103 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1104 print('## Setting up NFT sets failed.')
1107 def teardown_nftset(self
, *filters
):
1108 if not shutil
.which('nft'):
1109 print('## Tearing down NFT sets skipped: nft command not found.')
1111 for filter_name
in filters
:
1112 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1113 print('## Tearing down NFT sets failed.')
1115 if call(f
'nft delete table inet sd_test') != 0:
1116 print('## Tearing down NFT table failed.')
1119 def check_nftset(self
, filter_name
, contents
):
1120 if not shutil
.which('nft'):
1121 print('## Checking NFT sets skipped: nft command not found.')
1123 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1125 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1127 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1135 @expectedFailureIfAlternativeNameIsNotAvailable()
1136 def test_altname(self
):
1137 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1139 self
.wait_online('dummy98:degraded')
1141 output
= networkctl_status('dummy98')
1142 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1144 @expectedFailureIfAlternativeNameIsNotAvailable()
1145 def test_rename_to_altname(self
):
1146 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1147 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1149 self
.wait_online('dummyalt:degraded')
1151 output
= networkctl_status('dummyalt')
1152 self
.assertIn('hogehogehogehogehogehoge', output
)
1153 self
.assertNotIn('dummy98', output
)
1155 def test_reconfigure(self
):
1156 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1158 self
.wait_online('dummy98:routable')
1160 output
= check_output('ip -4 address show dev dummy98')
1162 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1163 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1164 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1166 check_output('ip address del 10.1.2.3/16 dev dummy98')
1167 check_output('ip address del 10.1.2.4/16 dev dummy98')
1168 check_output('ip address del 10.2.2.4/16 dev dummy98')
1170 networkctl_reconfigure('dummy98')
1171 self
.wait_online('dummy98:routable')
1173 output
= check_output('ip -4 address show dev dummy98')
1175 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1176 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1177 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1179 remove_network_unit('25-address-static.network')
1182 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1184 output
= check_output('ip -4 address show dev dummy98')
1186 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1187 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1188 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1190 copy_network_unit('25-address-static.network', copy_dropins
=False)
1192 self
.wait_online('dummy98:routable')
1194 output
= check_output('ip -4 address show dev dummy98')
1196 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1197 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1198 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1200 def test_renew(self
):
1202 self
.wait_online('veth99:routable', 'veth-peer:routable')
1203 output
= networkctl_status('veth99')
1205 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1206 self
.assertIn('Gateway: 192.168.5.3', output
)
1207 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1208 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1210 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1213 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1215 for verb
in ['renew', 'forcerenew']:
1216 networkctl(verb
, 'veth99')
1218 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1221 def test_up_down(self
):
1222 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1224 self
.wait_online('dummy98:routable')
1226 networkctl('down', 'dummy98')
1227 self
.wait_online('dummy98:off')
1228 networkctl('up', 'dummy98')
1229 self
.wait_online('dummy98:routable')
1230 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1231 self
.wait_online('dummy98:off')
1232 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1233 self
.wait_online('dummy98:routable')
1235 def test_reload(self
):
1238 copy_network_unit('11-dummy.netdev')
1240 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1242 copy_network_unit('11-dummy.network')
1244 self
.wait_online('test1:degraded')
1246 remove_network_unit('11-dummy.network')
1248 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1250 remove_network_unit('11-dummy.netdev')
1252 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1254 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1256 self
.wait_operstate('test1', 'degraded')
1258 def test_glob(self
):
1259 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1262 self
.wait_online('test1:degraded')
1264 output
= networkctl('list')
1265 self
.assertRegex(output
, '1 lo ')
1266 self
.assertRegex(output
, 'test1')
1268 output
= networkctl('list', 'test1')
1269 self
.assertNotRegex(output
, '1 lo ')
1270 self
.assertRegex(output
, 'test1')
1272 output
= networkctl('list', 'te*')
1273 self
.assertNotRegex(output
, '1 lo ')
1274 self
.assertRegex(output
, 'test1')
1276 output
= networkctl_status('te*')
1277 self
.assertNotRegex(output
, '1: lo ')
1278 self
.assertRegex(output
, 'test1')
1280 output
= networkctl_status('tes[a-z][0-9]')
1281 self
.assertNotRegex(output
, '1: lo ')
1282 self
.assertRegex(output
, 'test1')
1285 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1288 self
.wait_online('test1:degraded')
1290 output
= networkctl_status('test1')
1291 self
.assertRegex(output
, 'MTU: 1600')
1293 def test_type(self
):
1294 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1296 self
.wait_online('test1:degraded')
1298 output
= networkctl_status('test1')
1300 self
.assertRegex(output
, 'Type: ether')
1302 output
= networkctl_status('lo')
1304 self
.assertRegex(output
, 'Type: loopback')
1306 def test_unit_file(self
):
1307 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1309 self
.wait_online('test1:degraded')
1311 output
= networkctl_status('test1')
1313 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1314 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1315 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1316 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1318 output
= read_networkd_log()
1319 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
)
1321 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1322 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1323 # Let's reprocess the interface and drop the property.
1324 udevadm_trigger('/sys/class/net/lo')
1325 output
= networkctl_status('lo')
1327 self
.assertIn('Link File: n/a', output
)
1328 self
.assertIn('Network File: n/a', output
)
1330 def test_delete_links(self
):
1331 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1332 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1335 self
.wait_online('test1:degraded', 'veth99:degraded', 'veth-peer:degraded')
1337 networkctl('delete', 'test1', 'veth99')
1338 self
.check_link_exists('test1', expected
=False)
1339 self
.check_link_exists('veth99', expected
=False)
1340 self
.check_link_exists('veth-peer', expected
=False)
1342 def test_label(self
):
1345 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1353 @expectedFailureIfAlternativeNameIsNotAvailable()
1354 def test_match(self
):
1355 copy_network_unit('12-dummy-mac.netdev',
1356 '12-dummy-match-mac-01.network',
1357 '12-dummy-match-mac-02.network',
1358 '12-dummy-match-renamed.network',
1359 '12-dummy-match-altname.network',
1360 '12-dummy-altname.link')
1363 self
.wait_online('dummy98:routable')
1364 output
= networkctl_status('dummy98')
1365 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1366 output
= check_output('ip -4 address show dev dummy98')
1367 self
.assertIn('10.0.0.1/16', output
)
1369 check_output('ip link set dev dummy98 down')
1370 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1372 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1373 self
.wait_online('dummy98:routable')
1374 output
= networkctl_status('dummy98')
1375 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1377 check_output('ip link set dev dummy98 down')
1378 check_output('ip link set dev dummy98 name dummy98-1')
1380 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1381 self
.wait_online('dummy98-1:routable')
1382 output
= networkctl_status('dummy98-1')
1383 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1385 check_output('ip link set dev dummy98-1 down')
1386 check_output('ip link set dev dummy98-1 name dummy98-2')
1387 udevadm_trigger('/sys/class/net/dummy98-2')
1389 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1390 self
.wait_online('dummy98-2:routable')
1391 output
= networkctl_status('dummy98-2')
1392 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1394 def test_match_udev_property(self
):
1395 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1397 self
.wait_online('dummy98:routable')
1399 output
= networkctl_status('dummy98')
1401 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1403 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1411 def test_wait_online_any(self
):
1412 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1415 self
.wait_online('bridge99', 'test1:degraded', bool_any
=True)
1417 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1418 self
.wait_operstate('test1', 'degraded')
1420 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1428 def test_dropin_and_name_conflict(self
):
1429 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1432 self
.wait_online('dropin-test:off', setup_state
='unmanaged')
1434 output
= check_output('ip link show dropin-test')
1436 self
.assertRegex(output
, '00:50:56:c0:00:28')
1438 @expectedFailureIfModuleIsNotAvailable('bareudp')
1439 def test_bareudp(self
):
1440 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1443 self
.wait_online('bareudp99:degraded')
1445 output
= check_output('ip -d link show bareudp99')
1447 self
.assertRegex(output
, 'dstport 1000 ')
1448 self
.assertRegex(output
, 'ethertype ip ')
1450 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1451 def test_batadv(self
):
1452 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1455 self
.wait_online('batadv99:degraded')
1457 output
= check_output('ip -d link show batadv99')
1459 self
.assertRegex(output
, 'batadv')
1461 def test_bridge(self
):
1462 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1465 self
.wait_online('bridge99:no-carrier')
1467 tick
= os
.sysconf('SC_CLK_TCK')
1468 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1469 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1470 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1471 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1472 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1473 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1474 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1475 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1476 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1478 output
= networkctl_status('bridge99')
1480 self
.assertRegex(output
, 'Priority: 9')
1481 self
.assertRegex(output
, 'STP: yes')
1482 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1484 output
= check_output('ip -d link show bridge99')
1486 self
.assertIn('vlan_filtering 1 ', output
)
1487 self
.assertIn('vlan_protocol 802.1ad ', output
)
1488 self
.assertIn('vlan_default_pvid 9 ', output
)
1490 def test_bond(self
):
1491 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
1494 self
.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state
='unmanaged')
1496 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1497 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1498 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1499 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1500 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1501 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1502 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1503 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1504 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1505 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1506 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1508 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1509 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1511 output
= networkctl_status('bond99')
1513 self
.assertIn('Mode: 802.3ad', output
)
1514 self
.assertIn('Miimon: 1s', output
)
1515 self
.assertIn('Updelay: 2s', output
)
1516 self
.assertIn('Downdelay: 2s', output
)
1518 output
= networkctl_status('bond98')
1520 self
.assertIn('Mode: balance-tlb', output
)
1522 output
= networkctl_status('bond97')
1525 self
.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
1526 self
.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
1528 def test_vlan(self
):
1529 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1530 '21-vlan.network', '21-vlan-test1.network')
1533 self
.wait_online('test1:degraded', 'vlan99:routable')
1535 output
= check_output('ip -d link show test1')
1537 self
.assertRegex(output
, ' mtu 2000 ')
1539 output
= check_output('ip -d link show vlan99')
1541 self
.assertIn(' mtu 2000 ', output
)
1542 self
.assertIn('REORDER_HDR', output
)
1543 self
.assertIn('LOOSE_BINDING', output
)
1544 self
.assertIn('GVRP', output
)
1545 self
.assertIn('MVRP', output
)
1546 self
.assertIn(' id 99 ', output
)
1547 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1548 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1550 output
= check_output('ip -4 address show dev test1')
1552 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1553 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1555 output
= check_output('ip -4 address show dev vlan99')
1557 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1559 def test_vlan_on_bond(self
):
1560 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1561 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1563 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1564 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1566 self
.wait_online('bond99:off')
1567 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1569 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1570 # that the issue is fixed by the commit, let's allow to match both string.
1571 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1575 if log_re
.search(read_networkd_log()):
1580 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1582 self
.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1584 def test_macvtap(self
):
1586 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1592 print(f
'### test_macvtap(mode={mode})')
1593 with self
.subTest(mode
=mode
):
1594 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1595 '11-dummy.netdev', '25-macvtap.network')
1596 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1597 f
.write('[MACVTAP]\nMode=' + mode
)
1600 self
.wait_online('macvtap99:degraded',
1601 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1603 output
= check_output('ip -d link show macvtap99')
1605 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1607 def test_macvlan(self
):
1609 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1615 print(f
'### test_macvlan(mode={mode})')
1616 with self
.subTest(mode
=mode
):
1617 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1618 '11-dummy.netdev', '25-macvlan.network')
1619 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1620 f
.write('[MACVLAN]\nMode=' + mode
)
1623 self
.wait_online('macvlan99:degraded',
1624 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1626 output
= check_output('ip -d link show test1')
1628 self
.assertIn(' mtu 2000 ', output
)
1630 output
= check_output('ip -d link show macvlan99')
1632 self
.assertIn(' mtu 2000 ', output
)
1633 self
.assertIn(f
' macvlan mode {mode} ', output
)
1635 remove_link('test1')
1638 check_output("ip link add test1 type dummy")
1639 self
.wait_online('macvlan99:degraded',
1640 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1642 output
= check_output('ip -d link show test1')
1644 self
.assertIn(' mtu 2000 ', output
)
1646 output
= check_output('ip -d link show macvlan99')
1648 self
.assertIn(' mtu 2000 ', output
)
1649 self
.assertIn(f
' macvlan mode {mode} ', output
)
1650 self
.assertIn(' bcqueuelen 1234 ', output
)
1651 self
.assertIn(' bclim 2147483647 ', output
)
1653 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1654 def test_ipvlan(self
):
1656 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1662 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1663 with self
.subTest(mode
=mode
, flag
=flag
):
1664 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1665 '11-dummy.netdev', '25-ipvlan.network')
1666 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1667 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1670 self
.wait_online('ipvlan99:degraded', 'test1:degraded')
1672 output
= check_output('ip -d link show ipvlan99')
1674 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1676 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1677 def test_ipvtap(self
):
1679 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1685 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1686 with self
.subTest(mode
=mode
, flag
=flag
):
1687 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1688 '11-dummy.netdev', '25-ipvtap.network')
1689 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1690 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1693 self
.wait_online('ipvtap99:degraded', 'test1:degraded')
1695 output
= check_output('ip -d link show ipvtap99')
1697 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1699 def test_veth(self
):
1700 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1701 '25-veth-mtu.netdev')
1704 self
.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
1706 output
= check_output('ip -d link show veth99')
1708 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1709 output
= check_output('ip -d link show veth-peer')
1711 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1713 output
= check_output('ip -d link show veth-mtu')
1715 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1716 self
.assertRegex(output
, 'mtu 1800')
1717 output
= check_output('ip -d link show veth-mtu-peer')
1719 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1720 self
.assertRegex(output
, 'mtu 1800')
1722 def test_tuntap(self
):
1723 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1726 self
.wait_online('testtun99:degraded', 'testtap99:degraded')
1728 pid
= networkd_pid()
1729 name
= psutil
.Process(pid
).name()[:15]
1731 output
= check_output('ip -d tuntap show')
1733 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1734 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1736 output
= check_output('ip -d link show testtun99')
1738 # Old ip command does not support IFF_ flags
1739 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1740 self
.assertIn('UP,LOWER_UP', output
)
1742 output
= check_output('ip -d link show testtap99')
1744 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1745 self
.assertIn('UP,LOWER_UP', output
)
1747 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1750 self
.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state
='unmanaged')
1752 pid
= networkd_pid()
1753 name
= psutil
.Process(pid
).name()[:15]
1755 output
= check_output('ip -d tuntap show')
1757 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1758 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1760 output
= check_output('ip -d link show testtun99')
1762 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1763 self
.assertIn('UP,LOWER_UP', output
)
1765 output
= check_output('ip -d link show testtap99')
1767 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1768 self
.assertIn('UP,LOWER_UP', output
)
1770 clear_network_units()
1772 self
.wait_online('testtun99:off', 'testtap99:off', setup_state
='unmanaged')
1774 output
= check_output('ip -d tuntap show')
1776 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1777 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1782 output
= check_output('ip -d link show testtun99')
1784 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1785 if 'NO-CARRIER' in output
:
1793 output
= check_output('ip -d link show testtap99')
1795 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1796 if 'NO-CARRIER' in output
:
1801 @expectedFailureIfModuleIsNotAvailable('vrf')
1803 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1806 self
.wait_online('vrf99:carrier')
1808 @expectedFailureIfModuleIsNotAvailable('vcan')
1809 def test_vcan(self
):
1810 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1811 '25-vcan98.netdev', '25-vcan98.network')
1814 self
.wait_online('vcan99:carrier', 'vcan98:carrier')
1815 # For can devices, 'carrier' is the default required operational state.
1816 self
.wait_online('vcan99', 'vcan98')
1818 # https://github.com/systemd/systemd/issues/30140
1819 output
= check_output('ip -d link show vcan99')
1821 self
.assertIn('mtu 16 ', output
)
1823 output
= check_output('ip -d link show vcan98')
1825 self
.assertIn('mtu 16 ', output
)
1827 @expectedFailureIfModuleIsNotAvailable('vxcan')
1828 def test_vxcan(self
):
1829 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1832 self
.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
1833 # For can devices, 'carrier' is the default required operational state.
1834 self
.wait_online('vxcan99', 'vxcan-peer')
1836 @expectedFailureIfModuleIsNotAvailable('wireguard')
1837 def test_wireguard(self
):
1838 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1839 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1840 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1842 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1843 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1844 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1845 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1847 self
.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
1849 output
= check_output('ip -4 address show dev wg99')
1851 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1853 output
= check_output('ip -4 address show dev wg99')
1855 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1857 output
= check_output('ip -6 address show dev wg99')
1859 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1861 output
= check_output('ip -4 address show dev wg98')
1863 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1865 output
= check_output('ip -6 address show dev wg98')
1867 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1869 output
= check_output('ip -4 route show dev wg99 table 1234')
1871 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1873 output
= check_output('ip -6 route show dev wg99 table 1234')
1875 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1877 output
= check_output('ip -6 route show dev wg98 table 1234')
1879 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1909 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1910 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1911 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1912 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1913 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1914 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1915 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1916 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1917 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1918 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1919 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1920 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1921 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1922 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1923 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1924 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1926 if shutil
.which('wg'):
1929 output
= check_output('wg show wg99 listen-port')
1930 self
.assertEqual(output
, '51820')
1931 output
= check_output('wg show wg99 fwmark')
1932 self
.assertEqual(output
, '0x4d2')
1933 output
= check_output('wg show wg99 private-key')
1934 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1935 output
= check_output('wg show wg99 allowed-ips')
1936 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1937 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1938 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1939 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1940 output
= check_output('wg show wg99 persistent-keepalive')
1941 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1942 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1943 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1944 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1945 output
= check_output('wg show wg99 endpoints')
1946 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1947 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1948 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1949 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1950 output
= check_output('wg show wg99 preshared-keys')
1951 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1952 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1953 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1954 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1956 output
= check_output('wg show wg98 private-key')
1957 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1959 output
= check_output('wg show wg97 listen-port')
1960 self
.assertEqual(output
, '51821')
1961 output
= check_output('wg show wg97 fwmark')
1962 self
.assertEqual(output
, '0x4d3')
1964 def test_geneve(self
):
1965 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1968 self
.wait_online('geneve99:degraded')
1970 output
= check_output('ip -d link show geneve99')
1972 self
.assertRegex(output
, '192.168.22.1')
1973 self
.assertRegex(output
, '6082')
1974 self
.assertRegex(output
, 'udpcsum')
1975 self
.assertRegex(output
, 'udp6zerocsumrx')
1977 def test_ipip_tunnel(self
):
1978 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1979 '25-ipip-tunnel.netdev', '25-tunnel.network',
1980 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1981 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1982 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1984 self
.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
1986 output
= check_output('ip -d link show ipiptun99')
1988 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1989 output
= check_output('ip -d link show ipiptun98')
1991 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1992 output
= check_output('ip -d link show ipiptun97')
1994 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1995 output
= check_output('ip -d link show ipiptun96')
1997 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1999 def test_gre_tunnel(self
):
2000 copy_network_unit('12-dummy.netdev', '25-gretun.network',
2001 '25-gre-tunnel.netdev', '25-tunnel.network',
2002 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2003 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2004 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2006 self
.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2008 output
= check_output('ip -d link show gretun99')
2010 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2011 self
.assertRegex(output
, 'ikey 1.2.3.103')
2012 self
.assertRegex(output
, 'okey 1.2.4.103')
2013 self
.assertRegex(output
, 'iseq')
2014 self
.assertRegex(output
, 'oseq')
2015 output
= check_output('ip -d link show gretun98')
2017 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2018 self
.assertRegex(output
, 'ikey 0.0.0.104')
2019 self
.assertRegex(output
, 'okey 0.0.0.104')
2020 self
.assertNotRegex(output
, 'iseq')
2021 self
.assertNotRegex(output
, 'oseq')
2022 output
= check_output('ip -d link show gretun97')
2024 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2025 self
.assertRegex(output
, 'ikey 0.0.0.105')
2026 self
.assertRegex(output
, 'okey 0.0.0.105')
2027 self
.assertNotRegex(output
, 'iseq')
2028 self
.assertNotRegex(output
, 'oseq')
2029 output
= check_output('ip -d link show gretun96')
2031 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2032 self
.assertRegex(output
, 'ikey 0.0.0.106')
2033 self
.assertRegex(output
, 'okey 0.0.0.106')
2034 self
.assertNotRegex(output
, 'iseq')
2035 self
.assertNotRegex(output
, 'oseq')
2037 def test_ip6gre_tunnel(self
):
2038 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2039 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2040 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2041 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2042 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2045 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2047 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2049 output
= check_output('ip -d link show ip6gretun99')
2051 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2052 output
= check_output('ip -d link show ip6gretun98')
2054 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2055 output
= check_output('ip -d link show ip6gretun97')
2057 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2058 output
= check_output('ip -d link show ip6gretun96')
2060 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2062 def test_gretap_tunnel(self
):
2063 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2064 '25-gretap-tunnel.netdev', '25-tunnel.network',
2065 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2067 self
.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2069 output
= check_output('ip -d link show gretap99')
2071 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2072 self
.assertRegex(output
, 'ikey 0.0.0.106')
2073 self
.assertRegex(output
, 'okey 0.0.0.106')
2074 self
.assertRegex(output
, 'iseq')
2075 self
.assertRegex(output
, 'oseq')
2076 self
.assertIn('nopmtudisc', output
)
2077 self
.assertIn('ignore-df', output
)
2078 output
= check_output('ip -d link show gretap98')
2080 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2081 self
.assertRegex(output
, 'ikey 0.0.0.107')
2082 self
.assertRegex(output
, 'okey 0.0.0.107')
2083 self
.assertRegex(output
, 'iseq')
2084 self
.assertRegex(output
, 'oseq')
2086 def test_ip6gretap_tunnel(self
):
2087 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2088 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2089 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2091 self
.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2093 output
= check_output('ip -d link show ip6gretap99')
2095 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2096 output
= check_output('ip -d link show ip6gretap98')
2098 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2100 def test_vti_tunnel(self
):
2101 copy_network_unit('12-dummy.netdev', '25-vti.network',
2102 '25-vti-tunnel.netdev', '25-tunnel.network',
2103 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2104 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2105 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2107 self
.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2109 output
= check_output('ip -d link show vtitun99')
2111 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2112 output
= check_output('ip -d link show vtitun98')
2114 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2115 output
= check_output('ip -d link show vtitun97')
2117 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2118 output
= check_output('ip -d link show vtitun96')
2120 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2122 def test_vti6_tunnel(self
):
2123 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2124 '25-vti6-tunnel.netdev', '25-tunnel.network',
2125 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2126 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2128 self
.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2130 output
= check_output('ip -d link show vti6tun99')
2132 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2133 output
= check_output('ip -d link show vti6tun98')
2135 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2136 output
= check_output('ip -d link show vti6tun97')
2138 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2140 def test_ip6tnl_tunnel(self
):
2141 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2142 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2143 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2144 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2145 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2146 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2147 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2149 self
.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2150 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2151 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2153 output
= check_output('ip -d link show ip6tnl99')
2155 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2156 output
= check_output('ip -d link show ip6tnl98')
2158 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2159 output
= check_output('ip -d link show ip6tnl97')
2161 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2162 output
= check_output('ip -d link show ip6tnl-external')
2164 self
.assertIn('ip6tnl-external@NONE:', output
)
2165 self
.assertIn('ip6tnl external ', output
)
2166 output
= check_output('ip -d link show ip6tnl-slaac')
2168 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2170 output
= check_output('ip -6 address show veth99')
2172 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2174 output
= check_output('ip -4 route show default')
2176 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2178 def test_sit_tunnel(self
):
2179 copy_network_unit('12-dummy.netdev', '25-sit.network',
2180 '25-sit-tunnel.netdev', '25-tunnel.network',
2181 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2182 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2183 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2185 self
.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2187 output
= check_output('ip -d link show sittun99')
2189 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2190 output
= check_output('ip -d link show sittun98')
2192 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2193 output
= check_output('ip -d link show sittun97')
2195 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2196 output
= check_output('ip -d link show sittun96')
2198 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2200 def test_isatap_tunnel(self
):
2201 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2202 '25-isatap-tunnel.netdev', '25-tunnel.network')
2204 self
.wait_online('isataptun99:routable', 'dummy98:degraded')
2206 output
= check_output('ip -d link show isataptun99')
2208 self
.assertRegex(output
, "isatap ")
2210 def test_6rd_tunnel(self
):
2211 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2212 '25-6rd-tunnel.netdev', '25-tunnel.network')
2214 self
.wait_online('sittun99:routable', 'dummy98:degraded')
2216 output
= check_output('ip -d link show sittun99')
2218 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2220 @expectedFailureIfERSPANv0IsNotSupported()
2221 def test_erspan_tunnel_v0(self
):
2222 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2223 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2224 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2226 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2228 output
= check_output('ip -d link show erspan99')
2230 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2231 self
.assertIn('erspan_ver 0', output
)
2232 self
.assertNotIn('erspan_index 123', output
)
2233 self
.assertNotIn('erspan_dir ingress', output
)
2234 self
.assertNotIn('erspan_hwid 1f', output
)
2235 self
.assertIn('ikey 0.0.0.101', output
)
2236 self
.assertIn('iseq', output
)
2237 self
.assertIn('nopmtudisc', output
)
2238 self
.assertIn('ignore-df', output
)
2239 output
= check_output('ip -d link show erspan98')
2241 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2242 self
.assertIn('erspan_ver 0', output
)
2243 self
.assertNotIn('erspan_index 124', output
)
2244 self
.assertNotIn('erspan_dir egress', output
)
2245 self
.assertNotIn('erspan_hwid 2f', output
)
2246 self
.assertIn('ikey 0.0.0.102', output
)
2247 self
.assertIn('iseq', output
)
2249 def test_erspan_tunnel_v1(self
):
2250 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2251 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2252 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2254 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2256 output
= check_output('ip -d link show erspan99')
2258 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2259 self
.assertIn('erspan_ver 1', output
)
2260 self
.assertIn('erspan_index 123', output
)
2261 self
.assertNotIn('erspan_dir ingress', output
)
2262 self
.assertNotIn('erspan_hwid 1f', output
)
2263 self
.assertIn('ikey 0.0.0.101', output
)
2264 self
.assertIn('okey 0.0.0.101', output
)
2265 self
.assertIn('iseq', output
)
2266 self
.assertIn('oseq', output
)
2267 output
= check_output('ip -d link show erspan98')
2269 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2270 self
.assertIn('erspan_ver 1', output
)
2271 self
.assertIn('erspan_index 124', output
)
2272 self
.assertNotIn('erspan_dir egress', output
)
2273 self
.assertNotIn('erspan_hwid 2f', output
)
2274 self
.assertIn('ikey 0.0.0.102', output
)
2275 self
.assertIn('okey 0.0.0.102', output
)
2276 self
.assertIn('iseq', output
)
2277 self
.assertIn('oseq', output
)
2279 @expectedFailureIfERSPANv2IsNotSupported()
2280 def test_erspan_tunnel_v2(self
):
2281 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2282 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2283 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2285 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2287 output
= check_output('ip -d link show erspan99')
2289 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2290 self
.assertIn('erspan_ver 2', output
)
2291 self
.assertNotIn('erspan_index 123', output
)
2292 self
.assertIn('erspan_dir ingress', output
)
2293 self
.assertIn('erspan_hwid 0x1f', output
)
2294 self
.assertIn('ikey 0.0.0.101', output
)
2295 self
.assertIn('okey 0.0.0.101', output
)
2296 self
.assertIn('iseq', output
)
2297 self
.assertIn('oseq', output
)
2298 output
= check_output('ip -d link show erspan98')
2300 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2301 self
.assertIn('erspan_ver 2', output
)
2302 self
.assertNotIn('erspan_index 124', output
)
2303 self
.assertIn('erspan_dir egress', output
)
2304 self
.assertIn('erspan_hwid 0x2f', output
)
2305 self
.assertIn('ikey 0.0.0.102', output
)
2306 self
.assertIn('okey 0.0.0.102', output
)
2307 self
.assertIn('iseq', output
)
2308 self
.assertIn('oseq', output
)
2310 def test_tunnel_independent(self
):
2311 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2314 self
.wait_online('ipiptun99:carrier')
2316 def test_tunnel_independent_loopback(self
):
2317 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2320 self
.wait_online('ipiptun99:carrier')
2322 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2323 def test_xfrm(self
):
2324 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2325 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2326 '26-netdev-link-local-addressing-yes.network')
2329 self
.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
2331 output
= check_output('ip -d link show dev xfrm98')
2333 self
.assertIn('xfrm98@dummy98:', output
)
2334 self
.assertIn('xfrm if_id 0x98 ', output
)
2336 output
= check_output('ip -d link show dev xfrm99')
2338 self
.assertIn('xfrm99@lo:', output
)
2339 self
.assertIn('xfrm if_id 0x99 ', output
)
2341 @expectedFailureIfModuleIsNotAvailable('fou')
2343 # The following redundant check is necessary for CentOS CI.
2344 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2345 self
.assertTrue(is_module_available('fou'))
2347 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2348 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2349 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2352 self
.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state
='unmanaged')
2354 output
= check_output('ip fou show')
2356 self
.assertRegex(output
, 'port 55555 ipproto 4')
2357 self
.assertRegex(output
, 'port 55556 ipproto 47')
2359 output
= check_output('ip -d link show ipiptun96')
2361 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2362 output
= check_output('ip -d link show sittun96')
2364 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2365 output
= check_output('ip -d link show gretun96')
2367 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2368 output
= check_output('ip -d link show gretap96')
2370 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2372 def test_vxlan(self
):
2373 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2374 '25-vxlan.netdev', '25-vxlan.network',
2375 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2376 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2377 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2378 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2381 self
.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2382 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
2384 output
= check_output('ip -d -d link show vxlan99')
2386 self
.assertIn('999', output
)
2387 self
.assertIn('5555', output
)
2388 self
.assertIn('l2miss', output
)
2389 self
.assertIn('l3miss', output
)
2390 self
.assertIn('gbp', output
)
2391 # Since [0] some of the options use slightly different names and some
2392 # options with default values are shown only if the -d(etails) setting
2394 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2395 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2396 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2397 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2398 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2399 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2401 output
= check_output('bridge fdb show dev vxlan99')
2403 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2404 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2405 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2407 output
= networkctl_status('vxlan99')
2409 self
.assertIn('VNI: 999', output
)
2410 self
.assertIn('Destination Port: 5555', output
)
2411 self
.assertIn('Underlying Device: test1', output
)
2413 output
= check_output('bridge fdb show dev vxlan97')
2415 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2416 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2417 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2419 output
= check_output('ip -d link show vxlan-slaac')
2421 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2423 output
= check_output('ip -6 address show veth99')
2425 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2427 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2428 def test_macsec(self
):
2429 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2430 '26-macsec.network', '12-dummy.netdev')
2433 self
.wait_online('dummy98:degraded', 'macsec99:routable')
2435 output
= check_output('ip -d link show macsec99')
2437 self
.assertRegex(output
, 'macsec99@dummy98')
2438 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2439 self
.assertRegex(output
, 'encrypt on')
2441 output
= check_output('ip macsec show macsec99')
2443 self
.assertRegex(output
, 'encrypt on')
2444 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2445 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2446 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2447 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2448 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2449 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2450 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2451 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2452 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2453 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2454 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2456 def test_nlmon(self
):
2457 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2460 self
.wait_online('nlmon99:carrier')
2462 @expectedFailureIfModuleIsNotAvailable('ifb')
2464 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2467 self
.wait_online('ifb99:degraded')
2469 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2470 def test_rps_cpu_1(self
):
2471 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
2474 self
.wait_online('dummy98:carrier')
2476 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2478 self
.assertEqual(int(output
.replace(',', ''), base
=16), 2)
2480 @unittest.skipUnless(os
.cpu_count() >= 2, reason
="CPU count should be >= 2 to pass this test")
2481 def test_rps_cpu_0_1(self
):
2482 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
2485 self
.wait_online('dummy98:carrier')
2487 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2489 self
.assertEqual(int(output
.replace(',', ''), base
=16), 3)
2491 @unittest.skipUnless(os
.cpu_count() >= 4, reason
="CPU count should be >= 4 to pass this test")
2492 def test_rps_cpu_multi(self
):
2493 copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
2496 self
.wait_online('dummy98:carrier')
2498 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2500 self
.assertEqual(int(output
.replace(',', ''), base
=16), 15)
2502 def test_rps_cpu(self
):
2503 cpu_count
= os
.cpu_count()
2505 copy_network_unit('12-dummy.netdev', '12-dummy.network')
2508 self
.wait_online('dummy98:carrier')
2511 copy_network_unit('25-rps-cpu-0.link')
2512 udevadm_trigger('/sys/class/net/dummy98')
2513 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2515 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2516 remove_network_unit('25-rps-cpu-0.link')
2519 copy_network_unit('25-rps-cpu-all.link')
2520 udevadm_trigger('/sys/class/net/dummy98')
2521 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2523 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2524 remove_network_unit('25-rps-cpu-all.link')
2527 copy_network_unit('24-rps-cpu-disable.link')
2528 udevadm_trigger('/sys/class/net/dummy98')
2529 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2531 self
.assertEqual(int(output
.replace(',', ''), base
=16), 0)
2532 remove_network_unit('24-rps-cpu-disable.link')
2535 copy_network_unit('25-rps-cpu-all.link')
2536 udevadm_trigger('/sys/class/net/dummy98')
2537 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2539 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2540 remove_network_unit('25-rps-cpu-all.link')
2542 # empty -> unchanged
2543 copy_network_unit('24-rps-cpu-empty.link')
2544 udevadm_trigger('/sys/class/net/dummy98')
2545 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2547 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2548 remove_network_unit('24-rps-cpu-empty.link')
2550 # 0, then empty -> unchanged
2551 copy_network_unit('25-rps-cpu-0-empty.link')
2552 udevadm_trigger('/sys/class/net/dummy98')
2553 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2555 self
.assertEqual(f
"{int(output.replace(',', ''), base=16):x}", f
'{(1 << cpu_count) - 1:x}')
2556 remove_network_unit('25-rps-cpu-0-empty.link')
2558 # 0, then invalid -> 0
2559 copy_network_unit('25-rps-cpu-0-invalid.link')
2560 udevadm_trigger('/sys/class/net/dummy98')
2561 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2563 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2564 remove_network_unit('25-rps-cpu-0-invalid.link')
2566 # invalid -> unchanged
2567 copy_network_unit('24-rps-cpu-invalid.link')
2568 udevadm_trigger('/sys/class/net/dummy98')
2569 output
= check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
2571 self
.assertEqual(int(output
.replace(',', ''), base
=16), 1)
2572 remove_network_unit('24-rps-cpu-invalid.link')
2574 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2582 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2583 def test_l2tp_udp(self
):
2584 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2585 '25-l2tp-udp.netdev', '25-l2tp.network')
2588 self
.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
2590 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2592 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2593 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2594 self
.assertRegex(output
, "Peer tunnel 11")
2595 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2596 self
.assertRegex(output
, "UDP checksum: enabled")
2598 output
= check_output('ip l2tp show session tid 10 session_id 15')
2600 self
.assertRegex(output
, "Session 15 in tunnel 10")
2601 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2602 self
.assertRegex(output
, "interface name: l2tp-ses1")
2604 output
= check_output('ip l2tp show session tid 10 session_id 17')
2606 self
.assertRegex(output
, "Session 17 in tunnel 10")
2607 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2608 self
.assertRegex(output
, "interface name: l2tp-ses2")
2610 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2611 def test_l2tp_ip(self
):
2612 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2613 '25-l2tp-ip.netdev', '25-l2tp.network')
2616 self
.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
2618 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2620 self
.assertRegex(output
, "Tunnel 10, encap IP")
2621 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2622 self
.assertRegex(output
, "Peer tunnel 12")
2624 output
= check_output('ip l2tp show session tid 10 session_id 25')
2626 self
.assertRegex(output
, "Session 25 in tunnel 10")
2627 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2628 self
.assertRegex(output
, "interface name: l2tp-ses3")
2630 output
= check_output('ip l2tp show session tid 10 session_id 27')
2632 self
.assertRegex(output
, "Session 27 in tunnel 10")
2633 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2634 self
.assertRegex(output
, "interface name: l2tp-ses4")
2636 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2644 def verify_address_static(
2674 output
= check_output('ip address show dev dummy98')
2678 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2679 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2680 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2681 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2682 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2683 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2686 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2687 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2688 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2691 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2692 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2693 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2696 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2697 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2698 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2699 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2700 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2701 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2704 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2705 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2708 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2709 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2710 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2711 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2714 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2715 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2717 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2719 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2721 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2723 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2726 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2727 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2728 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2729 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2732 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2733 prefix16
= ip4_null_16
[:-len('.0.1')]
2734 self
.assertTrue(ip4_null_24
.endswith('.1'))
2735 prefix24
= ip4_null_24
[:-len('.1')]
2736 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2737 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2738 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2739 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2742 self
.assertNotIn('10.4.4.1', output
)
2743 self
.assertNotIn('10.5.4.1', output
)
2744 self
.assertNotIn('10.5.5.1', output
)
2745 self
.assertNotIn('10.8.2.1', output
)
2746 self
.assertNotIn('10.9.3.1', output
)
2747 self
.assertNotIn('2001:db8:0:f101::2', output
)
2748 self
.assertNotIn('2001:db8:0:f103::4', output
)
2751 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2753 check_json(networkctl_json())
2755 def test_address_static(self
):
2756 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2757 self
.setup_nftset('addr4', 'ipv4_addr')
2758 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2759 self
.setup_nftset('ifindex', 'iface_index')
2762 self
.wait_online('dummy98:routable')
2766 output
= check_output('ip -4 --json address show dev dummy98')
2767 for i
in json
.loads(output
)[0]['addr_info']:
2768 if i
['label'] == 'subnet16':
2769 ip4_null_16
= i
['local']
2770 elif i
['label'] == 'subnet24':
2771 ip4_null_24
= i
['local']
2772 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2773 self
.assertTrue(ip4_null_24
.endswith('.1'))
2777 output
= check_output('ip -6 --json address show dev dummy98')
2778 for i
in json
.loads(output
)[0]['addr_info']:
2779 if i
['prefixlen'] == 73:
2780 ip6_null_73
= i
['local']
2781 elif i
['prefixlen'] == 74:
2782 ip6_null_74
= i
['local']
2783 self
.assertTrue(ip6_null_73
.endswith(':1'))
2784 self
.assertTrue(ip6_null_74
.endswith(':1'))
2786 self
.verify_address_static(
2791 broadcast2
=' brd 10.4.2.255',
2792 broadcast3
=' brd 10.4.3.63',
2793 peer1
=' peer 10.5.1.101/24',
2794 peer2
=' peer 10.5.2.101/24',
2795 peer3
='/24 brd 10.5.3.255',
2796 peer4
=' peer 2001:db8:0:f103::101/128',
2797 peer5
=' peer 2001:db8:0:f103::102/128',
2802 deprecated2
=' deprecated',
2804 deprecated4
=' deprecated',
2806 flag1
=' noprefixroute',
2808 flag3
=' noprefixroute',
2809 flag4
=' home mngtmpaddr',
2810 ip4_null_16
=ip4_null_16
,
2811 ip4_null_24
=ip4_null_24
,
2812 ip6_null_73
=ip6_null_73
,
2813 ip6_null_74
=ip6_null_74
,
2816 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2817 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2818 self
.check_nftset('ifindex', 'dummy98')
2820 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2822 copy_network_unit('25-address-static.network.d/10-override.conf')
2824 self
.wait_online('dummy98:routable')
2825 self
.verify_address_static(
2826 label1
='new-label1',
2828 label3
='new-label3',
2829 broadcast1
=' brd 10.4.1.255',
2831 broadcast3
=' brd 10.4.3.31',
2832 peer1
=' peer 10.5.1.102/24',
2833 peer2
='/24 brd 10.5.2.255',
2834 peer3
=' peer 10.5.3.102/24',
2835 peer4
=' peer 2001:db8:0:f103::201/128',
2837 peer6
=' peer 2001:db8:0:f103::203/128',
2840 deprecated1
=' deprecated',
2842 deprecated3
=' deprecated',
2846 flag2
=' noprefixroute',
2847 flag3
=' home mngtmpaddr',
2848 flag4
=' noprefixroute',
2849 ip4_null_16
=ip4_null_16
,
2850 ip4_null_24
=ip4_null_24
,
2851 ip6_null_73
=ip6_null_73
,
2852 ip6_null_74
=ip6_null_74
,
2855 networkctl_reconfigure('dummy98')
2856 self
.wait_online('dummy98:routable')
2857 self
.verify_address_static(
2858 label1
='new-label1',
2860 label3
='new-label3',
2861 broadcast1
=' brd 10.4.1.255',
2863 broadcast3
=' brd 10.4.3.31',
2864 peer1
=' peer 10.5.1.102/24',
2865 peer2
='/24 brd 10.5.2.255',
2866 peer3
=' peer 10.5.3.102/24',
2867 peer4
=' peer 2001:db8:0:f103::201/128',
2869 peer6
=' peer 2001:db8:0:f103::203/128',
2872 deprecated1
=' deprecated',
2874 deprecated3
=' deprecated',
2878 flag2
=' noprefixroute',
2879 flag3
=' home mngtmpaddr',
2880 flag4
=' noprefixroute',
2881 ip4_null_16
=ip4_null_16
,
2882 ip4_null_24
=ip4_null_24
,
2883 ip6_null_73
=ip6_null_73
,
2884 ip6_null_74
=ip6_null_74
,
2888 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2889 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2890 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2891 output
= check_output('ip address show dev dummy98')
2893 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2894 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2896 # 2. reconfigure the interface, and check the deprecated flag is set again
2897 networkctl_reconfigure('dummy98')
2898 self
.wait_online('dummy98:routable')
2899 self
.verify_address_static(
2900 label1
='new-label1',
2902 label3
='new-label3',
2903 broadcast1
=' brd 10.4.1.255',
2905 broadcast3
=' brd 10.4.3.31',
2906 peer1
=' peer 10.5.1.102/24',
2907 peer2
='/24 brd 10.5.2.255',
2908 peer3
=' peer 10.5.3.102/24',
2909 peer4
=' peer 2001:db8:0:f103::201/128',
2911 peer6
=' peer 2001:db8:0:f103::203/128',
2914 deprecated1
=' deprecated',
2916 deprecated3
=' deprecated',
2920 flag2
=' noprefixroute',
2921 flag3
=' home mngtmpaddr',
2922 flag4
=' noprefixroute',
2923 ip4_null_16
=ip4_null_16
,
2924 ip4_null_24
=ip4_null_24
,
2925 ip6_null_73
=ip6_null_73
,
2926 ip6_null_74
=ip6_null_74
,
2929 # test for ENOBUFS issue #17012 (with reload)
2930 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2932 self
.wait_online('dummy98:routable')
2933 output
= check_output('ip -4 address show dev dummy98')
2934 for i
in range(1, 254):
2935 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2937 # (with reconfigure)
2938 networkctl_reconfigure('dummy98')
2939 self
.wait_online('dummy98:routable')
2940 output
= check_output('ip -4 address show dev dummy98')
2941 for i
in range(1, 254):
2942 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2944 # test for an empty string assignment for Address= in [Network]
2945 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2947 self
.wait_online('dummy98:routable')
2948 output
= check_output('ip -4 address show dev dummy98')
2949 for i
in range(1, 254):
2950 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2951 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2953 def test_address_ipv4acd(self
):
2954 check_output('ip netns add ns99')
2955 check_output('ip link add veth99 type veth peer veth-peer')
2956 check_output('ip link set veth-peer netns ns99')
2957 check_output('ip link set veth99 up')
2958 check_output('ip netns exec ns99 ip link set veth-peer up')
2959 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2961 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2963 self
.wait_online('veth99:routable')
2965 output
= check_output('ip -4 address show dev veth99')
2967 self
.assertNotIn('192.168.100.10/24', output
)
2968 self
.assertIn('192.168.100.11/24', output
)
2970 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2972 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2974 output
= check_output('ip -4 address show dev veth99')
2976 self
.assertNotIn('192.168.100.10/24', output
)
2977 self
.assertIn('192.168.100.11/24', output
)
2979 def test_address_peer_ipv4(self
):
2980 # test for issue #17304
2981 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2983 for trial
in range(2):
2989 self
.wait_online('dummy98:routable')
2991 output
= check_output('ip -4 address show dev dummy98')
2992 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2994 @expectedFailureIfModuleIsNotAvailable('vrf')
2995 def test_prefix_route(self
):
2996 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2997 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2998 '25-vrf.netdev', '25-vrf.network')
2999 for trial
in range(2):
3005 self
.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
3007 output
= check_output('ip route show table 42 dev dummy98')
3008 print('### ip route show table 42 dev dummy98')
3010 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
3011 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
3012 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
3013 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
3014 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
3015 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
3016 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
3017 output
= check_output('ip -6 route show table 42 dev dummy98')
3018 print('### ip -6 route show table 42 dev dummy98')
3022 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
3023 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
3024 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
3025 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
3026 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
3027 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
3028 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3029 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3033 output
= check_output('ip route show dev test1')
3034 print('### ip route show dev test1')
3036 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
3037 output
= check_output('ip route show table local dev test1')
3038 print('### ip route show table local dev test1')
3040 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
3041 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
3042 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
3043 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
3044 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
3045 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
3046 output
= check_output('ip -6 route show dev test1')
3047 print('### ip -6 route show dev test1')
3049 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
3050 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
3051 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
3052 output
= check_output('ip -6 route show table local dev test1')
3053 print('### ip -6 route show table local dev test1')
3055 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
3056 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
3057 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
3058 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
3059 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
3061 def test_configure_without_carrier(self
):
3062 copy_network_unit('11-dummy.netdev')
3064 self
.wait_operstate('test1', 'off', '')
3065 check_output('ip link set dev test1 up carrier off')
3067 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
3069 self
.wait_online('test1:no-carrier')
3071 carrier_map
= {'on': '1', 'off': '0'}
3072 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3073 for carrier
in ['off', 'on', 'off']:
3074 with self
.subTest(carrier
=carrier
):
3075 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3076 check_output(f
'ip link set dev test1 carrier {carrier}')
3077 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3079 output
= networkctl_status('test1')
3081 self
.assertRegex(output
, '192.168.0.15')
3082 self
.assertRegex(output
, '192.168.0.1')
3083 self
.assertRegex(output
, routable_map
[carrier
])
3085 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
3086 copy_network_unit('11-dummy.netdev')
3088 self
.wait_operstate('test1', 'off', '')
3089 check_output('ip link set dev test1 up carrier off')
3091 copy_network_unit('25-test1.network')
3093 self
.wait_online('test1:no-carrier')
3095 carrier_map
= {'on': '1', 'off': '0'}
3096 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
3097 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
3098 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
3099 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
3100 check_output(f
'ip link set dev test1 carrier {carrier}')
3101 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
3103 output
= networkctl_status('test1')
3106 self
.assertRegex(output
, '192.168.0.15')
3107 self
.assertRegex(output
, '192.168.0.1')
3109 self
.assertNotRegex(output
, '192.168.0.15')
3110 self
.assertNotRegex(output
, '192.168.0.1')
3111 self
.assertRegex(output
, routable_map
[carrier
])
3113 def test_routing_policy_rule(self
):
3114 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
3116 self
.wait_online('test1:degraded')
3118 output
= check_output('ip rule list iif test1 priority 111')
3120 self
.assertRegex(output
, '111:')
3121 self
.assertRegex(output
, 'from 192.168.100.18')
3122 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3123 self
.assertRegex(output
, 'iif test1')
3124 self
.assertRegex(output
, 'oif test1')
3125 self
.assertRegex(output
, 'lookup 7')
3127 output
= check_output('ip rule list iif test1 priority 101')
3129 self
.assertRegex(output
, '101:')
3130 self
.assertRegex(output
, 'from all')
3131 self
.assertRegex(output
, 'iif test1')
3132 self
.assertRegex(output
, 'lookup 9')
3134 output
= check_output('ip -6 rule list iif test1 priority 100')
3136 self
.assertRegex(output
, '100:')
3137 self
.assertRegex(output
, 'from all')
3138 self
.assertRegex(output
, 'iif test1')
3139 self
.assertRegex(output
, 'lookup 8')
3141 output
= check_output('ip rule list iif test1 priority 102')
3143 self
.assertRegex(output
, '102:')
3144 self
.assertRegex(output
, 'from 0.0.0.0/8')
3145 self
.assertRegex(output
, 'iif test1')
3146 self
.assertRegex(output
, 'lookup 10')
3148 check_json(networkctl_json())
3150 def test_routing_policy_rule_issue_11280(self
):
3151 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3152 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3154 for trial
in range(3):
3155 restart_networkd(show_logs
=(trial
> 0))
3156 self
.wait_online('test1:degraded', 'dummy98:degraded')
3158 output
= check_output('ip rule list table 7')
3160 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3162 output
= check_output('ip rule list table 8')
3164 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3166 def test_routing_policy_rule_reconfigure(self
):
3167 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3169 self
.wait_online('test1:degraded')
3171 output
= check_output('ip rule list table 1011')
3173 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3174 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3175 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3176 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3178 output
= check_output('ip -6 rule list table 1011')
3180 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3182 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3184 self
.wait_online('test1:degraded')
3186 output
= check_output('ip rule list table 1011')
3188 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3189 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3190 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3191 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3193 output
= check_output('ip -6 rule list table 1011')
3195 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3196 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3198 call('ip rule delete priority 10111')
3199 call('ip rule delete priority 10112')
3200 call('ip rule delete priority 10113')
3201 call('ip rule delete priority 10114')
3202 call('ip -6 rule delete priority 10113')
3204 output
= check_output('ip rule list table 1011')
3206 self
.assertEqual(output
, '')
3208 output
= check_output('ip -6 rule list table 1011')
3210 self
.assertEqual(output
, '')
3212 networkctl_reconfigure('test1')
3213 self
.wait_online('test1:degraded')
3215 output
= check_output('ip rule list table 1011')
3217 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3218 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3219 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3220 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3222 output
= check_output('ip -6 rule list table 1011')
3224 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3226 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3227 def test_routing_policy_rule_port_range(self
):
3228 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3230 self
.wait_online('test1:degraded')
3232 output
= check_output('ip rule')
3234 self
.assertRegex(output
, '111')
3235 self
.assertRegex(output
, 'from 192.168.100.18')
3236 self
.assertRegex(output
, '1123-1150')
3237 self
.assertRegex(output
, '3224-3290')
3238 self
.assertRegex(output
, 'tcp')
3239 self
.assertRegex(output
, 'lookup 7')
3241 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3242 def test_routing_policy_rule_invert(self
):
3243 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3245 self
.wait_online('test1:degraded')
3247 output
= check_output('ip rule')
3249 self
.assertRegex(output
, '111')
3250 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3251 self
.assertRegex(output
, 'tcp')
3252 self
.assertRegex(output
, 'lookup 7')
3254 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3255 def test_routing_policy_rule_l3mdev(self
):
3256 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3258 self
.wait_online('test1:degraded')
3260 output
= check_output('ip rule')
3262 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3263 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3265 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3266 def test_routing_policy_rule_uidrange(self
):
3267 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3269 self
.wait_online('test1:degraded')
3271 output
= check_output('ip rule')
3273 self
.assertRegex(output
, '111')
3274 self
.assertRegex(output
, 'from 192.168.100.18')
3275 self
.assertRegex(output
, 'lookup 7')
3276 self
.assertRegex(output
, 'uidrange 100-200')
3278 def _test_route_static(self
, manage_foreign_routes
):
3279 if not manage_foreign_routes
:
3280 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3282 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3283 '25-route-static-test1.network', '11-dummy.netdev')
3285 self
.wait_online('dummy98:routable')
3287 output
= networkctl_status('dummy98')
3290 print('### ip -6 route show dev dummy98')
3291 output
= check_output('ip -6 route show dev dummy98')
3293 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3294 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3295 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3297 print('### ip -6 route show default')
3298 output
= check_output('ip -6 route show default')
3300 self
.assertIn('default', output
)
3301 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3303 print('### ip -4 route show dev dummy98')
3304 output
= check_output('ip -4 route show dev dummy98')
3306 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3307 self
.assertIn('149.10.124.64 proto static scope link', output
)
3308 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3309 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3310 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3311 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3312 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3313 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3315 print('### ip -4 route show dev dummy98 default')
3316 output
= check_output('ip -4 route show dev dummy98 default')
3318 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3319 self
.assertIn('default via 149.10.124.64 proto static', output
)
3320 self
.assertIn('default proto static', output
)
3321 self
.assertIn('default via 1.1.8.104 proto static', output
)
3323 print('### ip -4 route show table local dev dummy98')
3324 output
= check_output('ip -4 route show table local dev dummy98')
3326 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3327 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3328 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3330 print('### ip -4 route show type blackhole')
3331 output
= check_output('ip -4 route show type blackhole')
3333 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3335 print('### ip -4 route show type unreachable')
3336 output
= check_output('ip -4 route show type unreachable')
3338 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3340 print('### ip -4 route show type prohibit')
3341 output
= check_output('ip -4 route show type prohibit')
3343 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3345 print('### ip -6 route show type blackhole')
3346 output
= check_output('ip -6 route show type blackhole')
3348 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3350 print('### ip -6 route show type unreachable')
3351 output
= check_output('ip -6 route show type unreachable')
3353 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3355 print('### ip -6 route show type prohibit')
3356 output
= check_output('ip -6 route show type prohibit')
3358 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3360 print('### ip route show 192.168.10.1')
3361 output
= check_output('ip route show 192.168.10.1')
3363 self
.assertIn('192.168.10.1 proto static', output
)
3364 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3365 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3366 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3367 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3369 print('### ip route show 192.168.10.2')
3370 output
= check_output('ip route show 192.168.10.2')
3372 # old ip command does not show IPv6 gateways...
3373 self
.assertIn('192.168.10.2 proto static', output
)
3374 self
.assertIn('nexthop', output
)
3375 self
.assertIn('dev test1 weight 20', output
)
3376 self
.assertIn('dev test1 weight 30', output
)
3377 self
.assertIn('dev dummy98 weight 10', output
)
3378 self
.assertIn('dev dummy98 weight 5', output
)
3380 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3381 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3383 # old ip command does not show 'nexthop' keyword and weight...
3384 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3385 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3386 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3387 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3388 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3390 check_json(networkctl_json())
3392 copy_network_unit('25-address-static.network', copy_dropins
=False)
3394 self
.wait_online('dummy98:routable')
3396 # check all routes managed by Manager are removed
3397 print('### ip -4 route show type blackhole')
3398 output
= check_output('ip -4 route show type blackhole')
3400 self
.assertEqual(output
, '')
3402 print('### ip -4 route show type unreachable')
3403 output
= check_output('ip -4 route show type unreachable')
3405 self
.assertEqual(output
, '')
3407 print('### ip -4 route show type prohibit')
3408 output
= check_output('ip -4 route show type prohibit')
3410 self
.assertEqual(output
, '')
3412 print('### ip -6 route show type blackhole')
3413 output
= check_output('ip -6 route show type blackhole')
3415 self
.assertEqual(output
, '')
3417 print('### ip -6 route show type unreachable')
3418 output
= check_output('ip -6 route show type unreachable')
3420 self
.assertEqual(output
, '')
3422 print('### ip -6 route show type prohibit')
3423 output
= check_output('ip -6 route show type prohibit')
3425 self
.assertEqual(output
, '')
3427 remove_network_unit('25-address-static.network')
3429 self
.wait_online('dummy98:routable')
3431 # check all routes managed by Manager are reconfigured
3432 print('### ip -4 route show type blackhole')
3433 output
= check_output('ip -4 route show type blackhole')
3435 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3437 print('### ip -4 route show type unreachable')
3438 output
= check_output('ip -4 route show type unreachable')
3440 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3442 print('### ip -4 route show type prohibit')
3443 output
= check_output('ip -4 route show type prohibit')
3445 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3447 print('### ip -6 route show type blackhole')
3448 output
= check_output('ip -6 route show type blackhole')
3450 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3452 print('### ip -6 route show type unreachable')
3453 output
= check_output('ip -6 route show type unreachable')
3455 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3457 print('### ip -6 route show type prohibit')
3458 output
= check_output('ip -6 route show type prohibit')
3460 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3462 remove_link('dummy98')
3465 # check all routes managed by Manager are removed
3466 print('### ip -4 route show type blackhole')
3467 output
= check_output('ip -4 route show type blackhole')
3469 self
.assertEqual(output
, '')
3471 print('### ip -4 route show type unreachable')
3472 output
= check_output('ip -4 route show type unreachable')
3474 self
.assertEqual(output
, '')
3476 print('### ip -4 route show type prohibit')
3477 output
= check_output('ip -4 route show type prohibit')
3479 self
.assertEqual(output
, '')
3481 print('### ip -6 route show type blackhole')
3482 output
= check_output('ip -6 route show type blackhole')
3484 self
.assertEqual(output
, '')
3486 print('### ip -6 route show type unreachable')
3487 output
= check_output('ip -6 route show type unreachable')
3489 self
.assertEqual(output
, '')
3491 print('### ip -6 route show type prohibit')
3492 output
= check_output('ip -6 route show type prohibit')
3494 self
.assertEqual(output
, '')
3496 def test_route_static(self
):
3498 for manage_foreign_routes
in [True, False]:
3504 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3505 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3506 self
._test
_route
_static
(manage_foreign_routes
)
3508 @expectedFailureIfRTA_VIAIsNotSupported()
3509 def test_route_via_ipv6(self
):
3510 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3512 self
.wait_online('dummy98:routable')
3514 output
= networkctl_status('dummy98')
3517 print('### ip -6 route show dev dummy98')
3518 output
= check_output('ip -6 route show dev dummy98')
3520 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3521 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3523 print('### ip -4 route show dev dummy98')
3524 output
= check_output('ip -4 route show dev dummy98')
3526 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3527 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3529 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3530 def test_route_congctl(self
):
3531 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3533 self
.wait_online('dummy98:routable')
3535 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3536 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3538 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3539 self
.assertIn('congctl dctcp', output
)
3541 print('### ip -4 route show dev dummy98 149.10.124.66')
3542 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3544 self
.assertIn('149.10.124.66 proto static', output
)
3545 self
.assertIn('congctl dctcp', output
)
3546 self
.assertIn('rto_min 300s', output
)
3548 @expectedFailureIfModuleIsNotAvailable('vrf')
3549 def test_route_vrf(self
):
3550 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3551 '25-vrf.netdev', '25-vrf.network')
3553 self
.wait_online('dummy98:routable', 'vrf99:carrier')
3555 output
= check_output('ip route show vrf vrf99')
3557 self
.assertRegex(output
, 'default via 192.168.100.1')
3559 output
= check_output('ip route show')
3561 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3563 def test_gateway_reconfigure(self
):
3564 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3566 self
.wait_online('dummy98:routable')
3567 print('### ip -4 route show dev dummy98 default')
3568 output
= check_output('ip -4 route show dev dummy98 default')
3570 self
.assertIn('default via 149.10.124.59 proto static', output
)
3571 self
.assertNotIn('149.10.124.60', output
)
3573 remove_network_unit('25-gateway-static.network')
3574 copy_network_unit('25-gateway-next-static.network')
3576 self
.wait_online('dummy98:routable')
3577 print('### ip -4 route show dev dummy98 default')
3578 output
= check_output('ip -4 route show dev dummy98 default')
3580 self
.assertNotIn('149.10.124.59', output
)
3581 self
.assertIn('default via 149.10.124.60 proto static', output
)
3583 def test_ip_route_ipv6_src_route(self
):
3584 # a dummy device does not make the addresses go through tentative state, so we
3585 # reuse a bond from an earlier test, which does make the addresses go through
3586 # tentative state, and do our test on that
3587 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3589 self
.wait_online('dummy98:enslaved', 'bond199:routable')
3591 output
= check_output('ip -6 route list dev bond199')
3593 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3595 def test_route_preferred_source_with_existing_address(self
):
3597 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3602 networkctl_reconfigure('dummy98')
3604 self
.wait_online('dummy98:routable')
3606 output
= check_output('ip -6 route list dev dummy98')
3608 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3610 def test_ip_link_mac_address(self
):
3611 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3613 self
.wait_online('dummy98:degraded')
3615 output
= check_output('ip link show dummy98')
3617 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3619 def test_ip_link_unmanaged(self
):
3620 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3623 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3625 def test_ipv6_address_label(self
):
3626 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3628 self
.wait_online('dummy98:degraded')
3630 output
= check_output('ip addrlabel list')
3632 self
.assertRegex(output
, '2004:da8:1::/64')
3634 def test_ipv6_proxy_ndp(self
):
3635 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3638 self
.wait_online('dummy98:routable')
3640 output
= check_output('ip neighbor show proxy dev dummy98')
3642 for i
in range(1, 5):
3643 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3645 def test_ipv6_neigh_retrans_time(self
):
3647 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3650 self
.wait_online(f
'{link}:degraded')
3651 remove_network_unit('25-dummy.network')
3653 # expect retrans_time_ms updated
3654 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3656 self
.wait_online(f
'{link}:degraded')
3657 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3658 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3660 # expect retrans_time_ms unchanged
3661 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3663 self
.wait_online(f
'{link}:degraded')
3664 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3665 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3667 # expect retrans_time_ms unchanged
3668 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.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-toobig.network')
3674 # expect retrans_time_ms unchanged
3675 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.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-infinity.network')
3681 # expect retrans_time_ms unchanged
3682 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.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-invalid.network')
3688 # expect retrans_time_ms updated
3689 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3691 self
.wait_online(f
'{link}:degraded')
3692 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3693 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3695 def test_neighbor(self
):
3696 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3697 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3698 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3701 self
.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
3703 print('### ip neigh list dev gretun97')
3704 output
= check_output('ip neigh list dev gretun97')
3706 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3707 self
.assertNotIn('10.0.0.23', output
)
3709 print('### ip neigh list dev ip6gretun97')
3710 output
= check_output('ip neigh list dev ip6gretun97')
3712 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3713 self
.assertNotIn('2001:db8:0:f102::18', output
)
3715 print('### ip neigh list dev dummy98')
3716 output
= check_output('ip neigh list dev dummy98')
3718 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3719 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3720 self
.assertNotIn('2004:da8:1:0::2', output
)
3721 self
.assertNotIn('192.168.10.2', output
)
3722 self
.assertNotIn('00:00:5e:00:02:67', output
)
3724 check_json(networkctl_json())
3726 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3727 # the valid configurations in 10-step1.conf.
3728 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3730 self
.wait_online('dummy98:degraded')
3732 print('### ip neigh list dev dummy98')
3733 output
= check_output('ip neigh list dev dummy98')
3735 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3736 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3737 self
.assertNotIn('2004:da8:1:0::2', output
)
3738 self
.assertNotIn('192.168.10.2', output
)
3739 self
.assertNotIn('00:00:5e:00:02:67', output
)
3741 check_json(networkctl_json())
3743 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3744 '25-neighbor-dummy.network.d/10-step2.conf')
3745 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3747 self
.wait_online('dummy98:degraded')
3749 print('### ip neigh list dev dummy98')
3750 output
= check_output('ip neigh list dev dummy98')
3752 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3753 self
.assertNotIn('00:00:5e:00:02:65', output
)
3754 self
.assertNotIn('00:00:5e:00:02:66', output
)
3755 self
.assertNotIn('00:00:5e:00:03:65', output
)
3756 self
.assertNotIn('2004:da8:1::1', output
)
3758 def test_link_local_addressing(self
):
3759 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3760 '25-link-local-addressing-no.network', '12-dummy.netdev')
3762 self
.wait_online('test1:degraded', 'dummy98:carrier')
3764 output
= check_output('ip address show dev test1')
3766 self
.assertRegex(output
, 'inet .* scope link')
3767 self
.assertRegex(output
, 'inet6 .* scope link')
3769 output
= check_output('ip address show dev dummy98')
3771 self
.assertNotRegex(output
, 'inet6* .* scope link')
3773 # Documentation/networking/ip-sysctl.txt
3775 # addr_gen_mode - INTEGER
3776 # Defines how link-local and autoconf addresses are generated.
3778 # 0: generate address based on EUI64 (default)
3779 # 1: do no generate a link-local address, use EUI64 for addresses generated
3781 # 2: generate stable privacy addresses, using the secret from
3782 # stable_secret (RFC7217)
3783 # 3: generate stable privacy addresses, using a random secret if unset
3785 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3786 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3787 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3789 def test_link_local_addressing_ipv6ll(self
):
3790 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3792 self
.wait_online('dummy98:degraded')
3794 # An IPv6LL address exists by default.
3795 output
= check_output('ip address show dev dummy98')
3797 self
.assertRegex(output
, 'inet6 .* scope link')
3799 copy_network_unit('25-link-local-addressing-no.network')
3801 self
.wait_online('dummy98:carrier')
3803 # Check if the IPv6LL address is removed.
3804 output
= check_output('ip address show dev dummy98')
3806 self
.assertNotRegex(output
, 'inet6 .* scope link')
3808 remove_network_unit('25-link-local-addressing-no.network')
3810 self
.wait_online('dummy98:degraded')
3812 # Check if a new IPv6LL address is assigned.
3813 output
= check_output('ip address show dev dummy98')
3815 self
.assertRegex(output
, 'inet6 .* scope link')
3817 def test_sysctl(self
):
3818 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3819 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3821 self
.wait_online('dummy98:degraded')
3823 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3824 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3825 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3826 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3827 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3828 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3829 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3830 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3831 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3832 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3834 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3836 self
.wait_online('dummy98:degraded')
3838 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3840 def test_sysctl_disable_ipv6(self
):
3841 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3843 print('## Disable ipv6')
3844 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3845 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3848 self
.wait_online('dummy98:routable')
3850 output
= check_output('ip -4 address show dummy98')
3852 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3853 output
= check_output('ip -6 address show dummy98')
3855 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3856 self
.assertRegex(output
, 'inet6 .* scope link')
3857 output
= check_output('ip -4 route show dev dummy98')
3859 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3860 output
= check_output('ip -6 route show default')
3862 self
.assertRegex(output
, 'default')
3863 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3865 remove_link('dummy98')
3867 print('## Enable ipv6')
3868 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3869 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3872 self
.wait_online('dummy98:routable')
3874 output
= check_output('ip -4 address show dummy98')
3876 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3877 output
= check_output('ip -6 address show dummy98')
3879 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3880 self
.assertRegex(output
, 'inet6 .* scope link')
3881 output
= check_output('ip -4 route show dev dummy98')
3883 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3884 output
= check_output('ip -6 route show default')
3886 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3888 def test_bind_carrier(self
):
3889 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3892 # no bound interface.
3893 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3894 output
= check_output('ip address show test1')
3896 self
.assertNotIn('UP,LOWER_UP', output
)
3897 self
.assertIn('DOWN', output
)
3898 self
.assertNotIn('192.168.10', output
)
3900 # add one bound interface. The interface will be up.
3901 check_output('ip link add dummy98 type dummy')
3902 check_output('ip link set dummy98 up')
3903 self
.wait_online('test1:routable')
3904 output
= check_output('ip address show test1')
3906 self
.assertIn('UP,LOWER_UP', output
)
3907 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3909 # add another bound interface. The interface is still up.
3910 check_output('ip link add dummy99 type dummy')
3911 check_output('ip link set dummy99 up')
3912 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3913 output
= check_output('ip address show test1')
3915 self
.assertIn('UP,LOWER_UP', output
)
3916 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3918 # remove one of the bound interfaces. The interface is still up
3919 remove_link('dummy98')
3920 output
= check_output('ip address show test1')
3922 self
.assertIn('UP,LOWER_UP', output
)
3923 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3925 # bring down the remaining bound interface. The interface will be down.
3926 check_output('ip link set dummy99 down')
3927 self
.wait_operstate('test1', 'off')
3928 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3929 output
= check_output('ip address show test1')
3931 self
.assertNotIn('UP,LOWER_UP', output
)
3932 self
.assertIn('DOWN', output
)
3933 self
.assertNotIn('192.168.10', output
)
3935 # bring up the bound interface. The interface will be up.
3936 check_output('ip link set dummy99 up')
3937 self
.wait_online('test1:routable')
3938 output
= check_output('ip address show test1')
3940 self
.assertIn('UP,LOWER_UP', output
)
3941 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3943 # remove the remaining bound interface. The interface will be down.
3944 remove_link('dummy99')
3945 self
.wait_operstate('test1', 'off')
3946 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3947 output
= check_output('ip address show test1')
3949 self
.assertNotIn('UP,LOWER_UP', output
)
3950 self
.assertIn('DOWN', output
)
3951 self
.assertNotIn('192.168.10', output
)
3953 # re-add one bound interface. The interface will be up.
3954 check_output('ip link add dummy98 type dummy')
3955 check_output('ip link set dummy98 up')
3956 self
.wait_online('test1:routable')
3957 output
= check_output('ip address show test1')
3959 self
.assertIn('UP,LOWER_UP', output
)
3960 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3962 def _test_activation_policy(self
, interface
, test
):
3963 conffile
= '25-activation-policy.network'
3965 conffile
= f
'{conffile}.d/{test}.conf'
3966 if interface
== 'vlan99':
3967 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3968 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3971 always
= test
.startswith('always')
3972 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3973 expect_up
= initial_up
3974 next_up
= not expect_up
3976 if test
.endswith('down'):
3977 self
.wait_activated(interface
)
3979 for iteration
in range(4):
3980 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3981 operstate
= 'routable' if expect_up
else 'off'
3982 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3983 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3986 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3987 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3988 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3990 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3993 check_output(f
'ip link set dev {interface} up')
3995 check_output(f
'ip link set dev {interface} down')
3996 expect_up
= initial_up
if always
else next_up
3997 next_up
= not next_up
4001 def test_activation_policy(self
):
4003 for interface
in ['test1', 'vlan99']:
4004 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
4010 print(f
'### test_activation_policy(interface={interface}, test={test})')
4011 with self
.subTest(interface
=interface
, test
=test
):
4012 self
._test
_activation
_policy
(interface
, test
)
4014 def _test_activation_policy_required_for_online(self
, policy
, required
):
4015 conffile
= '25-activation-policy.network'
4016 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
4018 units
+= [f
'{conffile}.d/{policy}.conf']
4020 units
+= [f
'{conffile}.d/required-{required}.conf']
4021 copy_network_unit(*units
, copy_dropins
=False)
4024 if policy
.endswith('down'):
4025 self
.wait_activated('test1')
4027 if policy
.endswith('down') or policy
== 'manual':
4028 self
.wait_operstate('test1', 'off', setup_state
='configuring')
4030 self
.wait_online('test1')
4032 if policy
== 'always-down':
4033 # if always-down, required for online is forced to no
4036 # otherwise if required for online is specified, it should match that
4037 expected
= required
== 'yes'
4039 # otherwise if only policy specified, required for online defaults to
4040 # true if policy is up, always-up, or bound
4041 expected
= policy
.endswith('up') or policy
== 'bound'
4043 # default is true, if neither are specified
4046 output
= networkctl_status('test1')
4049 yesno
= 'yes' if expected
else 'no'
4050 self
.assertRegex(output
, f
'Required For Online: {yesno}')
4052 def test_activation_policy_required_for_online(self
):
4054 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
4055 for required
in ['yes', 'no', '']:
4061 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
4062 with self
.subTest(policy
=policy
, required
=required
):
4063 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
4065 def test_domain(self
):
4066 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
4068 self
.wait_online('dummy98:routable')
4070 output
= networkctl_status('dummy98')
4072 self
.assertRegex(output
, 'Address: 192.168.42.100')
4073 self
.assertRegex(output
, 'DNS: 192.168.42.1')
4074 self
.assertRegex(output
, 'Search Domains: one')
4076 def test_keep_configuration_static(self
):
4077 check_output('ip link add name dummy98 type dummy')
4078 check_output('ip address add 10.1.2.3/16 dev dummy98')
4079 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
4080 output
= check_output('ip address show dummy98')
4082 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4083 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4084 output
= check_output('ip route show dev dummy98')
4087 copy_network_unit('24-keep-configuration-static.network')
4089 self
.wait_online('dummy98:routable')
4091 output
= check_output('ip address show dummy98')
4093 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
4094 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
4096 def check_nexthop(self
, manage_foreign_nexthops
, first
):
4097 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
4099 output
= check_output('ip nexthop list dev veth99')
4102 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
4103 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
4105 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
4106 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
4107 self
.assertIn('id 3 dev veth99', output
)
4108 self
.assertIn('id 4 dev veth99', output
)
4110 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
4112 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
4113 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
4114 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
4115 if manage_foreign_nexthops
:
4116 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
4118 output
= check_output('ip nexthop list dev dummy98')
4121 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4123 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4124 if manage_foreign_nexthops
:
4125 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4127 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4129 # kernel manages blackhole nexthops on lo
4130 output
= check_output('ip nexthop list dev lo')
4133 self
.assertIn('id 6 blackhole', output
)
4134 self
.assertIn('id 7 blackhole', output
)
4136 self
.assertIn('id 1 blackhole', output
)
4137 self
.assertIn('id 2 blackhole', output
)
4139 # group nexthops are shown with -0 option
4141 output
= check_output('ip -0 nexthop list id 21')
4143 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4145 output
= check_output('ip -0 nexthop list id 20')
4147 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4149 output
= check_output('ip route show dev veth99 10.10.10.10')
4152 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4154 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4156 output
= check_output('ip route show dev veth99 10.10.10.11')
4159 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4161 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4163 output
= check_output('ip route show dev veth99 10.10.10.12')
4166 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4168 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4170 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4173 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4175 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4177 output
= check_output('ip route show 10.10.10.13')
4180 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4182 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4184 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4187 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4189 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4191 output
= check_output('ip route show 10.10.10.14')
4194 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4195 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4197 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4198 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4199 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4201 output
= networkctl_json()
4203 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4205 def _test_nexthop(self
, manage_foreign_nexthops
):
4206 if not manage_foreign_nexthops
:
4207 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4209 check_output('ip link add dummy98 type dummy')
4210 check_output('ip link set dummy98 up')
4211 check_output('ip address add 192.168.20.20/24 dev dummy98')
4212 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4214 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4215 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4218 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4220 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4221 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4223 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4225 remove_network_unit('25-nexthop-2.network')
4226 copy_network_unit('25-nexthop-nothing.network')
4228 self
.wait_online('veth99:routable', 'veth-peer:routable')
4230 output
= check_output('ip nexthop list dev veth99')
4232 self
.assertEqual(output
, '')
4233 output
= check_output('ip nexthop list dev lo')
4235 self
.assertEqual(output
, '')
4237 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4238 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4239 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4240 # here to test reconfiguring with different .network files does not trigger race.
4241 # See also comments in link_drop_requests().
4242 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4243 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4245 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4247 # Remove nexthop with ID 20
4248 check_output('ip nexthop del id 20')
4249 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4252 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4253 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4254 # hence test1 should be stuck in the configuring state.
4255 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4257 # Wait for a while, and check if the interface is still in the configuring state.
4259 output
= networkctl_status('test1')
4260 self
.assertIn('State: routable (configuring)', output
)
4262 # Check if the route which needs nexthop 20 and 21 are forgotten.
4263 output
= networkctl_json()
4265 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4267 # Reconfigure the interface that has nexthop with ID 20 and 21,
4268 # then the route requested by test1 can be configured.
4269 networkctl_reconfigure('dummy98')
4270 self
.wait_online('test1:routable')
4272 # Check if the requested route actually configured.
4273 output
= check_output('ip route show 10.10.11.10')
4275 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4276 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4277 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4279 remove_link('veth99')
4282 output
= check_output('ip nexthop list dev lo')
4284 self
.assertEqual(output
, '')
4286 @expectedFailureIfNexthopIsNotAvailable()
4287 def test_nexthop(self
):
4289 for manage_foreign_nexthops
in [True, False]:
4295 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4296 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4297 self
._test
_nexthop
(manage_foreign_nexthops
)
4299 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4307 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4308 def test_qdisc_cake(self
):
4309 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4311 self
.wait_online('dummy98:routable')
4313 output
= check_output('tc qdisc show dev dummy98')
4315 self
.assertIn('qdisc cake 3a: root', output
)
4316 self
.assertIn('bandwidth 500Mbit', output
)
4317 self
.assertIn('autorate-ingress', output
)
4318 self
.assertIn('diffserv8', output
)
4319 self
.assertIn('dual-dsthost', output
)
4320 self
.assertIn(' nat', output
)
4321 self
.assertIn(' wash', output
)
4322 self
.assertIn(' split-gso', output
)
4323 self
.assertIn(' raw', output
)
4324 self
.assertIn(' atm', output
)
4325 self
.assertIn('overhead 128', output
)
4326 self
.assertIn('mpu 20', output
)
4327 self
.assertIn('fwmark 0xff00', output
)
4328 self
.assertIn('rtt 1s', output
)
4329 self
.assertIn('ack-filter-aggressive', output
)
4331 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4332 def test_qdisc_codel(self
):
4333 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4335 self
.wait_online('dummy98:routable')
4337 output
= check_output('tc qdisc show dev dummy98')
4339 self
.assertRegex(output
, 'qdisc codel 33: root')
4340 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4342 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4343 def test_qdisc_drr(self
):
4344 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4346 self
.wait_online('dummy98:routable')
4348 output
= check_output('tc qdisc show dev dummy98')
4350 self
.assertRegex(output
, 'qdisc drr 2: root')
4351 output
= check_output('tc class show dev dummy98')
4353 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4355 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4356 def test_qdisc_ets(self
):
4357 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4359 self
.wait_online('dummy98:routable')
4361 output
= check_output('tc qdisc show dev dummy98')
4364 self
.assertRegex(output
, 'qdisc ets 3a: root')
4365 self
.assertRegex(output
, 'bands 10 strict 3')
4366 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4367 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4369 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4370 def test_qdisc_fq(self
):
4371 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4373 self
.wait_online('dummy98:routable')
4375 output
= check_output('tc qdisc show dev dummy98')
4377 self
.assertRegex(output
, 'qdisc fq 32: root')
4378 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4379 self
.assertRegex(output
, 'quantum 1500')
4380 self
.assertRegex(output
, 'initial_quantum 13000')
4381 self
.assertRegex(output
, 'maxrate 1Mbit')
4383 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4384 def test_qdisc_fq_codel(self
):
4385 copy_network_unit('25-qdisc-fq_codel.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_codel 34: root')
4392 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')
4394 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4395 def test_qdisc_fq_pie(self
):
4396 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4398 self
.wait_online('dummy98:routable')
4400 output
= check_output('tc qdisc show dev dummy98')
4403 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4404 self
.assertRegex(output
, 'limit 200000p')
4406 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4407 def test_qdisc_gred(self
):
4408 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4410 self
.wait_online('dummy98:routable')
4412 output
= check_output('tc qdisc show dev dummy98')
4414 self
.assertRegex(output
, 'qdisc gred 38: root')
4415 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4417 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4418 def test_qdisc_hhf(self
):
4419 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4421 self
.wait_online('dummy98:routable')
4423 output
= check_output('tc qdisc show dev dummy98')
4425 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4426 self
.assertRegex(output
, 'limit 1022p')
4428 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4429 def test_qdisc_htb_fifo(self
):
4430 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4432 self
.wait_online('dummy98:routable')
4434 output
= check_output('tc qdisc show dev dummy98')
4436 self
.assertRegex(output
, 'qdisc htb 2: root')
4437 self
.assertRegex(output
, r
'default (0x30|30)')
4439 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4440 self
.assertRegex(output
, 'limit 100000p')
4442 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4443 self
.assertRegex(output
, 'limit 1000000')
4445 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4446 self
.assertRegex(output
, 'limit 1023p')
4448 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4450 output
= check_output('tc -d class show dev dummy98')
4452 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4453 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4454 # which is fixed in v6.3.0 by
4455 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4456 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4457 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4458 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4459 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4460 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4461 self
.assertRegex(output
, 'burst 123456')
4462 self
.assertRegex(output
, 'cburst 123457')
4464 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4465 def test_qdisc_ingress(self
):
4466 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4467 '25-qdisc-ingress.network', '11-dummy.netdev')
4469 self
.wait_online('dummy98:routable', 'test1:routable')
4471 output
= check_output('tc qdisc show dev dummy98')
4473 self
.assertRegex(output
, 'qdisc clsact')
4475 output
= check_output('tc qdisc show dev test1')
4477 self
.assertRegex(output
, 'qdisc ingress')
4479 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4480 def test_qdisc_netem(self
):
4481 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4482 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4484 self
.wait_online('dummy98:routable', 'test1:routable')
4486 output
= check_output('tc qdisc show dev dummy98')
4488 self
.assertRegex(output
, 'qdisc netem 30: root')
4489 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4491 output
= check_output('tc qdisc show dev test1')
4493 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4494 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4496 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4497 def test_qdisc_pie(self
):
4498 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4500 self
.wait_online('dummy98:routable')
4502 output
= check_output('tc qdisc show dev dummy98')
4504 self
.assertRegex(output
, 'qdisc pie 3a: root')
4505 self
.assertRegex(output
, 'limit 200000')
4507 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4508 def test_qdisc_qfq(self
):
4509 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4511 self
.wait_online('dummy98:routable')
4513 output
= check_output('tc qdisc show dev dummy98')
4515 self
.assertRegex(output
, 'qdisc qfq 2: root')
4516 output
= check_output('tc class show dev dummy98')
4518 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4519 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4521 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4522 def test_qdisc_sfb(self
):
4523 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4525 self
.wait_online('dummy98:routable')
4527 output
= check_output('tc qdisc show dev dummy98')
4529 self
.assertRegex(output
, 'qdisc sfb 39: root')
4530 self
.assertRegex(output
, 'limit 200000')
4532 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4533 def test_qdisc_sfq(self
):
4534 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4536 self
.wait_online('dummy98:routable')
4538 output
= check_output('tc qdisc show dev dummy98')
4540 self
.assertRegex(output
, 'qdisc sfq 36: root')
4541 self
.assertRegex(output
, 'perturb 5sec')
4543 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4544 def test_qdisc_tbf(self
):
4545 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4547 self
.wait_online('dummy98:routable')
4549 output
= check_output('tc qdisc show dev dummy98')
4551 self
.assertRegex(output
, 'qdisc tbf 35: root')
4552 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4554 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4555 def test_qdisc_teql(self
):
4556 call_quiet('rmmod sch_teql')
4558 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4560 self
.wait_links('dummy98')
4561 check_output('modprobe sch_teql max_equalizers=2')
4562 self
.wait_online('dummy98:routable')
4564 output
= check_output('tc qdisc show dev dummy98')
4566 self
.assertRegex(output
, 'qdisc teql1 31: root')
4568 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4576 def test_state_file(self
):
4577 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4579 self
.wait_online('dummy98:routable')
4581 # make link state file updated
4582 resolvectl('revert', 'dummy98')
4584 check_json(networkctl_json())
4586 output
= read_link_state_file('dummy98')
4588 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4589 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4590 self
.assertIn('ADMIN_STATE=configured', output
)
4591 self
.assertIn('OPER_STATE=routable', output
)
4592 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4593 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4594 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4595 self
.assertIn('ACTIVATION_POLICY=up', output
)
4596 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4597 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4598 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4599 self
.assertIn('DOMAINS=hogehoge', output
)
4600 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4601 self
.assertIn('LLMNR=no', output
)
4602 self
.assertIn('MDNS=yes', output
)
4603 self
.assertIn('DNSSEC=no', output
)
4605 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4606 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4607 resolvectl('llmnr', 'dummy98', 'yes')
4608 resolvectl('mdns', 'dummy98', 'no')
4609 resolvectl('dnssec', 'dummy98', 'yes')
4610 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4612 check_json(networkctl_json())
4614 output
= read_link_state_file('dummy98')
4616 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4617 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4618 self
.assertIn('DOMAINS=hogehogehoge', output
)
4619 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4620 self
.assertIn('LLMNR=yes', output
)
4621 self
.assertIn('MDNS=no', output
)
4622 self
.assertIn('DNSSEC=yes', output
)
4624 timedatectl('revert', 'dummy98')
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=0.fedora.pool.ntp.org 1.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 resolvectl('revert', 'dummy98')
4640 check_json(networkctl_json())
4642 output
= read_link_state_file('dummy98')
4644 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4645 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4646 self
.assertIn('DOMAINS=hogehoge', output
)
4647 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4648 self
.assertIn('LLMNR=no', output
)
4649 self
.assertIn('MDNS=yes', output
)
4650 self
.assertIn('DNSSEC=no', output
)
4652 def test_address_state(self
):
4653 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4656 self
.wait_online('dummy98:degraded')
4658 output
= read_link_state_file('dummy98')
4659 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4660 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4662 # with a routable IPv4 address
4663 check_output('ip address add 10.1.2.3/16 dev dummy98')
4664 self
.wait_online('dummy98:routable', ipv4
=True)
4665 self
.wait_online('dummy98:routable')
4667 output
= read_link_state_file('dummy98')
4668 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4669 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4671 check_output('ip address del 10.1.2.3/16 dev dummy98')
4673 # with a routable IPv6 address
4674 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4675 self
.wait_online('dummy98:routable', ipv6
=True)
4676 self
.wait_online('dummy98:routable')
4678 output
= read_link_state_file('dummy98')
4679 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4680 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4682 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4690 def test_bond_keep_master(self
):
4691 check_output('ip link add bond199 type bond mode active-backup')
4692 check_output('ip link add dummy98 type dummy')
4693 check_output('ip link set dummy98 master bond199')
4695 copy_network_unit('23-keep-master.network')
4697 self
.wait_online('dummy98:enslaved')
4699 output
= check_output('ip -d link show bond199')
4701 self
.assertRegex(output
, 'active_slave dummy98')
4703 output
= check_output('ip -d link show dummy98')
4705 self
.assertRegex(output
, 'master bond199')
4707 def test_bond_active_slave(self
):
4708 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4710 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4712 output
= check_output('ip -d link show bond199')
4714 self
.assertIn('active_slave dummy98', output
)
4716 # test case for issue #31165.
4717 since
= datetime
.datetime
.now()
4718 networkctl_reconfigure('dummy98')
4719 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4720 self
.assertNotIn('dummy98: Bringing link down', read_networkd_log(since
=since
))
4722 def test_bond_primary_slave(self
):
4723 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4725 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4727 output
= check_output('ip -d link show bond199')
4729 self
.assertIn('primary dummy98', output
)
4732 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4733 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4734 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4735 f
.write(f
'[Link]\nMACAddress={mac}\n')
4738 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4740 output
= check_output('ip -d link show bond199')
4742 self
.assertIn(f
'link/ether {mac}', output
)
4744 def test_bond_operstate(self
):
4745 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4746 '25-bond99.network', '25-bond-slave.network')
4748 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
4750 output
= check_output('ip -d link show dummy98')
4752 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4754 output
= check_output('ip -d link show test1')
4756 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4758 output
= check_output('ip -d link show bond99')
4760 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4762 self
.wait_operstate('dummy98', 'enslaved')
4763 self
.wait_operstate('test1', 'enslaved')
4764 self
.wait_operstate('bond99', 'routable')
4766 check_output('ip link set dummy98 down')
4768 self
.wait_operstate('dummy98', 'off')
4769 self
.wait_operstate('test1', 'enslaved')
4770 self
.wait_operstate('bond99', 'routable')
4772 check_output('ip link set dummy98 up')
4774 self
.wait_operstate('dummy98', 'enslaved')
4775 self
.wait_operstate('test1', 'enslaved')
4776 self
.wait_operstate('bond99', 'routable')
4778 check_output('ip link set dummy98 down')
4779 check_output('ip link set test1 down')
4781 self
.wait_operstate('dummy98', 'off')
4782 self
.wait_operstate('test1', 'off')
4784 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4785 # Huh? Kernel does not recognize that all slave interfaces are down?
4786 # Let's confirm that networkd's operstate is consistent with ip's result.
4787 self
.assertNotRegex(output
, 'NO-CARRIER')
4789 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4797 def test_bridge_mac_none(self
):
4798 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4799 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4801 self
.wait_online('dummy98:enslaved', 'bridge99:degraded')
4803 output
= check_output('ip link show dev dummy98')
4805 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4807 output
= check_output('ip link show dev bridge99')
4809 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4811 def test_bridge_vlan(self
):
4812 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4813 '26-bridge.netdev', '26-bridge-vlan-master.network',
4816 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4818 output
= check_output('bridge vlan show dev test1')
4820 # check if the default VID is removed
4821 self
.assertNotIn('1 Egress Untagged', output
)
4822 for i
in range(1000, 3000):
4824 self
.assertIn(f
'{i} PVID', output
)
4825 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4826 self
.assertIn(f
'{i} Egress Untagged', output
)
4827 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4828 self
.assertIn(f
'{i}', output
)
4830 self
.assertNotIn(f
'{i}', output
)
4832 output
= check_output('bridge vlan show dev bridge99')
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(1022, 1026) or i
in range(1203, 1209):
4840 self
.assertIn(f
'{i} Egress Untagged', output
)
4841 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4842 self
.assertIn(f
'{i}', output
)
4844 self
.assertNotIn(f
'{i}', output
)
4847 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4848 '26-bridge-vlan-master.network.d/10-override.conf')
4850 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4852 output
= check_output('bridge vlan show dev test1')
4854 for i
in range(1000, 3000):
4856 self
.assertIn(f
'{i} PVID', output
)
4857 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4858 self
.assertIn(f
'{i} Egress Untagged', output
)
4859 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4860 self
.assertIn(f
'{i}', output
)
4862 self
.assertNotIn(f
'{i}', output
)
4864 output
= check_output('bridge vlan show dev bridge99')
4866 for i
in range(1000, 3000):
4868 self
.assertIn(f
'{i} PVID', output
)
4869 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4870 self
.assertIn(f
'{i} Egress Untagged', output
)
4871 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4872 self
.assertIn(f
'{i}', output
)
4874 self
.assertNotIn(f
'{i}', output
)
4876 # Remove several vlan IDs
4877 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4878 '26-bridge-vlan-master.network.d/20-override.conf')
4880 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4882 output
= check_output('bridge vlan show dev test1')
4884 for i
in range(1000, 3000):
4886 self
.assertIn(f
'{i} PVID', output
)
4887 elif i
in range(2012, 2016):
4888 self
.assertIn(f
'{i} Egress Untagged', output
)
4889 elif i
in range(2008, 2014):
4890 self
.assertIn(f
'{i}', output
)
4892 self
.assertNotIn(f
'{i}', output
)
4894 output
= check_output('bridge vlan show dev bridge99')
4896 for i
in range(1000, 3000):
4898 self
.assertIn(f
'{i} PVID', output
)
4899 elif i
in range(2022, 2026):
4900 self
.assertIn(f
'{i} Egress Untagged', output
)
4901 elif i
in range(2018, 2024):
4902 self
.assertIn(f
'{i}', output
)
4904 self
.assertNotIn(f
'{i}', output
)
4906 # Remove all vlan IDs
4907 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4908 '26-bridge-vlan-master.network.d/30-override.conf')
4910 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4912 output
= check_output('bridge vlan show dev test1')
4914 self
.assertNotIn('PVID', output
)
4915 for i
in range(1000, 3000):
4916 self
.assertNotIn(f
'{i}', output
)
4918 output
= check_output('bridge vlan show dev bridge99')
4920 self
.assertNotIn('PVID', output
)
4921 for i
in range(1000, 3000):
4922 self
.assertNotIn(f
'{i}', output
)
4924 def test_bridge_vlan_issue_20373(self
):
4925 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4926 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4927 '21-vlan.netdev', '21-vlan.network')
4929 self
.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
4931 output
= check_output('bridge vlan show dev test1')
4933 self
.assertIn('100 PVID Egress Untagged', output
)
4934 self
.assertIn('560', output
)
4935 self
.assertIn('600', output
)
4937 output
= check_output('bridge vlan show dev bridge99')
4939 self
.assertIn('1 PVID Egress Untagged', output
)
4940 self
.assertIn('100', output
)
4941 self
.assertIn('600', output
)
4943 def test_bridge_mdb(self
):
4944 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4945 '26-bridge.netdev', '26-bridge-mdb-master.network')
4947 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4949 output
= check_output('bridge mdb show dev bridge99')
4951 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4952 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4954 # Old kernel may not support bridge MDB entries on bridge master
4955 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4956 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4957 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4959 def test_bridge_keep_master(self
):
4960 check_output('ip link add bridge99 type bridge')
4961 check_output('ip link set bridge99 up')
4962 check_output('ip link add dummy98 type dummy')
4963 check_output('ip link set dummy98 master bridge99')
4965 copy_network_unit('23-keep-master.network')
4967 self
.wait_online('dummy98:enslaved')
4969 output
= check_output('ip -d link show dummy98')
4971 self
.assertRegex(output
, 'master bridge99')
4972 self
.assertRegex(output
, 'bridge')
4974 output
= check_output('bridge -d link show dummy98')
4976 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4977 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4978 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4979 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4980 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4981 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4982 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4983 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4984 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4985 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4986 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4987 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4989 def test_bridge_property(self
):
4990 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4991 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4992 '25-bridge99.network')
4994 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
4996 output
= check_output('ip -d link show bridge99')
4998 self
.assertIn('mtu 9000 ', output
)
5000 output
= check_output('ip -d link show test1')
5002 self
.assertIn('master bridge99 ', output
)
5003 self
.assertIn('bridge_slave', output
)
5004 self
.assertIn('mtu 9000 ', output
)
5006 output
= check_output('ip -d link show dummy98')
5008 self
.assertIn('master bridge99 ', output
)
5009 self
.assertIn('bridge_slave', output
)
5010 self
.assertIn('mtu 9000 ', output
)
5012 output
= check_output('ip addr show bridge99')
5014 self
.assertIn('192.168.0.15/24', output
)
5016 output
= check_output('bridge -d link show dummy98')
5018 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
5019 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
5020 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
5021 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
5022 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
5023 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
5024 # CONFIG_BRIDGE_IGMP_SNOOPING=y
5025 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
5026 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
5027 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
5028 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
5029 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
5030 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
5032 output
= check_output('bridge -d link show test1')
5034 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
5036 check_output('ip address add 192.168.0.16/24 dev bridge99')
5037 output
= check_output('ip addr show bridge99')
5039 self
.assertIn('192.168.0.16/24', output
)
5042 print('### ip -6 route list table all dev bridge99')
5043 output
= check_output('ip -6 route list table all dev bridge99')
5045 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5047 remove_link('test1')
5048 self
.wait_operstate('bridge99', 'routable')
5050 output
= check_output('ip -d link show bridge99')
5052 self
.assertIn('mtu 9000 ', output
)
5054 output
= check_output('ip -d link show dummy98')
5056 self
.assertIn('master bridge99 ', output
)
5057 self
.assertIn('bridge_slave', output
)
5058 self
.assertIn('mtu 9000 ', output
)
5060 remove_link('dummy98')
5061 self
.wait_operstate('bridge99', 'no-carrier')
5063 output
= check_output('ip -d link show bridge99')
5065 # When no carrier, the kernel may reset the MTU
5066 self
.assertIn('NO-CARRIER', output
)
5068 output
= check_output('ip address show bridge99')
5070 self
.assertNotIn('192.168.0.15/24', output
)
5071 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
5073 print('### ip -6 route list table all dev bridge99')
5074 output
= check_output('ip -6 route list table all dev bridge99')
5076 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
5078 check_output('ip link add dummy98 type dummy')
5079 self
.wait_online('dummy98:enslaved', 'bridge99:routable')
5081 output
= check_output('ip -d link show bridge99')
5083 self
.assertIn('mtu 9000 ', output
)
5085 output
= check_output('ip -d link show dummy98')
5087 self
.assertIn('master bridge99 ', output
)
5088 self
.assertIn('bridge_slave', output
)
5089 self
.assertIn('mtu 9000 ', output
)
5091 def test_bridge_configure_without_carrier(self
):
5092 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
5096 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
5097 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
5098 with self
.subTest(test
=test
):
5099 if test
== 'no-slave':
5100 # bridge has no slaves; it's up but *might* not have carrier
5101 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
5102 # due to a bug in the kernel, newly-created bridges are brought up
5103 # *with* carrier, unless they have had any setting changed; e.g.
5104 # their mac set, priority set, etc. Then, they will lose carrier
5105 # as soon as a (down) slave interface is added, and regain carrier
5106 # again once the slave interface is brought up.
5107 #self.check_link_attr('bridge99', 'carrier', '0')
5108 elif test
== 'add-slave':
5109 # add slave to bridge, but leave it down; bridge is definitely no-carrier
5110 self
.check_link_attr('test1', 'operstate', 'down')
5111 check_output('ip link set dev test1 master bridge99')
5112 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
5113 self
.check_link_attr('bridge99', 'carrier', '0')
5114 elif test
== 'slave-up':
5115 # bring up slave, which will have carrier; bridge gains carrier
5116 check_output('ip link set dev test1 up')
5117 self
.wait_online('bridge99:routable')
5118 self
.check_link_attr('bridge99', 'carrier', '1')
5119 elif test
== 'slave-no-carrier':
5120 # drop slave carrier; bridge loses carrier
5121 check_output('ip link set dev test1 carrier off')
5122 self
.wait_online('bridge99:no-carrier:no-carrier')
5123 self
.check_link_attr('bridge99', 'carrier', '0')
5124 elif test
== 'slave-carrier':
5125 # restore slave carrier; bridge gains carrier
5126 check_output('ip link set dev test1 carrier on')
5127 self
.wait_online('bridge99:routable')
5128 self
.check_link_attr('bridge99', 'carrier', '1')
5129 elif test
== 'slave-down':
5130 # bring down slave; bridge loses carrier
5131 check_output('ip link set dev test1 down')
5132 self
.wait_online('bridge99:no-carrier:no-carrier')
5133 self
.check_link_attr('bridge99', 'carrier', '0')
5135 output
= networkctl_status('bridge99')
5136 self
.assertRegex(output
, '10.1.2.3')
5137 self
.assertRegex(output
, '10.1.2.1')
5139 def test_bridge_ignore_carrier_loss(self
):
5140 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5141 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5142 '25-bridge99-ignore-carrier-loss.network')
5144 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5146 check_output('ip address add 192.168.0.16/24 dev bridge99')
5147 remove_link('test1', 'dummy98')
5150 output
= check_output('ip address show bridge99')
5152 self
.assertRegex(output
, 'NO-CARRIER')
5153 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5154 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5156 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5157 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5158 '25-bridge99-ignore-carrier-loss.network')
5160 self
.wait_online('bridge99:no-carrier')
5162 for trial
in range(4):
5163 check_output('ip link add dummy98 type dummy')
5164 check_output('ip link set dummy98 up')
5166 remove_link('dummy98')
5168 self
.wait_online('bridge99:routable', 'dummy98:enslaved')
5170 output
= check_output('ip address show bridge99')
5172 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5174 output
= check_output('ip rule list table 100')
5176 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5178 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5186 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5187 def test_sriov(self
):
5188 copy_network_unit('25-default.link', '25-sriov.network')
5190 call('modprobe netdevsim')
5192 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5195 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5199 self
.wait_online('eni99np1:routable')
5201 output
= check_output('ip link show dev eni99np1')
5203 self
.assertRegex(output
,
5204 '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 *'
5205 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5206 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5209 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5210 def test_sriov_udev(self
):
5211 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5213 call('modprobe netdevsim')
5215 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5219 self
.wait_online('eni99np1:routable')
5221 # the name eni99np1 may be an alternative name.
5222 ifname
= link_resolve('eni99np1')
5224 output
= check_output('ip link show dev eni99np1')
5226 self
.assertRegex(output
,
5227 '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 *'
5228 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5229 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5231 self
.assertNotIn('vf 3', output
)
5232 self
.assertNotIn('vf 4', output
)
5234 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5235 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5238 udevadm_trigger(f
'/sys/devices/netdevsim99/net/{ifname}')
5240 output
= check_output('ip link show dev eni99np1')
5242 self
.assertRegex(output
,
5243 '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 *'
5244 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5245 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5248 self
.assertNotIn('vf 4', output
)
5250 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5251 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5254 udevadm_trigger(f
'/sys/devices/netdevsim99/net/{ifname}')
5256 output
= check_output('ip link show dev eni99np1')
5258 self
.assertRegex(output
,
5259 '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 *'
5260 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5261 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5264 self
.assertNotIn('vf 4', output
)
5266 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5267 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5270 udevadm_trigger(f
'/sys/devices/netdevsim99/net/{ifname}')
5272 output
= check_output('ip link show dev eni99np1')
5274 self
.assertRegex(output
,
5275 '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 *'
5276 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5278 self
.assertNotIn('vf 2', output
)
5279 self
.assertNotIn('vf 3', output
)
5280 self
.assertNotIn('vf 4', output
)
5282 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5283 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5286 udevadm_trigger(f
'/sys/devices/netdevsim99/net/{ifname}')
5288 output
= check_output('ip link show dev eni99np1')
5290 self
.assertRegex(output
,
5291 '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 *'
5292 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5293 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5295 self
.assertNotIn('vf 3', output
)
5296 self
.assertNotIn('vf 4', output
)
5298 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5306 def test_lldp(self
):
5307 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5309 self
.wait_online('veth99:degraded', 'veth-peer:degraded')
5311 for trial
in range(10):
5315 output
= networkctl('lldp')
5317 if re
.search(r
'veth99 .* veth-peer .* .......a...', output
):
5322 # With interface name
5323 output
= networkctl('lldp', 'veth99');
5325 self
.assertRegex(output
, r
'veth99 .* veth-peer .* .......a...')
5327 # With interface name pattern
5328 output
= networkctl('lldp', 've*9');
5330 self
.assertRegex(output
, r
'veth99 .* veth-peer .* .......a...')
5333 output
= networkctl('--json=short', 'lldp')
5335 self
.assertIn('"InterfaceName":"veth99"', output
)
5336 self
.assertIn('"PortID":"veth-peer"', output
)
5337 self
.assertIn('"EnabledCapabilities":128', output
)
5339 # json format with interface name
5340 output
= networkctl('--json=short', 'lldp', 'veth99')
5342 self
.assertIn('"InterfaceName":"veth99"', output
)
5343 self
.assertIn('"PortID":"veth-peer"', output
)
5344 self
.assertIn('"EnabledCapabilities":128', output
)
5346 # json format with interface name pattern
5347 output
= networkctl('--json=short', 'lldp', 've*9')
5349 self
.assertIn('"InterfaceName":"veth99"', output
)
5350 self
.assertIn('"PortID":"veth-peer"', output
)
5351 self
.assertIn('"EnabledCapabilities":128', output
)
5353 # LLDP neighbors in status
5354 output
= networkctl_status('veth99')
5356 self
.assertRegex(output
, r
'Connected To: .* on port veth-peer')
5358 # enable forwarding, to enable the Router flag
5359 with
open(os
.path
.join(network_unit_dir
, '23-emit-lldp.network'), mode
='a', encoding
='utf-8') as f
:
5360 f
.write('[Network]\nIPv4Forwarding=yes\n')
5363 self
.wait_online('veth-peer:degraded')
5365 for trial
in range(10):
5369 output
= networkctl('lldp')
5371 if re
.search(r
'veth99 .* veth-peer .* ....r......', output
):
5376 # With interface name
5377 output
= networkctl('lldp', 'veth99');
5379 self
.assertRegex(output
, r
'veth99 .* veth-peer .* ....r......')
5381 # With interface name pattern
5382 output
= networkctl('lldp', 've*9');
5384 self
.assertRegex(output
, r
'veth99 .* veth-peer .* ....r......')
5387 output
= networkctl('--json=short', 'lldp')
5389 self
.assertIn('"InterfaceName":"veth99"', output
)
5390 self
.assertIn('"PortID":"veth-peer"', output
)
5391 self
.assertIn('"EnabledCapabilities":16', output
)
5393 # json format with interface name
5394 output
= networkctl('--json=short', 'lldp', 'veth99')
5396 self
.assertIn('"InterfaceName":"veth99"', output
)
5397 self
.assertIn('"PortID":"veth-peer"', output
)
5398 self
.assertIn('"EnabledCapabilities":16', output
)
5400 # json format with interface name pattern
5401 output
= networkctl('--json=short', 'lldp', 've*9')
5403 self
.assertIn('"InterfaceName":"veth99"', output
)
5404 self
.assertIn('"PortID":"veth-peer"', output
)
5405 self
.assertIn('"EnabledCapabilities":16', output
)
5407 # LLDP neighbors in status
5408 output
= networkctl_status('veth99')
5410 self
.assertRegex(output
, r
'Connected To: .* on port veth-peer')
5412 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5420 def test_ipv6_prefix_delegation(self
):
5421 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5422 self
.setup_nftset('addr6', 'ipv6_addr')
5423 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5424 self
.setup_nftset('ifindex', 'iface_index')
5426 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5428 # IPv6SendRA=yes implies IPv6Forwarding.
5429 self
.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5431 output
= resolvectl('dns', 'veth99')
5433 self
.assertRegex(output
, 'fe80::')
5434 self
.assertRegex(output
, '2002:da8:1::1')
5436 output
= resolvectl('domain', 'veth99')
5438 self
.assertIn('hogehoge.test', output
)
5440 output
= networkctl_status('veth99')
5442 self
.assertRegex(output
, '2002:da8:1:0')
5444 self
.check_netlabel('veth99', '2002:da8:1::/64')
5445 self
.check_netlabel('veth99', '2002:da8:2::/64')
5447 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5448 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5449 self
.check_nftset('network6', '2002:da8:1::/64')
5450 self
.check_nftset('network6', '2002:da8:2::/64')
5451 self
.check_nftset('ifindex', 'veth99')
5453 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5455 def check_ipv6_token_static(self
):
5456 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5458 output
= networkctl_status('veth99')
5460 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5461 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5462 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5463 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5465 def test_ipv6_token_static(self
):
5466 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5469 self
.check_ipv6_token_static()
5472 check_output('ip link set veth99 down')
5473 check_output('ip link set veth99 up')
5475 self
.check_ipv6_token_static()
5478 check_output('ip link set veth99 down')
5479 time
.sleep(random
.uniform(0, 0.1))
5480 check_output('ip link set veth99 up')
5481 time
.sleep(random
.uniform(0, 0.1))
5483 self
.check_ipv6_token_static()
5485 def test_ipv6_token_prefixstable(self
):
5486 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5488 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5490 output
= networkctl_status('veth99')
5492 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5493 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5495 def test_ipv6_token_prefixstable_without_address(self
):
5496 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5498 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5500 output
= networkctl_status('veth99')
5502 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5503 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5505 def check_router_hop_limit(self
, hop_limit
):
5506 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5508 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5510 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5512 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5514 def test_router_hop_limit(self
):
5515 copy_network_unit('25-veth-client.netdev',
5516 '25-veth-router.netdev',
5518 '25-veth-bridge.network',
5519 '25-veth-client.network',
5520 '25-veth-router-hop-limit.network',
5521 '25-bridge99.network')
5523 self
.wait_online('client-p:enslaved',
5524 'router:degraded', 'router-p:enslaved',
5525 'bridge99:routable')
5527 self
.check_router_hop_limit(42)
5529 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5530 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5534 self
.check_router_hop_limit(43)
5536 def test_router_preference(self
):
5537 copy_network_unit('25-veth-client.netdev',
5538 '25-veth-router-high.netdev',
5539 '25-veth-router-low.netdev',
5541 '25-veth-bridge.network',
5542 '25-veth-client.network',
5543 '25-veth-router-high.network',
5544 '25-veth-router-low.network',
5545 '25-bridge99.network')
5547 self
.wait_online('client-p:enslaved',
5548 'router-high:degraded', 'router-high-p:enslaved',
5549 'router-low:degraded', 'router-low-p:enslaved',
5550 'bridge99:routable')
5552 networkctl_reconfigure('client')
5553 self
.wait_online('client:routable')
5555 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5556 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5557 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5558 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5560 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5562 self
.assertIn('metric 512', output
)
5563 self
.assertIn('pref high', output
)
5564 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5566 self
.assertIn('metric 2048', output
)
5567 self
.assertIn('pref low', output
)
5569 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5570 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5573 self
.wait_online('client:routable')
5575 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5576 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5577 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5578 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5580 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5582 self
.assertIn('metric 100', output
)
5583 self
.assertNotIn('metric 512', output
)
5584 self
.assertIn('pref high', output
)
5585 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5587 self
.assertIn('metric 300', output
)
5588 self
.assertNotIn('metric 2048', output
)
5589 self
.assertIn('pref low', output
)
5591 # swap the preference (for issue #28439)
5592 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5593 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5595 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5596 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5598 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5600 self
.assertIn('metric 300', output
)
5601 self
.assertNotIn('metric 100', output
)
5602 self
.assertIn('pref low', output
)
5603 self
.assertNotIn('pref high', output
)
5604 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5606 self
.assertIn('metric 100', output
)
5607 self
.assertNotIn('metric 300', output
)
5608 self
.assertIn('pref high', output
)
5609 self
.assertNotIn('pref low', output
)
5611 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5612 def test_captive_portal(self
):
5613 copy_network_unit('25-veth-client.netdev',
5614 '25-veth-router-captive.netdev',
5616 '25-veth-client-captive.network',
5617 '25-veth-router-captive.network',
5618 '25-veth-bridge-captive.network',
5619 '25-bridge99.network')
5621 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5622 'router-captive:degraded', 'router-captivep:enslaved')
5624 start_radvd(config_file
='captive-portal.conf')
5625 networkctl_reconfigure('client')
5626 self
.wait_online('client:routable')
5628 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5629 output
= networkctl_status('client')
5631 self
.assertIn('Captive Portal: http://systemd.io', output
)
5633 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5634 def test_invalid_captive_portal(self
):
5635 def radvd_write_config(captive_portal_uri
):
5636 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5637 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5639 captive_portal_uris
= [
5640 "42ěščěškd ěšč ě s",
5645 copy_network_unit('25-veth-client.netdev',
5646 '25-veth-router-captive.netdev',
5648 '25-veth-client-captive.network',
5649 '25-veth-router-captive.network',
5650 '25-veth-bridge-captive.network',
5651 '25-bridge99.network')
5653 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5654 'router-captive:degraded', 'router-captivep:enslaved')
5656 for uri
in captive_portal_uris
:
5657 print(f
"Captive portal: {uri}")
5658 radvd_write_config(uri
)
5660 start_radvd(config_file
='bogus-captive-portal.conf')
5661 networkctl_reconfigure('client')
5662 self
.wait_online('client:routable')
5664 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5665 output
= networkctl_status('client')
5667 self
.assertNotIn('Captive Portal:', output
)
5669 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5677 def test_dhcp_server(self
):
5678 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5680 self
.wait_online('veth99:routable', 'veth-peer:routable')
5682 output
= networkctl_status('veth99')
5684 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5685 self
.assertIn('Gateway: 192.168.5.3', output
)
5686 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5687 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5689 output
= networkctl_status('veth-peer')
5690 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5692 def test_dhcp_server_null_server_address(self
):
5693 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5695 self
.wait_online('veth99:routable', 'veth-peer:routable')
5697 output
= check_output('ip --json address show dev veth-peer')
5698 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5699 print(server_address
)
5701 output
= check_output('ip --json address show dev veth99')
5702 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5703 print(client_address
)
5705 output
= networkctl_status('veth99')
5707 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5708 self
.assertIn(f
'Gateway: {server_address}', output
)
5709 self
.assertIn(f
'DNS: {server_address}', output
)
5710 self
.assertIn(f
'NTP: {server_address}', output
)
5712 output
= networkctl_status('veth-peer')
5713 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5715 def test_dhcp_server_with_uplink(self
):
5716 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5717 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5719 self
.wait_online('veth99:routable', 'veth-peer:routable')
5721 output
= networkctl_status('veth99')
5723 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5724 self
.assertIn('Gateway: 192.168.5.3', output
)
5725 self
.assertIn('DNS: 192.168.5.1', output
)
5726 self
.assertIn('NTP: 192.168.5.1', output
)
5728 def test_emit_router_timezone(self
):
5729 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5731 self
.wait_online('veth99:routable', 'veth-peer:routable')
5733 output
= networkctl_status('veth99')
5735 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5736 self
.assertIn('Gateway: 192.168.5.1', output
)
5737 self
.assertIn('Time Zone: Europe/Berlin', output
)
5739 def test_dhcp_server_static_lease(self
):
5740 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5742 self
.wait_online('veth99:routable', 'veth-peer:routable')
5744 output
= networkctl_status('veth99')
5746 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5747 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5749 def test_dhcp_server_static_lease_default_client_id(self
):
5750 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5752 self
.wait_online('veth99:routable', 'veth-peer:routable')
5754 output
= networkctl_status('veth99')
5756 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5757 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5759 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5767 def test_relay_agent(self
):
5768 copy_network_unit('25-agent-veth-client.netdev',
5769 '25-agent-veth-server.netdev',
5770 '25-agent-client.network',
5771 '25-agent-server.network',
5772 '25-agent-client-peer.network',
5773 '25-agent-server-peer.network')
5776 self
.wait_online('client:routable')
5778 output
= networkctl_status('client')
5780 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5782 def test_relay_agent_on_bridge(self
):
5783 copy_network_unit('25-agent-bridge.netdev',
5784 '25-agent-veth-client.netdev',
5785 '25-agent-bridge.network',
5786 '25-agent-bridge-port.network',
5787 '25-agent-client.network')
5789 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5792 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5794 if expect
in read_networkd_log():
5800 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5808 def test_dhcp_client_ipv6_only(self
):
5809 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5812 self
.wait_online('veth-peer:carrier')
5814 # information request mode
5815 # The name ipv6-only option may not be supported by older dnsmasq
5816 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5817 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5818 '--dhcp-option=option6:dns-server,[2600::ee]',
5819 '--dhcp-option=option6:ntp-server,[2600::ff]',
5820 ra_mode
='ra-stateless')
5821 self
.wait_online('veth99:routable', 'veth-peer:routable')
5823 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5824 # Let's wait for the expected DNS server being listed in the state file.
5825 for _
in range(100):
5826 output
= read_link_state_file('veth99')
5827 if 'DNS=2600::ee' in output
:
5831 # Check link state file
5832 print('## link state file')
5833 output
= read_link_state_file('veth99')
5835 self
.assertIn('DNS=2600::ee', output
)
5836 self
.assertIn('NTP=2600::ff', output
)
5838 # Check manager state file
5839 print('## manager state file')
5840 output
= read_manager_state_file()
5842 self
.assertRegex(output
, 'DNS=.*2600::ee')
5843 self
.assertRegex(output
, 'NTP=.*2600::ff')
5845 print('## dnsmasq log')
5846 output
= read_dnsmasq_log_file()
5848 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5849 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5850 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5851 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5852 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5855 check_json(networkctl_json('veth99'))
5859 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5860 '--dhcp-option=option6:dns-server,[2600::ee]',
5861 '--dhcp-option=option6:ntp-server,[2600::ff]')
5862 networkctl_reconfigure('veth99')
5863 self
.wait_online('veth99:routable', 'veth-peer:routable')
5866 output
= check_output('ip address show dev veth99 scope global')
5868 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5869 self
.assertNotIn('192.168.5', output
)
5871 # checking semi-static route
5872 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5874 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5876 # Confirm that ipv6 token is not set in the kernel
5877 output
= check_output('ip token show dev veth99')
5879 self
.assertRegex(output
, 'token :: dev veth99')
5881 # Make manager and link state file updated
5882 resolvectl('revert', 'veth99')
5884 # Check link state file
5885 print('## link state file')
5886 output
= read_link_state_file('veth99')
5888 self
.assertIn('DNS=2600::ee', output
)
5889 self
.assertIn('NTP=2600::ff', output
)
5891 # Check manager state file
5892 print('## manager state file')
5893 output
= read_manager_state_file()
5895 self
.assertRegex(output
, 'DNS=.*2600::ee')
5896 self
.assertRegex(output
, 'NTP=.*2600::ff')
5898 print('## dnsmasq log')
5899 output
= read_dnsmasq_log_file()
5901 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5902 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5903 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5904 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5905 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5906 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5909 check_json(networkctl_json('veth99'))
5911 # Testing without rapid commit support
5912 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5913 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5916 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5917 '--dhcp-option=option6:dns-server,[2600::ee]',
5918 '--dhcp-option=option6:ntp-server,[2600::ff]')
5921 self
.wait_online('veth99:routable', 'veth-peer:routable')
5924 output
= check_output('ip address show dev veth99 scope global')
5926 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5927 self
.assertNotIn('192.168.5', output
)
5929 # checking semi-static route
5930 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5932 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5934 # Make manager and link state file updated
5935 resolvectl('revert', 'veth99')
5937 # Check link state file
5938 print('## link state file')
5939 output
= read_link_state_file('veth99')
5941 self
.assertIn('DNS=2600::ee', output
)
5942 self
.assertIn('NTP=2600::ff', output
)
5944 # Check manager state file
5945 print('## manager state file')
5946 output
= read_manager_state_file()
5948 self
.assertRegex(output
, 'DNS=.*2600::ee')
5949 self
.assertRegex(output
, 'NTP=.*2600::ff')
5951 print('## dnsmasq log')
5952 output
= read_dnsmasq_log_file()
5954 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5955 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5956 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5957 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5958 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5959 self
.assertNotIn('rapid-commit', output
)
5962 check_json(networkctl_json('veth99'))
5964 def test_dhcp_client_ipv6_dbus_status(self
):
5965 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5967 self
.wait_online('veth-peer:carrier')
5969 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5970 # bit set) has yet been received and the configuration does not include WithoutRA=true
5971 state
= get_dhcp6_client_state('veth99')
5972 print(f
"DHCPv6 client state = {state}")
5973 self
.assertEqual(state
, 'stopped')
5975 state
= get_dhcp4_client_state('veth99')
5976 print(f
"DHCPv4 client state = {state}")
5977 self
.assertEqual(state
, 'selecting')
5979 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5980 self
.wait_online('veth99:routable', 'veth-peer:routable')
5982 state
= get_dhcp6_client_state('veth99')
5983 print(f
"DHCPv6 client state = {state}")
5984 self
.assertEqual(state
, 'bound')
5986 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5987 for _
in range(100):
5988 state
= get_dhcp4_client_state('veth99')
5989 if state
== 'stopped':
5993 print(f
"DHCPv4 client state = {state}")
5994 self
.assertEqual(state
, 'stopped')
5996 # restart dnsmasq to clear log
5998 start_dnsmasq('--dhcp-option=108,00:00:02:00')
6000 # Test renew command
6001 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
6002 networkctl('renew', 'veth99')
6004 for _
in range(100):
6005 state
= get_dhcp4_client_state('veth99')
6006 if state
== 'stopped':
6010 print(f
"DHCPv4 client state = {state}")
6011 self
.assertEqual(state
, 'stopped')
6013 print('## dnsmasq log')
6014 output
= read_dnsmasq_log_file()
6016 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
6017 self
.assertIn('DHCPOFFER(veth-peer)', output
)
6018 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6019 self
.assertNotIn('DHCPACK(veth-peer)', output
)
6021 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
6022 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
6025 self
.wait_online('veth-peer:carrier')
6027 self
.wait_online('veth99:routable', 'veth-peer:routable')
6030 output
= check_output('ip address show dev veth99 scope global')
6032 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
6033 self
.assertNotIn('192.168.5', output
)
6035 print('## dnsmasq log')
6036 output
= read_dnsmasq_log_file()
6038 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
6039 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
6040 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6041 self
.assertIn('DHCPREPLY(veth-peer)', output
)
6042 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
6044 def test_dhcp_client_ipv4_only(self
):
6045 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6047 self
.setup_nftset('addr4', 'ipv4_addr')
6048 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
6049 self
.setup_nftset('ifindex', 'iface_index')
6052 self
.wait_online('veth-peer:carrier')
6053 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6054 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
6055 '--dhcp-option=option:domain-search,example.com',
6056 '--dhcp-alternate-port=67,5555',
6057 ipv4_range
='192.168.5.110,192.168.5.119')
6058 self
.wait_online('veth99:routable', 'veth-peer:routable')
6059 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6061 print('## ip address show dev veth99 scope global')
6062 output
= check_output('ip address show dev veth99 scope global')
6064 self
.assertIn('mtu 1492', output
)
6065 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6066 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')
6067 self
.assertNotIn('2600::', output
)
6069 output
= check_output('ip -4 --json address show dev veth99')
6070 for i
in json
.loads(output
)[0]['addr_info']:
6071 if i
['label'] == 'test-label':
6072 address1
= i
['local']
6075 self
.assertFalse(True)
6077 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
6079 print('## ip route show table main dev veth99')
6080 output
= check_output('ip route show table main dev veth99')
6082 # no DHCP routes assigned to the main table
6083 self
.assertNotIn('proto dhcp', output
)
6085 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6086 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6087 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6088 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6090 print('## ip route show table 211 dev veth99')
6091 output
= check_output('ip route show table 211 dev veth99')
6093 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6094 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6095 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6096 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6097 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6098 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6100 print('## link state file')
6101 output
= read_link_state_file('veth99')
6103 # checking DNS server, SIP server, and Domains
6104 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
6105 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
6106 self
.assertIn('DOMAINS=example.com', output
)
6109 j
= json
.loads(networkctl_json('veth99'))
6111 self
.assertEqual(len(j
['DNS']), 2)
6114 self
.assertEqual(i
['Family'], 2)
6115 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6116 self
.assertRegex(a
, '^192.168.5.[67]$')
6117 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6118 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6119 self
.assertEqual('192.168.5.1', a
)
6121 self
.assertEqual(len(j
['SIP']), 2)
6124 self
.assertEqual(i
['Family'], 2)
6125 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6126 self
.assertRegex(a
, '^192.168.5.2[12]$')
6127 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6128 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6129 self
.assertEqual('192.168.5.1', a
)
6131 print('## dnsmasq log')
6132 output
= read_dnsmasq_log_file()
6134 self
.assertIn('vendor class: FooBarVendorTest', output
)
6135 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
6136 self
.assertIn('client provides name: test-hostname', output
)
6137 self
.assertIn('26:mtu', output
)
6139 # change address range, DNS servers, and Domains
6141 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6142 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6143 '--dhcp-option=option:domain-search,foo.example.com',
6144 '--dhcp-alternate-port=67,5555',
6145 ipv4_range
='192.168.5.120,192.168.5.129',)
6147 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6148 print('Wait for the DHCP lease to be expired')
6149 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
6150 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
6152 self
.wait_online('veth99:routable', 'veth-peer:routable')
6154 print('## ip address show dev veth99 scope global')
6155 output
= check_output('ip address show dev veth99 scope global')
6157 self
.assertIn('mtu 1492', output
)
6158 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6159 self
.assertNotIn(f
'{address1}', output
)
6160 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')
6161 self
.assertNotIn('2600::', output
)
6163 output
= check_output('ip -4 --json address show dev veth99')
6164 for i
in json
.loads(output
)[0]['addr_info']:
6165 if i
['label'] == 'test-label':
6166 address2
= i
['local']
6169 self
.assertFalse(True)
6171 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
6173 print('## ip route show table main dev veth99')
6174 output
= check_output('ip route show table main dev veth99')
6176 # no DHCP routes assigned to the main table
6177 self
.assertNotIn('proto dhcp', output
)
6179 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6180 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6181 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6182 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6184 print('## ip route show table 211 dev veth99')
6185 output
= check_output('ip route show table 211 dev veth99')
6187 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6188 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6189 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6190 self
.assertNotIn('192.168.5.6', output
)
6191 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6192 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6193 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6195 print('## link state file')
6196 output
= read_link_state_file('veth99')
6198 # checking DNS server, SIP server, and Domains
6199 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
6200 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
6201 self
.assertIn('DOMAINS=foo.example.com', output
)
6204 j
= json
.loads(networkctl_json('veth99'))
6206 self
.assertEqual(len(j
['DNS']), 3)
6209 self
.assertEqual(i
['Family'], 2)
6210 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6211 self
.assertRegex(a
, '^192.168.5.[178]$')
6212 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6213 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6214 self
.assertEqual('192.168.5.1', a
)
6216 self
.assertEqual(len(j
['SIP']), 2)
6219 self
.assertEqual(i
['Family'], 2)
6220 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6221 self
.assertRegex(a
, '^192.168.5.2[34]$')
6222 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6223 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6224 self
.assertEqual('192.168.5.1', a
)
6226 print('## dnsmasq log')
6227 output
= read_dnsmasq_log_file()
6229 self
.assertIn('vendor class: FooBarVendorTest', output
)
6230 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6231 self
.assertIn('client provides name: test-hostname', output
)
6232 self
.assertIn('26:mtu', output
)
6234 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6236 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6237 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6238 self
.check_nftset('ifindex', 'veth99')
6240 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6242 def test_dhcp_client_ipv4_dbus_status(self
):
6243 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6245 self
.wait_online('veth-peer:carrier')
6247 state
= get_dhcp4_client_state('veth99')
6248 print(f
"State = {state}")
6249 self
.assertEqual(state
, 'rebooting')
6251 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6252 '--dhcp-option=option:domain-search,example.com',
6253 '--dhcp-alternate-port=67,5555',
6254 ipv4_range
='192.168.5.110,192.168.5.119')
6255 self
.wait_online('veth99:routable', 'veth-peer:routable')
6256 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6258 state
= get_dhcp4_client_state('veth99')
6259 print(f
"State = {state}")
6260 self
.assertEqual(state
, 'bound')
6262 def test_dhcp_client_allow_list(self
):
6263 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6266 self
.wait_online('veth-peer:carrier')
6267 since
= datetime
.datetime
.now()
6270 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6272 if expect
in read_networkd_log(since
=since
):
6278 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6279 since
= datetime
.datetime
.now()
6282 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6284 if expect
in read_networkd_log(since
=since
):
6290 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6291 since
= datetime
.datetime
.now()
6294 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6296 if expect
in read_networkd_log(since
=since
):
6302 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6303 def test_dhcp_client_rapid_commit(self
):
6304 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6306 self
.wait_online('veth-peer:carrier')
6308 start_dnsmasq('--dhcp-rapid-commit')
6309 self
.wait_online('veth99:routable', 'veth-peer:routable')
6310 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6312 state
= get_dhcp4_client_state('veth99')
6313 print(f
"DHCPv4 client state = {state}")
6314 self
.assertEqual(state
, 'bound')
6316 output
= read_dnsmasq_log_file()
6317 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6318 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6319 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6320 self
.assertIn('DHCPACK(veth-peer)', output
)
6322 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6323 copy_network_unit('25-veth.netdev',
6324 '25-dhcp-server-ipv6-only-mode.network',
6325 '25-dhcp-client-ipv6-only-mode.network')
6327 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6328 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6330 state
= get_dhcp4_client_state('veth99')
6331 print(f
"State = {state}")
6332 self
.assertEqual(state
, 'bound')
6334 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6336 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6342 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6343 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6344 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6346 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6347 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6348 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6349 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6350 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6351 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6352 copy_network_unit(*testunits
, copy_dropins
=False)
6355 self
.wait_online('veth-peer:carrier')
6356 additional_options
= [
6357 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6358 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6359 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6362 additional_options
+= [
6363 '--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'
6365 start_dnsmasq(*additional_options
)
6366 self
.wait_online('veth99:routable', 'veth-peer:routable')
6368 output
= check_output('ip -4 route show dev veth99')
6374 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6375 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6376 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6377 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6378 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6380 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6381 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6382 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6383 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6385 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6386 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6387 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6388 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6389 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6390 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6391 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6392 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6395 if use_gateway
and (not classless
or not use_routes
):
6396 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6398 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6400 # Check route to gateway
6401 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6402 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6404 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6406 # Check RoutesToDNS= and RoutesToNTP=
6407 if dns_and_ntp_routes
:
6408 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6409 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6412 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6413 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6415 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6416 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6418 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6419 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6421 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6422 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6423 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6424 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6426 check_json(networkctl_json())
6428 def test_dhcp_client_settings_anonymize(self
):
6429 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6431 self
.wait_online('veth-peer:carrier')
6433 self
.wait_online('veth99:routable', 'veth-peer:routable')
6435 print('## dnsmasq log')
6436 output
= read_dnsmasq_log_file()
6438 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6439 self
.assertNotIn('test-hostname', output
)
6440 self
.assertNotIn('26:mtu', output
)
6442 def test_dhcp_keep_configuration_dhcp(self
):
6443 copy_network_unit('25-veth.netdev',
6444 '25-dhcp-server-veth-peer.network',
6445 '25-dhcp-client-keep-configuration-dhcp.network')
6447 self
.wait_online('veth-peer:carrier')
6449 self
.wait_online('veth99:routable', 'veth-peer:routable')
6451 output
= check_output('ip address show dev veth99 scope global')
6453 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6454 'valid_lft forever preferred_lft forever')
6456 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6459 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6460 print('Wait for the DHCP lease to be expired')
6463 # The lease address should be kept after the lease expired
6464 output
= check_output('ip address show dev veth99 scope global')
6466 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6467 'valid_lft forever preferred_lft forever')
6471 # The lease address should be kept after networkd stopped
6472 output
= check_output('ip address show dev veth99 scope global')
6474 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6475 'valid_lft forever preferred_lft forever')
6477 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6478 f
.write('[Network]\nDHCP=no\n')
6481 self
.wait_online('veth99:routable', 'veth-peer:routable')
6483 # Still the lease address should be kept after networkd restarted
6484 output
= check_output('ip address show dev veth99 scope global')
6486 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6487 'valid_lft forever preferred_lft forever')
6489 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6490 copy_network_unit('25-veth.netdev',
6491 '25-dhcp-server-veth-peer.network',
6492 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6494 self
.wait_online('veth-peer:carrier')
6496 self
.wait_online('veth99:routable', 'veth-peer:routable')
6498 output
= check_output('ip address show dev veth99 scope global')
6500 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6505 output
= check_output('ip address show dev veth99 scope global')
6507 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6510 self
.wait_online('veth-peer:routable')
6512 output
= check_output('ip address show dev veth99 scope global')
6514 self
.assertNotIn('192.168.5.', output
)
6516 def test_dhcp_client_reuse_address_as_static(self
):
6517 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6519 self
.wait_online('veth-peer:carrier')
6521 self
.wait_online('veth99:routable', 'veth-peer:routable')
6523 # link become 'routable' when at least one protocol provide an valid address.
6524 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6525 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6527 output
= check_output('ip address show dev veth99 scope global')
6528 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6529 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6530 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6531 print(static_network
)
6533 remove_network_unit('25-dhcp-client.network')
6535 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6536 f
.write(static_network
)
6539 self
.wait_online('veth99:routable')
6541 output
= check_output('ip -4 address show dev veth99 scope global')
6543 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6544 'valid_lft forever preferred_lft forever')
6546 output
= check_output('ip -6 address show dev veth99 scope global')
6548 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6549 'valid_lft forever preferred_lft forever')
6551 @expectedFailureIfModuleIsNotAvailable('vrf')
6552 def test_dhcp_client_vrf(self
):
6553 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6554 '25-vrf.netdev', '25-vrf.network')
6556 self
.wait_online('veth-peer:carrier')
6558 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6560 # link become 'routable' when at least one protocol provide an valid address.
6561 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6562 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6564 print('## ip -d link show dev vrf99')
6565 output
= check_output('ip -d link show dev vrf99')
6567 self
.assertRegex(output
, 'vrf table 42')
6569 print('## ip address show vrf vrf99')
6570 output
= check_output('ip address show vrf vrf99')
6572 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6573 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6574 self
.assertRegex(output
, 'inet6 .* scope link')
6576 print('## ip address show dev veth99')
6577 output
= check_output('ip address show dev veth99')
6579 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6580 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6581 self
.assertRegex(output
, 'inet6 .* scope link')
6583 print('## ip route show vrf vrf99')
6584 output
= check_output('ip route show vrf vrf99')
6586 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6587 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6588 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6590 print('## ip route show table main dev veth99')
6591 output
= check_output('ip route show table main dev veth99')
6593 self
.assertEqual(output
, '')
6595 def test_dhcp_client_gateway_onlink_implicit(self
):
6596 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6597 '25-dhcp-client-gateway-onlink-implicit.network')
6599 self
.wait_online('veth-peer:carrier')
6601 self
.wait_online('veth99:routable', 'veth-peer:routable')
6603 output
= networkctl_status('veth99')
6605 self
.assertRegex(output
, '192.168.5')
6607 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6609 self
.assertRegex(output
, 'onlink')
6610 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6612 self
.assertRegex(output
, 'onlink')
6614 def test_dhcp_client_with_ipv4ll(self
):
6615 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6616 '25-dhcp-client-with-ipv4ll.network')
6618 # we need to increase timeout above default, as this will need to wait for
6619 # systemd-networkd to get the dhcpv4 transient failure event
6620 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6622 output
= check_output('ip -4 address show dev veth99')
6624 self
.assertNotIn('192.168.5.', output
)
6625 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6628 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6629 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6630 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')
6631 self
.wait_online('veth99:routable')
6633 output
= check_output('ip -4 address show dev veth99')
6635 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6636 self
.assertNotIn('169.254.', output
)
6637 self
.assertNotIn('scope link', output
)
6640 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6641 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)
6642 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6644 output
= check_output('ip -4 address show dev veth99')
6646 self
.assertNotIn('192.168.5.', output
)
6647 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6649 def test_dhcp_client_use_dns(self
):
6650 def check(self
, ipv4
, ipv6
):
6651 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6652 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6653 f
.write('[DHCPv4]\nUseDNS=')
6654 f
.write('yes' if ipv4
else 'no')
6655 f
.write('\n[DHCPv6]\nUseDNS=')
6656 f
.write('yes' if ipv6
else 'no')
6657 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6660 self
.wait_online('veth99:routable')
6662 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6663 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6664 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6666 # make resolved re-read the link state file
6667 resolvectl('revert', 'veth99')
6669 output
= resolvectl('dns', 'veth99')
6672 self
.assertIn('192.168.5.1', output
)
6674 self
.assertNotIn('192.168.5.1', output
)
6676 self
.assertIn('2600::1', output
)
6678 self
.assertNotIn('2600::1', output
)
6680 check_json(networkctl_json())
6682 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6685 self
.wait_online('veth-peer:carrier')
6686 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6687 '--dhcp-option=option6:dns-server,[2600::1]')
6689 check(self
, True, True)
6690 check(self
, True, False)
6691 check(self
, False, True)
6692 check(self
, False, False)
6694 def test_dhcp_client_use_captive_portal(self
):
6695 def check(self
, ipv4
, ipv6
):
6696 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6697 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6698 f
.write('[DHCPv4]\nUseCaptivePortal=')
6699 f
.write('yes' if ipv4
else 'no')
6700 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6701 f
.write('yes' if ipv6
else 'no')
6702 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6705 self
.wait_online('veth99:routable')
6707 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6708 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6709 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6711 output
= networkctl_status('veth99')
6714 self
.assertIn('Captive Portal: http://systemd.io', output
)
6716 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6718 check_json(networkctl_json())
6720 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6723 self
.wait_online('veth-peer:carrier')
6724 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6725 '--dhcp-option=option6:103,http://systemd.io')
6727 check(self
, True, True)
6728 check(self
, True, False)
6729 check(self
, False, True)
6730 check(self
, False, False)
6732 def test_dhcp_client_reject_captive_portal(self
):
6733 def check(self
, ipv4
, ipv6
):
6734 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6735 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6736 f
.write('[DHCPv4]\nUseCaptivePortal=')
6737 f
.write('yes' if ipv4
else 'no')
6738 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6739 f
.write('yes' if ipv6
else 'no')
6740 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6743 self
.wait_online('veth99:routable')
6745 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6746 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6747 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6749 output
= networkctl_status('veth99')
6751 self
.assertNotIn('Captive Portal: ', output
)
6752 self
.assertNotIn('invalid/url', output
)
6754 check_json(networkctl_json())
6756 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6759 self
.wait_online('veth-peer:carrier')
6760 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6761 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6762 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6764 check(self
, True, True)
6765 check(self
, True, False)
6766 check(self
, False, True)
6767 check(self
, False, False)
6769 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6777 def check_dhcp6_prefix(self
, link
):
6778 description
= get_link_description(link
)
6780 self
.assertIn('DHCPv6Client', description
.keys())
6781 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6783 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6785 self
.assertEqual(len(prefixInfo
), 1)
6787 self
.assertIn('Prefix', prefixInfo
[0].keys())
6788 self
.assertIn('PrefixLength', prefixInfo
[0].keys())
6789 self
.assertIn('PreferredLifetimeUSec', prefixInfo
[0].keys())
6790 self
.assertIn('ValidLifetimeUSec', prefixInfo
[0].keys())
6792 self
.assertEqual(prefixInfo
[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6793 self
.assertEqual(prefixInfo
[0]['PrefixLength'], 56)
6794 self
.assertGreater(prefixInfo
[0]['PreferredLifetimeUSec'], 0)
6795 self
.assertGreater(prefixInfo
[0]['ValidLifetimeUSec'], 0)
6797 def test_dhcp6pd_no_address(self
):
6799 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6802 self
.wait_online('veth-peer:routable')
6803 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6804 self
.wait_online('veth99:degraded')
6806 print('### ip -6 address show dev veth99 scope global')
6807 output
= check_output('ip -6 address show dev veth99 scope global')
6809 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6811 self
.check_dhcp6_prefix('veth99')
6813 def test_dhcp6pd_no_assign(self
):
6814 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6815 # However, the server does not provide IA_NA. For issue #31349.
6816 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6819 self
.wait_online('veth-peer:routable')
6820 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd-no-range.conf', ipv
='-6')
6821 self
.wait_online('veth99:degraded')
6823 print('### ip -6 address show dev veth99 scope global')
6824 output
= check_output('ip -6 address show dev veth99 scope global')
6826 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6828 self
.check_dhcp6_prefix('veth99')
6830 def test_dhcp6pd(self
):
6831 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6832 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6833 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6834 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6835 '25-dhcp-pd-downstream-dummy97.network',
6836 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6837 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6840 self
.wait_online('veth-peer:routable')
6841 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6842 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6843 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6845 self
.setup_nftset('addr6', 'ipv6_addr')
6846 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6847 self
.setup_nftset('ifindex', 'iface_index')
6849 # Check DBus assigned prefix information to veth99
6850 self
.check_dhcp6_prefix('veth99')
6852 print('### ip -6 address show dev veth-peer scope global')
6853 output
= check_output('ip -6 address show dev veth-peer scope global')
6855 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6859 # dummy97: 0x01 (The link will appear later)
6861 # dummy99: auto -> 0x02 (No address assignment)
6866 print('### ip -6 address show dev veth99 scope global')
6867 output
= check_output('ip -6 address show dev veth99 scope global')
6870 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6871 # address in IA_PD (Token=static)
6872 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6873 # address in IA_PD (Token=eui64)
6874 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6875 # address in IA_PD (temporary)
6876 # Note that the temporary addresses may appear after the link enters configured state
6877 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')
6879 print('### ip -6 address show dev test1 scope global')
6880 output
= check_output('ip -6 address show dev test1 scope global')
6882 # address in IA_PD (Token=static)
6883 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6884 # address in IA_PD (temporary)
6885 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')
6887 print('### ip -6 address show dev dummy98 scope global')
6888 output
= check_output('ip -6 address show dev dummy98 scope global')
6890 # address in IA_PD (Token=static)
6891 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6892 # address in IA_PD (temporary)
6893 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')
6895 print('### ip -6 address show dev dummy99 scope global')
6896 output
= check_output('ip -6 address show dev dummy99 scope global')
6899 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6901 print('### ip -6 address show dev veth97 scope global')
6902 output
= check_output('ip -6 address show dev veth97 scope global')
6904 # address in IA_PD (Token=static)
6905 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6906 # address in IA_PD (Token=eui64)
6907 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6908 # address in IA_PD (temporary)
6909 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')
6911 print('### ip -6 address show dev veth97-peer scope global')
6912 output
= check_output('ip -6 address show dev veth97-peer scope global')
6914 # NDisc address (Token=static)
6915 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6916 # NDisc address (Token=eui64)
6917 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6918 # NDisc address (temporary)
6919 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')
6921 print('### ip -6 address show dev veth98 scope global')
6922 output
= check_output('ip -6 address show dev veth98 scope global')
6924 # address in IA_PD (Token=static)
6925 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6926 # address in IA_PD (Token=eui64)
6927 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6928 # address in IA_PD (temporary)
6929 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')
6931 print('### ip -6 address show dev veth98-peer scope global')
6932 output
= check_output('ip -6 address show dev veth98-peer scope global')
6934 # NDisc address (Token=static)
6935 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6936 # NDisc address (Token=eui64)
6937 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6938 # NDisc address (temporary)
6939 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')
6941 print('### ip -6 route show type unreachable')
6942 output
= check_output('ip -6 route show type unreachable')
6944 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6946 print('### ip -6 route show dev veth99')
6947 output
= check_output('ip -6 route show dev veth99')
6949 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6951 print('### ip -6 route show dev test1')
6952 output
= check_output('ip -6 route show dev test1')
6954 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6956 print('### ip -6 route show dev dummy98')
6957 output
= check_output('ip -6 route show dev dummy98')
6959 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6961 print('### ip -6 route show dev dummy99')
6962 output
= check_output('ip -6 route show dev dummy99')
6964 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6966 print('### ip -6 route show dev veth97')
6967 output
= check_output('ip -6 route show dev veth97')
6969 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6971 print('### ip -6 route show dev veth97-peer')
6972 output
= check_output('ip -6 route show dev veth97-peer')
6974 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6976 print('### ip -6 route show dev veth98')
6977 output
= check_output('ip -6 route show dev veth98')
6979 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6981 print('### ip -6 route show dev veth98-peer')
6982 output
= check_output('ip -6 route show dev veth98-peer')
6984 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6986 # Test case for a downstream which appears later
6987 check_output('ip link add dummy97 type dummy')
6988 self
.wait_online('dummy97:routable')
6990 print('### ip -6 address show dev dummy97 scope global')
6991 output
= check_output('ip -6 address show dev dummy97 scope global')
6993 # address in IA_PD (Token=static)
6994 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6995 # address in IA_PD (temporary)
6996 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')
6998 print('### ip -6 route show dev dummy97')
6999 output
= check_output('ip -6 route show dev dummy97')
7001 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
7003 # Test case for reconfigure
7004 networkctl_reconfigure('dummy98', 'dummy99')
7005 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7007 print('### ip -6 address show dev dummy98 scope global')
7008 output
= check_output('ip -6 address show dev dummy98 scope global')
7010 # address in IA_PD (Token=static)
7011 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7012 # address in IA_PD (temporary)
7013 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')
7015 print('### ip -6 address show dev dummy99 scope global')
7016 output
= check_output('ip -6 address show dev dummy99 scope global')
7019 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
7021 print('### ip -6 route show dev dummy98')
7022 output
= check_output('ip -6 route show dev dummy98')
7024 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
7026 print('### ip -6 route show dev dummy99')
7027 output
= check_output('ip -6 route show dev dummy99')
7029 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
7031 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
7033 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
7034 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
7035 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
7036 self
.check_nftset('ifindex', 'dummy98')
7038 self
.teardown_nftset('addr6', 'network6', 'ifindex')
7040 def verify_dhcp4_6rd(self
, tunnel_name
):
7041 print('### ip -4 address show dev veth-peer scope global')
7042 output
= check_output('ip -4 address show dev veth-peer scope global')
7044 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
7048 # dummy97: 0x01 (The link will appear later)
7050 # dummy99: auto -> 0x0[23] (No address assignment)
7051 # 6rd-XXX: auto -> 0x0[23]
7056 print('### ip -4 address show dev veth99 scope global')
7057 output
= check_output('ip -4 address show dev veth99 scope global')
7059 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7061 print('### ip -6 address show dev veth99 scope global')
7062 output
= check_output('ip -6 address show dev veth99 scope global')
7064 # address in IA_PD (Token=static)
7065 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7066 # address in IA_PD (Token=eui64)
7067 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7068 # address in IA_PD (temporary)
7069 # Note that the temporary addresses may appear after the link enters configured state
7070 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')
7072 print('### ip -6 address show dev test1 scope global')
7073 output
= check_output('ip -6 address show dev test1 scope global')
7075 # address in IA_PD (Token=static)
7076 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7077 # address in IA_PD (temporary)
7078 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')
7080 print('### ip -6 address show dev dummy98 scope global')
7081 output
= check_output('ip -6 address show dev dummy98 scope global')
7083 # address in IA_PD (Token=static)
7084 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7085 # address in IA_PD (temporary)
7086 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')
7088 print('### ip -6 address show dev dummy99 scope global')
7089 output
= check_output('ip -6 address show dev dummy99 scope global')
7092 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7094 print('### ip -6 address show dev veth97 scope global')
7095 output
= check_output('ip -6 address show dev veth97 scope global')
7097 # address in IA_PD (Token=static)
7098 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7099 # address in IA_PD (Token=eui64)
7100 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7101 # address in IA_PD (temporary)
7102 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')
7104 print('### ip -6 address show dev veth97-peer scope global')
7105 output
= check_output('ip -6 address show dev veth97-peer scope global')
7107 # NDisc address (Token=static)
7108 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7109 # NDisc address (Token=eui64)
7110 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7111 # NDisc address (temporary)
7112 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')
7114 print('### ip -6 address show dev veth98 scope global')
7115 output
= check_output('ip -6 address show dev veth98 scope global')
7117 # address in IA_PD (Token=static)
7118 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7119 # address in IA_PD (Token=eui64)
7120 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7121 # address in IA_PD (temporary)
7122 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')
7124 print('### ip -6 address show dev veth98-peer scope global')
7125 output
= check_output('ip -6 address show dev veth98-peer scope global')
7127 # NDisc address (Token=static)
7128 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7129 # NDisc address (Token=eui64)
7130 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7131 # NDisc address (temporary)
7132 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')
7134 print('### ip -6 route show type unreachable')
7135 output
= check_output('ip -6 route show type unreachable')
7137 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7139 print('### ip -6 route show dev veth99')
7140 output
= check_output('ip -6 route show dev veth99')
7142 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7144 print('### ip -6 route show dev test1')
7145 output
= check_output('ip -6 route show dev test1')
7147 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7149 print('### ip -6 route show dev dummy98')
7150 output
= check_output('ip -6 route show dev dummy98')
7152 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7154 print('### ip -6 route show dev dummy99')
7155 output
= check_output('ip -6 route show dev dummy99')
7157 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7159 print('### ip -6 route show dev veth97')
7160 output
= check_output('ip -6 route show dev veth97')
7162 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7164 print('### ip -6 route show dev veth97-peer')
7165 output
= check_output('ip -6 route show dev veth97-peer')
7167 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7169 print('### ip -6 route show dev veth98')
7170 output
= check_output('ip -6 route show dev veth98')
7172 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7174 print('### ip -6 route show dev veth98-peer')
7175 output
= check_output('ip -6 route show dev veth98-peer')
7177 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7179 print('### ip -6 address show dev dummy97 scope global')
7180 output
= check_output('ip -6 address show dev dummy97 scope global')
7182 # address in IA_PD (Token=static)
7183 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7184 # address in IA_PD (temporary)
7185 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')
7187 print('### ip -6 route show dev dummy97')
7188 output
= check_output('ip -6 route show dev dummy97')
7190 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7192 print(f
'### ip -d link show dev {tunnel_name}')
7193 output
= check_output(f
'ip -d link show dev {tunnel_name}')
7195 self
.assertIn('link/sit 10.100.100.', output
)
7196 self
.assertIn('local 10.100.100.', output
)
7197 self
.assertIn('ttl 64', output
)
7198 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
7199 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
7201 print(f
'### ip -6 address show dev {tunnel_name}')
7202 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
7204 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')
7205 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7207 print(f
'### ip -6 route show dev {tunnel_name}')
7208 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
7210 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7211 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
7213 print('### ip -6 route show default')
7214 output
= check_output('ip -6 route show default')
7216 self
.assertIn('default', output
)
7217 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
7219 def test_dhcp4_6rd(self
):
7220 def get_dhcp_6rd_prefix(link
):
7221 description
= get_link_description(link
)
7223 self
.assertIn('DHCPv4Client', description
.keys())
7224 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
7226 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
7227 self
.assertIn('Prefix', prefixInfo
.keys())
7228 self
.assertIn('PrefixLength', prefixInfo
.keys())
7229 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
7230 self
.assertIn('BorderRouters', prefixInfo
.keys())
7234 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7235 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7236 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7237 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7238 '25-dhcp-pd-downstream-dummy97.network',
7239 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7240 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7241 '80-6rd-tunnel.network')
7244 self
.wait_online('veth-peer:routable')
7247 # 6rd-prefix: 2001:db8::/32
7248 # br-addresss: 10.0.0.1
7250 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',
7251 ipv4_range
='10.100.100.100,10.100.100.200',
7252 ipv4_router
='10.0.0.1')
7253 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7254 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7256 # Check the DBus interface for assigned prefix information
7257 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7259 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7260 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7261 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7262 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7264 # Test case for a downstream which appears later
7265 check_output('ip link add dummy97 type dummy')
7266 self
.wait_online('dummy97:routable')
7270 for name
in os
.listdir('/sys/class/net/'):
7271 if name
.startswith('6rd-'):
7275 self
.wait_online(f
'{tunnel_name}:routable')
7277 self
.verify_dhcp4_6rd(tunnel_name
)
7279 # Test case for reconfigure
7280 networkctl_reconfigure('dummy98', 'dummy99')
7281 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7283 self
.verify_dhcp4_6rd(tunnel_name
)
7285 print('Wait for the DHCP lease to be renewed/rebind')
7288 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7289 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7291 self
.verify_dhcp4_6rd(tunnel_name
)
7293 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7301 def test_ipv6_route_prefix(self
):
7302 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7303 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7306 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7308 output
= check_output('ip address show dev veth-peer')
7310 self
.assertIn('inet6 2001:db8:0:1:', output
)
7311 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7312 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7314 output
= check_output('ip -6 route show dev veth-peer')
7316 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7317 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7318 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7319 self
.assertIn('2001:db0:fff::/64 via ', output
)
7320 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7321 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7323 output
= check_output('ip address show dev veth99')
7325 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7326 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7327 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7328 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7330 output
= resolvectl('dns', 'veth-peer')
7332 self
.assertRegex(output
, '2001:db8:1:1::2')
7334 output
= resolvectl('domain', 'veth-peer')
7336 self
.assertIn('example.com', output
)
7338 check_json(networkctl_json())
7340 output
= networkctl_json('veth-peer')
7344 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7346 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7347 self
.assertEqual(prefix
, '64:ff9b::')
7349 prefix_length
= pref64
['PrefixLength']
7350 self
.assertEqual(prefix_length
, 96)
7352 def test_ipv6_route_prefix_deny_list(self
):
7353 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7354 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7357 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7359 output
= check_output('ip address show dev veth-peer')
7361 self
.assertIn('inet6 2001:db8:0:1:', output
)
7362 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7364 output
= check_output('ip -6 route show dev veth-peer')
7366 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7367 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7368 self
.assertIn('2001:db0:fff::/64 via ', output
)
7369 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7371 output
= check_output('ip address show dev veth99')
7373 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7374 self
.assertIn('inet6 2001:db8:0:2:', output
)
7376 output
= resolvectl('dns', 'veth-peer')
7378 self
.assertRegex(output
, '2001:db8:1:1::2')
7380 output
= resolvectl('domain', 'veth-peer')
7382 self
.assertIn('example.com', output
)
7384 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7392 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7398 self
.wait_online('dummy98:routable')
7399 self
.check_link_attr('dummy98', 'mtu', mtu
)
7400 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7402 # test normal restart
7404 self
.wait_online('dummy98:routable')
7405 self
.check_link_attr('dummy98', 'mtu', mtu
)
7406 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7409 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7411 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7412 ''' test setting mtu/ipv6_mtu with interface already up '''
7415 # note - changing the device mtu resets the ipv6 mtu
7416 check_output('ip link set up mtu 1501 dev dummy98')
7417 check_output('ip link set up mtu 1500 dev dummy98')
7418 self
.check_link_attr('dummy98', 'mtu', '1500')
7419 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7421 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7423 def test_mtu_network(self
):
7424 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7425 self
.check_mtu('1600')
7427 def test_mtu_netdev(self
):
7428 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7429 # note - MTU set by .netdev happens ONLY at device creation!
7430 self
.check_mtu('1600', reset
=False)
7432 def test_mtu_link(self
):
7433 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7434 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7435 self
.check_mtu('1600', reset
=False)
7437 def test_ipv6_mtu(self
):
7438 ''' set ipv6 mtu without setting device mtu '''
7439 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7440 self
.check_mtu('1500', '1400')
7442 def test_ipv6_mtu_toolarge(self
):
7443 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7444 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7445 self
.check_mtu('1500', '1500')
7447 def test_mtu_network_ipv6_mtu(self
):
7448 ''' set ipv6 mtu and set device mtu via network file '''
7449 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7450 self
.check_mtu('1600', '1550')
7452 def test_mtu_netdev_ipv6_mtu(self
):
7453 ''' set ipv6 mtu and set device mtu via netdev file '''
7454 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7455 self
.check_mtu('1600', '1550', reset
=False)
7457 def test_mtu_link_ipv6_mtu(self
):
7458 ''' set ipv6 mtu and set device mtu via link file '''
7459 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7460 self
.check_mtu('1600', '1550', reset
=False)
7463 if __name__
== '__main__':
7464 parser
= argparse
.ArgumentParser()
7465 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7466 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7467 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7468 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7469 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7470 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7471 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7472 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7473 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7474 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7475 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7476 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7477 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7478 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7479 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7480 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7481 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
)
7482 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7485 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7486 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7487 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7488 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7489 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7490 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7491 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7492 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7493 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7494 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7495 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7496 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7497 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7500 networkd_bin
= ns
.networkd_bin
7502 resolved_bin
= ns
.resolved_bin
7503 if ns
.timesyncd_bin
:
7504 timesyncd_bin
= ns
.timesyncd_bin
7506 udevd_bin
= ns
.udevd_bin
7507 if ns
.wait_online_bin
:
7508 wait_online_bin
= ns
.wait_online_bin
7509 if ns
.networkctl_bin
:
7510 networkctl_bin
= ns
.networkctl_bin
7511 if ns
.resolvectl_bin
:
7512 resolvectl_bin
= ns
.resolvectl_bin
7513 if ns
.timedatectl_bin
:
7514 timedatectl_bin
= ns
.timedatectl_bin
7516 udevadm_bin
= ns
.udevadm_bin
7519 systemd_source_dir
= ns
.source_dir
7521 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7522 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7523 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7525 use_valgrind
= ns
.use_valgrind
7526 enable_debug
= ns
.enable_debug
7527 asan_options
= ns
.asan_options
7528 lsan_options
= ns
.lsan_options
7529 ubsan_options
= ns
.ubsan_options
7530 with_coverage
= ns
.with_coverage
7533 # Do not forget the trailing space.
7534 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7536 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7537 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7538 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7539 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7540 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7543 env
.update({'ASAN_OPTIONS': asan_options
})
7545 env
.update({'LSAN_OPTIONS': lsan_options
})
7547 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7549 env
.update({'SYSTEMD_MEMPOOL': '0'})
7551 wait_online_env
= env
.copy()
7553 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7555 sys
.argv
[1:] = unknown_args
7556 unittest
.main(verbosity
=3)