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', output
):
5322 # With interface name
5323 output
= networkctl('lldp', 'veth99');
5325 self
.assertRegex(output
, r
'veth99 .* veth-peer')
5327 # With interface name pattern
5328 output
= networkctl('lldp', 've*9');
5330 self
.assertRegex(output
, r
'veth99 .* veth-peer')
5333 output
= networkctl('--json=short', 'lldp')
5335 self
.assertIn('"InterfaceName":"veth99"', output
)
5336 self
.assertIn('"PortID":"veth-peer"', output
)
5338 # json format with interface name
5339 output
= networkctl('--json=short', 'lldp', 'veth99')
5341 self
.assertIn('"InterfaceName":"veth99"', output
)
5342 self
.assertIn('"PortID":"veth-peer"', output
)
5344 # json format with interface name pattern
5345 output
= networkctl('--json=short', 'lldp', 've*9')
5347 self
.assertIn('"InterfaceName":"veth99"', output
)
5348 self
.assertIn('"PortID":"veth-peer"', output
)
5350 # LLDP neighbors in status
5351 output
= networkctl_status('veth99')
5353 self
.assertRegex(output
, r
'Connected To: .* on port veth-peer')
5355 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5363 def test_ipv6_prefix_delegation(self
):
5364 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5365 self
.setup_nftset('addr6', 'ipv6_addr')
5366 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5367 self
.setup_nftset('ifindex', 'iface_index')
5369 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5371 # IPv6SendRA=yes implies IPv6Forwarding.
5372 self
.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5374 output
= resolvectl('dns', 'veth99')
5376 self
.assertRegex(output
, 'fe80::')
5377 self
.assertRegex(output
, '2002:da8:1::1')
5379 output
= resolvectl('domain', 'veth99')
5381 self
.assertIn('hogehoge.test', output
)
5383 output
= networkctl_status('veth99')
5385 self
.assertRegex(output
, '2002:da8:1:0')
5387 self
.check_netlabel('veth99', '2002:da8:1::/64')
5388 self
.check_netlabel('veth99', '2002:da8:2::/64')
5390 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5391 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5392 self
.check_nftset('network6', '2002:da8:1::/64')
5393 self
.check_nftset('network6', '2002:da8:2::/64')
5394 self
.check_nftset('ifindex', 'veth99')
5396 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5398 def check_ipv6_token_static(self
):
5399 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5401 output
= networkctl_status('veth99')
5403 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5404 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5405 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5406 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5408 def test_ipv6_token_static(self
):
5409 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5412 self
.check_ipv6_token_static()
5415 check_output('ip link set veth99 down')
5416 check_output('ip link set veth99 up')
5418 self
.check_ipv6_token_static()
5421 check_output('ip link set veth99 down')
5422 time
.sleep(random
.uniform(0, 0.1))
5423 check_output('ip link set veth99 up')
5424 time
.sleep(random
.uniform(0, 0.1))
5426 self
.check_ipv6_token_static()
5428 def test_ipv6_token_prefixstable(self
):
5429 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5431 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5433 output
= networkctl_status('veth99')
5435 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5436 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5438 def test_ipv6_token_prefixstable_without_address(self
):
5439 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5441 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5443 output
= networkctl_status('veth99')
5445 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5446 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5448 def check_router_hop_limit(self
, hop_limit
):
5449 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5451 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5453 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5455 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5457 def test_router_hop_limit(self
):
5458 copy_network_unit('25-veth-client.netdev',
5459 '25-veth-router.netdev',
5461 '25-veth-bridge.network',
5462 '25-veth-client.network',
5463 '25-veth-router-hop-limit.network',
5464 '25-bridge99.network')
5466 self
.wait_online('client-p:enslaved',
5467 'router:degraded', 'router-p:enslaved',
5468 'bridge99:routable')
5470 self
.check_router_hop_limit(42)
5472 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5473 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5477 self
.check_router_hop_limit(43)
5479 def test_router_preference(self
):
5480 copy_network_unit('25-veth-client.netdev',
5481 '25-veth-router-high.netdev',
5482 '25-veth-router-low.netdev',
5484 '25-veth-bridge.network',
5485 '25-veth-client.network',
5486 '25-veth-router-high.network',
5487 '25-veth-router-low.network',
5488 '25-bridge99.network')
5490 self
.wait_online('client-p:enslaved',
5491 'router-high:degraded', 'router-high-p:enslaved',
5492 'router-low:degraded', 'router-low-p:enslaved',
5493 'bridge99:routable')
5495 networkctl_reconfigure('client')
5496 self
.wait_online('client:routable')
5498 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5499 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5500 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5501 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5503 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5505 self
.assertIn('metric 512', output
)
5506 self
.assertIn('pref high', output
)
5507 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5509 self
.assertIn('metric 2048', output
)
5510 self
.assertIn('pref low', output
)
5512 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5513 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5516 self
.wait_online('client:routable')
5518 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5519 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5520 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5521 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5523 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5525 self
.assertIn('metric 100', output
)
5526 self
.assertNotIn('metric 512', output
)
5527 self
.assertIn('pref high', output
)
5528 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5530 self
.assertIn('metric 300', output
)
5531 self
.assertNotIn('metric 2048', output
)
5532 self
.assertIn('pref low', output
)
5534 # swap the preference (for issue #28439)
5535 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5536 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5538 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5539 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5541 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5543 self
.assertIn('metric 300', output
)
5544 self
.assertNotIn('metric 100', output
)
5545 self
.assertIn('pref low', output
)
5546 self
.assertNotIn('pref high', output
)
5547 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5549 self
.assertIn('metric 100', output
)
5550 self
.assertNotIn('metric 300', output
)
5551 self
.assertIn('pref high', output
)
5552 self
.assertNotIn('pref low', output
)
5554 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5555 def test_captive_portal(self
):
5556 copy_network_unit('25-veth-client.netdev',
5557 '25-veth-router-captive.netdev',
5559 '25-veth-client-captive.network',
5560 '25-veth-router-captive.network',
5561 '25-veth-bridge-captive.network',
5562 '25-bridge99.network')
5564 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5565 'router-captive:degraded', 'router-captivep:enslaved')
5567 start_radvd(config_file
='captive-portal.conf')
5568 networkctl_reconfigure('client')
5569 self
.wait_online('client:routable')
5571 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5572 output
= networkctl_status('client')
5574 self
.assertIn('Captive Portal: http://systemd.io', output
)
5576 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5577 def test_invalid_captive_portal(self
):
5578 def radvd_write_config(captive_portal_uri
):
5579 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5580 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5582 captive_portal_uris
= [
5583 "42ěščěškd ěšč ě s",
5588 copy_network_unit('25-veth-client.netdev',
5589 '25-veth-router-captive.netdev',
5591 '25-veth-client-captive.network',
5592 '25-veth-router-captive.network',
5593 '25-veth-bridge-captive.network',
5594 '25-bridge99.network')
5596 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5597 'router-captive:degraded', 'router-captivep:enslaved')
5599 for uri
in captive_portal_uris
:
5600 print(f
"Captive portal: {uri}")
5601 radvd_write_config(uri
)
5603 start_radvd(config_file
='bogus-captive-portal.conf')
5604 networkctl_reconfigure('client')
5605 self
.wait_online('client:routable')
5607 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5608 output
= networkctl_status('client')
5610 self
.assertNotIn('Captive Portal:', output
)
5612 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5620 def test_dhcp_server(self
):
5621 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5623 self
.wait_online('veth99:routable', 'veth-peer:routable')
5625 output
= networkctl_status('veth99')
5627 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5628 self
.assertIn('Gateway: 192.168.5.3', output
)
5629 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5630 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5632 output
= networkctl_status('veth-peer')
5633 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5635 def test_dhcp_server_null_server_address(self
):
5636 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5638 self
.wait_online('veth99:routable', 'veth-peer:routable')
5640 output
= check_output('ip --json address show dev veth-peer')
5641 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5642 print(server_address
)
5644 output
= check_output('ip --json address show dev veth99')
5645 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5646 print(client_address
)
5648 output
= networkctl_status('veth99')
5650 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5651 self
.assertIn(f
'Gateway: {server_address}', output
)
5652 self
.assertIn(f
'DNS: {server_address}', output
)
5653 self
.assertIn(f
'NTP: {server_address}', output
)
5655 output
= networkctl_status('veth-peer')
5656 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5658 def test_dhcp_server_with_uplink(self
):
5659 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5660 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5662 self
.wait_online('veth99:routable', 'veth-peer:routable')
5664 output
= networkctl_status('veth99')
5666 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5667 self
.assertIn('Gateway: 192.168.5.3', output
)
5668 self
.assertIn('DNS: 192.168.5.1', output
)
5669 self
.assertIn('NTP: 192.168.5.1', output
)
5671 def test_emit_router_timezone(self
):
5672 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5674 self
.wait_online('veth99:routable', 'veth-peer:routable')
5676 output
= networkctl_status('veth99')
5678 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5679 self
.assertIn('Gateway: 192.168.5.1', output
)
5680 self
.assertIn('Time Zone: Europe/Berlin', output
)
5682 def test_dhcp_server_static_lease(self
):
5683 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5685 self
.wait_online('veth99:routable', 'veth-peer:routable')
5687 output
= networkctl_status('veth99')
5689 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5690 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5692 def test_dhcp_server_static_lease_default_client_id(self
):
5693 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5695 self
.wait_online('veth99:routable', 'veth-peer:routable')
5697 output
= networkctl_status('veth99')
5699 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5700 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5702 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5710 def test_relay_agent(self
):
5711 copy_network_unit('25-agent-veth-client.netdev',
5712 '25-agent-veth-server.netdev',
5713 '25-agent-client.network',
5714 '25-agent-server.network',
5715 '25-agent-client-peer.network',
5716 '25-agent-server-peer.network')
5719 self
.wait_online('client:routable')
5721 output
= networkctl_status('client')
5723 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5725 def test_relay_agent_on_bridge(self
):
5726 copy_network_unit('25-agent-bridge.netdev',
5727 '25-agent-veth-client.netdev',
5728 '25-agent-bridge.network',
5729 '25-agent-bridge-port.network',
5730 '25-agent-client.network')
5732 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5735 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5737 if expect
in read_networkd_log():
5743 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5751 def test_dhcp_client_ipv6_only(self
):
5752 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5755 self
.wait_online('veth-peer:carrier')
5757 # information request mode
5758 # The name ipv6-only option may not be supported by older dnsmasq
5759 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5760 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5761 '--dhcp-option=option6:dns-server,[2600::ee]',
5762 '--dhcp-option=option6:ntp-server,[2600::ff]',
5763 ra_mode
='ra-stateless')
5764 self
.wait_online('veth99:routable', 'veth-peer:routable')
5766 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5767 # Let's wait for the expected DNS server being listed in the state file.
5768 for _
in range(100):
5769 output
= read_link_state_file('veth99')
5770 if 'DNS=2600::ee' in output
:
5774 # Check link state file
5775 print('## link state file')
5776 output
= read_link_state_file('veth99')
5778 self
.assertIn('DNS=2600::ee', output
)
5779 self
.assertIn('NTP=2600::ff', output
)
5781 # Check manager state file
5782 print('## manager state file')
5783 output
= read_manager_state_file()
5785 self
.assertRegex(output
, 'DNS=.*2600::ee')
5786 self
.assertRegex(output
, 'NTP=.*2600::ff')
5788 print('## dnsmasq log')
5789 output
= read_dnsmasq_log_file()
5791 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5792 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5793 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5794 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5795 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5798 check_json(networkctl_json('veth99'))
5802 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5803 '--dhcp-option=option6:dns-server,[2600::ee]',
5804 '--dhcp-option=option6:ntp-server,[2600::ff]')
5805 networkctl_reconfigure('veth99')
5806 self
.wait_online('veth99:routable', 'veth-peer:routable')
5809 output
= check_output('ip address show dev veth99 scope global')
5811 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5812 self
.assertNotIn('192.168.5', output
)
5814 # checking semi-static route
5815 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5817 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5819 # Confirm that ipv6 token is not set in the kernel
5820 output
= check_output('ip token show dev veth99')
5822 self
.assertRegex(output
, 'token :: dev veth99')
5824 # Make manager and link state file updated
5825 resolvectl('revert', 'veth99')
5827 # Check link state file
5828 print('## link state file')
5829 output
= read_link_state_file('veth99')
5831 self
.assertIn('DNS=2600::ee', output
)
5832 self
.assertIn('NTP=2600::ff', output
)
5834 # Check manager state file
5835 print('## manager state file')
5836 output
= read_manager_state_file()
5838 self
.assertRegex(output
, 'DNS=.*2600::ee')
5839 self
.assertRegex(output
, 'NTP=.*2600::ff')
5841 print('## dnsmasq log')
5842 output
= read_dnsmasq_log_file()
5844 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5845 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5846 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5847 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5848 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5849 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5852 check_json(networkctl_json('veth99'))
5854 # Testing without rapid commit support
5855 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5856 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
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]')
5864 self
.wait_online('veth99:routable', 'veth-peer:routable')
5867 output
= check_output('ip address show dev veth99 scope global')
5869 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5870 self
.assertNotIn('192.168.5', output
)
5872 # checking semi-static route
5873 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5875 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5877 # Make manager and link state file updated
5878 resolvectl('revert', 'veth99')
5880 # Check link state file
5881 print('## link state file')
5882 output
= read_link_state_file('veth99')
5884 self
.assertIn('DNS=2600::ee', output
)
5885 self
.assertIn('NTP=2600::ff', output
)
5887 # Check manager state file
5888 print('## manager state file')
5889 output
= read_manager_state_file()
5891 self
.assertRegex(output
, 'DNS=.*2600::ee')
5892 self
.assertRegex(output
, 'NTP=.*2600::ff')
5894 print('## dnsmasq log')
5895 output
= read_dnsmasq_log_file()
5897 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5898 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5899 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5900 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5901 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5902 self
.assertNotIn('rapid-commit', output
)
5905 check_json(networkctl_json('veth99'))
5907 def test_dhcp_client_ipv6_dbus_status(self
):
5908 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5910 self
.wait_online('veth-peer:carrier')
5912 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5913 # bit set) has yet been received and the configuration does not include WithoutRA=true
5914 state
= get_dhcp6_client_state('veth99')
5915 print(f
"DHCPv6 client state = {state}")
5916 self
.assertEqual(state
, 'stopped')
5918 state
= get_dhcp4_client_state('veth99')
5919 print(f
"DHCPv4 client state = {state}")
5920 self
.assertEqual(state
, 'selecting')
5922 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5923 self
.wait_online('veth99:routable', 'veth-peer:routable')
5925 state
= get_dhcp6_client_state('veth99')
5926 print(f
"DHCPv6 client state = {state}")
5927 self
.assertEqual(state
, 'bound')
5929 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5930 for _
in range(100):
5931 state
= get_dhcp4_client_state('veth99')
5932 if state
== 'stopped':
5936 print(f
"DHCPv4 client state = {state}")
5937 self
.assertEqual(state
, 'stopped')
5939 # restart dnsmasq to clear log
5941 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5943 # Test renew command
5944 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5945 networkctl('renew', 'veth99')
5947 for _
in range(100):
5948 state
= get_dhcp4_client_state('veth99')
5949 if state
== 'stopped':
5953 print(f
"DHCPv4 client state = {state}")
5954 self
.assertEqual(state
, 'stopped')
5956 print('## dnsmasq log')
5957 output
= read_dnsmasq_log_file()
5959 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5960 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5961 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5962 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5964 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5965 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5968 self
.wait_online('veth-peer:carrier')
5970 self
.wait_online('veth99:routable', 'veth-peer:routable')
5973 output
= check_output('ip address show dev veth99 scope global')
5975 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5976 self
.assertNotIn('192.168.5', output
)
5978 print('## dnsmasq log')
5979 output
= read_dnsmasq_log_file()
5981 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5982 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5983 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5984 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5985 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5987 def test_dhcp_client_ipv4_only(self
):
5988 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5990 self
.setup_nftset('addr4', 'ipv4_addr')
5991 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5992 self
.setup_nftset('ifindex', 'iface_index')
5995 self
.wait_online('veth-peer:carrier')
5996 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5997 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5998 '--dhcp-option=option:domain-search,example.com',
5999 '--dhcp-alternate-port=67,5555',
6000 ipv4_range
='192.168.5.110,192.168.5.119')
6001 self
.wait_online('veth99:routable', 'veth-peer:routable')
6002 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6004 print('## ip address show dev veth99 scope global')
6005 output
= check_output('ip address show dev veth99 scope global')
6007 self
.assertIn('mtu 1492', output
)
6008 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6009 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')
6010 self
.assertNotIn('2600::', output
)
6012 output
= check_output('ip -4 --json address show dev veth99')
6013 for i
in json
.loads(output
)[0]['addr_info']:
6014 if i
['label'] == 'test-label':
6015 address1
= i
['local']
6018 self
.assertFalse(True)
6020 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
6022 print('## ip route show table main dev veth99')
6023 output
= check_output('ip route show table main dev veth99')
6025 # no DHCP routes assigned to the main table
6026 self
.assertNotIn('proto dhcp', output
)
6028 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6029 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6030 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6031 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6033 print('## ip route show table 211 dev veth99')
6034 output
= check_output('ip route show table 211 dev veth99')
6036 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
6037 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
6038 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
6039 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
6040 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
6041 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6043 print('## link state file')
6044 output
= read_link_state_file('veth99')
6046 # checking DNS server, SIP server, and Domains
6047 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
6048 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
6049 self
.assertIn('DOMAINS=example.com', output
)
6052 j
= json
.loads(networkctl_json('veth99'))
6054 self
.assertEqual(len(j
['DNS']), 2)
6057 self
.assertEqual(i
['Family'], 2)
6058 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6059 self
.assertRegex(a
, '^192.168.5.[67]$')
6060 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6061 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6062 self
.assertEqual('192.168.5.1', a
)
6064 self
.assertEqual(len(j
['SIP']), 2)
6067 self
.assertEqual(i
['Family'], 2)
6068 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6069 self
.assertRegex(a
, '^192.168.5.2[12]$')
6070 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6071 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6072 self
.assertEqual('192.168.5.1', a
)
6074 print('## dnsmasq log')
6075 output
= read_dnsmasq_log_file()
6077 self
.assertIn('vendor class: FooBarVendorTest', output
)
6078 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
6079 self
.assertIn('client provides name: test-hostname', output
)
6080 self
.assertIn('26:mtu', output
)
6082 # change address range, DNS servers, and Domains
6084 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
6085 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
6086 '--dhcp-option=option:domain-search,foo.example.com',
6087 '--dhcp-alternate-port=67,5555',
6088 ipv4_range
='192.168.5.120,192.168.5.129',)
6090 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6091 print('Wait for the DHCP lease to be expired')
6092 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
6093 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
6095 self
.wait_online('veth99:routable', 'veth-peer:routable')
6097 print('## ip address show dev veth99 scope global')
6098 output
= check_output('ip address show dev veth99 scope global')
6100 self
.assertIn('mtu 1492', output
)
6101 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
6102 self
.assertNotIn(f
'{address1}', output
)
6103 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')
6104 self
.assertNotIn('2600::', output
)
6106 output
= check_output('ip -4 --json address show dev veth99')
6107 for i
in json
.loads(output
)[0]['addr_info']:
6108 if i
['label'] == 'test-label':
6109 address2
= i
['local']
6112 self
.assertFalse(True)
6114 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
6116 print('## ip route show table main dev veth99')
6117 output
= check_output('ip route show table main dev veth99')
6119 # no DHCP routes assigned to the main table
6120 self
.assertNotIn('proto dhcp', output
)
6122 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
6123 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
6124 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
6125 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
6127 print('## ip route show table 211 dev veth99')
6128 output
= check_output('ip route show table 211 dev veth99')
6130 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
6131 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
6132 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
6133 self
.assertNotIn('192.168.5.6', output
)
6134 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
6135 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
6136 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
6138 print('## link state file')
6139 output
= read_link_state_file('veth99')
6141 # checking DNS server, SIP server, and Domains
6142 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
6143 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
6144 self
.assertIn('DOMAINS=foo.example.com', output
)
6147 j
= json
.loads(networkctl_json('veth99'))
6149 self
.assertEqual(len(j
['DNS']), 3)
6152 self
.assertEqual(i
['Family'], 2)
6153 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6154 self
.assertRegex(a
, '^192.168.5.[178]$')
6155 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6156 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6157 self
.assertEqual('192.168.5.1', a
)
6159 self
.assertEqual(len(j
['SIP']), 2)
6162 self
.assertEqual(i
['Family'], 2)
6163 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6164 self
.assertRegex(a
, '^192.168.5.2[34]$')
6165 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6166 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6167 self
.assertEqual('192.168.5.1', a
)
6169 print('## dnsmasq log')
6170 output
= read_dnsmasq_log_file()
6172 self
.assertIn('vendor class: FooBarVendorTest', output
)
6173 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6174 self
.assertIn('client provides name: test-hostname', output
)
6175 self
.assertIn('26:mtu', output
)
6177 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6179 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6180 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6181 self
.check_nftset('ifindex', 'veth99')
6183 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6185 def test_dhcp_client_ipv4_dbus_status(self
):
6186 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6188 self
.wait_online('veth-peer:carrier')
6190 state
= get_dhcp4_client_state('veth99')
6191 print(f
"State = {state}")
6192 self
.assertEqual(state
, 'rebooting')
6194 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6195 '--dhcp-option=option:domain-search,example.com',
6196 '--dhcp-alternate-port=67,5555',
6197 ipv4_range
='192.168.5.110,192.168.5.119')
6198 self
.wait_online('veth99:routable', 'veth-peer:routable')
6199 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6201 state
= get_dhcp4_client_state('veth99')
6202 print(f
"State = {state}")
6203 self
.assertEqual(state
, 'bound')
6205 def test_dhcp_client_allow_list(self
):
6206 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6209 self
.wait_online('veth-peer:carrier')
6210 since
= datetime
.datetime
.now()
6213 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6215 if expect
in read_networkd_log(since
=since
):
6221 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6222 since
= datetime
.datetime
.now()
6225 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6227 if expect
in read_networkd_log(since
=since
):
6233 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6234 since
= datetime
.datetime
.now()
6237 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6239 if expect
in read_networkd_log(since
=since
):
6245 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6246 def test_dhcp_client_rapid_commit(self
):
6247 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6249 self
.wait_online('veth-peer:carrier')
6251 start_dnsmasq('--dhcp-rapid-commit')
6252 self
.wait_online('veth99:routable', 'veth-peer:routable')
6253 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6255 state
= get_dhcp4_client_state('veth99')
6256 print(f
"DHCPv4 client state = {state}")
6257 self
.assertEqual(state
, 'bound')
6259 output
= read_dnsmasq_log_file()
6260 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6261 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6262 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6263 self
.assertIn('DHCPACK(veth-peer)', output
)
6265 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6266 copy_network_unit('25-veth.netdev',
6267 '25-dhcp-server-ipv6-only-mode.network',
6268 '25-dhcp-client-ipv6-only-mode.network')
6270 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6271 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6273 state
= get_dhcp4_client_state('veth99')
6274 print(f
"State = {state}")
6275 self
.assertEqual(state
, 'bound')
6277 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6279 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6285 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6286 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6287 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6289 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6290 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6291 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6292 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6293 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6294 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6295 copy_network_unit(*testunits
, copy_dropins
=False)
6298 self
.wait_online('veth-peer:carrier')
6299 additional_options
= [
6300 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6301 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6302 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6305 additional_options
+= [
6306 '--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'
6308 start_dnsmasq(*additional_options
)
6309 self
.wait_online('veth99:routable', 'veth-peer:routable')
6311 output
= check_output('ip -4 route show dev veth99')
6317 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6318 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6319 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6320 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6321 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6323 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6324 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6325 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6326 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6328 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6329 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6330 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6331 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6332 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6333 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6334 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6335 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6338 if use_gateway
and (not classless
or not use_routes
):
6339 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6341 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6343 # Check route to gateway
6344 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6345 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6347 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6349 # Check RoutesToDNS= and RoutesToNTP=
6350 if dns_and_ntp_routes
:
6351 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6352 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6355 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6356 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6358 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6359 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6361 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6362 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6364 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6365 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6366 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6367 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6369 check_json(networkctl_json())
6371 def test_dhcp_client_settings_anonymize(self
):
6372 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6374 self
.wait_online('veth-peer:carrier')
6376 self
.wait_online('veth99:routable', 'veth-peer:routable')
6378 print('## dnsmasq log')
6379 output
= read_dnsmasq_log_file()
6381 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6382 self
.assertNotIn('test-hostname', output
)
6383 self
.assertNotIn('26:mtu', output
)
6385 def test_dhcp_keep_configuration_dhcp(self
):
6386 copy_network_unit('25-veth.netdev',
6387 '25-dhcp-server-veth-peer.network',
6388 '25-dhcp-client-keep-configuration-dhcp.network')
6390 self
.wait_online('veth-peer:carrier')
6392 self
.wait_online('veth99:routable', 'veth-peer:routable')
6394 output
= check_output('ip address show dev veth99 scope global')
6396 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6397 'valid_lft forever preferred_lft forever')
6399 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6402 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6403 print('Wait for the DHCP lease to be expired')
6406 # The lease address should be kept after the lease expired
6407 output
= check_output('ip address show dev veth99 scope global')
6409 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6410 'valid_lft forever preferred_lft forever')
6414 # The lease address should be kept after networkd stopped
6415 output
= check_output('ip address show dev veth99 scope global')
6417 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6418 'valid_lft forever preferred_lft forever')
6420 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6421 f
.write('[Network]\nDHCP=no\n')
6424 self
.wait_online('veth99:routable', 'veth-peer:routable')
6426 # Still the lease address should be kept after networkd restarted
6427 output
= check_output('ip address show dev veth99 scope global')
6429 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6430 'valid_lft forever preferred_lft forever')
6432 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6433 copy_network_unit('25-veth.netdev',
6434 '25-dhcp-server-veth-peer.network',
6435 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6437 self
.wait_online('veth-peer:carrier')
6439 self
.wait_online('veth99:routable', 'veth-peer:routable')
6441 output
= check_output('ip address show dev veth99 scope global')
6443 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6448 output
= check_output('ip address show dev veth99 scope global')
6450 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6453 self
.wait_online('veth-peer:routable')
6455 output
= check_output('ip address show dev veth99 scope global')
6457 self
.assertNotIn('192.168.5.', output
)
6459 def test_dhcp_client_reuse_address_as_static(self
):
6460 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6462 self
.wait_online('veth-peer:carrier')
6464 self
.wait_online('veth99:routable', 'veth-peer:routable')
6466 # link become 'routable' when at least one protocol provide an valid address.
6467 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6468 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6470 output
= check_output('ip address show dev veth99 scope global')
6471 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6472 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6473 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6474 print(static_network
)
6476 remove_network_unit('25-dhcp-client.network')
6478 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6479 f
.write(static_network
)
6482 self
.wait_online('veth99:routable')
6484 output
= check_output('ip -4 address show dev veth99 scope global')
6486 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6487 'valid_lft forever preferred_lft forever')
6489 output
= check_output('ip -6 address show dev veth99 scope global')
6491 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6492 'valid_lft forever preferred_lft forever')
6494 @expectedFailureIfModuleIsNotAvailable('vrf')
6495 def test_dhcp_client_vrf(self
):
6496 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6497 '25-vrf.netdev', '25-vrf.network')
6499 self
.wait_online('veth-peer:carrier')
6501 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6503 # link become 'routable' when at least one protocol provide an valid address.
6504 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6505 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6507 print('## ip -d link show dev vrf99')
6508 output
= check_output('ip -d link show dev vrf99')
6510 self
.assertRegex(output
, 'vrf table 42')
6512 print('## ip address show vrf vrf99')
6513 output
= check_output('ip address show vrf vrf99')
6515 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6516 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6517 self
.assertRegex(output
, 'inet6 .* scope link')
6519 print('## ip address show dev veth99')
6520 output
= check_output('ip address show dev veth99')
6522 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6523 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6524 self
.assertRegex(output
, 'inet6 .* scope link')
6526 print('## ip route show vrf vrf99')
6527 output
= check_output('ip route show vrf vrf99')
6529 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6530 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6531 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6533 print('## ip route show table main dev veth99')
6534 output
= check_output('ip route show table main dev veth99')
6536 self
.assertEqual(output
, '')
6538 def test_dhcp_client_gateway_onlink_implicit(self
):
6539 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6540 '25-dhcp-client-gateway-onlink-implicit.network')
6542 self
.wait_online('veth-peer:carrier')
6544 self
.wait_online('veth99:routable', 'veth-peer:routable')
6546 output
= networkctl_status('veth99')
6548 self
.assertRegex(output
, '192.168.5')
6550 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6552 self
.assertRegex(output
, 'onlink')
6553 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6555 self
.assertRegex(output
, 'onlink')
6557 def test_dhcp_client_with_ipv4ll(self
):
6558 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6559 '25-dhcp-client-with-ipv4ll.network')
6561 # we need to increase timeout above default, as this will need to wait for
6562 # systemd-networkd to get the dhcpv4 transient failure event
6563 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6565 output
= check_output('ip -4 address show dev veth99')
6567 self
.assertNotIn('192.168.5.', output
)
6568 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6571 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6572 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6573 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')
6574 self
.wait_online('veth99:routable')
6576 output
= check_output('ip -4 address show dev veth99')
6578 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6579 self
.assertNotIn('169.254.', output
)
6580 self
.assertNotIn('scope link', output
)
6583 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6584 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)
6585 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6587 output
= check_output('ip -4 address show dev veth99')
6589 self
.assertNotIn('192.168.5.', output
)
6590 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6592 def test_dhcp_client_use_dns(self
):
6593 def check(self
, ipv4
, ipv6
):
6594 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6595 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6596 f
.write('[DHCPv4]\nUseDNS=')
6597 f
.write('yes' if ipv4
else 'no')
6598 f
.write('\n[DHCPv6]\nUseDNS=')
6599 f
.write('yes' if ipv6
else 'no')
6600 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6603 self
.wait_online('veth99:routable')
6605 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6606 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6607 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6609 # make resolved re-read the link state file
6610 resolvectl('revert', 'veth99')
6612 output
= resolvectl('dns', 'veth99')
6615 self
.assertIn('192.168.5.1', output
)
6617 self
.assertNotIn('192.168.5.1', output
)
6619 self
.assertIn('2600::1', output
)
6621 self
.assertNotIn('2600::1', output
)
6623 check_json(networkctl_json())
6625 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6628 self
.wait_online('veth-peer:carrier')
6629 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6630 '--dhcp-option=option6:dns-server,[2600::1]')
6632 check(self
, True, True)
6633 check(self
, True, False)
6634 check(self
, False, True)
6635 check(self
, False, False)
6637 def test_dhcp_client_use_captive_portal(self
):
6638 def check(self
, ipv4
, ipv6
):
6639 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6640 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6641 f
.write('[DHCPv4]\nUseCaptivePortal=')
6642 f
.write('yes' if ipv4
else 'no')
6643 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6644 f
.write('yes' if ipv6
else 'no')
6645 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6648 self
.wait_online('veth99:routable')
6650 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6651 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6652 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6654 output
= networkctl_status('veth99')
6657 self
.assertIn('Captive Portal: http://systemd.io', output
)
6659 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6661 check_json(networkctl_json())
6663 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6666 self
.wait_online('veth-peer:carrier')
6667 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6668 '--dhcp-option=option6:103,http://systemd.io')
6670 check(self
, True, True)
6671 check(self
, True, False)
6672 check(self
, False, True)
6673 check(self
, False, False)
6675 def test_dhcp_client_reject_captive_portal(self
):
6676 def check(self
, ipv4
, ipv6
):
6677 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6678 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6679 f
.write('[DHCPv4]\nUseCaptivePortal=')
6680 f
.write('yes' if ipv4
else 'no')
6681 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6682 f
.write('yes' if ipv6
else 'no')
6683 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6686 self
.wait_online('veth99:routable')
6688 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6689 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6690 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6692 output
= networkctl_status('veth99')
6694 self
.assertNotIn('Captive Portal: ', output
)
6695 self
.assertNotIn('invalid/url', output
)
6697 check_json(networkctl_json())
6699 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6702 self
.wait_online('veth-peer:carrier')
6703 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6704 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6705 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6707 check(self
, True, True)
6708 check(self
, True, False)
6709 check(self
, False, True)
6710 check(self
, False, False)
6712 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6720 def check_dhcp6_prefix(self
, link
):
6721 description
= get_link_description(link
)
6723 self
.assertIn('DHCPv6Client', description
.keys())
6724 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6726 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6728 self
.assertEqual(len(prefixInfo
), 1)
6730 self
.assertIn('Prefix', prefixInfo
[0].keys())
6731 self
.assertIn('PrefixLength', prefixInfo
[0].keys())
6732 self
.assertIn('PreferredLifetimeUSec', prefixInfo
[0].keys())
6733 self
.assertIn('ValidLifetimeUSec', prefixInfo
[0].keys())
6735 self
.assertEqual(prefixInfo
[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6736 self
.assertEqual(prefixInfo
[0]['PrefixLength'], 56)
6737 self
.assertGreater(prefixInfo
[0]['PreferredLifetimeUSec'], 0)
6738 self
.assertGreater(prefixInfo
[0]['ValidLifetimeUSec'], 0)
6740 def test_dhcp6pd_no_address(self
):
6742 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6745 self
.wait_online('veth-peer:routable')
6746 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6747 self
.wait_online('veth99:degraded')
6749 print('### ip -6 address show dev veth99 scope global')
6750 output
= check_output('ip -6 address show dev veth99 scope global')
6752 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6754 self
.check_dhcp6_prefix('veth99')
6756 def test_dhcp6pd_no_assign(self
):
6757 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6758 # However, the server does not provide IA_NA. For issue #31349.
6759 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6762 self
.wait_online('veth-peer:routable')
6763 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd-no-range.conf', ipv
='-6')
6764 self
.wait_online('veth99:degraded')
6766 print('### ip -6 address show dev veth99 scope global')
6767 output
= check_output('ip -6 address show dev veth99 scope global')
6769 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6771 self
.check_dhcp6_prefix('veth99')
6773 def test_dhcp6pd(self
):
6774 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6775 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6776 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6777 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6778 '25-dhcp-pd-downstream-dummy97.network',
6779 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6780 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6783 self
.wait_online('veth-peer:routable')
6784 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6785 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6786 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6788 self
.setup_nftset('addr6', 'ipv6_addr')
6789 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6790 self
.setup_nftset('ifindex', 'iface_index')
6792 # Check DBus assigned prefix information to veth99
6793 self
.check_dhcp6_prefix('veth99')
6795 print('### ip -6 address show dev veth-peer scope global')
6796 output
= check_output('ip -6 address show dev veth-peer scope global')
6798 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6802 # dummy97: 0x01 (The link will appear later)
6804 # dummy99: auto -> 0x02 (No address assignment)
6809 print('### ip -6 address show dev veth99 scope global')
6810 output
= check_output('ip -6 address show dev veth99 scope global')
6813 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6814 # address in IA_PD (Token=static)
6815 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6816 # address in IA_PD (Token=eui64)
6817 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6818 # address in IA_PD (temporary)
6819 # Note that the temporary addresses may appear after the link enters configured state
6820 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')
6822 print('### ip -6 address show dev test1 scope global')
6823 output
= check_output('ip -6 address show dev test1 scope global')
6825 # address in IA_PD (Token=static)
6826 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6827 # address in IA_PD (temporary)
6828 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')
6830 print('### ip -6 address show dev dummy98 scope global')
6831 output
= check_output('ip -6 address show dev dummy98 scope global')
6833 # address in IA_PD (Token=static)
6834 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6835 # address in IA_PD (temporary)
6836 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')
6838 print('### ip -6 address show dev dummy99 scope global')
6839 output
= check_output('ip -6 address show dev dummy99 scope global')
6842 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6844 print('### ip -6 address show dev veth97 scope global')
6845 output
= check_output('ip -6 address show dev veth97 scope global')
6847 # address in IA_PD (Token=static)
6848 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6849 # address in IA_PD (Token=eui64)
6850 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6851 # address in IA_PD (temporary)
6852 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')
6854 print('### ip -6 address show dev veth97-peer scope global')
6855 output
= check_output('ip -6 address show dev veth97-peer scope global')
6857 # NDisc address (Token=static)
6858 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6859 # NDisc address (Token=eui64)
6860 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6861 # NDisc address (temporary)
6862 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')
6864 print('### ip -6 address show dev veth98 scope global')
6865 output
= check_output('ip -6 address show dev veth98 scope global')
6867 # address in IA_PD (Token=static)
6868 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6869 # address in IA_PD (Token=eui64)
6870 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6871 # address in IA_PD (temporary)
6872 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')
6874 print('### ip -6 address show dev veth98-peer scope global')
6875 output
= check_output('ip -6 address show dev veth98-peer scope global')
6877 # NDisc address (Token=static)
6878 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6879 # NDisc address (Token=eui64)
6880 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6881 # NDisc address (temporary)
6882 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')
6884 print('### ip -6 route show type unreachable')
6885 output
= check_output('ip -6 route show type unreachable')
6887 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6889 print('### ip -6 route show dev veth99')
6890 output
= check_output('ip -6 route show dev veth99')
6892 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6894 print('### ip -6 route show dev test1')
6895 output
= check_output('ip -6 route show dev test1')
6897 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6899 print('### ip -6 route show dev dummy98')
6900 output
= check_output('ip -6 route show dev dummy98')
6902 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6904 print('### ip -6 route show dev dummy99')
6905 output
= check_output('ip -6 route show dev dummy99')
6907 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6909 print('### ip -6 route show dev veth97')
6910 output
= check_output('ip -6 route show dev veth97')
6912 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6914 print('### ip -6 route show dev veth97-peer')
6915 output
= check_output('ip -6 route show dev veth97-peer')
6917 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6919 print('### ip -6 route show dev veth98')
6920 output
= check_output('ip -6 route show dev veth98')
6922 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6924 print('### ip -6 route show dev veth98-peer')
6925 output
= check_output('ip -6 route show dev veth98-peer')
6927 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6929 # Test case for a downstream which appears later
6930 check_output('ip link add dummy97 type dummy')
6931 self
.wait_online('dummy97:routable')
6933 print('### ip -6 address show dev dummy97 scope global')
6934 output
= check_output('ip -6 address show dev dummy97 scope global')
6936 # address in IA_PD (Token=static)
6937 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6938 # address in IA_PD (temporary)
6939 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')
6941 print('### ip -6 route show dev dummy97')
6942 output
= check_output('ip -6 route show dev dummy97')
6944 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6946 # Test case for reconfigure
6947 networkctl_reconfigure('dummy98', 'dummy99')
6948 self
.wait_online('dummy98:routable', 'dummy99:degraded')
6950 print('### ip -6 address show dev dummy98 scope global')
6951 output
= check_output('ip -6 address show dev dummy98 scope global')
6953 # address in IA_PD (Token=static)
6954 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6955 # address in IA_PD (temporary)
6956 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')
6958 print('### ip -6 address show dev dummy99 scope global')
6959 output
= check_output('ip -6 address show dev dummy99 scope global')
6962 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6964 print('### ip -6 route show dev dummy98')
6965 output
= check_output('ip -6 route show dev dummy98')
6967 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6969 print('### ip -6 route show dev dummy99')
6970 output
= check_output('ip -6 route show dev dummy99')
6972 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6974 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6976 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6977 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6978 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6979 self
.check_nftset('ifindex', 'dummy98')
6981 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6983 def verify_dhcp4_6rd(self
, tunnel_name
):
6984 print('### ip -4 address show dev veth-peer scope global')
6985 output
= check_output('ip -4 address show dev veth-peer scope global')
6987 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6991 # dummy97: 0x01 (The link will appear later)
6993 # dummy99: auto -> 0x0[23] (No address assignment)
6994 # 6rd-XXX: auto -> 0x0[23]
6999 print('### ip -4 address show dev veth99 scope global')
7000 output
= check_output('ip -4 address show dev veth99 scope global')
7002 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
7004 print('### ip -6 address show dev veth99 scope global')
7005 output
= check_output('ip -6 address show dev veth99 scope global')
7007 # address in IA_PD (Token=static)
7008 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7009 # address in IA_PD (Token=eui64)
7010 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
7011 # address in IA_PD (temporary)
7012 # Note that the temporary addresses may appear after the link enters configured state
7013 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')
7015 print('### ip -6 address show dev test1 scope global')
7016 output
= check_output('ip -6 address show dev test1 scope global')
7018 # address in IA_PD (Token=static)
7019 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7020 # address in IA_PD (temporary)
7021 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')
7023 print('### ip -6 address show dev dummy98 scope global')
7024 output
= check_output('ip -6 address show dev dummy98 scope global')
7026 # address in IA_PD (Token=static)
7027 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7028 # address in IA_PD (temporary)
7029 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')
7031 print('### ip -6 address show dev dummy99 scope global')
7032 output
= check_output('ip -6 address show dev dummy99 scope global')
7035 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
7037 print('### ip -6 address show dev veth97 scope global')
7038 output
= check_output('ip -6 address show dev veth97 scope global')
7040 # address in IA_PD (Token=static)
7041 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7042 # address in IA_PD (Token=eui64)
7043 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
7044 # address in IA_PD (temporary)
7045 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')
7047 print('### ip -6 address show dev veth97-peer scope global')
7048 output
= check_output('ip -6 address show dev veth97-peer scope global')
7050 # NDisc address (Token=static)
7051 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7052 # NDisc address (Token=eui64)
7053 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7054 # NDisc address (temporary)
7055 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')
7057 print('### ip -6 address show dev veth98 scope global')
7058 output
= check_output('ip -6 address show dev veth98 scope global')
7060 # address in IA_PD (Token=static)
7061 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7062 # address in IA_PD (Token=eui64)
7063 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
7064 # address in IA_PD (temporary)
7065 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')
7067 print('### ip -6 address show dev veth98-peer scope global')
7068 output
= check_output('ip -6 address show dev veth98-peer scope global')
7070 # NDisc address (Token=static)
7071 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
7072 # NDisc address (Token=eui64)
7073 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
7074 # NDisc address (temporary)
7075 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')
7077 print('### ip -6 route show type unreachable')
7078 output
= check_output('ip -6 route show type unreachable')
7080 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
7082 print('### ip -6 route show dev veth99')
7083 output
= check_output('ip -6 route show dev veth99')
7085 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
7087 print('### ip -6 route show dev test1')
7088 output
= check_output('ip -6 route show dev test1')
7090 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7092 print('### ip -6 route show dev dummy98')
7093 output
= check_output('ip -6 route show dev dummy98')
7095 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
7097 print('### ip -6 route show dev dummy99')
7098 output
= check_output('ip -6 route show dev dummy99')
7100 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
7102 print('### ip -6 route show dev veth97')
7103 output
= check_output('ip -6 route show dev veth97')
7105 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
7107 print('### ip -6 route show dev veth97-peer')
7108 output
= check_output('ip -6 route show dev veth97-peer')
7110 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
7112 print('### ip -6 route show dev veth98')
7113 output
= check_output('ip -6 route show dev veth98')
7115 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
7117 print('### ip -6 route show dev veth98-peer')
7118 output
= check_output('ip -6 route show dev veth98-peer')
7120 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
7122 print('### ip -6 address show dev dummy97 scope global')
7123 output
= check_output('ip -6 address show dev dummy97 scope global')
7125 # address in IA_PD (Token=static)
7126 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
7127 # address in IA_PD (temporary)
7128 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')
7130 print('### ip -6 route show dev dummy97')
7131 output
= check_output('ip -6 route show dev dummy97')
7133 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
7135 print(f
'### ip -d link show dev {tunnel_name}')
7136 output
= check_output(f
'ip -d link show dev {tunnel_name}')
7138 self
.assertIn('link/sit 10.100.100.', output
)
7139 self
.assertIn('local 10.100.100.', output
)
7140 self
.assertIn('ttl 64', output
)
7141 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
7142 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
7144 print(f
'### ip -6 address show dev {tunnel_name}')
7145 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
7147 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')
7148 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7150 print(f
'### ip -6 route show dev {tunnel_name}')
7151 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
7153 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7154 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
7156 print('### ip -6 route show default')
7157 output
= check_output('ip -6 route show default')
7159 self
.assertIn('default', output
)
7160 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
7162 def test_dhcp4_6rd(self
):
7163 def get_dhcp_6rd_prefix(link
):
7164 description
= get_link_description(link
)
7166 self
.assertIn('DHCPv4Client', description
.keys())
7167 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
7169 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
7170 self
.assertIn('Prefix', prefixInfo
.keys())
7171 self
.assertIn('PrefixLength', prefixInfo
.keys())
7172 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
7173 self
.assertIn('BorderRouters', prefixInfo
.keys())
7177 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7178 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7179 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7180 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7181 '25-dhcp-pd-downstream-dummy97.network',
7182 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7183 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7184 '80-6rd-tunnel.network')
7187 self
.wait_online('veth-peer:routable')
7190 # 6rd-prefix: 2001:db8::/32
7191 # br-addresss: 10.0.0.1
7193 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',
7194 ipv4_range
='10.100.100.100,10.100.100.200',
7195 ipv4_router
='10.0.0.1')
7196 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7197 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7199 # Check the DBus interface for assigned prefix information
7200 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7202 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7203 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7204 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7205 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7207 # Test case for a downstream which appears later
7208 check_output('ip link add dummy97 type dummy')
7209 self
.wait_online('dummy97:routable')
7213 for name
in os
.listdir('/sys/class/net/'):
7214 if name
.startswith('6rd-'):
7218 self
.wait_online(f
'{tunnel_name}:routable')
7220 self
.verify_dhcp4_6rd(tunnel_name
)
7222 # Test case for reconfigure
7223 networkctl_reconfigure('dummy98', 'dummy99')
7224 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7226 self
.verify_dhcp4_6rd(tunnel_name
)
7228 print('Wait for the DHCP lease to be renewed/rebind')
7231 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7232 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7234 self
.verify_dhcp4_6rd(tunnel_name
)
7236 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7244 def test_ipv6_route_prefix(self
):
7245 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7246 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7249 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7251 output
= check_output('ip address show dev veth-peer')
7253 self
.assertIn('inet6 2001:db8:0:1:', output
)
7254 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7255 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7257 output
= check_output('ip -6 route show dev veth-peer')
7259 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7260 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7261 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7262 self
.assertIn('2001:db0:fff::/64 via ', output
)
7263 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7264 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7266 output
= check_output('ip address show dev veth99')
7268 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7269 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7270 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7271 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7273 output
= resolvectl('dns', 'veth-peer')
7275 self
.assertRegex(output
, '2001:db8:1:1::2')
7277 output
= resolvectl('domain', 'veth-peer')
7279 self
.assertIn('example.com', output
)
7281 check_json(networkctl_json())
7283 output
= networkctl_json('veth-peer')
7287 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7289 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7290 self
.assertEqual(prefix
, '64:ff9b::')
7292 prefix_length
= pref64
['PrefixLength']
7293 self
.assertEqual(prefix_length
, 96)
7295 def test_ipv6_route_prefix_deny_list(self
):
7296 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7297 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7300 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7302 output
= check_output('ip address show dev veth-peer')
7304 self
.assertIn('inet6 2001:db8:0:1:', output
)
7305 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7307 output
= check_output('ip -6 route show dev veth-peer')
7309 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7310 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7311 self
.assertIn('2001:db0:fff::/64 via ', output
)
7312 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7314 output
= check_output('ip address show dev veth99')
7316 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7317 self
.assertIn('inet6 2001:db8:0:2:', output
)
7319 output
= resolvectl('dns', 'veth-peer')
7321 self
.assertRegex(output
, '2001:db8:1:1::2')
7323 output
= resolvectl('domain', 'veth-peer')
7325 self
.assertIn('example.com', output
)
7327 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7335 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7341 self
.wait_online('dummy98:routable')
7342 self
.check_link_attr('dummy98', 'mtu', mtu
)
7343 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7345 # test normal restart
7347 self
.wait_online('dummy98:routable')
7348 self
.check_link_attr('dummy98', 'mtu', mtu
)
7349 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7352 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7354 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7355 ''' test setting mtu/ipv6_mtu with interface already up '''
7358 # note - changing the device mtu resets the ipv6 mtu
7359 check_output('ip link set up mtu 1501 dev dummy98')
7360 check_output('ip link set up mtu 1500 dev dummy98')
7361 self
.check_link_attr('dummy98', 'mtu', '1500')
7362 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7364 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7366 def test_mtu_network(self
):
7367 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7368 self
.check_mtu('1600')
7370 def test_mtu_netdev(self
):
7371 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7372 # note - MTU set by .netdev happens ONLY at device creation!
7373 self
.check_mtu('1600', reset
=False)
7375 def test_mtu_link(self
):
7376 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7377 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7378 self
.check_mtu('1600', reset
=False)
7380 def test_ipv6_mtu(self
):
7381 ''' set ipv6 mtu without setting device mtu '''
7382 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7383 self
.check_mtu('1500', '1400')
7385 def test_ipv6_mtu_toolarge(self
):
7386 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7387 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7388 self
.check_mtu('1500', '1500')
7390 def test_mtu_network_ipv6_mtu(self
):
7391 ''' set ipv6 mtu and set device mtu via network file '''
7392 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7393 self
.check_mtu('1600', '1550')
7395 def test_mtu_netdev_ipv6_mtu(self
):
7396 ''' set ipv6 mtu and set device mtu via netdev file '''
7397 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7398 self
.check_mtu('1600', '1550', reset
=False)
7400 def test_mtu_link_ipv6_mtu(self
):
7401 ''' set ipv6 mtu and set device mtu via link file '''
7402 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7403 self
.check_mtu('1600', '1550', reset
=False)
7406 if __name__
== '__main__':
7407 parser
= argparse
.ArgumentParser()
7408 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7409 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7410 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7411 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7412 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7413 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7414 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7415 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7416 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7417 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7418 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7419 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7420 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7421 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7422 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7423 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7424 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
)
7425 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7428 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7429 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7430 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7431 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7432 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7433 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7434 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7435 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7436 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7437 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7438 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7439 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7440 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7443 networkd_bin
= ns
.networkd_bin
7445 resolved_bin
= ns
.resolved_bin
7446 if ns
.timesyncd_bin
:
7447 timesyncd_bin
= ns
.timesyncd_bin
7449 udevd_bin
= ns
.udevd_bin
7450 if ns
.wait_online_bin
:
7451 wait_online_bin
= ns
.wait_online_bin
7452 if ns
.networkctl_bin
:
7453 networkctl_bin
= ns
.networkctl_bin
7454 if ns
.resolvectl_bin
:
7455 resolvectl_bin
= ns
.resolvectl_bin
7456 if ns
.timedatectl_bin
:
7457 timedatectl_bin
= ns
.timedatectl_bin
7459 udevadm_bin
= ns
.udevadm_bin
7462 systemd_source_dir
= ns
.source_dir
7464 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7465 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7466 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7468 use_valgrind
= ns
.use_valgrind
7469 enable_debug
= ns
.enable_debug
7470 asan_options
= ns
.asan_options
7471 lsan_options
= ns
.lsan_options
7472 ubsan_options
= ns
.ubsan_options
7473 with_coverage
= ns
.with_coverage
7476 # Do not forget the trailing space.
7477 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7479 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7480 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7481 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7482 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7483 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7486 env
.update({'ASAN_OPTIONS': asan_options
})
7488 env
.update({'LSAN_OPTIONS': lsan_options
})
7490 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7492 env
.update({'SYSTEMD_MEMPOOL': '0'})
7494 wait_online_env
= env
.copy()
7496 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7498 sys
.argv
[1:] = unknown_args
7499 unittest
.main(verbosity
=3)