2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # systemd-networkd tests
5 # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
6 # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
8 # To run an individual test, specify it as a command line argument in the form
9 # of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
10 # function called test_ipv6_mtu(). To run just that test use:
12 # sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
14 # Similarly, other individual tests can be run, eg.:
16 # sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
37 network_unit_dir
= '/run/systemd/network'
38 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
39 networkd_ci_temp_dir
= '/run/networkd-ci'
40 udev_rules_dir
= '/run/udev/rules.d'
41 credstore_dir
= '/run/credstore'
43 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
44 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
45 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
47 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
48 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
50 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
52 systemd_lib_paths
= ['/usr/lib/systemd', '/lib/systemd']
53 which_paths
= ':'.join(systemd_lib_paths
+ os
.getenv('PATH', os
.defpath
).lstrip(':').split(':'))
54 systemd_source_dir
= None
56 networkd_bin
= shutil
.which('systemd-networkd', path
=which_paths
)
57 resolved_bin
= shutil
.which('systemd-resolved', path
=which_paths
)
58 timesyncd_bin
= shutil
.which('systemd-timesyncd', path
=which_paths
)
59 udevd_bin
= shutil
.which('systemd-udevd', path
=which_paths
)
60 wait_online_bin
= shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
61 networkctl_bin
= shutil
.which('networkctl', path
=which_paths
)
62 resolvectl_bin
= shutil
.which('resolvectl', path
=which_paths
)
63 timedatectl_bin
= shutil
.which('timedatectl', path
=which_paths
)
64 udevadm_bin
= shutil
.which('udevadm', path
=which_paths
)
65 systemd_udev_rules_build_dir
= None
93 saved_ipv4_rules
= None
94 saved_ipv6_rules
= None
98 if os
.path
.exists(path
):
102 shutil
.rmtree(path
, ignore_errors
=True)
105 shutil
.copy(src
, dst
)
108 shutil
.copytree(src
, dst
, copy_function
=shutil
.copy
)
111 os
.makedirs(path
, exist_ok
=True)
114 pathlib
.Path(path
).touch()
116 # pylint: disable=R1710
117 def check_output(*command
, **kwargs
):
118 # This checks the result and returns stdout (and stderr) on success.
119 command
= command
[0].split() + list(command
[1:])
120 ret
= subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
, **kwargs
)
121 if ret
.returncode
== 0:
122 return ret
.stdout
.rstrip()
123 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
125 ret
.check_returncode()
127 def call(*command
, **kwargs
):
128 # This returns returncode. stdout and stderr are merged and shown in console
129 command
= command
[0].split() + list(command
[1:])
130 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).returncode
132 def call_check(*command
, **kwargs
):
133 # Same as call() above, but it triggers CalledProcessError if rc != 0
134 command
= command
[0].split() + list(command
[1:])
135 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).check_returncode()
137 def call_quiet(*command
, **kwargs
):
138 command
= command
[0].split() + list(command
[1:])
139 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
, **kwargs
).returncode
141 def run(*command
, **kwargs
):
142 # This returns CompletedProcess instance.
143 command
= command
[0].split() + list(command
[1:])
144 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, **kwargs
)
146 def check_json(string
):
149 except json
.JSONDecodeError
:
150 print(f
"String is not a valid JSON: '{string}'")
153 def is_module_available(*module_names
):
154 for module_name
in module_names
:
155 lsmod_output
= check_output('lsmod')
156 module_re
= re
.compile(rf
'^{re.escape(module_name)}\b', re
.MULTILINE
)
157 if not module_re
.search(lsmod_output
) and call_quiet('modprobe', module_name
) != 0:
161 def expectedFailureIfModuleIsNotAvailable(*module_names
):
163 return func
if is_module_available(*module_names
) else unittest
.expectedFailure(func
)
167 def expectedFailureIfERSPANv0IsNotSupported():
168 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
170 rc
= call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 0')
171 remove_link('erspan99')
172 return func
if rc
== 0 else unittest
.expectedFailure(func
)
176 def expectedFailureIfERSPANv2IsNotSupported():
177 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
179 rc
= call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 2')
180 remove_link('erspan99')
181 return func
if rc
== 0 else unittest
.expectedFailure(func
)
185 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
187 rc
= call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
188 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
189 return func
if rc
== 0 else unittest
.expectedFailure(func
)
193 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
195 rc
= call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
196 call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
197 return func
if rc
== 0 else unittest
.expectedFailure(func
)
201 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
204 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
205 ret
= run('ip rule list from 192.168.100.19 table 7')
206 supported
= ret
.returncode
== 0 and 'uidrange 200-300' in ret
.stdout
207 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
208 return func
if supported
else unittest
.expectedFailure(func
)
212 def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
214 rc
= call_quiet('ip rule add not from 192.168.100.19 l3mdev')
215 call_quiet('ip rule del not from 192.168.100.19 l3mdev')
216 return func
if rc
== 0 else unittest
.expectedFailure(func
)
220 def expectedFailureIfNexthopIsNotAvailable():
222 rc
= call_quiet('ip nexthop list')
223 return func
if rc
== 0 else unittest
.expectedFailure(func
)
227 def expectedFailureIfRTA_VIAIsNotSupported():
229 call_quiet('ip link add dummy98 type dummy')
230 call_quiet('ip link set up dev dummy98')
231 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
232 rc
= call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
233 remove_link('dummy98')
234 return func
if rc
== 0 else unittest
.expectedFailure(func
)
238 def expectedFailureIfAlternativeNameIsNotAvailable():
240 call_quiet('ip link add dummy98 type dummy')
242 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
243 call_quiet('ip link show dev hogehogehogehogehoge') == 0
244 remove_link('dummy98')
245 return func
if supported
else unittest
.expectedFailure(func
)
249 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
251 def finalize(func
, supported
):
252 call_quiet('rmmod netdevsim')
253 return func
if supported
else unittest
.expectedFailure(func
)
255 call_quiet('rmmod netdevsim')
256 if call_quiet('modprobe netdevsim') != 0:
257 return finalize(func
, False)
260 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
263 return finalize(func
, False)
265 return finalize(func
, os
.path
.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
269 # pylint: disable=C0415
270 def compare_kernel_version(min_kernel_version
):
273 from packaging
import version
275 print('Failed to import either platform or packaging module, assuming the comparison failed')
278 # Get only the actual kernel version without any build/distro/arch stuff
279 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
280 kver
= platform
.release().split('-')[0]
281 # Get also rid of '+'
282 kver
= kver
.split('+')[0]
284 return version
.parse(kver
) >= version
.parse(min_kernel_version
)
287 check_output(*udevadm_cmd
, 'control', '--reload')
289 def copy_network_unit(*units
, copy_dropins
=True):
291 Copy networkd unit files into the testbed.
293 Any networkd unit file type can be specified, as well as drop-in files.
295 By default, all drop-ins for a specified unit file are copied in;
296 to avoid that specify dropins=False.
298 When a drop-in file is specified, its unit file is also copied in automatically.
301 mkdir_p(network_unit_dir
)
303 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
304 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
306 if unit
.endswith('.conf'):
308 unit
= os
.path
.dirname(dropin
).rstrip('.d')
309 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
311 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
313 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
315 if unit
.endswith('.link'):
321 def copy_credential(src
, target
):
322 mkdir_p(credstore_dir
)
323 cp(os
.path
.join(networkd_ci_temp_dir
, src
),
324 os
.path
.join(credstore_dir
, target
))
326 def remove_network_unit(*units
):
328 Remove previously copied unit files from the testbed.
330 Drop-ins will be removed automatically.
334 rm_f(os
.path
.join(network_unit_dir
, unit
))
335 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
337 if unit
.endswith('.link') or unit
.endswith('.link.d'):
343 def clear_network_units():
345 if os
.path
.exists(network_unit_dir
):
346 units
= os
.listdir(network_unit_dir
)
348 if unit
.endswith('.link') or unit
.endswith('.link.d'):
351 rm_rf(network_unit_dir
)
356 def copy_networkd_conf_dropin(*dropins
):
357 """Copy networkd.conf dropin files into the testbed."""
358 mkdir_p(networkd_conf_dropin_dir
)
359 for dropin
in dropins
:
360 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
362 def remove_networkd_conf_dropin(*dropins
):
363 """Remove previously copied networkd.conf dropin files from the testbed."""
364 for dropin
in dropins
:
365 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
367 def clear_networkd_conf_dropins():
368 rm_rf(networkd_conf_dropin_dir
)
370 def setup_systemd_udev_rules():
371 if not systemd_udev_rules_build_dir
:
374 mkdir_p(udev_rules_dir
)
376 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
377 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
379 for rule
in os
.listdir(path
):
380 if not rule
.endswith(".rules"):
382 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
384 def copy_udev_rule(*rules
):
385 """Copy udev rules"""
386 mkdir_p(udev_rules_dir
)
388 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
390 def remove_udev_rule(*rules
):
391 """Remove previously copied udev rules"""
393 rm_f(os
.path
.join(udev_rules_dir
, rule
))
395 def clear_udev_rules():
396 rm_rf(udev_rules_dir
)
398 def save_active_units():
399 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
400 'systemd-resolved.service', 'systemd-timesyncd.service',
401 'firewalld.service']:
402 if call(f
'systemctl is-active --quiet {u}') == 0:
403 call(f
'systemctl stop {u}')
404 active_units
.append(u
)
406 def restore_active_units():
407 if 'systemd-networkd.socket' in active_units
:
408 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
409 for u
in active_units
:
410 call(f
'systemctl restart {u}')
412 def create_unit_dropin(unit
, contents
):
413 mkdir_p(f
'/run/systemd/system/{unit}.d')
414 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
415 f
.write('\n'.join(contents
))
417 def create_service_dropin(service
, command
, additional_settings
=None):
421 f
'ExecStart=!!{valgrind_cmd}{command}',
424 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
426 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
428 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
430 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
431 if asan_options
or lsan_options
or ubsan_options
:
432 drop_in
+= ['SystemCallFilter=']
433 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
434 drop_in
+= ['MemoryDenyWriteExecute=no']
437 'Environment=SYSTEMD_MEMPOOL=0',
445 if additional_settings
:
446 drop_in
+= additional_settings
448 create_unit_dropin(f
'{service}.service', drop_in
)
450 def link_exists(link
):
451 return call_quiet(f
'ip link show {link}') == 0
453 def link_resolve(link
):
454 return check_output(f
'ip link show {link}').split(':')[1].strip()
456 def remove_link(*links
, protect
=False):
458 if protect
and link
in protected_links
:
460 if link_exists(link
):
461 call(f
'ip link del dev {link}')
463 def save_existing_links():
464 links
= os
.listdir('/sys/class/net')
466 if link_exists(link
):
467 protected_links
.add(link
)
469 print('### The following links will be protected:')
470 print(', '.join(sorted(list(protected_links
))))
473 links
= os
.listdir('/sys/class/net')
474 remove_link(*links
, protect
=True)
476 def flush_nexthops():
477 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
478 # Hence, we cannot restore nexthops in a simple way.
479 # Let's assume there is no nexthop used in the system
480 call_quiet('ip nexthop flush')
483 # pylint: disable=global-statement
485 saved_routes
= check_output('ip route show table all')
486 print('### The following routes will be protected:')
491 output
= check_output('ip route show table all')
492 for line
in output
.splitlines():
493 if line
in saved_routes
:
495 if 'proto kernel' in line
:
497 if ' dev ' in line
and not ' dev lo ' in line
:
501 print('### Removing routes that did not exist when the test started.')
503 call(f
'ip route del {line}')
505 def save_routing_policy_rules():
506 # pylint: disable=global-statement
507 global saved_ipv4_rules
, saved_ipv6_rules
509 output
= check_output(f
'ip -{ipv} rule show')
510 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
514 saved_ipv4_rules
= save(4)
515 saved_ipv6_rules
= save(6)
517 def flush_routing_policy_rules():
518 def flush(ipv
, saved_rules
):
520 output
= check_output(f
'ip -{ipv} rule show')
521 for line
in output
.splitlines():
522 if line
in saved_rules
:
526 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
528 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
529 priority
= words
[0].rstrip(':')
530 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
532 flush(4, saved_ipv4_rules
)
533 flush(6, saved_ipv6_rules
)
535 def flush_fou_ports():
536 ret
= run('ip fou show')
537 if ret
.returncode
!= 0:
538 return # fou may not be supported
539 for line
in ret
.stdout
.splitlines():
540 port
= line
.split()[1]
541 call(f
'ip fou del port {port}')
543 def flush_l2tp_tunnels():
545 ret
= run('ip l2tp show tunnel')
546 if ret
.returncode
!= 0:
547 return # l2tp may not be supported
548 for line
in ret
.stdout
.splitlines():
550 if words
[0] == 'Tunnel':
551 tid
= words
[1].rstrip(',')
552 call(f
'ip l2tp del tunnel tunnel_id {tid}')
555 # Removing L2TP tunnel is asynchronous and slightly takes a time.
558 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
559 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
563 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
566 # pylint: disable=global-statement
567 global saved_timezone
568 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
569 if r
.returncode
== 0:
570 saved_timezone
= r
.stdout
.rstrip()
571 print(f
'### Saved timezone: {saved_timezone}')
573 def restore_timezone():
575 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
577 def read_link_attr(*args
):
578 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
579 return f
.readline().strip()
581 def read_manager_state_file():
582 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
585 def read_link_state_file(link
):
586 ifindex
= read_link_attr(link
, 'ifindex')
587 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
588 with
open(path
, encoding
='utf-8') as f
:
591 def read_ip_sysctl_attr(link
, attribute
, ipv
):
592 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
593 return f
.readline().strip()
595 def read_ip_neigh_sysctl_attr(link
, attribute
, ipv
):
596 with
open(os
.path
.join('/proc/sys/net', ipv
, 'neigh', link
, attribute
), encoding
='utf-8') as f
:
597 return f
.readline().strip()
599 def read_ipv6_sysctl_attr(link
, attribute
):
600 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
602 def read_ipv6_neigh_sysctl_attr(link
, attribute
):
603 return read_ip_neigh_sysctl_attr(link
, attribute
, 'ipv6')
605 def read_ipv4_sysctl_attr(link
, attribute
):
606 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
608 def stop_by_pid_file(pid_file
):
609 if not os
.path
.exists(pid_file
):
611 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
612 pid
= f
.read().rstrip(' \t\r\n\0')
613 os
.kill(int(pid
), signal
.SIGTERM
)
617 print(f
"PID {pid} is still alive, waiting...")
620 if e
.errno
== errno
.ESRCH
:
622 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
625 def start_dnsmasq(*additional_options
, interface
='veth-peer', ra_mode
=None, ipv4_range
='192.168.5.10,192.168.5.200', ipv4_router
='192.168.5.1', ipv6_range
='2600::10,2600::20'):
627 ra_mode
= f
',{ra_mode}'
633 f
'--log-facility={dnsmasq_log_file}',
634 '--log-queries=extra',
636 f
'--pid-file={dnsmasq_pid_file}',
637 '--conf-file=/dev/null',
639 f
'--interface={interface}',
640 f
'--dhcp-leasefile={dnsmasq_lease_file}',
642 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
643 f
'--dhcp-range={ipv4_range},2m',
644 '--dhcp-option=option:mtu,1492',
645 f
'--dhcp-option=option:router,{ipv4_router}',
648 ) + additional_options
649 check_output(*command
)
652 stop_by_pid_file(dnsmasq_pid_file
)
653 rm_f(dnsmasq_lease_file
)
654 rm_f(dnsmasq_log_file
)
656 def read_dnsmasq_log_file():
657 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
660 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
661 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
662 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
663 touch(isc_dhcpd_lease_file
)
664 check_output(isc_dhcpd_command
)
666 def stop_isc_dhcpd():
667 stop_by_pid_file(isc_dhcpd_pid_file
)
668 rm_f(isc_dhcpd_lease_file
)
670 def get_dbus_link_path(link
):
671 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
672 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
673 'GetLinkByName', 's', link
])
675 assert out
.startswith(b
'io ')
677 assert out
.endswith(b
'"')
679 return out
[:-1].split('"')[1]
681 def get_dhcp_client_state(link
, family
):
682 link_path
= get_dbus_link_path(link
)
684 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
685 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
686 assert out
.startswith(b
's "')
688 assert out
.endswith(b
'"')
689 return out
[3:-1].decode()
691 def get_dhcp4_client_state(link
):
692 return get_dhcp_client_state(link
, '4')
694 def get_dhcp6_client_state(link
):
695 return get_dhcp_client_state(link
, '6')
697 def get_link_description(link
):
698 link_path
= get_dbus_link_path(link
)
700 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
701 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
702 assert out
.startswith(b
's "')
704 assert out
.endswith(b
'"')
705 json_raw
= out
[2:].decode()
707 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
708 check_json(description
)
709 return json
.loads(description
) # Now parse the json
711 def start_radvd(*additional_options
, config_file
):
712 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
715 f
'--pidfile={radvd_pid_file}',
716 f
'--config={config_file_path}',
717 '--logmethod=stderr',
718 ) + additional_options
719 check_output(*command
)
722 stop_by_pid_file(radvd_pid_file
)
724 def radvd_check_config(config_file
):
725 if not shutil
.which('radvd'):
726 print('radvd is not installed, assuming the config check failed')
729 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
730 # set up (one instance is @unittest.skipX())
731 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
732 return call(f
'radvd --config={config_file_path} --configtest') == 0
734 def networkd_invocation_id():
735 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
737 def read_networkd_log(invocation_id
=None, since
=None):
738 if not invocation_id
:
739 invocation_id
= networkd_invocation_id()
743 '--output=short-monotonic',
744 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
747 command
.append(f
'--since={since}')
748 check_output('journalctl --sync')
749 return check_output(*command
)
751 def networkd_is_failed():
752 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
754 def stop_networkd(show_logs
=True):
756 invocation_id
= networkd_invocation_id()
757 check_output('systemctl stop systemd-networkd.socket')
758 check_output('systemctl stop systemd-networkd.service')
760 print(read_networkd_log(invocation_id
))
761 # Check if networkd exits cleanly.
762 assert not networkd_is_failed()
764 def start_networkd():
765 check_output('systemctl start systemd-networkd')
767 def restart_networkd(show_logs
=True):
769 invocation_id
= networkd_invocation_id()
770 check_output('systemctl restart systemd-networkd.service')
772 print(read_networkd_log(invocation_id
))
775 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
777 def networkctl(*args
):
778 # Do not call networkctl if networkd is in failed state.
779 # Otherwise, networkd may be restarted and we may get wrong results.
780 assert not networkd_is_failed()
781 return check_output(*(networkctl_cmd
+ list(args
)), env
=env
)
783 def networkctl_status(*args
):
784 return networkctl('-n', '0', 'status', *args
)
786 def networkctl_json(*args
):
787 return networkctl('--json=short', 'status', *args
)
789 def networkctl_reconfigure(*links
):
790 networkctl('reconfigure', *links
)
792 def networkctl_reload():
795 def resolvectl(*args
):
796 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
798 def timedatectl(*args
):
799 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
804 def tear_down_common():
805 # 1. stop DHCP/RA servers
811 call_quiet('rmmod netdevsim')
812 call_quiet('rmmod sch_teql')
814 # 3. remove network namespace
815 call_quiet('ip netns del ns99')
825 clear_network_units()
826 clear_networkd_conf_dropins()
831 flush_routing_policy_rules()
835 rm_rf(networkd_ci_temp_dir
)
836 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
838 clear_network_units()
839 clear_networkd_conf_dropins()
842 setup_systemd_udev_rules()
843 copy_udev_rule('00-debug-net.rules')
847 save_existing_links()
849 save_routing_policy_rules()
852 create_service_dropin('systemd-networkd', networkd_bin
,
855 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
857 'StartLimitIntervalSec=0'])
858 create_service_dropin('systemd-resolved', resolved_bin
)
859 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
861 # TODO: also run udevd with sanitizers, valgrind, or coverage
862 #create_service_dropin('systemd-udevd', udevd_bin,
863 # f'{udevadm_bin} control --reload --timeout 0')
865 'systemd-udevd.service',
869 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
873 'systemd-networkd.socket',
876 'StartLimitIntervalSec=0',
880 check_output('systemctl daemon-reload')
881 print(check_output('systemctl cat systemd-networkd.service'))
882 print(check_output('systemctl cat systemd-resolved.service'))
883 print(check_output('systemctl cat systemd-timesyncd.service'))
884 print(check_output('systemctl cat systemd-udevd.service'))
885 check_output('systemctl restart systemd-resolved.service')
886 check_output('systemctl restart systemd-timesyncd.service')
887 check_output('systemctl restart systemd-udevd.service')
889 def tearDownModule():
890 rm_rf(networkd_ci_temp_dir
)
892 clear_network_units()
893 clear_networkd_conf_dropins()
897 rm_rf('/run/systemd/system/systemd-networkd.service.d')
898 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
899 rm_rf('/run/systemd/system/systemd-resolved.service.d')
900 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
901 rm_rf('/run/systemd/system/systemd-udevd.service.d')
902 check_output('systemctl daemon-reload')
903 check_output('systemctl restart systemd-udevd.service')
904 restore_active_units()
907 # pylint: disable=no-member
909 def check_link_exists(self
, link
, expected
=True):
911 self
.assertTrue(link_exists(link
))
913 self
.assertFalse(link_exists(link
))
915 def check_link_attr(self
, *args
):
916 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
918 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
919 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
920 if allow_enoent
and not os
.path
.exists(path
):
922 with
open(path
, encoding
='utf-8') as f
:
923 self
.assertEqual(f
.readline().strip(), expected
)
925 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
926 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
928 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
929 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
931 def check_ipv6_neigh_sysctl_attr(self
, link
, attribute
, expected
):
932 self
.assertEqual(read_ipv6_neigh_sysctl_attr(link
, attribute
), expected
)
934 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
935 def links_exist(*links
):
937 if not link_exists(link
):
941 for iteration
in range(timeout
+ 1):
945 if links_exist(*links
):
948 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
951 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
952 # wait for the interface is activated.
953 needle
= f
'{link}: Bringing link {state}'
955 for iteration
in range(timeout
+ 1):
958 if not link_exists(link
):
960 output
= read_networkd_log()
961 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
964 self
.fail(f
'Timed out waiting for {link} activated.')
967 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
968 """Wait for the link to reach the specified operstate and/or setup state.
970 Specify None or '' for either operstate or setup_state to ignore that state.
971 This will recheck until the state conditions are met or the timeout expires.
973 If the link successfully matches the requested state, this returns True.
974 If this times out waiting for the link to match, the behavior depends on the
975 'fail_assert' parameter; if True, this causes a test assertion failure,
976 otherwise this returns False. The default is to cause assertion failure.
978 Note that this function matches on *exactly* the given operstate and setup_state.
979 To wait for a link to reach *or exceed* a given operstate, use wait_online().
986 for secs
in range(setup_timeout
+ 1):
989 if not link_exists(link
):
991 output
= networkctl_status(link
)
992 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
996 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
999 def wait_online(self
, *links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
1000 """Wait for the links to reach the specified operstate and/or setup state.
1002 This is similar to wait_operstate() but can be used for multiple links,
1003 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1004 The operstate should be specified in the link name, like 'eth0:degraded'.
1005 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1007 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1008 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1010 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1011 If this is set, no setup_state checks are done.
1013 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1014 This is applied only for the operational state 'degraded' or above.
1016 Note that this function waits for the links to reach *or exceed* the given operstate.
1017 However, the setup_state, if specified, must be matched *exactly*.
1019 This returns if the links reached the requested operstate/setup_state; otherwise it
1020 raises CalledProcessError or fails test assertion.
1022 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1030 check_output(*args
, env
=wait_online_env
)
1031 except subprocess
.CalledProcessError
:
1032 if networkd_is_failed():
1033 print('!!!!! systemd-networkd.service is failed !!!!!')
1034 call('systemctl status systemd-networkd.service')
1036 # show detailed status on failure
1037 for link
in links_with_operstate
:
1038 name
= link
.split(':')[0]
1039 if link_exists(name
):
1040 networkctl_status(name
)
1042 if not bool_any
and setup_state
:
1043 for link
in links_with_operstate
:
1044 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1046 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1047 for i
in range(timeout_sec
):
1050 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1051 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1054 self
.assertRegex(output
, address_regex
)
1056 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1057 for i
in range(timeout_sec
):
1060 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1061 if not re
.search(address_regex
, output
):
1064 self
.assertNotRegex(output
, address_regex
)
1066 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1067 for i
in range(timeout_sec
):
1070 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1071 if re
.search(route_regex
, output
):
1074 self
.assertRegex(output
, route_regex
)
1076 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1077 if not shutil
.which('selinuxenabled'):
1078 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1079 elif call_quiet('selinuxenabled') != 0:
1080 print('## Checking NetLabel skipped: SELinux disabled.')
1081 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1082 print('## Checking NetLabel skipped: netlabelctl command not found.')
1084 output
= check_output('netlabelctl unlbl list')
1086 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1088 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1089 if not shutil
.which('nft'):
1090 print('## Setting up NFT sets skipped: nft command not found.')
1092 if call(f
'nft add table inet sd_test') != 0:
1093 print('## Setting up NFT table failed.')
1095 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1096 print('## Setting up NFT sets failed.')
1099 def teardown_nftset(self
, *filters
):
1100 if not shutil
.which('nft'):
1101 print('## Tearing down NFT sets skipped: nft command not found.')
1103 for filter_name
in filters
:
1104 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1105 print('## Tearing down NFT sets failed.')
1107 if call(f
'nft delete table inet sd_test') != 0:
1108 print('## Tearing down NFT table failed.')
1111 def check_nftset(self
, filter_name
, contents
):
1112 if not shutil
.which('nft'):
1113 print('## Checking NFT sets skipped: nft command not found.')
1115 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1117 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1119 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1127 @expectedFailureIfAlternativeNameIsNotAvailable()
1128 def test_altname(self
):
1129 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1131 self
.wait_online('dummy98:degraded')
1133 output
= networkctl_status('dummy98')
1134 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1136 @expectedFailureIfAlternativeNameIsNotAvailable()
1137 def test_rename_to_altname(self
):
1138 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1139 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1141 self
.wait_online('dummyalt:degraded')
1143 output
= networkctl_status('dummyalt')
1144 self
.assertIn('hogehogehogehogehogehoge', output
)
1145 self
.assertNotIn('dummy98', output
)
1147 def test_reconfigure(self
):
1148 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1150 self
.wait_online('dummy98:routable')
1152 output
= check_output('ip -4 address show dev dummy98')
1154 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1155 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1156 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1158 check_output('ip address del 10.1.2.3/16 dev dummy98')
1159 check_output('ip address del 10.1.2.4/16 dev dummy98')
1160 check_output('ip address del 10.2.2.4/16 dev dummy98')
1162 networkctl_reconfigure('dummy98')
1163 self
.wait_online('dummy98:routable')
1165 output
= check_output('ip -4 address show dev dummy98')
1167 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1168 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1169 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1171 remove_network_unit('25-address-static.network')
1174 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1176 output
= check_output('ip -4 address show dev dummy98')
1178 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1179 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1180 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1182 copy_network_unit('25-address-static.network', copy_dropins
=False)
1184 self
.wait_online('dummy98:routable')
1186 output
= check_output('ip -4 address show dev dummy98')
1188 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1189 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1190 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1192 def test_renew(self
):
1194 self
.wait_online('veth99:routable', 'veth-peer:routable')
1195 output
= networkctl_status('veth99')
1197 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1198 self
.assertIn('Gateway: 192.168.5.3', output
)
1199 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1200 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1202 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1205 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1207 for verb
in ['renew', 'forcerenew']:
1208 networkctl(verb
, 'veth99')
1210 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1213 def test_up_down(self
):
1214 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1216 self
.wait_online('dummy98:routable')
1218 networkctl('down', 'dummy98')
1219 self
.wait_online('dummy98:off')
1220 networkctl('up', 'dummy98')
1221 self
.wait_online('dummy98:routable')
1222 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1223 self
.wait_online('dummy98:off')
1224 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1225 self
.wait_online('dummy98:routable')
1227 def test_reload(self
):
1230 copy_network_unit('11-dummy.netdev')
1232 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1234 copy_network_unit('11-dummy.network')
1236 self
.wait_online('test1:degraded')
1238 remove_network_unit('11-dummy.network')
1240 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1242 remove_network_unit('11-dummy.netdev')
1244 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1246 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1248 self
.wait_operstate('test1', 'degraded')
1250 def test_glob(self
):
1251 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1254 self
.wait_online('test1:degraded')
1256 output
= networkctl('list')
1257 self
.assertRegex(output
, '1 lo ')
1258 self
.assertRegex(output
, 'test1')
1260 output
= networkctl('list', 'test1')
1261 self
.assertNotRegex(output
, '1 lo ')
1262 self
.assertRegex(output
, 'test1')
1264 output
= networkctl('list', 'te*')
1265 self
.assertNotRegex(output
, '1 lo ')
1266 self
.assertRegex(output
, 'test1')
1268 output
= networkctl_status('te*')
1269 self
.assertNotRegex(output
, '1: lo ')
1270 self
.assertRegex(output
, 'test1')
1272 output
= networkctl_status('tes[a-z][0-9]')
1273 self
.assertNotRegex(output
, '1: lo ')
1274 self
.assertRegex(output
, 'test1')
1277 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1280 self
.wait_online('test1:degraded')
1282 output
= networkctl_status('test1')
1283 self
.assertRegex(output
, 'MTU: 1600')
1285 def test_type(self
):
1286 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1288 self
.wait_online('test1:degraded')
1290 output
= networkctl_status('test1')
1292 self
.assertRegex(output
, 'Type: ether')
1294 output
= networkctl_status('lo')
1296 self
.assertRegex(output
, 'Type: loopback')
1298 def test_unit_file(self
):
1299 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1301 self
.wait_online('test1:degraded')
1303 output
= networkctl_status('test1')
1305 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1306 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1307 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1308 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1310 output
= read_networkd_log()
1311 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
)
1313 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1314 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1315 # Let's reprocess the interface and drop the property.
1316 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1317 output
= networkctl_status('lo')
1319 self
.assertIn('Link File: n/a', output
)
1320 self
.assertIn('Network File: n/a', output
)
1322 def test_delete_links(self
):
1323 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1324 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1327 self
.wait_online('test1:degraded', 'veth99:degraded', 'veth-peer:degraded')
1329 networkctl('delete', 'test1', 'veth99')
1330 self
.check_link_exists('test1', expected
=False)
1331 self
.check_link_exists('veth99', expected
=False)
1332 self
.check_link_exists('veth-peer', expected
=False)
1334 def test_label(self
):
1337 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1345 @expectedFailureIfAlternativeNameIsNotAvailable()
1346 def test_match(self
):
1347 copy_network_unit('12-dummy-mac.netdev',
1348 '12-dummy-match-mac-01.network',
1349 '12-dummy-match-mac-02.network',
1350 '12-dummy-match-renamed.network',
1351 '12-dummy-match-altname.network',
1352 '12-dummy-altname.link')
1355 self
.wait_online('dummy98:routable')
1356 output
= networkctl_status('dummy98')
1357 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1358 output
= check_output('ip -4 address show dev dummy98')
1359 self
.assertIn('10.0.0.1/16', output
)
1361 check_output('ip link set dev dummy98 down')
1362 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1364 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1365 self
.wait_online('dummy98:routable')
1366 output
= networkctl_status('dummy98')
1367 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1369 check_output('ip link set dev dummy98 down')
1370 check_output('ip link set dev dummy98 name dummy98-1')
1372 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1373 self
.wait_online('dummy98-1:routable')
1374 output
= networkctl_status('dummy98-1')
1375 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1377 check_output('ip link set dev dummy98-1 down')
1378 check_output('ip link set dev dummy98-1 name dummy98-2')
1379 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1381 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1382 self
.wait_online('dummy98-2:routable')
1383 output
= networkctl_status('dummy98-2')
1384 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1386 def test_match_udev_property(self
):
1387 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1389 self
.wait_online('dummy98:routable')
1391 output
= networkctl_status('dummy98')
1393 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1395 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1403 def test_wait_online_any(self
):
1404 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1407 self
.wait_online('bridge99', 'test1:degraded', bool_any
=True)
1409 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1410 self
.wait_operstate('test1', 'degraded')
1412 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1420 def test_dropin_and_name_conflict(self
):
1421 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1424 self
.wait_online('dropin-test:off', setup_state
='unmanaged')
1426 output
= check_output('ip link show dropin-test')
1428 self
.assertRegex(output
, '00:50:56:c0:00:28')
1430 @expectedFailureIfModuleIsNotAvailable('bareudp')
1431 def test_bareudp(self
):
1432 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1435 self
.wait_online('bareudp99:degraded')
1437 output
= check_output('ip -d link show bareudp99')
1439 self
.assertRegex(output
, 'dstport 1000 ')
1440 self
.assertRegex(output
, 'ethertype ip ')
1442 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1443 def test_batadv(self
):
1444 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1447 self
.wait_online('batadv99:degraded')
1449 output
= check_output('ip -d link show batadv99')
1451 self
.assertRegex(output
, 'batadv')
1453 def test_bridge(self
):
1454 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1457 self
.wait_online('bridge99:no-carrier')
1459 tick
= os
.sysconf('SC_CLK_TCK')
1460 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1461 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1462 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1463 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1464 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1465 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1466 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1467 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1468 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1470 output
= networkctl_status('bridge99')
1472 self
.assertRegex(output
, 'Priority: 9')
1473 self
.assertRegex(output
, 'STP: yes')
1474 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1476 output
= check_output('ip -d link show bridge99')
1478 self
.assertIn('vlan_filtering 1 ', output
)
1479 self
.assertIn('vlan_protocol 802.1ad ', output
)
1480 self
.assertIn('vlan_default_pvid 9 ', output
)
1482 def test_bond(self
):
1483 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
1486 self
.wait_online('bond99:off', 'bond98:off', setup_state
='unmanaged')
1488 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1489 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1490 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1491 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1492 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1493 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1494 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1495 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1496 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1497 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1498 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1500 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1501 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1503 output
= networkctl_status('bond99')
1505 self
.assertIn('Mode: 802.3ad', output
)
1506 self
.assertIn('Miimon: 1s', output
)
1507 self
.assertIn('Updelay: 2s', output
)
1508 self
.assertIn('Downdelay: 2s', output
)
1510 output
= networkctl_status('bond98')
1512 self
.assertIn('Mode: balance-tlb', output
)
1514 def test_vlan(self
):
1515 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1516 '21-vlan.network', '21-vlan-test1.network')
1519 self
.wait_online('test1:degraded', 'vlan99:routable')
1521 output
= check_output('ip -d link show test1')
1523 self
.assertRegex(output
, ' mtu 2000 ')
1525 output
= check_output('ip -d link show vlan99')
1527 self
.assertIn(' mtu 2000 ', output
)
1528 self
.assertIn('REORDER_HDR', output
)
1529 self
.assertIn('LOOSE_BINDING', output
)
1530 self
.assertIn('GVRP', output
)
1531 self
.assertIn('MVRP', output
)
1532 self
.assertIn(' id 99 ', output
)
1533 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1534 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1536 output
= check_output('ip -4 address show dev test1')
1538 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1539 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1541 output
= check_output('ip -4 address show dev vlan99')
1543 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1545 def test_vlan_on_bond(self
):
1546 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1547 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1549 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1550 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1552 self
.wait_online('bond99:off')
1553 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1555 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1556 # that the issue is fixed by the commit, let's allow to match both string.
1557 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1561 if log_re
.search(read_networkd_log()):
1566 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1568 self
.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1570 def test_macvtap(self
):
1572 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1578 print(f
'### test_macvtap(mode={mode})')
1579 with self
.subTest(mode
=mode
):
1580 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1581 '11-dummy.netdev', '25-macvtap.network')
1582 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1583 f
.write('[MACVTAP]\nMode=' + mode
)
1586 self
.wait_online('macvtap99:degraded',
1587 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1589 output
= check_output('ip -d link show macvtap99')
1591 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1593 def test_macvlan(self
):
1595 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1601 print(f
'### test_macvlan(mode={mode})')
1602 with self
.subTest(mode
=mode
):
1603 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1604 '11-dummy.netdev', '25-macvlan.network')
1605 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1606 f
.write('[MACVLAN]\nMode=' + mode
)
1609 self
.wait_online('macvlan99:degraded',
1610 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1612 output
= check_output('ip -d link show test1')
1614 self
.assertRegex(output
, ' mtu 2000 ')
1616 output
= check_output('ip -d link show macvlan99')
1618 self
.assertRegex(output
, ' mtu 2000 ')
1619 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1621 remove_link('test1')
1624 check_output("ip link add test1 type dummy")
1625 self
.wait_online('macvlan99:degraded',
1626 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1628 output
= check_output('ip -d link show test1')
1630 self
.assertRegex(output
, ' mtu 2000 ')
1632 output
= check_output('ip -d link show macvlan99')
1634 self
.assertRegex(output
, ' mtu 2000 ')
1635 self
.assertRegex(output
, 'macvlan mode ' + mode
+ ' ')
1637 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1638 def test_ipvlan(self
):
1640 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1646 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1647 with self
.subTest(mode
=mode
, flag
=flag
):
1648 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1649 '11-dummy.netdev', '25-ipvlan.network')
1650 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1651 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1654 self
.wait_online('ipvlan99:degraded', 'test1:degraded')
1656 output
= check_output('ip -d link show ipvlan99')
1658 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1660 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1661 def test_ipvtap(self
):
1663 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1669 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1670 with self
.subTest(mode
=mode
, flag
=flag
):
1671 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1672 '11-dummy.netdev', '25-ipvtap.network')
1673 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1674 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1677 self
.wait_online('ipvtap99:degraded', 'test1:degraded')
1679 output
= check_output('ip -d link show ipvtap99')
1681 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1683 def test_veth(self
):
1684 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1685 '25-veth-mtu.netdev')
1688 self
.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
1690 output
= check_output('ip -d link show veth99')
1692 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1693 output
= check_output('ip -d link show veth-peer')
1695 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1697 output
= check_output('ip -d link show veth-mtu')
1699 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1700 self
.assertRegex(output
, 'mtu 1800')
1701 output
= check_output('ip -d link show veth-mtu-peer')
1703 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1704 self
.assertRegex(output
, 'mtu 1800')
1706 def test_tuntap(self
):
1707 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1710 self
.wait_online('testtun99:degraded', 'testtap99:degraded')
1712 pid
= networkd_pid()
1713 name
= psutil
.Process(pid
).name()[:15]
1715 output
= check_output('ip -d tuntap show')
1717 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1718 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1720 output
= check_output('ip -d link show testtun99')
1722 # Old ip command does not support IFF_ flags
1723 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1724 self
.assertIn('UP,LOWER_UP', output
)
1726 output
= check_output('ip -d link show testtap99')
1728 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1729 self
.assertIn('UP,LOWER_UP', output
)
1731 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1734 self
.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state
='unmanaged')
1736 pid
= networkd_pid()
1737 name
= psutil
.Process(pid
).name()[:15]
1739 output
= check_output('ip -d tuntap show')
1741 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1742 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1744 output
= check_output('ip -d link show testtun99')
1746 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1747 self
.assertIn('UP,LOWER_UP', output
)
1749 output
= check_output('ip -d link show testtap99')
1751 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1752 self
.assertIn('UP,LOWER_UP', output
)
1754 clear_network_units()
1756 self
.wait_online('testtun99:off', 'testtap99:off', setup_state
='unmanaged')
1758 output
= check_output('ip -d tuntap show')
1760 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1761 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1766 output
= check_output('ip -d link show testtun99')
1768 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1769 if 'NO-CARRIER' in output
:
1777 output
= check_output('ip -d link show testtap99')
1779 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1780 if 'NO-CARRIER' in output
:
1785 @expectedFailureIfModuleIsNotAvailable('vrf')
1787 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1790 self
.wait_online('vrf99:carrier')
1792 @expectedFailureIfModuleIsNotAvailable('vcan')
1793 def test_vcan(self
):
1794 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1795 '25-vcan98.netdev', '25-vcan98.network')
1798 self
.wait_online('vcan99:carrier', 'vcan98:carrier')
1799 # For can devices, 'carrier' is the default required operational state.
1800 self
.wait_online('vcan99', 'vcan98')
1802 # https://github.com/systemd/systemd/issues/30140
1803 output
= check_output('ip -d link show vcan99')
1805 self
.assertIn('mtu 16 ', output
)
1807 output
= check_output('ip -d link show vcan98')
1809 self
.assertIn('mtu 16 ', output
)
1811 @expectedFailureIfModuleIsNotAvailable('vxcan')
1812 def test_vxcan(self
):
1813 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1816 self
.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
1817 # For can devices, 'carrier' is the default required operational state.
1818 self
.wait_online('vxcan99', 'vxcan-peer')
1820 @expectedFailureIfModuleIsNotAvailable('wireguard')
1821 def test_wireguard(self
):
1822 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1823 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1824 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1826 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1827 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1828 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1829 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1831 self
.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
1833 output
= check_output('ip -4 address show dev wg99')
1835 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1837 output
= check_output('ip -4 address show dev wg99')
1839 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1841 output
= check_output('ip -6 address show dev wg99')
1843 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1845 output
= check_output('ip -4 address show dev wg98')
1847 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1849 output
= check_output('ip -6 address show dev wg98')
1851 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1853 output
= check_output('ip -4 route show dev wg99 table 1234')
1855 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1857 output
= check_output('ip -6 route show dev wg99 table 1234')
1859 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1861 output
= check_output('ip -6 route show dev wg98 table 1234')
1863 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1864 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1865 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1866 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1867 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1868 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1869 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1870 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1871 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1872 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1873 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1910 if shutil
.which('wg'):
1913 output
= check_output('wg show wg99 listen-port')
1914 self
.assertEqual(output
, '51820')
1915 output
= check_output('wg show wg99 fwmark')
1916 self
.assertEqual(output
, '0x4d2')
1917 output
= check_output('wg show wg99 private-key')
1918 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1919 output
= check_output('wg show wg99 allowed-ips')
1920 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1921 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1922 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1923 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1924 output
= check_output('wg show wg99 persistent-keepalive')
1925 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1926 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1927 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1928 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1929 output
= check_output('wg show wg99 endpoints')
1930 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1931 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1932 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1933 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1934 output
= check_output('wg show wg99 preshared-keys')
1935 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1936 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1937 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1938 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1940 output
= check_output('wg show wg98 private-key')
1941 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1943 output
= check_output('wg show wg97 listen-port')
1944 self
.assertEqual(output
, '51821')
1945 output
= check_output('wg show wg97 fwmark')
1946 self
.assertEqual(output
, '0x4d3')
1948 def test_geneve(self
):
1949 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1952 self
.wait_online('geneve99:degraded')
1954 output
= check_output('ip -d link show geneve99')
1956 self
.assertRegex(output
, '192.168.22.1')
1957 self
.assertRegex(output
, '6082')
1958 self
.assertRegex(output
, 'udpcsum')
1959 self
.assertRegex(output
, 'udp6zerocsumrx')
1961 def test_ipip_tunnel(self
):
1962 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1963 '25-ipip-tunnel.netdev', '25-tunnel.network',
1964 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1965 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1966 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1968 self
.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
1970 output
= check_output('ip -d link show ipiptun99')
1972 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1973 output
= check_output('ip -d link show ipiptun98')
1975 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1976 output
= check_output('ip -d link show ipiptun97')
1978 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1979 output
= check_output('ip -d link show ipiptun96')
1981 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1983 def test_gre_tunnel(self
):
1984 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1985 '25-gre-tunnel.netdev', '25-tunnel.network',
1986 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1987 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1988 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1990 self
.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
1992 output
= check_output('ip -d link show gretun99')
1994 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
1995 self
.assertRegex(output
, 'ikey 1.2.3.103')
1996 self
.assertRegex(output
, 'okey 1.2.4.103')
1997 self
.assertRegex(output
, 'iseq')
1998 self
.assertRegex(output
, 'oseq')
1999 output
= check_output('ip -d link show gretun98')
2001 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2002 self
.assertRegex(output
, 'ikey 0.0.0.104')
2003 self
.assertRegex(output
, 'okey 0.0.0.104')
2004 self
.assertNotRegex(output
, 'iseq')
2005 self
.assertNotRegex(output
, 'oseq')
2006 output
= check_output('ip -d link show gretun97')
2008 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2009 self
.assertRegex(output
, 'ikey 0.0.0.105')
2010 self
.assertRegex(output
, 'okey 0.0.0.105')
2011 self
.assertNotRegex(output
, 'iseq')
2012 self
.assertNotRegex(output
, 'oseq')
2013 output
= check_output('ip -d link show gretun96')
2015 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2016 self
.assertRegex(output
, 'ikey 0.0.0.106')
2017 self
.assertRegex(output
, 'okey 0.0.0.106')
2018 self
.assertNotRegex(output
, 'iseq')
2019 self
.assertNotRegex(output
, 'oseq')
2021 def test_ip6gre_tunnel(self
):
2022 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2023 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2024 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2025 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2026 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2029 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2031 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2033 output
= check_output('ip -d link show ip6gretun99')
2035 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2036 output
= check_output('ip -d link show ip6gretun98')
2038 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2039 output
= check_output('ip -d link show ip6gretun97')
2041 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2042 output
= check_output('ip -d link show ip6gretun96')
2044 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2046 def test_gretap_tunnel(self
):
2047 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2048 '25-gretap-tunnel.netdev', '25-tunnel.network',
2049 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2051 self
.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2053 output
= check_output('ip -d link show gretap99')
2055 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2056 self
.assertRegex(output
, 'ikey 0.0.0.106')
2057 self
.assertRegex(output
, 'okey 0.0.0.106')
2058 self
.assertRegex(output
, 'iseq')
2059 self
.assertRegex(output
, 'oseq')
2060 self
.assertIn('nopmtudisc', output
)
2061 self
.assertIn('ignore-df', output
)
2062 output
= check_output('ip -d link show gretap98')
2064 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2065 self
.assertRegex(output
, 'ikey 0.0.0.107')
2066 self
.assertRegex(output
, 'okey 0.0.0.107')
2067 self
.assertRegex(output
, 'iseq')
2068 self
.assertRegex(output
, 'oseq')
2070 def test_ip6gretap_tunnel(self
):
2071 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2072 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2073 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2075 self
.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2077 output
= check_output('ip -d link show ip6gretap99')
2079 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2080 output
= check_output('ip -d link show ip6gretap98')
2082 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2084 def test_vti_tunnel(self
):
2085 copy_network_unit('12-dummy.netdev', '25-vti.network',
2086 '25-vti-tunnel.netdev', '25-tunnel.network',
2087 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2088 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2089 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2091 self
.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2093 output
= check_output('ip -d link show vtitun99')
2095 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2096 output
= check_output('ip -d link show vtitun98')
2098 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2099 output
= check_output('ip -d link show vtitun97')
2101 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2102 output
= check_output('ip -d link show vtitun96')
2104 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2106 def test_vti6_tunnel(self
):
2107 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2108 '25-vti6-tunnel.netdev', '25-tunnel.network',
2109 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2110 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2112 self
.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2114 output
= check_output('ip -d link show vti6tun99')
2116 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2117 output
= check_output('ip -d link show vti6tun98')
2119 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2120 output
= check_output('ip -d link show vti6tun97')
2122 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2124 def test_ip6tnl_tunnel(self
):
2125 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2126 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2127 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2128 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2129 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2130 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2131 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2133 self
.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2134 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2135 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2137 output
= check_output('ip -d link show ip6tnl99')
2139 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2140 output
= check_output('ip -d link show ip6tnl98')
2142 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2143 output
= check_output('ip -d link show ip6tnl97')
2145 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2146 output
= check_output('ip -d link show ip6tnl-external')
2148 self
.assertIn('ip6tnl-external@NONE:', output
)
2149 self
.assertIn('ip6tnl external ', output
)
2150 output
= check_output('ip -d link show ip6tnl-slaac')
2152 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2154 output
= check_output('ip -6 address show veth99')
2156 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2158 output
= check_output('ip -4 route show default')
2160 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2162 def test_sit_tunnel(self
):
2163 copy_network_unit('12-dummy.netdev', '25-sit.network',
2164 '25-sit-tunnel.netdev', '25-tunnel.network',
2165 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2166 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2167 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2169 self
.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2171 output
= check_output('ip -d link show sittun99')
2173 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2174 output
= check_output('ip -d link show sittun98')
2176 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2177 output
= check_output('ip -d link show sittun97')
2179 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2180 output
= check_output('ip -d link show sittun96')
2182 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2184 def test_isatap_tunnel(self
):
2185 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2186 '25-isatap-tunnel.netdev', '25-tunnel.network')
2188 self
.wait_online('isataptun99:routable', 'dummy98:degraded')
2190 output
= check_output('ip -d link show isataptun99')
2192 self
.assertRegex(output
, "isatap ")
2194 def test_6rd_tunnel(self
):
2195 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2196 '25-6rd-tunnel.netdev', '25-tunnel.network')
2198 self
.wait_online('sittun99:routable', 'dummy98:degraded')
2200 output
= check_output('ip -d link show sittun99')
2202 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2204 @expectedFailureIfERSPANv0IsNotSupported()
2205 def test_erspan_tunnel_v0(self
):
2206 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2207 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2208 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2210 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2212 output
= check_output('ip -d link show erspan99')
2214 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2215 self
.assertIn('erspan_ver 0', output
)
2216 self
.assertNotIn('erspan_index 123', output
)
2217 self
.assertNotIn('erspan_dir ingress', output
)
2218 self
.assertNotIn('erspan_hwid 1f', output
)
2219 self
.assertIn('ikey 0.0.0.101', output
)
2220 self
.assertIn('iseq', output
)
2221 self
.assertIn('nopmtudisc', output
)
2222 self
.assertIn('ignore-df', output
)
2223 output
= check_output('ip -d link show erspan98')
2225 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2226 self
.assertIn('erspan_ver 0', output
)
2227 self
.assertNotIn('erspan_index 124', output
)
2228 self
.assertNotIn('erspan_dir egress', output
)
2229 self
.assertNotIn('erspan_hwid 2f', output
)
2230 self
.assertIn('ikey 0.0.0.102', output
)
2231 self
.assertIn('iseq', output
)
2233 def test_erspan_tunnel_v1(self
):
2234 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2235 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2236 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2238 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2240 output
= check_output('ip -d link show erspan99')
2242 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2243 self
.assertIn('erspan_ver 1', output
)
2244 self
.assertIn('erspan_index 123', output
)
2245 self
.assertNotIn('erspan_dir ingress', output
)
2246 self
.assertNotIn('erspan_hwid 1f', output
)
2247 self
.assertIn('ikey 0.0.0.101', output
)
2248 self
.assertIn('okey 0.0.0.101', output
)
2249 self
.assertIn('iseq', output
)
2250 self
.assertIn('oseq', output
)
2251 output
= check_output('ip -d link show erspan98')
2253 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2254 self
.assertIn('erspan_ver 1', output
)
2255 self
.assertIn('erspan_index 124', output
)
2256 self
.assertNotIn('erspan_dir egress', output
)
2257 self
.assertNotIn('erspan_hwid 2f', output
)
2258 self
.assertIn('ikey 0.0.0.102', output
)
2259 self
.assertIn('okey 0.0.0.102', output
)
2260 self
.assertIn('iseq', output
)
2261 self
.assertIn('oseq', output
)
2263 @expectedFailureIfERSPANv2IsNotSupported()
2264 def test_erspan_tunnel_v2(self
):
2265 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2266 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2267 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2269 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2271 output
= check_output('ip -d link show erspan99')
2273 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2274 self
.assertIn('erspan_ver 2', output
)
2275 self
.assertNotIn('erspan_index 123', output
)
2276 self
.assertIn('erspan_dir ingress', output
)
2277 self
.assertIn('erspan_hwid 0x1f', output
)
2278 self
.assertIn('ikey 0.0.0.101', output
)
2279 self
.assertIn('okey 0.0.0.101', output
)
2280 self
.assertIn('iseq', output
)
2281 self
.assertIn('oseq', output
)
2282 output
= check_output('ip -d link show erspan98')
2284 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2285 self
.assertIn('erspan_ver 2', output
)
2286 self
.assertNotIn('erspan_index 124', output
)
2287 self
.assertIn('erspan_dir egress', output
)
2288 self
.assertIn('erspan_hwid 0x2f', output
)
2289 self
.assertIn('ikey 0.0.0.102', output
)
2290 self
.assertIn('okey 0.0.0.102', output
)
2291 self
.assertIn('iseq', output
)
2292 self
.assertIn('oseq', output
)
2294 def test_tunnel_independent(self
):
2295 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2298 self
.wait_online('ipiptun99:carrier')
2300 def test_tunnel_independent_loopback(self
):
2301 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2304 self
.wait_online('ipiptun99:carrier')
2306 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2307 def test_xfrm(self
):
2308 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2309 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2310 '26-netdev-link-local-addressing-yes.network')
2313 self
.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
2315 output
= check_output('ip -d link show dev xfrm98')
2317 self
.assertIn('xfrm98@dummy98:', output
)
2318 self
.assertIn('xfrm if_id 0x98 ', output
)
2320 output
= check_output('ip -d link show dev xfrm99')
2322 self
.assertIn('xfrm99@lo:', output
)
2323 self
.assertIn('xfrm if_id 0x99 ', output
)
2325 @expectedFailureIfModuleIsNotAvailable('fou')
2327 # The following redundant check is necessary for CentOS CI.
2328 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2329 self
.assertTrue(is_module_available('fou'))
2331 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2332 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2333 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2336 self
.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state
='unmanaged')
2338 output
= check_output('ip fou show')
2340 self
.assertRegex(output
, 'port 55555 ipproto 4')
2341 self
.assertRegex(output
, 'port 55556 ipproto 47')
2343 output
= check_output('ip -d link show ipiptun96')
2345 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2346 output
= check_output('ip -d link show sittun96')
2348 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2349 output
= check_output('ip -d link show gretun96')
2351 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2352 output
= check_output('ip -d link show gretap96')
2354 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2356 def test_vxlan(self
):
2357 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2358 '25-vxlan.netdev', '25-vxlan.network',
2359 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2360 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2361 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2362 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2365 self
.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2366 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
2368 output
= check_output('ip -d -d link show vxlan99')
2370 self
.assertIn('999', output
)
2371 self
.assertIn('5555', output
)
2372 self
.assertIn('l2miss', output
)
2373 self
.assertIn('l3miss', output
)
2374 self
.assertIn('gbp', output
)
2375 # Since [0] some of the options use slightly different names and some
2376 # options with default values are shown only if the -d(etails) setting
2378 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2379 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2380 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2381 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2382 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2383 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2385 output
= check_output('bridge fdb show dev vxlan99')
2387 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2388 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2389 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2391 output
= networkctl_status('vxlan99')
2393 self
.assertIn('VNI: 999', output
)
2394 self
.assertIn('Destination Port: 5555', output
)
2395 self
.assertIn('Underlying Device: test1', output
)
2397 output
= check_output('bridge fdb show dev vxlan97')
2399 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2400 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2401 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2403 output
= check_output('ip -d link show vxlan-slaac')
2405 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2407 output
= check_output('ip -6 address show veth99')
2409 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2411 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2412 def test_macsec(self
):
2413 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2414 '26-macsec.network', '12-dummy.netdev')
2417 self
.wait_online('dummy98:degraded', 'macsec99:routable')
2419 output
= check_output('ip -d link show macsec99')
2421 self
.assertRegex(output
, 'macsec99@dummy98')
2422 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2423 self
.assertRegex(output
, 'encrypt on')
2425 output
= check_output('ip macsec show macsec99')
2427 self
.assertRegex(output
, 'encrypt on')
2428 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2429 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2430 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2431 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2432 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2433 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2434 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2435 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2436 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2437 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2438 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2440 def test_nlmon(self
):
2441 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2444 self
.wait_online('nlmon99:carrier')
2446 @expectedFailureIfModuleIsNotAvailable('ifb')
2448 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2451 self
.wait_online('ifb99:degraded')
2453 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2461 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2462 def test_l2tp_udp(self
):
2463 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2464 '25-l2tp-udp.netdev', '25-l2tp.network')
2467 self
.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
2469 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2471 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2472 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2473 self
.assertRegex(output
, "Peer tunnel 11")
2474 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2475 self
.assertRegex(output
, "UDP checksum: enabled")
2477 output
= check_output('ip l2tp show session tid 10 session_id 15')
2479 self
.assertRegex(output
, "Session 15 in tunnel 10")
2480 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2481 self
.assertRegex(output
, "interface name: l2tp-ses1")
2483 output
= check_output('ip l2tp show session tid 10 session_id 17')
2485 self
.assertRegex(output
, "Session 17 in tunnel 10")
2486 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2487 self
.assertRegex(output
, "interface name: l2tp-ses2")
2489 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2490 def test_l2tp_ip(self
):
2491 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2492 '25-l2tp-ip.netdev', '25-l2tp.network')
2495 self
.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
2497 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2499 self
.assertRegex(output
, "Tunnel 10, encap IP")
2500 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2501 self
.assertRegex(output
, "Peer tunnel 12")
2503 output
= check_output('ip l2tp show session tid 10 session_id 25')
2505 self
.assertRegex(output
, "Session 25 in tunnel 10")
2506 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2507 self
.assertRegex(output
, "interface name: l2tp-ses3")
2509 output
= check_output('ip l2tp show session tid 10 session_id 27')
2511 self
.assertRegex(output
, "Session 27 in tunnel 10")
2512 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2513 self
.assertRegex(output
, "interface name: l2tp-ses4")
2515 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2523 def verify_address_static(
2553 output
= check_output('ip address show dev dummy98')
2557 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2558 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2559 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2560 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2561 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2562 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2565 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2566 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2567 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2570 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2571 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2572 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2575 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2576 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2577 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2578 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2579 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2580 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2583 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2584 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2587 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2588 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2589 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2590 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2593 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2594 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2596 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2598 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2600 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2602 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2605 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2606 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2607 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2608 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2611 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2612 prefix16
= ip4_null_16
[:-len('.0.1')]
2613 self
.assertTrue(ip4_null_24
.endswith('.1'))
2614 prefix24
= ip4_null_24
[:-len('.1')]
2615 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2616 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2617 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2618 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2621 self
.assertNotIn('10.4.4.1', output
)
2622 self
.assertNotIn('10.5.4.1', output
)
2623 self
.assertNotIn('10.5.5.1', output
)
2624 self
.assertNotIn('10.8.2.1', output
)
2625 self
.assertNotIn('10.9.3.1', output
)
2626 self
.assertNotIn('2001:db8:0:f101::2', output
)
2627 self
.assertNotIn('2001:db8:0:f103::4', output
)
2630 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2632 check_json(networkctl_json())
2634 def test_address_static(self
):
2635 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2636 self
.setup_nftset('addr4', 'ipv4_addr')
2637 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2638 self
.setup_nftset('ifindex', 'iface_index')
2641 self
.wait_online('dummy98:routable')
2645 output
= check_output('ip -4 --json address show dev dummy98')
2646 for i
in json
.loads(output
)[0]['addr_info']:
2647 if i
['label'] == 'subnet16':
2648 ip4_null_16
= i
['local']
2649 elif i
['label'] == 'subnet24':
2650 ip4_null_24
= i
['local']
2651 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2652 self
.assertTrue(ip4_null_24
.endswith('.1'))
2656 output
= check_output('ip -6 --json address show dev dummy98')
2657 for i
in json
.loads(output
)[0]['addr_info']:
2658 if i
['prefixlen'] == 73:
2659 ip6_null_73
= i
['local']
2660 elif i
['prefixlen'] == 74:
2661 ip6_null_74
= i
['local']
2662 self
.assertTrue(ip6_null_73
.endswith(':1'))
2663 self
.assertTrue(ip6_null_74
.endswith(':1'))
2665 self
.verify_address_static(
2670 broadcast2
=' brd 10.4.2.255',
2671 broadcast3
=' brd 10.4.3.63',
2672 peer1
=' peer 10.5.1.101/24',
2673 peer2
=' peer 10.5.2.101/24',
2674 peer3
='/24 brd 10.5.3.255',
2675 peer4
=' peer 2001:db8:0:f103::101/128',
2676 peer5
=' peer 2001:db8:0:f103::102/128',
2681 deprecated2
=' deprecated',
2683 deprecated4
=' deprecated',
2685 flag1
=' noprefixroute',
2687 flag3
=' noprefixroute',
2688 flag4
=' home mngtmpaddr',
2689 ip4_null_16
=ip4_null_16
,
2690 ip4_null_24
=ip4_null_24
,
2691 ip6_null_73
=ip6_null_73
,
2692 ip6_null_74
=ip6_null_74
,
2695 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2696 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2697 self
.check_nftset('ifindex', 'dummy98')
2699 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2701 copy_network_unit('25-address-static.network.d/10-override.conf')
2703 self
.wait_online('dummy98:routable')
2704 self
.verify_address_static(
2705 label1
='new-label1',
2707 label3
='new-label3',
2708 broadcast1
=' brd 10.4.1.255',
2710 broadcast3
=' brd 10.4.3.31',
2711 peer1
=' peer 10.5.1.102/24',
2712 peer2
='/24 brd 10.5.2.255',
2713 peer3
=' peer 10.5.3.102/24',
2714 peer4
=' peer 2001:db8:0:f103::201/128',
2716 peer6
=' peer 2001:db8:0:f103::203/128',
2719 deprecated1
=' deprecated',
2721 deprecated3
=' deprecated',
2725 flag2
=' noprefixroute',
2726 flag3
=' home mngtmpaddr',
2727 flag4
=' noprefixroute',
2728 ip4_null_16
=ip4_null_16
,
2729 ip4_null_24
=ip4_null_24
,
2730 ip6_null_73
=ip6_null_73
,
2731 ip6_null_74
=ip6_null_74
,
2734 networkctl_reconfigure('dummy98')
2735 self
.wait_online('dummy98:routable')
2736 self
.verify_address_static(
2737 label1
='new-label1',
2739 label3
='new-label3',
2740 broadcast1
=' brd 10.4.1.255',
2742 broadcast3
=' brd 10.4.3.31',
2743 peer1
=' peer 10.5.1.102/24',
2744 peer2
='/24 brd 10.5.2.255',
2745 peer3
=' peer 10.5.3.102/24',
2746 peer4
=' peer 2001:db8:0:f103::201/128',
2748 peer6
=' peer 2001:db8:0:f103::203/128',
2751 deprecated1
=' deprecated',
2753 deprecated3
=' deprecated',
2757 flag2
=' noprefixroute',
2758 flag3
=' home mngtmpaddr',
2759 flag4
=' noprefixroute',
2760 ip4_null_16
=ip4_null_16
,
2761 ip4_null_24
=ip4_null_24
,
2762 ip6_null_73
=ip6_null_73
,
2763 ip6_null_74
=ip6_null_74
,
2767 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2768 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2769 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2770 output
= check_output('ip address show dev dummy98')
2772 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2773 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2775 # 2. reconfigure the interface, and check the deprecated flag is set again
2776 networkctl_reconfigure('dummy98')
2777 self
.wait_online('dummy98:routable')
2778 self
.verify_address_static(
2779 label1
='new-label1',
2781 label3
='new-label3',
2782 broadcast1
=' brd 10.4.1.255',
2784 broadcast3
=' brd 10.4.3.31',
2785 peer1
=' peer 10.5.1.102/24',
2786 peer2
='/24 brd 10.5.2.255',
2787 peer3
=' peer 10.5.3.102/24',
2788 peer4
=' peer 2001:db8:0:f103::201/128',
2790 peer6
=' peer 2001:db8:0:f103::203/128',
2793 deprecated1
=' deprecated',
2795 deprecated3
=' deprecated',
2799 flag2
=' noprefixroute',
2800 flag3
=' home mngtmpaddr',
2801 flag4
=' noprefixroute',
2802 ip4_null_16
=ip4_null_16
,
2803 ip4_null_24
=ip4_null_24
,
2804 ip6_null_73
=ip6_null_73
,
2805 ip6_null_74
=ip6_null_74
,
2808 # test for ENOBUFS issue #17012 (with reload)
2809 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2811 self
.wait_online('dummy98:routable')
2812 output
= check_output('ip -4 address show dev dummy98')
2813 for i
in range(1, 254):
2814 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2816 # (with reconfigure)
2817 networkctl_reconfigure('dummy98')
2818 self
.wait_online('dummy98:routable')
2819 output
= check_output('ip -4 address show dev dummy98')
2820 for i
in range(1, 254):
2821 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2823 # test for an empty string assignment for Address= in [Network]
2824 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2826 self
.wait_online('dummy98:routable')
2827 output
= check_output('ip -4 address show dev dummy98')
2828 for i
in range(1, 254):
2829 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2830 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2832 def test_address_ipv4acd(self
):
2833 check_output('ip netns add ns99')
2834 check_output('ip link add veth99 type veth peer veth-peer')
2835 check_output('ip link set veth-peer netns ns99')
2836 check_output('ip link set veth99 up')
2837 check_output('ip netns exec ns99 ip link set veth-peer up')
2838 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2840 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2842 self
.wait_online('veth99:routable')
2844 output
= check_output('ip -4 address show dev veth99')
2846 self
.assertNotIn('192.168.100.10/24', output
)
2847 self
.assertIn('192.168.100.11/24', output
)
2849 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2851 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2853 output
= check_output('ip -4 address show dev veth99')
2855 self
.assertNotIn('192.168.100.10/24', output
)
2856 self
.assertIn('192.168.100.11/24', output
)
2858 def test_address_peer_ipv4(self
):
2859 # test for issue #17304
2860 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2862 for trial
in range(2):
2868 self
.wait_online('dummy98:routable')
2870 output
= check_output('ip -4 address show dev dummy98')
2871 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2873 @expectedFailureIfModuleIsNotAvailable('vrf')
2874 def test_prefix_route(self
):
2875 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2876 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2877 '25-vrf.netdev', '25-vrf.network')
2878 for trial
in range(2):
2884 self
.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
2886 output
= check_output('ip route show table 42 dev dummy98')
2887 print('### ip route show table 42 dev dummy98')
2889 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2890 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2891 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2892 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2893 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2894 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2895 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2896 output
= check_output('ip -6 route show table 42 dev dummy98')
2897 print('### ip -6 route show table 42 dev dummy98')
2901 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2902 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2903 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2904 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2905 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2906 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2907 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2908 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2912 output
= check_output('ip route show dev test1')
2913 print('### ip route show dev test1')
2915 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2916 output
= check_output('ip route show table local dev test1')
2917 print('### ip route show table local dev test1')
2919 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2920 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2921 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2922 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2923 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2924 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2925 output
= check_output('ip -6 route show dev test1')
2926 print('### ip -6 route show dev test1')
2928 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2929 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2930 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2931 output
= check_output('ip -6 route show table local dev test1')
2932 print('### ip -6 route show table local dev test1')
2934 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2935 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2936 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2937 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2938 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2940 def test_configure_without_carrier(self
):
2941 copy_network_unit('11-dummy.netdev')
2943 self
.wait_operstate('test1', 'off', '')
2944 check_output('ip link set dev test1 up carrier off')
2946 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2948 self
.wait_online('test1:no-carrier')
2950 carrier_map
= {'on': '1', 'off': '0'}
2951 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2952 for carrier
in ['off', 'on', 'off']:
2953 with self
.subTest(carrier
=carrier
):
2954 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2955 check_output(f
'ip link set dev test1 carrier {carrier}')
2956 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
2958 output
= networkctl_status('test1')
2960 self
.assertRegex(output
, '192.168.0.15')
2961 self
.assertRegex(output
, '192.168.0.1')
2962 self
.assertRegex(output
, routable_map
[carrier
])
2964 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2965 copy_network_unit('11-dummy.netdev')
2967 self
.wait_operstate('test1', 'off', '')
2968 check_output('ip link set dev test1 up carrier off')
2970 copy_network_unit('25-test1.network')
2972 self
.wait_online('test1:no-carrier')
2974 carrier_map
= {'on': '1', 'off': '0'}
2975 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2976 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2977 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2978 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2979 check_output(f
'ip link set dev test1 carrier {carrier}')
2980 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
2982 output
= networkctl_status('test1')
2985 self
.assertRegex(output
, '192.168.0.15')
2986 self
.assertRegex(output
, '192.168.0.1')
2988 self
.assertNotRegex(output
, '192.168.0.15')
2989 self
.assertNotRegex(output
, '192.168.0.1')
2990 self
.assertRegex(output
, routable_map
[carrier
])
2992 def test_routing_policy_rule(self
):
2993 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
2995 self
.wait_online('test1:degraded')
2997 output
= check_output('ip rule list iif test1 priority 111')
2999 self
.assertRegex(output
, '111:')
3000 self
.assertRegex(output
, 'from 192.168.100.18')
3001 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3002 self
.assertRegex(output
, 'iif test1')
3003 self
.assertRegex(output
, 'oif test1')
3004 self
.assertRegex(output
, 'lookup 7')
3006 output
= check_output('ip rule list iif test1 priority 101')
3008 self
.assertRegex(output
, '101:')
3009 self
.assertRegex(output
, 'from all')
3010 self
.assertRegex(output
, 'iif test1')
3011 self
.assertRegex(output
, 'lookup 9')
3013 output
= check_output('ip -6 rule list iif test1 priority 100')
3015 self
.assertRegex(output
, '100:')
3016 self
.assertRegex(output
, 'from all')
3017 self
.assertRegex(output
, 'iif test1')
3018 self
.assertRegex(output
, 'lookup 8')
3020 output
= check_output('ip rule list iif test1 priority 102')
3022 self
.assertRegex(output
, '102:')
3023 self
.assertRegex(output
, 'from 0.0.0.0/8')
3024 self
.assertRegex(output
, 'iif test1')
3025 self
.assertRegex(output
, 'lookup 10')
3027 check_json(networkctl_json())
3029 def test_routing_policy_rule_issue_11280(self
):
3030 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3031 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3033 for trial
in range(3):
3034 restart_networkd(show_logs
=(trial
> 0))
3035 self
.wait_online('test1:degraded', 'dummy98:degraded')
3037 output
= check_output('ip rule list table 7')
3039 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3041 output
= check_output('ip rule list table 8')
3043 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3045 def test_routing_policy_rule_reconfigure(self
):
3046 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3048 self
.wait_online('test1:degraded')
3050 output
= check_output('ip rule list table 1011')
3052 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3053 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3054 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3055 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3057 output
= check_output('ip -6 rule list table 1011')
3059 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3061 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3063 self
.wait_online('test1:degraded')
3065 output
= check_output('ip rule list table 1011')
3067 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3068 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3069 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3070 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3072 output
= check_output('ip -6 rule list table 1011')
3074 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3075 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3077 call('ip rule delete priority 10111')
3078 call('ip rule delete priority 10112')
3079 call('ip rule delete priority 10113')
3080 call('ip rule delete priority 10114')
3081 call('ip -6 rule delete priority 10113')
3083 output
= check_output('ip rule list table 1011')
3085 self
.assertEqual(output
, '')
3087 output
= check_output('ip -6 rule list table 1011')
3089 self
.assertEqual(output
, '')
3091 networkctl_reconfigure('test1')
3092 self
.wait_online('test1:degraded')
3094 output
= check_output('ip rule list table 1011')
3096 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3097 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3098 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3099 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3101 output
= check_output('ip -6 rule list table 1011')
3103 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3105 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3106 def test_routing_policy_rule_port_range(self
):
3107 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3109 self
.wait_online('test1:degraded')
3111 output
= check_output('ip rule')
3113 self
.assertRegex(output
, '111')
3114 self
.assertRegex(output
, 'from 192.168.100.18')
3115 self
.assertRegex(output
, '1123-1150')
3116 self
.assertRegex(output
, '3224-3290')
3117 self
.assertRegex(output
, 'tcp')
3118 self
.assertRegex(output
, 'lookup 7')
3120 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3121 def test_routing_policy_rule_invert(self
):
3122 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3124 self
.wait_online('test1:degraded')
3126 output
= check_output('ip rule')
3128 self
.assertRegex(output
, '111')
3129 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3130 self
.assertRegex(output
, 'tcp')
3131 self
.assertRegex(output
, 'lookup 7')
3133 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3134 def test_routing_policy_rule_l3mdev(self
):
3135 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3137 self
.wait_online('test1:degraded')
3139 output
= check_output('ip rule')
3141 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3142 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3144 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3145 def test_routing_policy_rule_uidrange(self
):
3146 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3148 self
.wait_online('test1:degraded')
3150 output
= check_output('ip rule')
3152 self
.assertRegex(output
, '111')
3153 self
.assertRegex(output
, 'from 192.168.100.18')
3154 self
.assertRegex(output
, 'lookup 7')
3155 self
.assertRegex(output
, 'uidrange 100-200')
3157 def _test_route_static(self
, manage_foreign_routes
):
3158 if not manage_foreign_routes
:
3159 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3161 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3162 '25-route-static-test1.network', '11-dummy.netdev')
3164 self
.wait_online('dummy98:routable')
3166 output
= networkctl_status('dummy98')
3169 print('### ip -6 route show dev dummy98')
3170 output
= check_output('ip -6 route show dev dummy98')
3172 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3173 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3174 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3176 print('### ip -6 route show default')
3177 output
= check_output('ip -6 route show default')
3179 self
.assertIn('default', output
)
3180 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3182 print('### ip -4 route show dev dummy98')
3183 output
= check_output('ip -4 route show dev dummy98')
3185 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3186 self
.assertIn('149.10.124.64 proto static scope link', output
)
3187 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3188 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3189 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3190 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3191 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3192 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3194 print('### ip -4 route show dev dummy98 default')
3195 output
= check_output('ip -4 route show dev dummy98 default')
3197 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3198 self
.assertIn('default via 149.10.124.64 proto static', output
)
3199 self
.assertIn('default proto static', output
)
3200 self
.assertIn('default via 1.1.8.104 proto static', output
)
3202 print('### ip -4 route show table local dev dummy98')
3203 output
= check_output('ip -4 route show table local dev dummy98')
3205 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3206 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3207 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3209 print('### ip -4 route show type blackhole')
3210 output
= check_output('ip -4 route show type blackhole')
3212 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3214 print('### ip -4 route show type unreachable')
3215 output
= check_output('ip -4 route show type unreachable')
3217 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3219 print('### ip -4 route show type prohibit')
3220 output
= check_output('ip -4 route show type prohibit')
3222 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3224 print('### ip -6 route show type blackhole')
3225 output
= check_output('ip -6 route show type blackhole')
3227 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3229 print('### ip -6 route show type unreachable')
3230 output
= check_output('ip -6 route show type unreachable')
3232 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3234 print('### ip -6 route show type prohibit')
3235 output
= check_output('ip -6 route show type prohibit')
3237 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3239 print('### ip route show 192.168.10.1')
3240 output
= check_output('ip route show 192.168.10.1')
3242 self
.assertIn('192.168.10.1 proto static', output
)
3243 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3244 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3245 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3246 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3248 print('### ip route show 192.168.10.2')
3249 output
= check_output('ip route show 192.168.10.2')
3251 # old ip command does not show IPv6 gateways...
3252 self
.assertIn('192.168.10.2 proto static', output
)
3253 self
.assertIn('nexthop', output
)
3254 self
.assertIn('dev test1 weight 20', output
)
3255 self
.assertIn('dev test1 weight 30', output
)
3256 self
.assertIn('dev dummy98 weight 10', output
)
3257 self
.assertIn('dev dummy98 weight 5', output
)
3259 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3260 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3262 # old ip command does not show 'nexthop' keyword and weight...
3263 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3264 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3265 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3266 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3267 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3269 check_json(networkctl_json())
3271 copy_network_unit('25-address-static.network', copy_dropins
=False)
3273 self
.wait_online('dummy98:routable')
3275 # check all routes managed by Manager are removed
3276 print('### ip -4 route show type blackhole')
3277 output
= check_output('ip -4 route show type blackhole')
3279 self
.assertEqual(output
, '')
3281 print('### ip -4 route show type unreachable')
3282 output
= check_output('ip -4 route show type unreachable')
3284 self
.assertEqual(output
, '')
3286 print('### ip -4 route show type prohibit')
3287 output
= check_output('ip -4 route show type prohibit')
3289 self
.assertEqual(output
, '')
3291 print('### ip -6 route show type blackhole')
3292 output
= check_output('ip -6 route show type blackhole')
3294 self
.assertEqual(output
, '')
3296 print('### ip -6 route show type unreachable')
3297 output
= check_output('ip -6 route show type unreachable')
3299 self
.assertEqual(output
, '')
3301 print('### ip -6 route show type prohibit')
3302 output
= check_output('ip -6 route show type prohibit')
3304 self
.assertEqual(output
, '')
3306 remove_network_unit('25-address-static.network')
3308 self
.wait_online('dummy98:routable')
3310 # check all routes managed by Manager are reconfigured
3311 print('### ip -4 route show type blackhole')
3312 output
= check_output('ip -4 route show type blackhole')
3314 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3316 print('### ip -4 route show type unreachable')
3317 output
= check_output('ip -4 route show type unreachable')
3319 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3321 print('### ip -4 route show type prohibit')
3322 output
= check_output('ip -4 route show type prohibit')
3324 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3326 print('### ip -6 route show type blackhole')
3327 output
= check_output('ip -6 route show type blackhole')
3329 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3331 print('### ip -6 route show type unreachable')
3332 output
= check_output('ip -6 route show type unreachable')
3334 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3336 print('### ip -6 route show type prohibit')
3337 output
= check_output('ip -6 route show type prohibit')
3339 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3341 remove_link('dummy98')
3344 # check all routes managed by Manager are removed
3345 print('### ip -4 route show type blackhole')
3346 output
= check_output('ip -4 route show type blackhole')
3348 self
.assertEqual(output
, '')
3350 print('### ip -4 route show type unreachable')
3351 output
= check_output('ip -4 route show type unreachable')
3353 self
.assertEqual(output
, '')
3355 print('### ip -4 route show type prohibit')
3356 output
= check_output('ip -4 route show type prohibit')
3358 self
.assertEqual(output
, '')
3360 print('### ip -6 route show type blackhole')
3361 output
= check_output('ip -6 route show type blackhole')
3363 self
.assertEqual(output
, '')
3365 print('### ip -6 route show type unreachable')
3366 output
= check_output('ip -6 route show type unreachable')
3368 self
.assertEqual(output
, '')
3370 print('### ip -6 route show type prohibit')
3371 output
= check_output('ip -6 route show type prohibit')
3373 self
.assertEqual(output
, '')
3375 def test_route_static(self
):
3377 for manage_foreign_routes
in [True, False]:
3383 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3384 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3385 self
._test
_route
_static
(manage_foreign_routes
)
3387 @expectedFailureIfRTA_VIAIsNotSupported()
3388 def test_route_via_ipv6(self
):
3389 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3391 self
.wait_online('dummy98:routable')
3393 output
= networkctl_status('dummy98')
3396 print('### ip -6 route show dev dummy98')
3397 output
= check_output('ip -6 route show dev dummy98')
3399 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3400 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3402 print('### ip -4 route show dev dummy98')
3403 output
= check_output('ip -4 route show dev dummy98')
3405 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3406 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3408 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3409 def test_route_congctl(self
):
3410 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3412 self
.wait_online('dummy98:routable')
3414 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3415 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3417 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3418 self
.assertIn('congctl dctcp', output
)
3420 print('### ip -4 route show dev dummy98 149.10.124.66')
3421 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3423 self
.assertIn('149.10.124.66 proto static', output
)
3424 self
.assertIn('congctl dctcp', output
)
3425 self
.assertIn('rto_min 300s', output
)
3427 @expectedFailureIfModuleIsNotAvailable('vrf')
3428 def test_route_vrf(self
):
3429 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3430 '25-vrf.netdev', '25-vrf.network')
3432 self
.wait_online('dummy98:routable', 'vrf99:carrier')
3434 output
= check_output('ip route show vrf vrf99')
3436 self
.assertRegex(output
, 'default via 192.168.100.1')
3438 output
= check_output('ip route show')
3440 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3442 def test_gateway_reconfigure(self
):
3443 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3445 self
.wait_online('dummy98:routable')
3446 print('### ip -4 route show dev dummy98 default')
3447 output
= check_output('ip -4 route show dev dummy98 default')
3449 self
.assertIn('default via 149.10.124.59 proto static', output
)
3450 self
.assertNotIn('149.10.124.60', output
)
3452 remove_network_unit('25-gateway-static.network')
3453 copy_network_unit('25-gateway-next-static.network')
3455 self
.wait_online('dummy98:routable')
3456 print('### ip -4 route show dev dummy98 default')
3457 output
= check_output('ip -4 route show dev dummy98 default')
3459 self
.assertNotIn('149.10.124.59', output
)
3460 self
.assertIn('default via 149.10.124.60 proto static', output
)
3462 def test_ip_route_ipv6_src_route(self
):
3463 # a dummy device does not make the addresses go through tentative state, so we
3464 # reuse a bond from an earlier test, which does make the addresses go through
3465 # tentative state, and do our test on that
3466 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3468 self
.wait_online('dummy98:enslaved', 'bond199:routable')
3470 output
= check_output('ip -6 route list dev bond199')
3472 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3474 def test_route_preferred_source_with_existing_address(self
):
3476 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3481 networkctl_reconfigure('dummy98')
3483 self
.wait_online('dummy98:routable')
3485 output
= check_output('ip -6 route list dev dummy98')
3487 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3489 def test_ip_link_mac_address(self
):
3490 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3492 self
.wait_online('dummy98:degraded')
3494 output
= check_output('ip link show dummy98')
3496 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3498 def test_ip_link_unmanaged(self
):
3499 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3502 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3504 def test_ipv6_address_label(self
):
3505 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3507 self
.wait_online('dummy98:degraded')
3509 output
= check_output('ip addrlabel list')
3511 self
.assertRegex(output
, '2004:da8:1::/64')
3513 def test_ipv6_proxy_ndp(self
):
3514 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3517 self
.wait_online('dummy98:routable')
3519 output
= check_output('ip neighbor show proxy dev dummy98')
3521 for i
in range(1, 5):
3522 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3524 def test_ipv6_neigh_retrans_time(self
):
3526 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3529 self
.wait_online(f
'{link}:degraded')
3530 remove_network_unit('25-dummy.network')
3532 # expect retrans_time_ms updated
3533 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3535 self
.wait_online(f
'{link}:degraded')
3536 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3537 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3539 # expect retrans_time_ms unchanged
3540 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3542 self
.wait_online(f
'{link}:degraded')
3543 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3544 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3546 # expect retrans_time_ms unchanged
3547 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3549 self
.wait_online(f
'{link}:degraded')
3550 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3551 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3553 # expect retrans_time_ms unchanged
3554 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3556 self
.wait_online(f
'{link}:degraded')
3557 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3558 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3560 # expect retrans_time_ms unchanged
3561 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3563 self
.wait_online(f
'{link}:degraded')
3564 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3565 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3567 # expect retrans_time_ms updated
3568 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3570 self
.wait_online(f
'{link}:degraded')
3571 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3572 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3574 def test_neighbor(self
):
3575 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3576 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3577 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3580 self
.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
3582 print('### ip neigh list dev gretun97')
3583 output
= check_output('ip neigh list dev gretun97')
3585 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3586 self
.assertNotIn('10.0.0.23', output
)
3588 print('### ip neigh list dev ip6gretun97')
3589 output
= check_output('ip neigh list dev ip6gretun97')
3591 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3592 self
.assertNotIn('2001:db8:0:f102::18', output
)
3594 print('### ip neigh list dev dummy98')
3595 output
= check_output('ip neigh list dev dummy98')
3597 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3598 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3599 self
.assertNotIn('2004:da8:1:0::2', output
)
3600 self
.assertNotIn('192.168.10.2', output
)
3601 self
.assertNotIn('00:00:5e:00:02:67', output
)
3603 check_json(networkctl_json())
3605 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3606 # the valid configurations in 10-step1.conf.
3607 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3609 self
.wait_online('dummy98:degraded')
3611 print('### ip neigh list dev dummy98')
3612 output
= check_output('ip neigh list dev dummy98')
3614 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3615 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3616 self
.assertNotIn('2004:da8:1:0::2', output
)
3617 self
.assertNotIn('192.168.10.2', output
)
3618 self
.assertNotIn('00:00:5e:00:02:67', output
)
3620 check_json(networkctl_json())
3622 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3623 '25-neighbor-dummy.network.d/10-step2.conf')
3624 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3626 self
.wait_online('dummy98:degraded')
3628 print('### ip neigh list dev dummy98')
3629 output
= check_output('ip neigh list dev dummy98')
3631 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3632 self
.assertNotIn('00:00:5e:00:02:65', output
)
3633 self
.assertNotIn('00:00:5e:00:02:66', output
)
3634 self
.assertNotIn('00:00:5e:00:03:65', output
)
3635 self
.assertNotIn('2004:da8:1::1', output
)
3637 def test_link_local_addressing(self
):
3638 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3639 '25-link-local-addressing-no.network', '12-dummy.netdev')
3641 self
.wait_online('test1:degraded', 'dummy98:carrier')
3643 output
= check_output('ip address show dev test1')
3645 self
.assertRegex(output
, 'inet .* scope link')
3646 self
.assertRegex(output
, 'inet6 .* scope link')
3648 output
= check_output('ip address show dev dummy98')
3650 self
.assertNotRegex(output
, 'inet6* .* scope link')
3652 # Documentation/networking/ip-sysctl.txt
3654 # addr_gen_mode - INTEGER
3655 # Defines how link-local and autoconf addresses are generated.
3657 # 0: generate address based on EUI64 (default)
3658 # 1: do no generate a link-local address, use EUI64 for addresses generated
3660 # 2: generate stable privacy addresses, using the secret from
3661 # stable_secret (RFC7217)
3662 # 3: generate stable privacy addresses, using a random secret if unset
3664 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3665 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3666 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3668 def test_link_local_addressing_ipv6ll(self
):
3669 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3671 self
.wait_online('dummy98:degraded')
3673 # An IPv6LL address exists by default.
3674 output
= check_output('ip address show dev dummy98')
3676 self
.assertRegex(output
, 'inet6 .* scope link')
3678 copy_network_unit('25-link-local-addressing-no.network')
3680 self
.wait_online('dummy98:carrier')
3682 # Check if the IPv6LL address is removed.
3683 output
= check_output('ip address show dev dummy98')
3685 self
.assertNotRegex(output
, 'inet6 .* scope link')
3687 remove_network_unit('25-link-local-addressing-no.network')
3689 self
.wait_online('dummy98:degraded')
3691 # Check if a new IPv6LL address is assigned.
3692 output
= check_output('ip address show dev dummy98')
3694 self
.assertRegex(output
, 'inet6 .* scope link')
3696 def test_sysctl(self
):
3697 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3698 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3700 self
.wait_online('dummy98:degraded')
3702 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3703 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3704 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3705 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3706 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3707 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3708 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3709 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3710 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3711 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3713 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3715 self
.wait_online('dummy98:degraded')
3717 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3719 def test_sysctl_disable_ipv6(self
):
3720 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3722 print('## Disable ipv6')
3723 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3724 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3727 self
.wait_online('dummy98:routable')
3729 output
= check_output('ip -4 address show dummy98')
3731 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3732 output
= check_output('ip -6 address show dummy98')
3734 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3735 self
.assertRegex(output
, 'inet6 .* scope link')
3736 output
= check_output('ip -4 route show dev dummy98')
3738 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3739 output
= check_output('ip -6 route show default')
3741 self
.assertRegex(output
, 'default')
3742 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3744 remove_link('dummy98')
3746 print('## Enable ipv6')
3747 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3748 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3751 self
.wait_online('dummy98:routable')
3753 output
= check_output('ip -4 address show dummy98')
3755 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3756 output
= check_output('ip -6 address show dummy98')
3758 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3759 self
.assertRegex(output
, 'inet6 .* scope link')
3760 output
= check_output('ip -4 route show dev dummy98')
3762 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3763 output
= check_output('ip -6 route show default')
3765 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3767 def test_bind_carrier(self
):
3768 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3771 # no bound interface.
3772 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3773 output
= check_output('ip address show test1')
3775 self
.assertNotIn('UP,LOWER_UP', output
)
3776 self
.assertIn('DOWN', output
)
3777 self
.assertNotIn('192.168.10', output
)
3779 # add one bound interface. The interface will be up.
3780 check_output('ip link add dummy98 type dummy')
3781 check_output('ip link set dummy98 up')
3782 self
.wait_online('test1:routable')
3783 output
= check_output('ip address show test1')
3785 self
.assertIn('UP,LOWER_UP', output
)
3786 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3788 # add another bound interface. The interface is still up.
3789 check_output('ip link add dummy99 type dummy')
3790 check_output('ip link set dummy99 up')
3791 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3792 output
= check_output('ip address show test1')
3794 self
.assertIn('UP,LOWER_UP', output
)
3795 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3797 # remove one of the bound interfaces. The interface is still up
3798 remove_link('dummy98')
3799 output
= check_output('ip address show test1')
3801 self
.assertIn('UP,LOWER_UP', output
)
3802 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3804 # bring down the remaining bound interface. The interface will be down.
3805 check_output('ip link set dummy99 down')
3806 self
.wait_operstate('test1', 'off')
3807 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3808 output
= check_output('ip address show test1')
3810 self
.assertNotIn('UP,LOWER_UP', output
)
3811 self
.assertIn('DOWN', output
)
3812 self
.assertNotIn('192.168.10', output
)
3814 # bring up the bound interface. The interface will be up.
3815 check_output('ip link set dummy99 up')
3816 self
.wait_online('test1:routable')
3817 output
= check_output('ip address show test1')
3819 self
.assertIn('UP,LOWER_UP', output
)
3820 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3822 # remove the remaining bound interface. The interface will be down.
3823 remove_link('dummy99')
3824 self
.wait_operstate('test1', 'off')
3825 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3826 output
= check_output('ip address show test1')
3828 self
.assertNotIn('UP,LOWER_UP', output
)
3829 self
.assertIn('DOWN', output
)
3830 self
.assertNotIn('192.168.10', output
)
3832 # re-add one bound interface. The interface will be up.
3833 check_output('ip link add dummy98 type dummy')
3834 check_output('ip link set dummy98 up')
3835 self
.wait_online('test1:routable')
3836 output
= check_output('ip address show test1')
3838 self
.assertIn('UP,LOWER_UP', output
)
3839 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3841 def _test_activation_policy(self
, interface
, test
):
3842 conffile
= '25-activation-policy.network'
3844 conffile
= f
'{conffile}.d/{test}.conf'
3845 if interface
== 'vlan99':
3846 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3847 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3850 always
= test
.startswith('always')
3851 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3852 expect_up
= initial_up
3853 next_up
= not expect_up
3855 if test
.endswith('down'):
3856 self
.wait_activated(interface
)
3858 for iteration
in range(4):
3859 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3860 operstate
= 'routable' if expect_up
else 'off'
3861 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3862 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3865 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3866 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3867 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3869 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3872 check_output(f
'ip link set dev {interface} up')
3874 check_output(f
'ip link set dev {interface} down')
3875 expect_up
= initial_up
if always
else next_up
3876 next_up
= not next_up
3880 def test_activation_policy(self
):
3882 for interface
in ['test1', 'vlan99']:
3883 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3889 print(f
'### test_activation_policy(interface={interface}, test={test})')
3890 with self
.subTest(interface
=interface
, test
=test
):
3891 self
._test
_activation
_policy
(interface
, test
)
3893 def _test_activation_policy_required_for_online(self
, policy
, required
):
3894 conffile
= '25-activation-policy.network'
3895 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3897 units
+= [f
'{conffile}.d/{policy}.conf']
3899 units
+= [f
'{conffile}.d/required-{required}.conf']
3900 copy_network_unit(*units
, copy_dropins
=False)
3903 if policy
.endswith('down'):
3904 self
.wait_activated('test1')
3906 if policy
.endswith('down') or policy
== 'manual':
3907 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3909 self
.wait_online('test1')
3911 if policy
== 'always-down':
3912 # if always-down, required for online is forced to no
3915 # otherwise if required for online is specified, it should match that
3916 expected
= required
== 'yes'
3918 # otherwise if only policy specified, required for online defaults to
3919 # true if policy is up, always-up, or bound
3920 expected
= policy
.endswith('up') or policy
== 'bound'
3922 # default is true, if neither are specified
3925 output
= networkctl_status('test1')
3928 yesno
= 'yes' if expected
else 'no'
3929 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3931 def test_activation_policy_required_for_online(self
):
3933 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3934 for required
in ['yes', 'no', '']:
3940 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3941 with self
.subTest(policy
=policy
, required
=required
):
3942 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3944 def test_domain(self
):
3945 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3947 self
.wait_online('dummy98:routable')
3949 output
= networkctl_status('dummy98')
3951 self
.assertRegex(output
, 'Address: 192.168.42.100')
3952 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3953 self
.assertRegex(output
, 'Search Domains: one')
3955 def test_keep_configuration_static(self
):
3956 check_output('ip link add name dummy98 type dummy')
3957 check_output('ip address add 10.1.2.3/16 dev dummy98')
3958 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3959 output
= check_output('ip address show dummy98')
3961 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3962 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3963 output
= check_output('ip route show dev dummy98')
3966 copy_network_unit('24-keep-configuration-static.network')
3968 self
.wait_online('dummy98:routable')
3970 output
= check_output('ip address show dummy98')
3972 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3973 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3975 def check_nexthop(self
, manage_foreign_nexthops
, first
):
3976 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
3978 output
= check_output('ip nexthop list dev veth99')
3981 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3982 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3984 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
3985 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
3986 self
.assertIn('id 3 dev veth99', output
)
3987 self
.assertIn('id 4 dev veth99', output
)
3989 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
3991 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
3992 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
3993 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
3994 if manage_foreign_nexthops
:
3995 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
3997 output
= check_output('ip nexthop list dev dummy98')
4000 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4002 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4003 if manage_foreign_nexthops
:
4004 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4006 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4008 # kernel manages blackhole nexthops on lo
4009 output
= check_output('ip nexthop list dev lo')
4012 self
.assertIn('id 6 blackhole', output
)
4013 self
.assertIn('id 7 blackhole', output
)
4015 self
.assertIn('id 1 blackhole', output
)
4016 self
.assertIn('id 2 blackhole', output
)
4018 # group nexthops are shown with -0 option
4020 output
= check_output('ip -0 nexthop list id 21')
4022 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4024 output
= check_output('ip -0 nexthop list id 20')
4026 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4028 output
= check_output('ip route show dev veth99 10.10.10.10')
4031 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4033 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4035 output
= check_output('ip route show dev veth99 10.10.10.11')
4038 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4040 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4042 output
= check_output('ip route show dev veth99 10.10.10.12')
4045 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4047 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4049 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4052 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4054 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4056 output
= check_output('ip route show 10.10.10.13')
4059 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4061 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4063 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4066 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4068 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4070 output
= check_output('ip route show 10.10.10.14')
4073 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4074 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4076 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4077 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4078 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4080 output
= networkctl_json()
4082 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4084 def _test_nexthop(self
, manage_foreign_nexthops
):
4085 if not manage_foreign_nexthops
:
4086 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4088 check_output('ip link add dummy98 type dummy')
4089 check_output('ip link set dummy98 up')
4090 check_output('ip address add 192.168.20.20/24 dev dummy98')
4091 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4093 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4094 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4097 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4099 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4100 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4102 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4104 remove_network_unit('25-nexthop-2.network')
4105 copy_network_unit('25-nexthop-nothing.network')
4107 self
.wait_online('veth99:routable', 'veth-peer:routable')
4109 output
= check_output('ip nexthop list dev veth99')
4111 self
.assertEqual(output
, '')
4112 output
= check_output('ip nexthop list dev lo')
4114 self
.assertEqual(output
, '')
4116 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4117 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4118 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4119 # here to test reconfiguring with different .network files does not trigger race.
4120 # See also comments in link_drop_requests().
4121 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4122 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4124 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4126 # Remove nexthop with ID 20
4127 check_output('ip nexthop del id 20')
4128 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4131 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4132 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4133 # hence test1 should be stuck in the configuring state.
4134 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4136 # Wait for a while, and check if the interface is still in the configuring state.
4138 output
= networkctl_status('test1')
4139 self
.assertIn('State: routable (configuring)', output
)
4141 # Check if the route which needs nexthop 20 and 21 are forgotten.
4142 output
= networkctl_json()
4144 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4146 # Reconfigure the interface that has nexthop with ID 20 and 21,
4147 # then the route requested by test1 can be configured.
4148 networkctl_reconfigure('dummy98')
4149 self
.wait_online('test1:routable')
4151 # Check if the requested route actually configured.
4152 output
= check_output('ip route show 10.10.11.10')
4154 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4155 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4156 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4158 remove_link('veth99')
4161 output
= check_output('ip nexthop list dev lo')
4163 self
.assertEqual(output
, '')
4165 @expectedFailureIfNexthopIsNotAvailable()
4166 def test_nexthop(self
):
4168 for manage_foreign_nexthops
in [True, False]:
4174 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4175 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4176 self
._test
_nexthop
(manage_foreign_nexthops
)
4178 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4186 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4187 def test_qdisc_cake(self
):
4188 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4190 self
.wait_online('dummy98:routable')
4192 output
= check_output('tc qdisc show dev dummy98')
4194 self
.assertIn('qdisc cake 3a: root', output
)
4195 self
.assertIn('bandwidth 500Mbit', output
)
4196 self
.assertIn('autorate-ingress', output
)
4197 self
.assertIn('diffserv8', output
)
4198 self
.assertIn('dual-dsthost', output
)
4199 self
.assertIn(' nat', output
)
4200 self
.assertIn(' wash', output
)
4201 self
.assertIn(' split-gso', output
)
4202 self
.assertIn(' raw', output
)
4203 self
.assertIn(' atm', output
)
4204 self
.assertIn('overhead 128', output
)
4205 self
.assertIn('mpu 20', output
)
4206 self
.assertIn('fwmark 0xff00', output
)
4207 self
.assertIn('rtt 1s', output
)
4208 self
.assertIn('ack-filter-aggressive', output
)
4210 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4211 def test_qdisc_codel(self
):
4212 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4214 self
.wait_online('dummy98:routable')
4216 output
= check_output('tc qdisc show dev dummy98')
4218 self
.assertRegex(output
, 'qdisc codel 33: root')
4219 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4221 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4222 def test_qdisc_drr(self
):
4223 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4225 self
.wait_online('dummy98:routable')
4227 output
= check_output('tc qdisc show dev dummy98')
4229 self
.assertRegex(output
, 'qdisc drr 2: root')
4230 output
= check_output('tc class show dev dummy98')
4232 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4234 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4235 def test_qdisc_ets(self
):
4236 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4238 self
.wait_online('dummy98:routable')
4240 output
= check_output('tc qdisc show dev dummy98')
4243 self
.assertRegex(output
, 'qdisc ets 3a: root')
4244 self
.assertRegex(output
, 'bands 10 strict 3')
4245 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4246 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4248 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4249 def test_qdisc_fq(self
):
4250 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4252 self
.wait_online('dummy98:routable')
4254 output
= check_output('tc qdisc show dev dummy98')
4256 self
.assertRegex(output
, 'qdisc fq 32: root')
4257 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4258 self
.assertRegex(output
, 'quantum 1500')
4259 self
.assertRegex(output
, 'initial_quantum 13000')
4260 self
.assertRegex(output
, 'maxrate 1Mbit')
4262 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4263 def test_qdisc_fq_codel(self
):
4264 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4266 self
.wait_online('dummy98:routable')
4268 output
= check_output('tc qdisc show dev dummy98')
4270 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4271 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')
4273 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4274 def test_qdisc_fq_pie(self
):
4275 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4277 self
.wait_online('dummy98:routable')
4279 output
= check_output('tc qdisc show dev dummy98')
4282 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4283 self
.assertRegex(output
, 'limit 200000p')
4285 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4286 def test_qdisc_gred(self
):
4287 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4289 self
.wait_online('dummy98:routable')
4291 output
= check_output('tc qdisc show dev dummy98')
4293 self
.assertRegex(output
, 'qdisc gred 38: root')
4294 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4296 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4297 def test_qdisc_hhf(self
):
4298 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4300 self
.wait_online('dummy98:routable')
4302 output
= check_output('tc qdisc show dev dummy98')
4304 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4305 self
.assertRegex(output
, 'limit 1022p')
4307 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4308 def test_qdisc_htb_fifo(self
):
4309 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4311 self
.wait_online('dummy98:routable')
4313 output
= check_output('tc qdisc show dev dummy98')
4315 self
.assertRegex(output
, 'qdisc htb 2: root')
4316 self
.assertRegex(output
, r
'default (0x30|30)')
4318 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4319 self
.assertRegex(output
, 'limit 100000p')
4321 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4322 self
.assertRegex(output
, 'limit 1000000')
4324 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4325 self
.assertRegex(output
, 'limit 1023p')
4327 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4329 output
= check_output('tc -d class show dev dummy98')
4331 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4332 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4333 # which is fixed in v6.3.0 by
4334 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4335 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4336 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4337 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4338 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4339 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4340 self
.assertRegex(output
, 'burst 123456')
4341 self
.assertRegex(output
, 'cburst 123457')
4343 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4344 def test_qdisc_ingress(self
):
4345 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4346 '25-qdisc-ingress.network', '11-dummy.netdev')
4348 self
.wait_online('dummy98:routable', 'test1:routable')
4350 output
= check_output('tc qdisc show dev dummy98')
4352 self
.assertRegex(output
, 'qdisc clsact')
4354 output
= check_output('tc qdisc show dev test1')
4356 self
.assertRegex(output
, 'qdisc ingress')
4358 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4359 def test_qdisc_netem(self
):
4360 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4361 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4363 self
.wait_online('dummy98:routable', 'test1:routable')
4365 output
= check_output('tc qdisc show dev dummy98')
4367 self
.assertRegex(output
, 'qdisc netem 30: root')
4368 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4370 output
= check_output('tc qdisc show dev test1')
4372 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4373 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4375 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4376 def test_qdisc_pie(self
):
4377 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4379 self
.wait_online('dummy98:routable')
4381 output
= check_output('tc qdisc show dev dummy98')
4383 self
.assertRegex(output
, 'qdisc pie 3a: root')
4384 self
.assertRegex(output
, 'limit 200000')
4386 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4387 def test_qdisc_qfq(self
):
4388 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4390 self
.wait_online('dummy98:routable')
4392 output
= check_output('tc qdisc show dev dummy98')
4394 self
.assertRegex(output
, 'qdisc qfq 2: root')
4395 output
= check_output('tc class show dev dummy98')
4397 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4398 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4400 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4401 def test_qdisc_sfb(self
):
4402 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4404 self
.wait_online('dummy98:routable')
4406 output
= check_output('tc qdisc show dev dummy98')
4408 self
.assertRegex(output
, 'qdisc sfb 39: root')
4409 self
.assertRegex(output
, 'limit 200000')
4411 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4412 def test_qdisc_sfq(self
):
4413 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4415 self
.wait_online('dummy98:routable')
4417 output
= check_output('tc qdisc show dev dummy98')
4419 self
.assertRegex(output
, 'qdisc sfq 36: root')
4420 self
.assertRegex(output
, 'perturb 5sec')
4422 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4423 def test_qdisc_tbf(self
):
4424 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4426 self
.wait_online('dummy98:routable')
4428 output
= check_output('tc qdisc show dev dummy98')
4430 self
.assertRegex(output
, 'qdisc tbf 35: root')
4431 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4433 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4434 def test_qdisc_teql(self
):
4435 call_quiet('rmmod sch_teql')
4437 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4439 self
.wait_links('dummy98')
4440 check_output('modprobe sch_teql max_equalizers=2')
4441 self
.wait_online('dummy98:routable')
4443 output
= check_output('tc qdisc show dev dummy98')
4445 self
.assertRegex(output
, 'qdisc teql1 31: root')
4447 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4455 def test_state_file(self
):
4456 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4458 self
.wait_online('dummy98:routable')
4460 # make link state file updated
4461 resolvectl('revert', 'dummy98')
4463 check_json(networkctl_json())
4465 output
= read_link_state_file('dummy98')
4467 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4468 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4469 self
.assertIn('ADMIN_STATE=configured', output
)
4470 self
.assertIn('OPER_STATE=routable', output
)
4471 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4472 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4473 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4474 self
.assertIn('ACTIVATION_POLICY=up', output
)
4475 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4476 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4477 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4478 self
.assertIn('DOMAINS=hogehoge', output
)
4479 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4480 self
.assertIn('LLMNR=no', output
)
4481 self
.assertIn('MDNS=yes', output
)
4482 self
.assertIn('DNSSEC=no', output
)
4484 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4485 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4486 resolvectl('llmnr', 'dummy98', 'yes')
4487 resolvectl('mdns', 'dummy98', 'no')
4488 resolvectl('dnssec', 'dummy98', 'yes')
4489 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4491 check_json(networkctl_json())
4493 output
= read_link_state_file('dummy98')
4495 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4496 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4497 self
.assertIn('DOMAINS=hogehogehoge', output
)
4498 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4499 self
.assertIn('LLMNR=yes', output
)
4500 self
.assertIn('MDNS=no', output
)
4501 self
.assertIn('DNSSEC=yes', output
)
4503 timedatectl('revert', 'dummy98')
4505 check_json(networkctl_json())
4507 output
= read_link_state_file('dummy98')
4509 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4510 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4511 self
.assertIn('DOMAINS=hogehogehoge', output
)
4512 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4513 self
.assertIn('LLMNR=yes', output
)
4514 self
.assertIn('MDNS=no', output
)
4515 self
.assertIn('DNSSEC=yes', output
)
4517 resolvectl('revert', 'dummy98')
4519 check_json(networkctl_json())
4521 output
= read_link_state_file('dummy98')
4523 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4524 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4525 self
.assertIn('DOMAINS=hogehoge', output
)
4526 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4527 self
.assertIn('LLMNR=no', output
)
4528 self
.assertIn('MDNS=yes', output
)
4529 self
.assertIn('DNSSEC=no', output
)
4531 def test_address_state(self
):
4532 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4535 self
.wait_online('dummy98:degraded')
4537 output
= read_link_state_file('dummy98')
4538 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4539 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4541 # with a routable IPv4 address
4542 check_output('ip address add 10.1.2.3/16 dev dummy98')
4543 self
.wait_online('dummy98:routable', ipv4
=True)
4544 self
.wait_online('dummy98:routable')
4546 output
= read_link_state_file('dummy98')
4547 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4548 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4550 check_output('ip address del 10.1.2.3/16 dev dummy98')
4552 # with a routable IPv6 address
4553 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4554 self
.wait_online('dummy98:routable', ipv6
=True)
4555 self
.wait_online('dummy98:routable')
4557 output
= read_link_state_file('dummy98')
4558 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4559 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4561 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4569 def test_bond_keep_master(self
):
4570 check_output('ip link add bond199 type bond mode active-backup')
4571 check_output('ip link add dummy98 type dummy')
4572 check_output('ip link set dummy98 master bond199')
4574 copy_network_unit('23-keep-master.network')
4576 self
.wait_online('dummy98:enslaved')
4578 output
= check_output('ip -d link show bond199')
4580 self
.assertRegex(output
, 'active_slave dummy98')
4582 output
= check_output('ip -d link show dummy98')
4584 self
.assertRegex(output
, 'master bond199')
4586 def test_bond_active_slave(self
):
4587 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4589 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4591 output
= check_output('ip -d link show bond199')
4593 self
.assertIn('active_slave dummy98', output
)
4595 # test case for issue #31165.
4596 since
= datetime
.datetime
.now()
4597 networkctl_reconfigure('dummy98')
4598 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4599 self
.assertNotIn('dummy98: Bringing link down', read_networkd_log(since
=since
))
4601 def test_bond_primary_slave(self
):
4602 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4604 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4606 output
= check_output('ip -d link show bond199')
4608 self
.assertIn('primary dummy98', output
)
4611 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4612 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4613 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4614 f
.write(f
'[Link]\nMACAddress={mac}\n')
4617 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4619 output
= check_output('ip -d link show bond199')
4621 self
.assertIn(f
'link/ether {mac}', output
)
4623 def test_bond_operstate(self
):
4624 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4625 '25-bond99.network', '25-bond-slave.network')
4627 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
4629 output
= check_output('ip -d link show dummy98')
4631 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4633 output
= check_output('ip -d link show test1')
4635 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4637 output
= check_output('ip -d link show bond99')
4639 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4641 self
.wait_operstate('dummy98', 'enslaved')
4642 self
.wait_operstate('test1', 'enslaved')
4643 self
.wait_operstate('bond99', 'routable')
4645 check_output('ip link set dummy98 down')
4647 self
.wait_operstate('dummy98', 'off')
4648 self
.wait_operstate('test1', 'enslaved')
4649 self
.wait_operstate('bond99', 'routable')
4651 check_output('ip link set dummy98 up')
4653 self
.wait_operstate('dummy98', 'enslaved')
4654 self
.wait_operstate('test1', 'enslaved')
4655 self
.wait_operstate('bond99', 'routable')
4657 check_output('ip link set dummy98 down')
4658 check_output('ip link set test1 down')
4660 self
.wait_operstate('dummy98', 'off')
4661 self
.wait_operstate('test1', 'off')
4663 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4664 # Huh? Kernel does not recognize that all slave interfaces are down?
4665 # Let's confirm that networkd's operstate is consistent with ip's result.
4666 self
.assertNotRegex(output
, 'NO-CARRIER')
4668 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4676 def test_bridge_mac_none(self
):
4677 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4678 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4680 self
.wait_online('dummy98:enslaved', 'bridge99:degraded')
4682 output
= check_output('ip link show dev dummy98')
4684 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4686 output
= check_output('ip link show dev bridge99')
4688 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4690 def test_bridge_vlan(self
):
4691 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4692 '26-bridge.netdev', '26-bridge-vlan-master.network',
4695 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4697 output
= check_output('bridge vlan show dev test1')
4699 # check if the default VID is removed
4700 self
.assertNotIn('1 Egress Untagged', output
)
4701 for i
in range(1000, 3000):
4703 self
.assertIn(f
'{i} PVID', output
)
4704 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4705 self
.assertIn(f
'{i} Egress Untagged', output
)
4706 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4707 self
.assertIn(f
'{i}', output
)
4709 self
.assertNotIn(f
'{i}', output
)
4711 output
= check_output('bridge vlan show dev bridge99')
4713 # check if the default VID is removed
4714 self
.assertNotIn('1 Egress Untagged', output
)
4715 for i
in range(1000, 3000):
4717 self
.assertIn(f
'{i} PVID', output
)
4718 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4719 self
.assertIn(f
'{i} Egress Untagged', output
)
4720 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4721 self
.assertIn(f
'{i}', output
)
4723 self
.assertNotIn(f
'{i}', output
)
4726 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4727 '26-bridge-vlan-master.network.d/10-override.conf')
4729 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4731 output
= check_output('bridge vlan show dev test1')
4733 for i
in range(1000, 3000):
4735 self
.assertIn(f
'{i} PVID', output
)
4736 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4737 self
.assertIn(f
'{i} Egress Untagged', output
)
4738 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4739 self
.assertIn(f
'{i}', output
)
4741 self
.assertNotIn(f
'{i}', output
)
4743 output
= check_output('bridge vlan show dev bridge99')
4745 for i
in range(1000, 3000):
4747 self
.assertIn(f
'{i} PVID', output
)
4748 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4749 self
.assertIn(f
'{i} Egress Untagged', output
)
4750 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4751 self
.assertIn(f
'{i}', output
)
4753 self
.assertNotIn(f
'{i}', output
)
4755 # Remove several vlan IDs
4756 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4757 '26-bridge-vlan-master.network.d/20-override.conf')
4759 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4761 output
= check_output('bridge vlan show dev test1')
4763 for i
in range(1000, 3000):
4765 self
.assertIn(f
'{i} PVID', output
)
4766 elif i
in range(2012, 2016):
4767 self
.assertIn(f
'{i} Egress Untagged', output
)
4768 elif i
in range(2008, 2014):
4769 self
.assertIn(f
'{i}', output
)
4771 self
.assertNotIn(f
'{i}', output
)
4773 output
= check_output('bridge vlan show dev bridge99')
4775 for i
in range(1000, 3000):
4777 self
.assertIn(f
'{i} PVID', output
)
4778 elif i
in range(2022, 2026):
4779 self
.assertIn(f
'{i} Egress Untagged', output
)
4780 elif i
in range(2018, 2024):
4781 self
.assertIn(f
'{i}', output
)
4783 self
.assertNotIn(f
'{i}', output
)
4785 # Remove all vlan IDs
4786 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4787 '26-bridge-vlan-master.network.d/30-override.conf')
4789 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4791 output
= check_output('bridge vlan show dev test1')
4793 self
.assertNotIn('PVID', output
)
4794 for i
in range(1000, 3000):
4795 self
.assertNotIn(f
'{i}', output
)
4797 output
= check_output('bridge vlan show dev bridge99')
4799 self
.assertNotIn('PVID', output
)
4800 for i
in range(1000, 3000):
4801 self
.assertNotIn(f
'{i}', output
)
4803 def test_bridge_vlan_issue_20373(self
):
4804 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4805 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4806 '21-vlan.netdev', '21-vlan.network')
4808 self
.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
4810 output
= check_output('bridge vlan show dev test1')
4812 self
.assertIn('100 PVID Egress Untagged', output
)
4813 self
.assertIn('560', output
)
4814 self
.assertIn('600', output
)
4816 output
= check_output('bridge vlan show dev bridge99')
4818 self
.assertIn('1 PVID Egress Untagged', output
)
4819 self
.assertIn('100', output
)
4820 self
.assertIn('600', output
)
4822 def test_bridge_mdb(self
):
4823 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4824 '26-bridge.netdev', '26-bridge-mdb-master.network')
4826 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4828 output
= check_output('bridge mdb show dev bridge99')
4830 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4831 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4833 # Old kernel may not support bridge MDB entries on bridge master
4834 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4835 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4836 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4838 def test_bridge_keep_master(self
):
4839 check_output('ip link add bridge99 type bridge')
4840 check_output('ip link set bridge99 up')
4841 check_output('ip link add dummy98 type dummy')
4842 check_output('ip link set dummy98 master bridge99')
4844 copy_network_unit('23-keep-master.network')
4846 self
.wait_online('dummy98:enslaved')
4848 output
= check_output('ip -d link show dummy98')
4850 self
.assertRegex(output
, 'master bridge99')
4851 self
.assertRegex(output
, 'bridge')
4853 output
= check_output('bridge -d link show dummy98')
4855 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4856 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4857 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4858 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4859 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4860 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4861 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4862 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4863 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4864 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4865 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4866 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4868 def test_bridge_property(self
):
4869 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4870 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4871 '25-bridge99.network')
4873 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
4875 output
= check_output('ip -d link show bridge99')
4877 self
.assertIn('mtu 9000 ', output
)
4879 output
= check_output('ip -d link show test1')
4881 self
.assertIn('master bridge99 ', output
)
4882 self
.assertIn('bridge_slave', output
)
4883 self
.assertIn('mtu 9000 ', output
)
4885 output
= check_output('ip -d link show dummy98')
4887 self
.assertIn('master bridge99 ', output
)
4888 self
.assertIn('bridge_slave', output
)
4889 self
.assertIn('mtu 9000 ', output
)
4891 output
= check_output('ip addr show bridge99')
4893 self
.assertIn('192.168.0.15/24', output
)
4895 output
= check_output('bridge -d link show dummy98')
4897 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4898 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4899 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4900 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4901 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4902 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4903 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4904 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4905 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4906 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4907 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4908 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4909 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4911 output
= check_output('bridge -d link show test1')
4913 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4915 check_output('ip address add 192.168.0.16/24 dev bridge99')
4916 output
= check_output('ip addr show bridge99')
4918 self
.assertIn('192.168.0.16/24', output
)
4921 print('### ip -6 route list table all dev bridge99')
4922 output
= check_output('ip -6 route list table all dev bridge99')
4924 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4926 remove_link('test1')
4927 self
.wait_operstate('bridge99', 'routable')
4929 output
= check_output('ip -d link show bridge99')
4931 self
.assertIn('mtu 9000 ', output
)
4933 output
= check_output('ip -d link show dummy98')
4935 self
.assertIn('master bridge99 ', output
)
4936 self
.assertIn('bridge_slave', output
)
4937 self
.assertIn('mtu 9000 ', output
)
4939 remove_link('dummy98')
4940 self
.wait_operstate('bridge99', 'no-carrier')
4942 output
= check_output('ip -d link show bridge99')
4944 # When no carrier, the kernel may reset the MTU
4945 self
.assertIn('NO-CARRIER', output
)
4947 output
= check_output('ip address show bridge99')
4949 self
.assertNotIn('192.168.0.15/24', output
)
4950 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4952 print('### ip -6 route list table all dev bridge99')
4953 output
= check_output('ip -6 route list table all dev bridge99')
4955 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4957 check_output('ip link add dummy98 type dummy')
4958 self
.wait_online('dummy98:enslaved', 'bridge99:routable')
4960 output
= check_output('ip -d link show bridge99')
4962 self
.assertIn('mtu 9000 ', output
)
4964 output
= check_output('ip -d link show dummy98')
4966 self
.assertIn('master bridge99 ', output
)
4967 self
.assertIn('bridge_slave', output
)
4968 self
.assertIn('mtu 9000 ', output
)
4970 def test_bridge_configure_without_carrier(self
):
4971 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4975 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4976 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4977 with self
.subTest(test
=test
):
4978 if test
== 'no-slave':
4979 # bridge has no slaves; it's up but *might* not have carrier
4980 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4981 # due to a bug in the kernel, newly-created bridges are brought up
4982 # *with* carrier, unless they have had any setting changed; e.g.
4983 # their mac set, priority set, etc. Then, they will lose carrier
4984 # as soon as a (down) slave interface is added, and regain carrier
4985 # again once the slave interface is brought up.
4986 #self.check_link_attr('bridge99', 'carrier', '0')
4987 elif test
== 'add-slave':
4988 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4989 self
.check_link_attr('test1', 'operstate', 'down')
4990 check_output('ip link set dev test1 master bridge99')
4991 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
4992 self
.check_link_attr('bridge99', 'carrier', '0')
4993 elif test
== 'slave-up':
4994 # bring up slave, which will have carrier; bridge gains carrier
4995 check_output('ip link set dev test1 up')
4996 self
.wait_online('bridge99:routable')
4997 self
.check_link_attr('bridge99', 'carrier', '1')
4998 elif test
== 'slave-no-carrier':
4999 # drop slave carrier; bridge loses carrier
5000 check_output('ip link set dev test1 carrier off')
5001 self
.wait_online('bridge99:no-carrier:no-carrier')
5002 self
.check_link_attr('bridge99', 'carrier', '0')
5003 elif test
== 'slave-carrier':
5004 # restore slave carrier; bridge gains carrier
5005 check_output('ip link set dev test1 carrier on')
5006 self
.wait_online('bridge99:routable')
5007 self
.check_link_attr('bridge99', 'carrier', '1')
5008 elif test
== 'slave-down':
5009 # bring down slave; bridge loses carrier
5010 check_output('ip link set dev test1 down')
5011 self
.wait_online('bridge99:no-carrier:no-carrier')
5012 self
.check_link_attr('bridge99', 'carrier', '0')
5014 output
= networkctl_status('bridge99')
5015 self
.assertRegex(output
, '10.1.2.3')
5016 self
.assertRegex(output
, '10.1.2.1')
5018 def test_bridge_ignore_carrier_loss(self
):
5019 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5020 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5021 '25-bridge99-ignore-carrier-loss.network')
5023 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5025 check_output('ip address add 192.168.0.16/24 dev bridge99')
5026 remove_link('test1', 'dummy98')
5029 output
= check_output('ip address show bridge99')
5031 self
.assertRegex(output
, 'NO-CARRIER')
5032 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5033 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5035 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5036 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5037 '25-bridge99-ignore-carrier-loss.network')
5039 self
.wait_online('bridge99:no-carrier')
5041 for trial
in range(4):
5042 check_output('ip link add dummy98 type dummy')
5043 check_output('ip link set dummy98 up')
5045 remove_link('dummy98')
5047 self
.wait_online('bridge99:routable', 'dummy98:enslaved')
5049 output
= check_output('ip address show bridge99')
5051 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5053 output
= check_output('ip rule list table 100')
5055 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5057 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5065 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5066 def test_sriov(self
):
5067 copy_network_unit('25-default.link', '25-sriov.network')
5069 call('modprobe netdevsim')
5071 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5074 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5078 self
.wait_online('eni99np1:routable')
5080 output
= check_output('ip link show dev eni99np1')
5082 self
.assertRegex(output
,
5083 '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 *'
5084 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5085 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5088 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5089 def test_sriov_udev(self
):
5090 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5092 call('modprobe netdevsim')
5094 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5098 self
.wait_online('eni99np1:routable')
5100 # the name eni99np1 may be an alternative name.
5101 ifname
= link_resolve('eni99np1')
5103 output
= check_output('ip link show dev eni99np1')
5105 self
.assertRegex(output
,
5106 '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 *'
5107 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5108 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5110 self
.assertNotIn('vf 3', output
)
5111 self
.assertNotIn('vf 4', output
)
5113 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5114 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5117 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5119 output
= check_output('ip link show dev eni99np1')
5121 self
.assertRegex(output
,
5122 '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 *'
5123 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5124 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5127 self
.assertNotIn('vf 4', output
)
5129 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5130 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5133 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5135 output
= check_output('ip link show dev eni99np1')
5137 self
.assertRegex(output
,
5138 '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 *'
5139 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5140 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5143 self
.assertNotIn('vf 4', output
)
5145 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5146 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5149 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5151 output
= check_output('ip link show dev eni99np1')
5153 self
.assertRegex(output
,
5154 '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 *'
5155 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5157 self
.assertNotIn('vf 2', output
)
5158 self
.assertNotIn('vf 3', output
)
5159 self
.assertNotIn('vf 4', output
)
5161 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5162 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5165 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5167 output
= check_output('ip link show dev eni99np1')
5169 self
.assertRegex(output
,
5170 '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 *'
5171 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5172 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5174 self
.assertNotIn('vf 3', output
)
5175 self
.assertNotIn('vf 4', output
)
5177 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5185 def test_lldp(self
):
5186 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5188 self
.wait_online('veth99:degraded', 'veth-peer:degraded')
5190 for trial
in range(10):
5194 output
= networkctl('lldp')
5196 if re
.search(r
'veth99 .* veth-peer', output
):
5201 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5209 def test_ipv6_prefix_delegation(self
):
5210 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5211 self
.setup_nftset('addr6', 'ipv6_addr')
5212 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5213 self
.setup_nftset('ifindex', 'iface_index')
5215 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5217 output
= resolvectl('dns', 'veth99')
5219 self
.assertRegex(output
, 'fe80::')
5220 self
.assertRegex(output
, '2002:da8:1::1')
5222 output
= resolvectl('domain', 'veth99')
5224 self
.assertIn('hogehoge.test', output
)
5226 output
= networkctl_status('veth99')
5228 self
.assertRegex(output
, '2002:da8:1:0')
5230 self
.check_netlabel('veth99', '2002:da8:1::/64')
5231 self
.check_netlabel('veth99', '2002:da8:2::/64')
5233 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5234 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5235 self
.check_nftset('network6', '2002:da8:1::/64')
5236 self
.check_nftset('network6', '2002:da8:2::/64')
5237 self
.check_nftset('ifindex', 'veth99')
5239 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5241 def check_ipv6_token_static(self
):
5242 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5244 output
= networkctl_status('veth99')
5246 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5247 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5248 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5249 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5251 def test_ipv6_token_static(self
):
5252 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5255 self
.check_ipv6_token_static()
5258 check_output('ip link set veth99 down')
5259 check_output('ip link set veth99 up')
5261 self
.check_ipv6_token_static()
5264 check_output('ip link set veth99 down')
5265 time
.sleep(random
.uniform(0, 0.1))
5266 check_output('ip link set veth99 up')
5267 time
.sleep(random
.uniform(0, 0.1))
5269 self
.check_ipv6_token_static()
5271 def test_ipv6_token_prefixstable(self
):
5272 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5274 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5276 output
= networkctl_status('veth99')
5278 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5279 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5281 def test_ipv6_token_prefixstable_without_address(self
):
5282 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5284 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5286 output
= networkctl_status('veth99')
5288 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5289 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5291 def check_router_hop_limit(self
, hop_limit
):
5292 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5294 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5296 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5298 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5300 def test_router_hop_limit(self
):
5301 copy_network_unit('25-veth-client.netdev',
5302 '25-veth-router.netdev',
5304 '25-veth-bridge.network',
5305 '25-veth-client.network',
5306 '25-veth-router-hop-limit.network',
5307 '25-bridge99.network')
5309 self
.wait_online('client-p:enslaved',
5310 'router:degraded', 'router-p:enslaved',
5311 'bridge99:routable')
5313 self
.check_router_hop_limit(42)
5315 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5316 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5320 self
.check_router_hop_limit(43)
5322 def test_router_preference(self
):
5323 copy_network_unit('25-veth-client.netdev',
5324 '25-veth-router-high.netdev',
5325 '25-veth-router-low.netdev',
5327 '25-veth-bridge.network',
5328 '25-veth-client.network',
5329 '25-veth-router-high.network',
5330 '25-veth-router-low.network',
5331 '25-bridge99.network')
5333 self
.wait_online('client-p:enslaved',
5334 'router-high:degraded', 'router-high-p:enslaved',
5335 'router-low:degraded', 'router-low-p:enslaved',
5336 'bridge99:routable')
5338 networkctl_reconfigure('client')
5339 self
.wait_online('client:routable')
5341 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5342 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5343 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5344 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5346 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5348 self
.assertIn('metric 512', output
)
5349 self
.assertIn('pref high', output
)
5350 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5352 self
.assertIn('metric 2048', output
)
5353 self
.assertIn('pref low', output
)
5355 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5356 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5359 self
.wait_online('client:routable')
5361 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5362 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5363 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5364 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5366 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5368 self
.assertIn('metric 100', output
)
5369 self
.assertNotIn('metric 512', output
)
5370 self
.assertIn('pref high', output
)
5371 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5373 self
.assertIn('metric 300', output
)
5374 self
.assertNotIn('metric 2048', output
)
5375 self
.assertIn('pref low', output
)
5377 # swap the preference (for issue #28439)
5378 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5379 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5381 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5382 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5384 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5386 self
.assertIn('metric 300', output
)
5387 self
.assertNotIn('metric 100', output
)
5388 self
.assertIn('pref low', output
)
5389 self
.assertNotIn('pref high', output
)
5390 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5392 self
.assertIn('metric 100', output
)
5393 self
.assertNotIn('metric 300', output
)
5394 self
.assertIn('pref high', output
)
5395 self
.assertNotIn('pref low', output
)
5397 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5398 def test_captive_portal(self
):
5399 copy_network_unit('25-veth-client.netdev',
5400 '25-veth-router-captive.netdev',
5402 '25-veth-client-captive.network',
5403 '25-veth-router-captive.network',
5404 '25-veth-bridge-captive.network',
5405 '25-bridge99.network')
5407 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5408 'router-captive:degraded', 'router-captivep:enslaved')
5410 start_radvd(config_file
='captive-portal.conf')
5411 networkctl_reconfigure('client')
5412 self
.wait_online('client:routable')
5414 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5415 output
= networkctl_status('client')
5417 self
.assertIn('Captive Portal: http://systemd.io', output
)
5419 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5420 def test_invalid_captive_portal(self
):
5421 def radvd_write_config(captive_portal_uri
):
5422 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5423 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5425 captive_portal_uris
= [
5426 "42ěščěškd ěšč ě s",
5431 copy_network_unit('25-veth-client.netdev',
5432 '25-veth-router-captive.netdev',
5434 '25-veth-client-captive.network',
5435 '25-veth-router-captive.network',
5436 '25-veth-bridge-captive.network',
5437 '25-bridge99.network')
5439 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5440 'router-captive:degraded', 'router-captivep:enslaved')
5442 for uri
in captive_portal_uris
:
5443 print(f
"Captive portal: {uri}")
5444 radvd_write_config(uri
)
5446 start_radvd(config_file
='bogus-captive-portal.conf')
5447 networkctl_reconfigure('client')
5448 self
.wait_online('client:routable')
5450 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5451 output
= networkctl_status('client')
5453 self
.assertNotIn('Captive Portal:', output
)
5455 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5463 def test_dhcp_server(self
):
5464 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5466 self
.wait_online('veth99:routable', 'veth-peer:routable')
5468 output
= networkctl_status('veth99')
5470 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5471 self
.assertIn('Gateway: 192.168.5.3', output
)
5472 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5473 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5475 output
= networkctl_status('veth-peer')
5476 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5478 def test_dhcp_server_null_server_address(self
):
5479 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5481 self
.wait_online('veth99:routable', 'veth-peer:routable')
5483 output
= check_output('ip --json address show dev veth-peer')
5484 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5485 print(server_address
)
5487 output
= check_output('ip --json address show dev veth99')
5488 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5489 print(client_address
)
5491 output
= networkctl_status('veth99')
5493 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5494 self
.assertIn(f
'Gateway: {server_address}', output
)
5495 self
.assertIn(f
'DNS: {server_address}', output
)
5496 self
.assertIn(f
'NTP: {server_address}', output
)
5498 output
= networkctl_status('veth-peer')
5499 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5501 def test_dhcp_server_with_uplink(self
):
5502 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5503 '12-dummy.netdev', '25-dhcp-server-uplink.network')
5505 self
.wait_online('veth99:routable', 'veth-peer:routable')
5507 output
= networkctl_status('veth99')
5509 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5510 self
.assertIn('Gateway: 192.168.5.3', output
)
5511 self
.assertIn('DNS: 192.168.5.1', output
)
5512 self
.assertIn('NTP: 192.168.5.1', output
)
5514 def test_emit_router_timezone(self
):
5515 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5517 self
.wait_online('veth99:routable', 'veth-peer:routable')
5519 output
= networkctl_status('veth99')
5521 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5522 self
.assertIn('Gateway: 192.168.5.1', output
)
5523 self
.assertIn('Time Zone: Europe/Berlin', output
)
5525 def test_dhcp_server_static_lease(self
):
5526 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5528 self
.wait_online('veth99:routable', 'veth-peer:routable')
5530 output
= networkctl_status('veth99')
5532 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5533 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5535 def test_dhcp_server_static_lease_default_client_id(self
):
5536 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5538 self
.wait_online('veth99:routable', 'veth-peer:routable')
5540 output
= networkctl_status('veth99')
5542 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5543 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5545 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5553 def test_relay_agent(self
):
5554 copy_network_unit('25-agent-veth-client.netdev',
5555 '25-agent-veth-server.netdev',
5556 '25-agent-client.network',
5557 '25-agent-server.network',
5558 '25-agent-client-peer.network',
5559 '25-agent-server-peer.network')
5562 self
.wait_online('client:routable')
5564 output
= networkctl_status('client')
5566 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5568 def test_replay_agent_on_bridge(self
):
5569 copy_network_unit('25-agent-bridge.netdev',
5570 '25-agent-veth-client.netdev',
5571 '25-agent-bridge.network',
5572 '25-agent-bridge-port.network',
5573 '25-agent-client.network')
5575 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5578 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5580 if expect
in read_networkd_log():
5586 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5594 def test_dhcp_client_ipv6_only(self
):
5595 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5598 self
.wait_online('veth-peer:carrier')
5600 # information request mode
5601 # The name ipv6-only option may not be supported by older dnsmasq
5602 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5603 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5604 '--dhcp-option=option6:dns-server,[2600::ee]',
5605 '--dhcp-option=option6:ntp-server,[2600::ff]',
5606 ra_mode
='ra-stateless')
5607 self
.wait_online('veth99:routable', 'veth-peer:routable')
5609 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5610 # Let's wait for the expected DNS server being listed in the state file.
5611 for _
in range(100):
5612 output
= read_link_state_file('veth99')
5613 if 'DNS=2600::ee' in output
:
5617 # Check link state file
5618 print('## link state file')
5619 output
= read_link_state_file('veth99')
5621 self
.assertIn('DNS=2600::ee', output
)
5622 self
.assertIn('NTP=2600::ff', output
)
5624 # Check manager state file
5625 print('## manager state file')
5626 output
= read_manager_state_file()
5628 self
.assertRegex(output
, 'DNS=.*2600::ee')
5629 self
.assertRegex(output
, 'NTP=.*2600::ff')
5631 print('## dnsmasq log')
5632 output
= read_dnsmasq_log_file()
5634 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5635 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5636 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5637 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5638 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5641 check_json(networkctl_json('veth99'))
5645 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5646 '--dhcp-option=option6:dns-server,[2600::ee]',
5647 '--dhcp-option=option6:ntp-server,[2600::ff]')
5648 networkctl_reconfigure('veth99')
5649 self
.wait_online('veth99:routable', 'veth-peer:routable')
5652 output
= check_output('ip address show dev veth99 scope global')
5654 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5655 self
.assertNotIn('192.168.5', output
)
5657 # checking semi-static route
5658 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5660 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5662 # Confirm that ipv6 token is not set in the kernel
5663 output
= check_output('ip token show dev veth99')
5665 self
.assertRegex(output
, 'token :: dev veth99')
5667 # Make manager and link state file updated
5668 resolvectl('revert', 'veth99')
5670 # Check link state file
5671 print('## link state file')
5672 output
= read_link_state_file('veth99')
5674 self
.assertIn('DNS=2600::ee', output
)
5675 self
.assertIn('NTP=2600::ff', output
)
5677 # Check manager state file
5678 print('## manager state file')
5679 output
= read_manager_state_file()
5681 self
.assertRegex(output
, 'DNS=.*2600::ee')
5682 self
.assertRegex(output
, 'NTP=.*2600::ff')
5684 print('## dnsmasq log')
5685 output
= read_dnsmasq_log_file()
5687 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5688 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5689 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5690 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5691 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5692 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5695 check_json(networkctl_json('veth99'))
5697 # Testing without rapid commit support
5698 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5699 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5702 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5703 '--dhcp-option=option6:dns-server,[2600::ee]',
5704 '--dhcp-option=option6:ntp-server,[2600::ff]')
5707 self
.wait_online('veth99:routable', 'veth-peer:routable')
5710 output
= check_output('ip address show dev veth99 scope global')
5712 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5713 self
.assertNotIn('192.168.5', output
)
5715 # checking semi-static route
5716 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5718 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5720 # Make manager and link state file updated
5721 resolvectl('revert', 'veth99')
5723 # Check link state file
5724 print('## link state file')
5725 output
= read_link_state_file('veth99')
5727 self
.assertIn('DNS=2600::ee', output
)
5728 self
.assertIn('NTP=2600::ff', output
)
5730 # Check manager state file
5731 print('## manager state file')
5732 output
= read_manager_state_file()
5734 self
.assertRegex(output
, 'DNS=.*2600::ee')
5735 self
.assertRegex(output
, 'NTP=.*2600::ff')
5737 print('## dnsmasq log')
5738 output
= read_dnsmasq_log_file()
5740 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5741 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5742 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5743 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5744 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5745 self
.assertNotIn('rapid-commit', output
)
5748 check_json(networkctl_json('veth99'))
5750 def test_dhcp_client_ipv6_dbus_status(self
):
5751 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5753 self
.wait_online('veth-peer:carrier')
5755 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5756 # bit set) has yet been received and the configuration does not include WithoutRA=true
5757 state
= get_dhcp6_client_state('veth99')
5758 print(f
"DHCPv6 client state = {state}")
5759 self
.assertEqual(state
, 'stopped')
5761 state
= get_dhcp4_client_state('veth99')
5762 print(f
"DHCPv4 client state = {state}")
5763 self
.assertEqual(state
, 'selecting')
5765 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5766 self
.wait_online('veth99:routable', 'veth-peer:routable')
5768 state
= get_dhcp6_client_state('veth99')
5769 print(f
"DHCPv6 client state = {state}")
5770 self
.assertEqual(state
, 'bound')
5772 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5773 for _
in range(100):
5774 state
= get_dhcp4_client_state('veth99')
5775 if state
== 'stopped':
5779 print(f
"DHCPv4 client state = {state}")
5780 self
.assertEqual(state
, 'stopped')
5782 # restart dnsmasq to clear log
5784 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5786 # Test renew command
5787 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5788 networkctl('renew', 'veth99')
5790 for _
in range(100):
5791 state
= get_dhcp4_client_state('veth99')
5792 if state
== 'stopped':
5796 print(f
"DHCPv4 client state = {state}")
5797 self
.assertEqual(state
, 'stopped')
5799 print('## dnsmasq log')
5800 output
= read_dnsmasq_log_file()
5802 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5803 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5804 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5805 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5807 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5808 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5811 self
.wait_online('veth-peer:carrier')
5813 self
.wait_online('veth99:routable', 'veth-peer:routable')
5816 output
= check_output('ip address show dev veth99 scope global')
5818 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5819 self
.assertNotIn('192.168.5', output
)
5821 print('## dnsmasq log')
5822 output
= read_dnsmasq_log_file()
5824 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5825 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5826 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5827 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5828 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5830 def test_dhcp_client_ipv4_only(self
):
5831 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5833 self
.setup_nftset('addr4', 'ipv4_addr')
5834 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5835 self
.setup_nftset('ifindex', 'iface_index')
5838 self
.wait_online('veth-peer:carrier')
5839 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5840 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5841 '--dhcp-option=option:domain-search,example.com',
5842 '--dhcp-alternate-port=67,5555',
5843 ipv4_range
='192.168.5.110,192.168.5.119')
5844 self
.wait_online('veth99:routable', 'veth-peer:routable')
5845 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5847 print('## ip address show dev veth99 scope global')
5848 output
= check_output('ip address show dev veth99 scope global')
5850 self
.assertIn('mtu 1492', output
)
5851 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5852 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')
5853 self
.assertNotIn('2600::', output
)
5855 output
= check_output('ip -4 --json address show dev veth99')
5856 for i
in json
.loads(output
)[0]['addr_info']:
5857 if i
['label'] == 'test-label':
5858 address1
= i
['local']
5861 self
.assertFalse(True)
5863 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5865 print('## ip route show table main dev veth99')
5866 output
= check_output('ip route show table main dev veth99')
5868 # no DHCP routes assigned to the main table
5869 self
.assertNotIn('proto dhcp', output
)
5871 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5872 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5873 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5874 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5876 print('## ip route show table 211 dev veth99')
5877 output
= check_output('ip route show table 211 dev veth99')
5879 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5880 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5881 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5882 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5883 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5884 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5886 print('## link state file')
5887 output
= read_link_state_file('veth99')
5889 # checking DNS server, SIP server, and Domains
5890 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5891 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5892 self
.assertIn('DOMAINS=example.com', output
)
5895 j
= json
.loads(networkctl_json('veth99'))
5897 self
.assertEqual(len(j
['DNS']), 2)
5900 self
.assertEqual(i
['Family'], 2)
5901 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5902 self
.assertRegex(a
, '^192.168.5.[67]$')
5903 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5904 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5905 self
.assertEqual('192.168.5.1', a
)
5907 self
.assertEqual(len(j
['SIP']), 2)
5910 self
.assertEqual(i
['Family'], 2)
5911 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5912 self
.assertRegex(a
, '^192.168.5.2[12]$')
5913 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5914 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5915 self
.assertEqual('192.168.5.1', a
)
5917 print('## dnsmasq log')
5918 output
= read_dnsmasq_log_file()
5920 self
.assertIn('vendor class: FooBarVendorTest', output
)
5921 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5922 self
.assertIn('client provides name: test-hostname', output
)
5923 self
.assertIn('26:mtu', output
)
5925 # change address range, DNS servers, and Domains
5927 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5928 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5929 '--dhcp-option=option:domain-search,foo.example.com',
5930 '--dhcp-alternate-port=67,5555',
5931 ipv4_range
='192.168.5.120,192.168.5.129',)
5933 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5934 print('Wait for the DHCP lease to be expired')
5935 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5936 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5938 self
.wait_online('veth99:routable', 'veth-peer:routable')
5940 print('## ip address show dev veth99 scope global')
5941 output
= check_output('ip address show dev veth99 scope global')
5943 self
.assertIn('mtu 1492', output
)
5944 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5945 self
.assertNotIn(f
'{address1}', output
)
5946 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')
5947 self
.assertNotIn('2600::', output
)
5949 output
= check_output('ip -4 --json address show dev veth99')
5950 for i
in json
.loads(output
)[0]['addr_info']:
5951 if i
['label'] == 'test-label':
5952 address2
= i
['local']
5955 self
.assertFalse(True)
5957 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5959 print('## ip route show table main dev veth99')
5960 output
= check_output('ip route show table main dev veth99')
5962 # no DHCP routes assigned to the main table
5963 self
.assertNotIn('proto dhcp', output
)
5965 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5966 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5967 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5968 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5970 print('## ip route show table 211 dev veth99')
5971 output
= check_output('ip route show table 211 dev veth99')
5973 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5974 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5975 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5976 self
.assertNotIn('192.168.5.6', output
)
5977 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5978 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5979 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5981 print('## link state file')
5982 output
= read_link_state_file('veth99')
5984 # checking DNS server, SIP server, and Domains
5985 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5986 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5987 self
.assertIn('DOMAINS=foo.example.com', output
)
5990 j
= json
.loads(networkctl_json('veth99'))
5992 self
.assertEqual(len(j
['DNS']), 3)
5995 self
.assertEqual(i
['Family'], 2)
5996 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5997 self
.assertRegex(a
, '^192.168.5.[178]$')
5998 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5999 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6000 self
.assertEqual('192.168.5.1', a
)
6002 self
.assertEqual(len(j
['SIP']), 2)
6005 self
.assertEqual(i
['Family'], 2)
6006 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6007 self
.assertRegex(a
, '^192.168.5.2[34]$')
6008 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6009 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6010 self
.assertEqual('192.168.5.1', a
)
6012 print('## dnsmasq log')
6013 output
= read_dnsmasq_log_file()
6015 self
.assertIn('vendor class: FooBarVendorTest', output
)
6016 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6017 self
.assertIn('client provides name: test-hostname', output
)
6018 self
.assertIn('26:mtu', output
)
6020 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6022 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6023 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6024 self
.check_nftset('ifindex', 'veth99')
6026 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6028 def test_dhcp_client_ipv4_dbus_status(self
):
6029 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6031 self
.wait_online('veth-peer:carrier')
6033 state
= get_dhcp4_client_state('veth99')
6034 print(f
"State = {state}")
6035 self
.assertEqual(state
, 'rebooting')
6037 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6038 '--dhcp-option=option:domain-search,example.com',
6039 '--dhcp-alternate-port=67,5555',
6040 ipv4_range
='192.168.5.110,192.168.5.119')
6041 self
.wait_online('veth99:routable', 'veth-peer:routable')
6042 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6044 state
= get_dhcp4_client_state('veth99')
6045 print(f
"State = {state}")
6046 self
.assertEqual(state
, 'bound')
6048 def test_dhcp_client_allow_list(self
):
6049 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6052 self
.wait_online('veth-peer:carrier')
6053 since
= datetime
.datetime
.now()
6056 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6058 if expect
in read_networkd_log(since
=since
):
6064 copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
6065 since
= datetime
.datetime
.now()
6068 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6070 if expect
in read_networkd_log(since
=since
):
6076 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6077 since
= datetime
.datetime
.now()
6080 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6082 if expect
in read_networkd_log(since
=since
):
6088 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6089 def test_dhcp_client_rapid_commit(self
):
6090 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6092 self
.wait_online('veth-peer:carrier')
6094 start_dnsmasq('--dhcp-rapid-commit')
6095 self
.wait_online('veth99:routable', 'veth-peer:routable')
6096 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6098 state
= get_dhcp4_client_state('veth99')
6099 print(f
"DHCPv4 client state = {state}")
6100 self
.assertEqual(state
, 'bound')
6102 output
= read_dnsmasq_log_file()
6103 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6104 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6105 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6106 self
.assertIn('DHCPACK(veth-peer)', output
)
6108 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6109 copy_network_unit('25-veth.netdev',
6110 '25-dhcp-server-ipv6-only-mode.network',
6111 '25-dhcp-client-ipv6-only-mode.network')
6113 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6114 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6116 state
= get_dhcp4_client_state('veth99')
6117 print(f
"State = {state}")
6118 self
.assertEqual(state
, 'bound')
6120 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6122 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6128 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6129 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6130 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6132 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6133 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6134 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6135 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6136 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6137 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6138 copy_network_unit(*testunits
, copy_dropins
=False)
6141 self
.wait_online('veth-peer:carrier')
6142 additional_options
= [
6143 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6144 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6145 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6148 additional_options
+= [
6149 '--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'
6151 start_dnsmasq(*additional_options
)
6152 self
.wait_online('veth99:routable', 'veth-peer:routable')
6154 output
= check_output('ip -4 route show dev veth99')
6160 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6161 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6162 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6163 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6164 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6166 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6167 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6168 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6169 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6171 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6172 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6173 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6174 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6175 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6176 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6177 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6178 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6181 if use_gateway
and (not classless
or not use_routes
):
6182 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6184 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6186 # Check route to gateway
6187 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6188 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6190 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6192 # Check RoutesToDNS= and RoutesToNTP=
6193 if dns_and_ntp_routes
:
6194 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6195 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6198 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6199 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6201 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6202 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6204 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6205 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6207 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6208 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6209 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6210 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6212 check_json(networkctl_json())
6214 def test_dhcp_client_settings_anonymize(self
):
6215 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6217 self
.wait_online('veth-peer:carrier')
6219 self
.wait_online('veth99:routable', 'veth-peer:routable')
6221 print('## dnsmasq log')
6222 output
= read_dnsmasq_log_file()
6224 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6225 self
.assertNotIn('test-hostname', output
)
6226 self
.assertNotIn('26:mtu', output
)
6228 def test_dhcp_keep_configuration_dhcp(self
):
6229 copy_network_unit('25-veth.netdev',
6230 '25-dhcp-server-veth-peer.network',
6231 '25-dhcp-client-keep-configuration-dhcp.network')
6233 self
.wait_online('veth-peer:carrier')
6235 self
.wait_online('veth99:routable', 'veth-peer:routable')
6237 output
= check_output('ip address show dev veth99 scope global')
6239 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6240 'valid_lft forever preferred_lft forever')
6242 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6245 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6246 print('Wait for the DHCP lease to be expired')
6249 # The lease address should be kept after the lease expired
6250 output
= check_output('ip address show dev veth99 scope global')
6252 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6253 'valid_lft forever preferred_lft forever')
6257 # The lease address should be kept after networkd stopped
6258 output
= check_output('ip address show dev veth99 scope global')
6260 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6261 'valid_lft forever preferred_lft forever')
6263 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6264 f
.write('[Network]\nDHCP=no\n')
6267 self
.wait_online('veth99:routable', 'veth-peer:routable')
6269 # Still the lease address should be kept after networkd restarted
6270 output
= check_output('ip address show dev veth99 scope global')
6272 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6273 'valid_lft forever preferred_lft forever')
6275 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6276 copy_network_unit('25-veth.netdev',
6277 '25-dhcp-server-veth-peer.network',
6278 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6280 self
.wait_online('veth-peer:carrier')
6282 self
.wait_online('veth99:routable', 'veth-peer:routable')
6284 output
= check_output('ip address show dev veth99 scope global')
6286 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6291 output
= check_output('ip address show dev veth99 scope global')
6293 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6296 self
.wait_online('veth-peer:routable')
6298 output
= check_output('ip address show dev veth99 scope global')
6300 self
.assertNotIn('192.168.5.', output
)
6302 def test_dhcp_client_reuse_address_as_static(self
):
6303 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6305 self
.wait_online('veth-peer:carrier')
6307 self
.wait_online('veth99:routable', 'veth-peer:routable')
6309 # link become 'routable' when at least one protocol provide an valid address.
6310 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6311 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6313 output
= check_output('ip address show dev veth99 scope global')
6314 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6315 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6316 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6317 print(static_network
)
6319 remove_network_unit('25-dhcp-client.network')
6321 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6322 f
.write(static_network
)
6325 self
.wait_online('veth99:routable')
6327 output
= check_output('ip -4 address show dev veth99 scope global')
6329 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6330 'valid_lft forever preferred_lft forever')
6332 output
= check_output('ip -6 address show dev veth99 scope global')
6334 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6335 'valid_lft forever preferred_lft forever')
6337 @expectedFailureIfModuleIsNotAvailable('vrf')
6338 def test_dhcp_client_vrf(self
):
6339 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6340 '25-vrf.netdev', '25-vrf.network')
6342 self
.wait_online('veth-peer:carrier')
6344 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6346 # link become 'routable' when at least one protocol provide an valid address.
6347 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6348 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6350 print('## ip -d link show dev vrf99')
6351 output
= check_output('ip -d link show dev vrf99')
6353 self
.assertRegex(output
, 'vrf table 42')
6355 print('## ip address show vrf vrf99')
6356 output
= check_output('ip address show vrf vrf99')
6358 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6359 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6360 self
.assertRegex(output
, 'inet6 .* scope link')
6362 print('## ip address show dev veth99')
6363 output
= check_output('ip address show dev veth99')
6365 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6366 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6367 self
.assertRegex(output
, 'inet6 .* scope link')
6369 print('## ip route show vrf vrf99')
6370 output
= check_output('ip route show vrf vrf99')
6372 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6373 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6374 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6376 print('## ip route show table main dev veth99')
6377 output
= check_output('ip route show table main dev veth99')
6379 self
.assertEqual(output
, '')
6381 def test_dhcp_client_gateway_onlink_implicit(self
):
6382 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6383 '25-dhcp-client-gateway-onlink-implicit.network')
6385 self
.wait_online('veth-peer:carrier')
6387 self
.wait_online('veth99:routable', 'veth-peer:routable')
6389 output
= networkctl_status('veth99')
6391 self
.assertRegex(output
, '192.168.5')
6393 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6395 self
.assertRegex(output
, 'onlink')
6396 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6398 self
.assertRegex(output
, 'onlink')
6400 def test_dhcp_client_with_ipv4ll(self
):
6401 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6402 '25-dhcp-client-with-ipv4ll.network')
6404 # we need to increase timeout above default, as this will need to wait for
6405 # systemd-networkd to get the dhcpv4 transient failure event
6406 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6408 output
= check_output('ip -4 address show dev veth99')
6410 self
.assertNotIn('192.168.5.', output
)
6411 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6414 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6415 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6416 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')
6417 self
.wait_online('veth99:routable')
6419 output
= check_output('ip -4 address show dev veth99')
6421 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6422 self
.assertNotIn('169.254.', output
)
6423 self
.assertNotIn('scope link', output
)
6426 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6427 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)
6428 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6430 output
= check_output('ip -4 address show dev veth99')
6432 self
.assertNotIn('192.168.5.', output
)
6433 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6435 def test_dhcp_client_use_dns(self
):
6436 def check(self
, ipv4
, ipv6
):
6437 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6438 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6439 f
.write('[DHCPv4]\nUseDNS=')
6440 f
.write('yes' if ipv4
else 'no')
6441 f
.write('\n[DHCPv6]\nUseDNS=')
6442 f
.write('yes' if ipv6
else 'no')
6443 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6446 self
.wait_online('veth99:routable')
6448 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6449 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6450 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6452 # make resolved re-read the link state file
6453 resolvectl('revert', 'veth99')
6455 output
= resolvectl('dns', 'veth99')
6458 self
.assertIn('192.168.5.1', output
)
6460 self
.assertNotIn('192.168.5.1', output
)
6462 self
.assertIn('2600::1', output
)
6464 self
.assertNotIn('2600::1', output
)
6466 check_json(networkctl_json())
6468 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6471 self
.wait_online('veth-peer:carrier')
6472 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6473 '--dhcp-option=option6:dns-server,[2600::1]')
6475 check(self
, True, True)
6476 check(self
, True, False)
6477 check(self
, False, True)
6478 check(self
, False, False)
6480 def test_dhcp_client_use_captive_portal(self
):
6481 def check(self
, ipv4
, ipv6
):
6482 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6483 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6484 f
.write('[DHCPv4]\nUseCaptivePortal=')
6485 f
.write('yes' if ipv4
else 'no')
6486 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6487 f
.write('yes' if ipv6
else 'no')
6488 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6491 self
.wait_online('veth99:routable')
6493 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6494 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6495 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6497 output
= networkctl_status('veth99')
6500 self
.assertIn('Captive Portal: http://systemd.io', output
)
6502 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6504 check_json(networkctl_json())
6506 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6509 self
.wait_online('veth-peer:carrier')
6510 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6511 '--dhcp-option=option6:103,http://systemd.io')
6513 check(self
, True, True)
6514 check(self
, True, False)
6515 check(self
, False, True)
6516 check(self
, False, False)
6518 def test_dhcp_client_reject_captive_portal(self
):
6519 def check(self
, ipv4
, ipv6
):
6520 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6521 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6522 f
.write('[DHCPv4]\nUseCaptivePortal=')
6523 f
.write('yes' if ipv4
else 'no')
6524 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6525 f
.write('yes' if ipv6
else 'no')
6526 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6529 self
.wait_online('veth99:routable')
6531 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6532 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6533 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6535 output
= networkctl_status('veth99')
6537 self
.assertNotIn('Captive Portal: ', output
)
6538 self
.assertNotIn('invalid/url', output
)
6540 check_json(networkctl_json())
6542 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6545 self
.wait_online('veth-peer:carrier')
6546 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6547 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6548 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6550 check(self
, True, True)
6551 check(self
, True, False)
6552 check(self
, False, True)
6553 check(self
, False, False)
6555 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6563 def test_dhcp6pd(self
):
6564 def get_dhcp6_prefix(link
):
6565 description
= get_link_description(link
)
6567 self
.assertIn('DHCPv6Client', description
.keys())
6568 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6570 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6574 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6575 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6576 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6577 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6578 '25-dhcp-pd-downstream-dummy97.network',
6579 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6580 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
6583 self
.setup_nftset('addr6', 'ipv6_addr')
6584 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6585 self
.setup_nftset('ifindex', 'iface_index')
6588 self
.wait_online('veth-peer:routable')
6589 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6590 self
.wait_online('veth99:degraded')
6592 # First, test UseAddress=no and Assign=no (issue #29979).
6593 # Note, due to the bug #29701, this test must be done at first.
6594 print('### ip -6 address show dev veth99 scope global')
6595 output
= check_output('ip -6 address show dev veth99 scope global')
6597 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6599 # Check DBus assigned prefix information to veth99
6600 prefixInfo
= get_dhcp6_prefix('veth99')
6602 self
.assertEqual(len(prefixInfo
), 1)
6603 prefixInfo
= prefixInfo
[0]
6605 self
.assertIn('Prefix', prefixInfo
.keys())
6606 self
.assertIn('PrefixLength', prefixInfo
.keys())
6607 self
.assertIn('PreferredLifetimeUSec', prefixInfo
.keys())
6608 self
.assertIn('ValidLifetimeUSec', prefixInfo
.keys())
6610 self
.assertEqual(prefixInfo
['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6611 self
.assertEqual(prefixInfo
['PrefixLength'], 56)
6612 self
.assertGreater(prefixInfo
['PreferredLifetimeUSec'], 0)
6613 self
.assertGreater(prefixInfo
['ValidLifetimeUSec'], 0)
6615 copy_network_unit('25-dhcp6pd-upstream.network.d/with-address.conf')
6617 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6618 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6620 print('### ip -6 address show dev veth-peer scope global')
6621 output
= check_output('ip -6 address show dev veth-peer scope global')
6623 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6627 # dummy97: 0x01 (The link will appear later)
6629 # dummy99: auto -> 0x02 (No address assignment)
6634 print('### ip -6 address show dev veth99 scope global')
6635 output
= check_output('ip -6 address show dev veth99 scope global')
6638 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6639 # address in IA_PD (Token=static)
6640 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6641 # address in IA_PD (Token=eui64)
6642 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6643 # address in IA_PD (temporary)
6644 # Note that the temporary addresses may appear after the link enters configured state
6645 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')
6647 print('### ip -6 address show dev test1 scope global')
6648 output
= check_output('ip -6 address show dev test1 scope global')
6650 # address in IA_PD (Token=static)
6651 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6652 # address in IA_PD (temporary)
6653 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')
6655 print('### ip -6 address show dev dummy98 scope global')
6656 output
= check_output('ip -6 address show dev dummy98 scope global')
6658 # address in IA_PD (Token=static)
6659 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6660 # address in IA_PD (temporary)
6661 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')
6663 print('### ip -6 address show dev dummy99 scope global')
6664 output
= check_output('ip -6 address show dev dummy99 scope global')
6667 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6669 print('### ip -6 address show dev veth97 scope global')
6670 output
= check_output('ip -6 address show dev veth97 scope global')
6672 # address in IA_PD (Token=static)
6673 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6674 # address in IA_PD (Token=eui64)
6675 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6676 # address in IA_PD (temporary)
6677 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')
6679 print('### ip -6 address show dev veth97-peer scope global')
6680 output
= check_output('ip -6 address show dev veth97-peer scope global')
6682 # NDisc address (Token=static)
6683 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6684 # NDisc address (Token=eui64)
6685 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6686 # NDisc address (temporary)
6687 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')
6689 print('### ip -6 address show dev veth98 scope global')
6690 output
= check_output('ip -6 address show dev veth98 scope global')
6692 # address in IA_PD (Token=static)
6693 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6694 # address in IA_PD (Token=eui64)
6695 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6696 # address in IA_PD (temporary)
6697 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')
6699 print('### ip -6 address show dev veth98-peer scope global')
6700 output
= check_output('ip -6 address show dev veth98-peer scope global')
6702 # NDisc address (Token=static)
6703 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6704 # NDisc address (Token=eui64)
6705 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6706 # NDisc address (temporary)
6707 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')
6709 print('### ip -6 route show type unreachable')
6710 output
= check_output('ip -6 route show type unreachable')
6712 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6714 print('### ip -6 route show dev veth99')
6715 output
= check_output('ip -6 route show dev veth99')
6717 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6719 print('### ip -6 route show dev test1')
6720 output
= check_output('ip -6 route show dev test1')
6722 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6724 print('### ip -6 route show dev dummy98')
6725 output
= check_output('ip -6 route show dev dummy98')
6727 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6729 print('### ip -6 route show dev dummy99')
6730 output
= check_output('ip -6 route show dev dummy99')
6732 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6734 print('### ip -6 route show dev veth97')
6735 output
= check_output('ip -6 route show dev veth97')
6737 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6739 print('### ip -6 route show dev veth97-peer')
6740 output
= check_output('ip -6 route show dev veth97-peer')
6742 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6744 print('### ip -6 route show dev veth98')
6745 output
= check_output('ip -6 route show dev veth98')
6747 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6749 print('### ip -6 route show dev veth98-peer')
6750 output
= check_output('ip -6 route show dev veth98-peer')
6752 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6754 # Test case for a downstream which appears later
6755 check_output('ip link add dummy97 type dummy')
6756 self
.wait_online('dummy97:routable')
6758 print('### ip -6 address show dev dummy97 scope global')
6759 output
= check_output('ip -6 address show dev dummy97 scope global')
6761 # address in IA_PD (Token=static)
6762 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6763 # address in IA_PD (temporary)
6764 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')
6766 print('### ip -6 route show dev dummy97')
6767 output
= check_output('ip -6 route show dev dummy97')
6769 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6771 # Test case for reconfigure
6772 networkctl_reconfigure('dummy98', 'dummy99')
6773 self
.wait_online('dummy98:routable', 'dummy99:degraded')
6775 print('### ip -6 address show dev dummy98 scope global')
6776 output
= check_output('ip -6 address show dev dummy98 scope global')
6778 # address in IA_PD (Token=static)
6779 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6780 # address in IA_PD (temporary)
6781 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')
6783 print('### ip -6 address show dev dummy99 scope global')
6784 output
= check_output('ip -6 address show dev dummy99 scope global')
6787 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6789 print('### ip -6 route show dev dummy98')
6790 output
= check_output('ip -6 route show dev dummy98')
6792 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6794 print('### ip -6 route show dev dummy99')
6795 output
= check_output('ip -6 route show dev dummy99')
6797 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6799 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6801 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6802 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6803 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6804 self
.check_nftset('ifindex', 'dummy98')
6806 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6808 def verify_dhcp4_6rd(self
, tunnel_name
):
6809 print('### ip -4 address show dev veth-peer scope global')
6810 output
= check_output('ip -4 address show dev veth-peer scope global')
6812 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6816 # dummy97: 0x01 (The link will appear later)
6818 # dummy99: auto -> 0x0[23] (No address assignment)
6819 # 6rd-XXX: auto -> 0x0[23]
6824 print('### ip -4 address show dev veth99 scope global')
6825 output
= check_output('ip -4 address show dev veth99 scope global')
6827 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6829 print('### ip -6 address show dev veth99 scope global')
6830 output
= check_output('ip -6 address show dev veth99 scope global')
6832 # address in IA_PD (Token=static)
6833 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6834 # address in IA_PD (Token=eui64)
6835 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6836 # address in IA_PD (temporary)
6837 # Note that the temporary addresses may appear after the link enters configured state
6838 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')
6840 print('### ip -6 address show dev test1 scope global')
6841 output
= check_output('ip -6 address show dev test1 scope global')
6843 # address in IA_PD (Token=static)
6844 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6845 # address in IA_PD (temporary)
6846 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')
6848 print('### ip -6 address show dev dummy98 scope global')
6849 output
= check_output('ip -6 address show dev dummy98 scope global')
6851 # address in IA_PD (Token=static)
6852 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6853 # address in IA_PD (temporary)
6854 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')
6856 print('### ip -6 address show dev dummy99 scope global')
6857 output
= check_output('ip -6 address show dev dummy99 scope global')
6860 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6862 print('### ip -6 address show dev veth97 scope global')
6863 output
= check_output('ip -6 address show dev veth97 scope global')
6865 # address in IA_PD (Token=static)
6866 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6867 # address in IA_PD (Token=eui64)
6868 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6869 # address in IA_PD (temporary)
6870 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')
6872 print('### ip -6 address show dev veth97-peer scope global')
6873 output
= check_output('ip -6 address show dev veth97-peer scope global')
6875 # NDisc address (Token=static)
6876 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6877 # NDisc address (Token=eui64)
6878 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6879 # NDisc address (temporary)
6880 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')
6882 print('### ip -6 address show dev veth98 scope global')
6883 output
= check_output('ip -6 address show dev veth98 scope global')
6885 # address in IA_PD (Token=static)
6886 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6887 # address in IA_PD (Token=eui64)
6888 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6889 # address in IA_PD (temporary)
6890 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')
6892 print('### ip -6 address show dev veth98-peer scope global')
6893 output
= check_output('ip -6 address show dev veth98-peer scope global')
6895 # NDisc address (Token=static)
6896 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6897 # NDisc address (Token=eui64)
6898 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6899 # NDisc address (temporary)
6900 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')
6902 print('### ip -6 route show type unreachable')
6903 output
= check_output('ip -6 route show type unreachable')
6905 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6907 print('### ip -6 route show dev veth99')
6908 output
= check_output('ip -6 route show dev veth99')
6910 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6912 print('### ip -6 route show dev test1')
6913 output
= check_output('ip -6 route show dev test1')
6915 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6917 print('### ip -6 route show dev dummy98')
6918 output
= check_output('ip -6 route show dev dummy98')
6920 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6922 print('### ip -6 route show dev dummy99')
6923 output
= check_output('ip -6 route show dev dummy99')
6925 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6927 print('### ip -6 route show dev veth97')
6928 output
= check_output('ip -6 route show dev veth97')
6930 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6932 print('### ip -6 route show dev veth97-peer')
6933 output
= check_output('ip -6 route show dev veth97-peer')
6935 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6937 print('### ip -6 route show dev veth98')
6938 output
= check_output('ip -6 route show dev veth98')
6940 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6942 print('### ip -6 route show dev veth98-peer')
6943 output
= check_output('ip -6 route show dev veth98-peer')
6945 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6947 print('### ip -6 address show dev dummy97 scope global')
6948 output
= check_output('ip -6 address show dev dummy97 scope global')
6950 # address in IA_PD (Token=static)
6951 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6952 # address in IA_PD (temporary)
6953 self
.wait_address('dummy97', 'inet6 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')
6955 print('### ip -6 route show dev dummy97')
6956 output
= check_output('ip -6 route show dev dummy97')
6958 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6960 print(f
'### ip -d link show dev {tunnel_name}')
6961 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6963 self
.assertIn('link/sit 10.100.100.', output
)
6964 self
.assertIn('local 10.100.100.', output
)
6965 self
.assertIn('ttl 64', output
)
6966 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6967 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6969 print(f
'### ip -6 address show dev {tunnel_name}')
6970 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
6972 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')
6973 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
6975 print(f
'### ip -6 route show dev {tunnel_name}')
6976 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
6978 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
6979 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
6981 print('### ip -6 route show default')
6982 output
= check_output('ip -6 route show default')
6984 self
.assertIn('default', output
)
6985 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
6987 def test_dhcp4_6rd(self
):
6988 def get_dhcp_6rd_prefix(link
):
6989 description
= get_link_description(link
)
6991 self
.assertIn('DHCPv4Client', description
.keys())
6992 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
6994 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
6995 self
.assertIn('Prefix', prefixInfo
.keys())
6996 self
.assertIn('PrefixLength', prefixInfo
.keys())
6997 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
6998 self
.assertIn('BorderRouters', prefixInfo
.keys())
7002 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7003 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7004 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7005 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7006 '25-dhcp-pd-downstream-dummy97.network',
7007 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7008 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7009 '80-6rd-tunnel.network')
7012 self
.wait_online('veth-peer:routable')
7015 # 6rd-prefix: 2001:db8::/32
7016 # br-addresss: 10.0.0.1
7018 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',
7019 ipv4_range
='10.100.100.100,10.100.100.200',
7020 ipv4_router
='10.0.0.1')
7021 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7022 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7024 # Check the DBus interface for assigned prefix information
7025 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7027 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7028 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7029 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7030 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7032 # Test case for a downstream which appears later
7033 check_output('ip link add dummy97 type dummy')
7034 self
.wait_online('dummy97:routable')
7038 for name
in os
.listdir('/sys/class/net/'):
7039 if name
.startswith('6rd-'):
7043 self
.wait_online(f
'{tunnel_name}:routable')
7045 self
.verify_dhcp4_6rd(tunnel_name
)
7047 # Test case for reconfigure
7048 networkctl_reconfigure('dummy98', 'dummy99')
7049 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7051 self
.verify_dhcp4_6rd(tunnel_name
)
7053 print('Wait for the DHCP lease to be renewed/rebind')
7056 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7057 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7059 self
.verify_dhcp4_6rd(tunnel_name
)
7061 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7069 def test_ipv6_route_prefix(self
):
7070 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7071 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7074 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7076 output
= check_output('ip address show dev veth-peer')
7078 self
.assertIn('inet6 2001:db8:0:1:', output
)
7079 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7080 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7082 output
= check_output('ip -6 route show dev veth-peer')
7084 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7085 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7086 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7087 self
.assertIn('2001:db0:fff::/64 via ', output
)
7088 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7089 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7091 output
= check_output('ip address show dev veth99')
7093 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7094 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7095 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7096 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7098 output
= resolvectl('dns', 'veth-peer')
7100 self
.assertRegex(output
, '2001:db8:1:1::2')
7102 output
= resolvectl('domain', 'veth-peer')
7104 self
.assertIn('example.com', output
)
7106 check_json(networkctl_json())
7108 output
= networkctl_json('veth-peer')
7112 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7114 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7115 self
.assertEqual(prefix
, '64:ff9b::')
7117 prefix_length
= pref64
['PrefixLength']
7118 self
.assertEqual(prefix_length
, 96)
7120 def test_ipv6_route_prefix_deny_list(self
):
7121 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7122 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7125 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7127 output
= check_output('ip address show dev veth-peer')
7129 self
.assertIn('inet6 2001:db8:0:1:', output
)
7130 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7132 output
= check_output('ip -6 route show dev veth-peer')
7134 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7135 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7136 self
.assertIn('2001:db0:fff::/64 via ', output
)
7137 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7139 output
= check_output('ip address show dev veth99')
7141 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7142 self
.assertIn('inet6 2001:db8:0:2:', output
)
7144 output
= resolvectl('dns', 'veth-peer')
7146 self
.assertRegex(output
, '2001:db8:1:1::2')
7148 output
= resolvectl('domain', 'veth-peer')
7150 self
.assertIn('example.com', output
)
7152 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7160 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7166 self
.wait_online('dummy98:routable')
7167 self
.check_link_attr('dummy98', 'mtu', mtu
)
7168 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7170 # test normal restart
7172 self
.wait_online('dummy98:routable')
7173 self
.check_link_attr('dummy98', 'mtu', mtu
)
7174 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7177 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7179 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7180 ''' test setting mtu/ipv6_mtu with interface already up '''
7183 # note - changing the device mtu resets the ipv6 mtu
7184 check_output('ip link set up mtu 1501 dev dummy98')
7185 check_output('ip link set up mtu 1500 dev dummy98')
7186 self
.check_link_attr('dummy98', 'mtu', '1500')
7187 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7189 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7191 def test_mtu_network(self
):
7192 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7193 self
.check_mtu('1600')
7195 def test_mtu_netdev(self
):
7196 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7197 # note - MTU set by .netdev happens ONLY at device creation!
7198 self
.check_mtu('1600', reset
=False)
7200 def test_mtu_link(self
):
7201 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7202 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7203 self
.check_mtu('1600', reset
=False)
7205 def test_ipv6_mtu(self
):
7206 ''' set ipv6 mtu without setting device mtu '''
7207 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7208 self
.check_mtu('1500', '1400')
7210 def test_ipv6_mtu_toolarge(self
):
7211 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7212 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7213 self
.check_mtu('1500', '1500')
7215 def test_mtu_network_ipv6_mtu(self
):
7216 ''' set ipv6 mtu and set device mtu via network file '''
7217 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7218 self
.check_mtu('1600', '1550')
7220 def test_mtu_netdev_ipv6_mtu(self
):
7221 ''' set ipv6 mtu and set device mtu via netdev file '''
7222 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7223 self
.check_mtu('1600', '1550', reset
=False)
7225 def test_mtu_link_ipv6_mtu(self
):
7226 ''' set ipv6 mtu and set device mtu via link file '''
7227 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7228 self
.check_mtu('1600', '1550', reset
=False)
7231 if __name__
== '__main__':
7232 parser
= argparse
.ArgumentParser()
7233 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7234 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7235 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7236 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7237 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7238 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7239 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7240 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7241 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7242 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7243 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7244 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7245 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7246 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7247 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7248 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7249 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
)
7250 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7253 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7254 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7255 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7256 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7257 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7258 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7259 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7260 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7261 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7262 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7263 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7264 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7265 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7268 networkd_bin
= ns
.networkd_bin
7270 resolved_bin
= ns
.resolved_bin
7271 if ns
.timesyncd_bin
:
7272 timesyncd_bin
= ns
.timesyncd_bin
7274 udevd_bin
= ns
.udevd_bin
7275 if ns
.wait_online_bin
:
7276 wait_online_bin
= ns
.wait_online_bin
7277 if ns
.networkctl_bin
:
7278 networkctl_bin
= ns
.networkctl_bin
7279 if ns
.resolvectl_bin
:
7280 resolvectl_bin
= ns
.resolvectl_bin
7281 if ns
.timedatectl_bin
:
7282 timedatectl_bin
= ns
.timedatectl_bin
7284 udevadm_bin
= ns
.udevadm_bin
7287 systemd_source_dir
= ns
.source_dir
7289 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7290 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7291 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7293 use_valgrind
= ns
.use_valgrind
7294 enable_debug
= ns
.enable_debug
7295 asan_options
= ns
.asan_options
7296 lsan_options
= ns
.lsan_options
7297 ubsan_options
= ns
.ubsan_options
7298 with_coverage
= ns
.with_coverage
7301 # Do not forget the trailing space.
7302 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7304 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7305 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7306 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7307 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7308 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7311 env
.update({'ASAN_OPTIONS': asan_options
})
7313 env
.update({'LSAN_OPTIONS': lsan_options
})
7315 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7317 env
.update({'SYSTEMD_MEMPOOL': '0'})
7319 wait_online_env
= env
.copy()
7321 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7323 sys
.argv
[1:] = unknown_args
7324 unittest
.main(verbosity
=3)