2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # systemd-networkd tests
5 # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
6 # simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
8 # To run an individual test, specify it as a command line argument in the form
9 # of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
10 # function called test_ipv6_mtu(). To run just that test use:
12 # sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
14 # Similarly, other individual tests can be run, eg.:
16 # sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
37 network_unit_dir
= '/run/systemd/network'
38 networkd_conf_dropin_dir
= '/run/systemd/networkd.conf.d'
39 networkd_ci_temp_dir
= '/run/networkd-ci'
40 udev_rules_dir
= '/run/udev/rules.d'
41 credstore_dir
= '/run/credstore'
43 dnsmasq_pid_file
= '/run/networkd-ci/test-dnsmasq.pid'
44 dnsmasq_log_file
= '/run/networkd-ci/test-dnsmasq.log'
45 dnsmasq_lease_file
= '/run/networkd-ci/test-dnsmasq.lease'
47 isc_dhcpd_pid_file
= '/run/networkd-ci/test-isc-dhcpd.pid'
48 isc_dhcpd_lease_file
= '/run/networkd-ci/test-isc-dhcpd.lease'
50 radvd_pid_file
= '/run/networkd-ci/test-radvd.pid'
52 systemd_lib_paths
= ['/usr/lib/systemd', '/lib/systemd']
53 which_paths
= ':'.join(systemd_lib_paths
+ os
.getenv('PATH', os
.defpath
).lstrip(':').split(':'))
54 systemd_source_dir
= None
56 networkd_bin
= shutil
.which('systemd-networkd', path
=which_paths
)
57 resolved_bin
= shutil
.which('systemd-resolved', path
=which_paths
)
58 timesyncd_bin
= shutil
.which('systemd-timesyncd', path
=which_paths
)
59 udevd_bin
= shutil
.which('systemd-udevd', path
=which_paths
)
60 wait_online_bin
= shutil
.which('systemd-networkd-wait-online', path
=which_paths
)
61 networkctl_bin
= shutil
.which('networkctl', path
=which_paths
)
62 resolvectl_bin
= shutil
.which('resolvectl', path
=which_paths
)
63 timedatectl_bin
= shutil
.which('timedatectl', path
=which_paths
)
64 udevadm_bin
= shutil
.which('udevadm', path
=which_paths
)
65 systemd_udev_rules_build_dir
= None
93 saved_ipv4_rules
= None
94 saved_ipv6_rules
= None
98 if os
.path
.exists(path
):
102 shutil
.rmtree(path
, ignore_errors
=True)
105 shutil
.copy(src
, dst
)
108 shutil
.copytree(src
, dst
, copy_function
=shutil
.copy
)
111 os
.makedirs(path
, exist_ok
=True)
114 pathlib
.Path(path
).touch()
116 # pylint: disable=R1710
117 def check_output(*command
, **kwargs
):
118 # This checks the result and returns stdout (and stderr) on success.
119 command
= command
[0].split() + list(command
[1:])
120 ret
= subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
, **kwargs
)
121 if ret
.returncode
== 0:
122 return ret
.stdout
.rstrip()
123 # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
125 ret
.check_returncode()
127 def call(*command
, **kwargs
):
128 # This returns returncode. stdout and stderr are merged and shown in console
129 command
= command
[0].split() + list(command
[1:])
130 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).returncode
132 def call_check(*command
, **kwargs
):
133 # Same as call() above, but it triggers CalledProcessError if rc != 0
134 command
= command
[0].split() + list(command
[1:])
135 return subprocess
.run(command
, check
=False, universal_newlines
=True, stderr
=subprocess
.STDOUT
, **kwargs
).check_returncode()
137 def call_quiet(*command
, **kwargs
):
138 command
= command
[0].split() + list(command
[1:])
139 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
, **kwargs
).returncode
141 def run(*command
, **kwargs
):
142 # This returns CompletedProcess instance.
143 command
= command
[0].split() + list(command
[1:])
144 return subprocess
.run(command
, check
=False, universal_newlines
=True, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, **kwargs
)
146 def check_json(string
):
149 except json
.JSONDecodeError
:
150 print(f
"String is not a valid JSON: '{string}'")
153 def is_module_available(*module_names
):
154 for module_name
in module_names
:
155 lsmod_output
= check_output('lsmod')
156 module_re
= re
.compile(rf
'^{re.escape(module_name)}\b', re
.MULTILINE
)
157 if not module_re
.search(lsmod_output
) and call_quiet('modprobe', module_name
) != 0:
161 def expectedFailureIfModuleIsNotAvailable(*module_names
):
163 return func
if is_module_available(*module_names
) else unittest
.expectedFailure(func
)
167 def expectedFailureIfERSPANv0IsNotSupported():
168 # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8)
170 rc
= call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 0')
171 remove_link('erspan99')
172 return func
if rc
== 0 else unittest
.expectedFailure(func
)
176 def expectedFailureIfERSPANv2IsNotSupported():
177 # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16)
179 rc
= call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 2')
180 remove_link('erspan99')
181 return func
if rc
== 0 else unittest
.expectedFailure(func
)
185 def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
187 rc
= call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
188 call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
189 return func
if rc
== 0 else unittest
.expectedFailure(func
)
193 def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
195 rc
= call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
196 call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
197 return func
if rc
== 0 else unittest
.expectedFailure(func
)
201 def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
204 if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0:
205 ret
= run('ip rule list from 192.168.100.19 table 7')
206 supported
= ret
.returncode
== 0 and 'uidrange 200-300' in ret
.stdout
207 call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
208 return func
if supported
else unittest
.expectedFailure(func
)
212 def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
214 rc
= call_quiet('ip rule add not from 192.168.100.19 l3mdev')
215 call_quiet('ip rule del not from 192.168.100.19 l3mdev')
216 return func
if rc
== 0 else unittest
.expectedFailure(func
)
220 def expectedFailureIfNexthopIsNotAvailable():
222 rc
= call_quiet('ip nexthop list')
223 return func
if rc
== 0 else unittest
.expectedFailure(func
)
227 def expectedFailureIfRTA_VIAIsNotSupported():
229 call_quiet('ip link add dummy98 type dummy')
230 call_quiet('ip link set up dev dummy98')
231 call_quiet('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98')
232 rc
= call_quiet('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98')
233 remove_link('dummy98')
234 return func
if rc
== 0 else unittest
.expectedFailure(func
)
238 def expectedFailureIfAlternativeNameIsNotAvailable():
240 call_quiet('ip link add dummy98 type dummy')
242 call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \
243 call_quiet('ip link show dev hogehogehogehogehoge') == 0
244 remove_link('dummy98')
245 return func
if supported
else unittest
.expectedFailure(func
)
249 def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
251 def finalize(func
, supported
):
252 call_quiet('rmmod netdevsim')
253 return func
if supported
else unittest
.expectedFailure(func
)
255 call_quiet('rmmod netdevsim')
256 if call_quiet('modprobe netdevsim') != 0:
257 return finalize(func
, False)
260 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
263 return finalize(func
, False)
265 return finalize(func
, os
.path
.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs'))
269 # pylint: disable=C0415
270 def compare_kernel_version(min_kernel_version
):
273 from packaging
import version
275 print('Failed to import either platform or packaging module, assuming the comparison failed')
278 # Get only the actual kernel version without any build/distro/arch stuff
279 # e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
280 kver
= platform
.release().split('-')[0]
281 # Get also rid of '+'
282 kver
= kver
.split('+')[0]
284 return version
.parse(kver
) >= version
.parse(min_kernel_version
)
287 check_output(*udevadm_cmd
, 'control', '--reload')
289 def copy_network_unit(*units
, copy_dropins
=True):
291 Copy networkd unit files into the testbed.
293 Any networkd unit file type can be specified, as well as drop-in files.
295 By default, all drop-ins for a specified unit file are copied in;
296 to avoid that specify dropins=False.
298 When a drop-in file is specified, its unit file is also copied in automatically.
301 mkdir_p(network_unit_dir
)
303 if copy_dropins
and os
.path
.exists(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d')):
304 cp_r(os
.path
.join(networkd_ci_temp_dir
, unit
+ '.d'), os
.path
.join(network_unit_dir
, unit
+ '.d'))
306 if unit
.endswith('.conf'):
308 unit
= os
.path
.dirname(dropin
).rstrip('.d')
309 dropindir
= os
.path
.join(network_unit_dir
, unit
+ '.d')
311 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), dropindir
)
313 cp(os
.path
.join(networkd_ci_temp_dir
, unit
), network_unit_dir
)
315 if unit
.endswith('.link'):
321 def copy_credential(src
, target
):
322 mkdir_p(credstore_dir
)
323 cp(os
.path
.join(networkd_ci_temp_dir
, src
),
324 os
.path
.join(credstore_dir
, target
))
326 def remove_network_unit(*units
):
328 Remove previously copied unit files from the testbed.
330 Drop-ins will be removed automatically.
334 rm_f(os
.path
.join(network_unit_dir
, unit
))
335 rm_rf(os
.path
.join(network_unit_dir
, unit
+ '.d'))
337 if unit
.endswith('.link') or unit
.endswith('.link.d'):
343 def clear_network_units():
345 if os
.path
.exists(network_unit_dir
):
346 units
= os
.listdir(network_unit_dir
)
348 if unit
.endswith('.link') or unit
.endswith('.link.d'):
351 rm_rf(network_unit_dir
)
356 def copy_networkd_conf_dropin(*dropins
):
357 """Copy networkd.conf dropin files into the testbed."""
358 mkdir_p(networkd_conf_dropin_dir
)
359 for dropin
in dropins
:
360 cp(os
.path
.join(networkd_ci_temp_dir
, dropin
), networkd_conf_dropin_dir
)
362 def remove_networkd_conf_dropin(*dropins
):
363 """Remove previously copied networkd.conf dropin files from the testbed."""
364 for dropin
in dropins
:
365 rm_f(os
.path
.join(networkd_conf_dropin_dir
, dropin
))
367 def clear_networkd_conf_dropins():
368 rm_rf(networkd_conf_dropin_dir
)
370 def setup_systemd_udev_rules():
371 if not systemd_udev_rules_build_dir
:
374 mkdir_p(udev_rules_dir
)
376 for path
in [systemd_udev_rules_build_dir
, os
.path
.join(systemd_source_dir
, "rules.d")]:
377 print(f
"Copying udev rules from {path} to {udev_rules_dir}")
379 for rule
in os
.listdir(path
):
380 if not rule
.endswith(".rules"):
382 cp(os
.path
.join(path
, rule
), udev_rules_dir
)
384 def copy_udev_rule(*rules
):
385 """Copy udev rules"""
386 mkdir_p(udev_rules_dir
)
388 cp(os
.path
.join(networkd_ci_temp_dir
, rule
), udev_rules_dir
)
390 def remove_udev_rule(*rules
):
391 """Remove previously copied udev rules"""
393 rm_f(os
.path
.join(udev_rules_dir
, rule
))
395 def clear_udev_rules():
396 rm_rf(udev_rules_dir
)
398 def save_active_units():
399 for u
in ['systemd-networkd.socket', 'systemd-networkd.service',
400 'systemd-resolved.service', 'systemd-timesyncd.service',
401 'firewalld.service']:
402 if call(f
'systemctl is-active --quiet {u}') == 0:
403 call(f
'systemctl stop {u}')
404 active_units
.append(u
)
406 def restore_active_units():
407 if 'systemd-networkd.socket' in active_units
:
408 call('systemctl stop systemd-networkd.socket systemd-networkd.service')
409 for u
in active_units
:
410 call(f
'systemctl restart {u}')
412 def create_unit_dropin(unit
, contents
):
413 mkdir_p(f
'/run/systemd/system/{unit}.d')
414 with
open(f
'/run/systemd/system/{unit}.d/00-override.conf', mode
='w', encoding
='utf-8') as f
:
415 f
.write('\n'.join(contents
))
417 def create_service_dropin(service
, command
, additional_settings
=None):
421 f
'ExecStart=!!{valgrind_cmd}{command}',
424 drop_in
+= ['Environment=SYSTEMD_LOG_LEVEL=debug']
426 drop_in
+= [f
'Environment=ASAN_OPTIONS="{asan_options}"']
428 drop_in
+= [f
'Environment=LSAN_OPTIONS="{lsan_options}"']
430 drop_in
+= [f
'Environment=UBSAN_OPTIONS="{ubsan_options}"']
431 if asan_options
or lsan_options
or ubsan_options
:
432 drop_in
+= ['SystemCallFilter=']
433 if use_valgrind
or asan_options
or lsan_options
or ubsan_options
:
434 drop_in
+= ['MemoryDenyWriteExecute=no']
437 'Environment=SYSTEMD_MEMPOOL=0',
445 if additional_settings
:
446 drop_in
+= additional_settings
448 create_unit_dropin(f
'{service}.service', drop_in
)
450 def link_exists(link
):
451 return call_quiet(f
'ip link show {link}') == 0
453 def link_resolve(link
):
454 return check_output(f
'ip link show {link}').split(':')[1].strip()
456 def remove_link(*links
, protect
=False):
458 if protect
and link
in protected_links
:
460 if link_exists(link
):
461 call(f
'ip link del dev {link}')
463 def save_existing_links():
464 links
= os
.listdir('/sys/class/net')
466 if link_exists(link
):
467 protected_links
.add(link
)
469 print('### The following links will be protected:')
470 print(', '.join(sorted(list(protected_links
))))
473 links
= os
.listdir('/sys/class/net')
474 remove_link(*links
, protect
=True)
476 def flush_nexthops():
477 # Currently, the 'ip nexthop' command does not have 'save' and 'restore'.
478 # Hence, we cannot restore nexthops in a simple way.
479 # Let's assume there is no nexthop used in the system
480 call_quiet('ip nexthop flush')
483 # pylint: disable=global-statement
485 saved_routes
= check_output('ip route show table all')
486 print('### The following routes will be protected:')
491 output
= check_output('ip route show table all')
492 for line
in output
.splitlines():
493 if line
in saved_routes
:
495 if 'proto kernel' in line
:
497 if ' dev ' in line
and not ' dev lo ' in line
:
501 print('### Removing routes that did not exist when the test started.')
503 call(f
'ip route del {line}')
505 def save_routing_policy_rules():
506 # pylint: disable=global-statement
507 global saved_ipv4_rules
, saved_ipv6_rules
509 output
= check_output(f
'ip -{ipv} rule show')
510 print(f
'### The following IPv{ipv} routing policy rules will be protected:')
514 saved_ipv4_rules
= save(4)
515 saved_ipv6_rules
= save(6)
517 def flush_routing_policy_rules():
518 def flush(ipv
, saved_rules
):
520 output
= check_output(f
'ip -{ipv} rule show')
521 for line
in output
.splitlines():
522 if line
in saved_rules
:
526 print(f
'### Removing IPv{ipv} routing policy rules that did not exist when the test started.')
528 words
= line
.replace('lookup [l3mdev-table]', 'l3mdev').split()
529 priority
= words
[0].rstrip(':')
530 call(f
'ip -{ipv} rule del priority {priority} ' + ' '.join(words
[1:]))
532 flush(4, saved_ipv4_rules
)
533 flush(6, saved_ipv6_rules
)
535 def flush_fou_ports():
536 ret
= run('ip fou show')
537 if ret
.returncode
!= 0:
538 return # fou may not be supported
539 for line
in ret
.stdout
.splitlines():
540 port
= line
.split()[1]
541 call(f
'ip fou del port {port}')
543 def flush_l2tp_tunnels():
545 ret
= run('ip l2tp show tunnel')
546 if ret
.returncode
!= 0:
547 return # l2tp may not be supported
548 for line
in ret
.stdout
.splitlines():
550 if words
[0] == 'Tunnel':
551 tid
= words
[1].rstrip(',')
552 call(f
'ip l2tp del tunnel tunnel_id {tid}')
555 # Removing L2TP tunnel is asynchronous and slightly takes a time.
558 r
= run(f
'ip l2tp show tunnel tunnel_id {tid}')
559 if r
.returncode
!= 0 or len(r
.stdout
.rstrip()) == 0:
563 print(f
'Cannot remove L2TP tunnel {tid}, ignoring.')
566 # pylint: disable=global-statement
567 global saved_timezone
568 r
= run(*timedatectl_cmd
, 'show', '--value', '--property', 'Timezone', env
=env
)
569 if r
.returncode
== 0:
570 saved_timezone
= r
.stdout
.rstrip()
571 print(f
'### Saved timezone: {saved_timezone}')
573 def restore_timezone():
575 call(*timedatectl_cmd
, 'set-timezone', f
'{saved_timezone}', env
=env
)
577 def read_link_attr(*args
):
578 with
open(os
.path
.join('/sys/class/net', *args
), encoding
='utf-8') as f
:
579 return f
.readline().strip()
581 def read_manager_state_file():
582 with
open('/run/systemd/netif/state', encoding
='utf-8') as f
:
585 def read_link_state_file(link
):
586 ifindex
= read_link_attr(link
, 'ifindex')
587 path
= os
.path
.join('/run/systemd/netif/links', ifindex
)
588 with
open(path
, encoding
='utf-8') as f
:
591 def read_ip_sysctl_attr(link
, attribute
, ipv
):
592 with
open(os
.path
.join('/proc/sys/net', ipv
, 'conf', link
, attribute
), encoding
='utf-8') as f
:
593 return f
.readline().strip()
595 def read_ip_neigh_sysctl_attr(link
, attribute
, ipv
):
596 with
open(os
.path
.join('/proc/sys/net', ipv
, 'neigh', link
, attribute
), encoding
='utf-8') as f
:
597 return f
.readline().strip()
599 def read_ipv6_sysctl_attr(link
, attribute
):
600 return read_ip_sysctl_attr(link
, attribute
, 'ipv6')
602 def read_ipv6_neigh_sysctl_attr(link
, attribute
):
603 return read_ip_neigh_sysctl_attr(link
, attribute
, 'ipv6')
605 def read_ipv4_sysctl_attr(link
, attribute
):
606 return read_ip_sysctl_attr(link
, attribute
, 'ipv4')
608 def stop_by_pid_file(pid_file
):
609 if not os
.path
.exists(pid_file
):
611 with
open(pid_file
, 'r', encoding
='utf-8') as f
:
612 pid
= f
.read().rstrip(' \t\r\n\0')
613 os
.kill(int(pid
), signal
.SIGTERM
)
617 print(f
"PID {pid} is still alive, waiting...")
620 if e
.errno
== errno
.ESRCH
:
622 print(f
"Unexpected exception when waiting for {pid} to die: {e.errno}")
625 def start_dnsmasq(*additional_options
, interface
='veth-peer', ra_mode
=None, ipv4_range
='192.168.5.10,192.168.5.200', ipv4_router
='192.168.5.1', ipv6_range
='2600::10,2600::20'):
627 ra_mode
= f
',{ra_mode}'
633 f
'--log-facility={dnsmasq_log_file}',
634 '--log-queries=extra',
636 f
'--pid-file={dnsmasq_pid_file}',
637 '--conf-file=/dev/null',
639 f
'--interface={interface}',
640 f
'--dhcp-leasefile={dnsmasq_lease_file}',
642 f
'--dhcp-range={ipv6_range}{ra_mode},2m',
643 f
'--dhcp-range={ipv4_range},2m',
644 '--dhcp-option=option:mtu,1492',
645 f
'--dhcp-option=option:router,{ipv4_router}',
648 ) + additional_options
649 check_output(*command
)
652 stop_by_pid_file(dnsmasq_pid_file
)
653 rm_f(dnsmasq_lease_file
)
654 rm_f(dnsmasq_log_file
)
656 def read_dnsmasq_log_file():
657 with
open(dnsmasq_log_file
, encoding
='utf-8') as f
:
660 def start_isc_dhcpd(conf_file
, ipv
, interface
='veth-peer'):
661 conf_file_path
= os
.path
.join(networkd_ci_temp_dir
, conf_file
)
662 isc_dhcpd_command
= f
'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
663 touch(isc_dhcpd_lease_file
)
664 check_output(isc_dhcpd_command
)
666 def stop_isc_dhcpd():
667 stop_by_pid_file(isc_dhcpd_pid_file
)
668 rm_f(isc_dhcpd_lease_file
)
670 def get_dbus_link_path(link
):
671 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
672 '/org/freedesktop/network1', 'org.freedesktop.network1.Manager',
673 'GetLinkByName', 's', link
])
675 assert out
.startswith(b
'io ')
677 assert out
.endswith(b
'"')
679 return out
[:-1].split('"')[1]
681 def get_dhcp_client_state(link
, family
):
682 link_path
= get_dbus_link_path(link
)
684 out
= subprocess
.check_output(['busctl', 'get-property', 'org.freedesktop.network1',
685 link_path
, f
'org.freedesktop.network1.DHCPv{family}Client', 'State'])
686 assert out
.startswith(b
's "')
688 assert out
.endswith(b
'"')
689 return out
[3:-1].decode()
691 def get_dhcp4_client_state(link
):
692 return get_dhcp_client_state(link
, '4')
694 def get_dhcp6_client_state(link
):
695 return get_dhcp_client_state(link
, '6')
697 def get_link_description(link
):
698 link_path
= get_dbus_link_path(link
)
700 out
= subprocess
.check_output(['busctl', 'call', 'org.freedesktop.network1',
701 link_path
, 'org.freedesktop.network1.Link', 'Describe'])
702 assert out
.startswith(b
's "')
704 assert out
.endswith(b
'"')
705 json_raw
= out
[2:].decode()
707 description
= json
.loads(json_raw
) # Convert from escaped sequences to json
708 check_json(description
)
709 return json
.loads(description
) # Now parse the json
711 def start_radvd(*additional_options
, config_file
):
712 config_file_path
= os
.path
.join(networkd_ci_temp_dir
, 'radvd', config_file
)
715 f
'--pidfile={radvd_pid_file}',
716 f
'--config={config_file_path}',
717 '--logmethod=stderr',
718 ) + additional_options
719 check_output(*command
)
722 stop_by_pid_file(radvd_pid_file
)
724 def radvd_check_config(config_file
):
725 if not shutil
.which('radvd'):
726 print('radvd is not installed, assuming the config check failed')
729 # Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
730 # set up (one instance is @unittest.skipX())
731 config_file_path
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf/radvd', config_file
)
732 return call(f
'radvd --config={config_file_path} --configtest') == 0
734 def networkd_invocation_id():
735 return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
737 def read_networkd_log(invocation_id
=None, since
=None):
738 if not invocation_id
:
739 invocation_id
= networkd_invocation_id()
743 '--output=short-monotonic',
744 f
'_SYSTEMD_INVOCATION_ID={invocation_id}',
747 command
.append(f
'--since={since}')
748 check_output('journalctl --sync')
749 return check_output(*command
)
751 def networkd_is_failed():
752 return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
754 def stop_networkd(show_logs
=True):
756 invocation_id
= networkd_invocation_id()
757 check_output('systemctl stop systemd-networkd.socket')
758 check_output('systemctl stop systemd-networkd.service')
760 print(read_networkd_log(invocation_id
))
761 # Check if networkd exits cleanly.
762 assert not networkd_is_failed()
764 def start_networkd():
765 check_output('systemctl start systemd-networkd')
767 def restart_networkd(show_logs
=True):
769 invocation_id
= networkd_invocation_id()
770 check_output('systemctl restart systemd-networkd.service')
772 print(read_networkd_log(invocation_id
))
775 return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
777 def networkctl(*args
):
778 # Do not call networkctl if networkd is in failed state.
779 # Otherwise, networkd may be restarted and we may get wrong results.
780 assert not networkd_is_failed()
781 return check_output(*(networkctl_cmd
+ list(args
)), env
=env
)
783 def networkctl_status(*args
):
784 return networkctl('-n', '0', 'status', *args
)
786 def networkctl_json(*args
):
787 return networkctl('--json=short', 'status', *args
)
789 def networkctl_reconfigure(*links
):
790 networkctl('reconfigure', *links
)
792 def networkctl_reload():
795 def resolvectl(*args
):
796 return check_output(*(resolvectl_cmd
+ list(args
)), env
=env
)
798 def timedatectl(*args
):
799 return check_output(*(timedatectl_cmd
+ list(args
)), env
=env
)
804 def tear_down_common():
805 # 1. stop DHCP/RA servers
811 call_quiet('rmmod netdevsim')
812 call_quiet('rmmod sch_teql')
814 # 3. remove network namespace
815 call_quiet('ip netns del ns99')
825 clear_network_units()
826 clear_networkd_conf_dropins()
831 flush_routing_policy_rules()
835 rm_rf(networkd_ci_temp_dir
)
836 cp_r(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), 'conf'), networkd_ci_temp_dir
)
838 clear_network_units()
839 clear_networkd_conf_dropins()
842 setup_systemd_udev_rules()
843 copy_udev_rule('00-debug-net.rules')
847 save_existing_links()
849 save_routing_policy_rules()
852 create_service_dropin('systemd-networkd', networkd_bin
,
855 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
857 'StartLimitIntervalSec=0'])
858 create_service_dropin('systemd-resolved', resolved_bin
)
859 create_service_dropin('systemd-timesyncd', timesyncd_bin
)
861 # TODO: also run udevd with sanitizers, valgrind, or coverage
862 #create_service_dropin('systemd-udevd', udevd_bin,
863 # f'{udevadm_bin} control --reload --timeout 0')
865 'systemd-udevd.service',
869 f
'ExecStart=!!@{udevd_bin} systemd-udevd',
873 'systemd-networkd.socket',
876 'StartLimitIntervalSec=0',
880 check_output('systemctl daemon-reload')
881 print(check_output('systemctl cat systemd-networkd.service'))
882 print(check_output('systemctl cat systemd-resolved.service'))
883 print(check_output('systemctl cat systemd-timesyncd.service'))
884 print(check_output('systemctl cat systemd-udevd.service'))
885 check_output('systemctl restart systemd-resolved.service')
886 check_output('systemctl restart systemd-timesyncd.service')
887 check_output('systemctl restart systemd-udevd.service')
889 def tearDownModule():
890 rm_rf(networkd_ci_temp_dir
)
892 clear_network_units()
893 clear_networkd_conf_dropins()
897 rm_rf('/run/systemd/system/systemd-networkd.service.d')
898 rm_rf('/run/systemd/system/systemd-networkd.socket.d')
899 rm_rf('/run/systemd/system/systemd-resolved.service.d')
900 rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
901 rm_rf('/run/systemd/system/systemd-udevd.service.d')
902 check_output('systemctl daemon-reload')
903 check_output('systemctl restart systemd-udevd.service')
904 restore_active_units()
907 # pylint: disable=no-member
909 def check_link_exists(self
, link
, expected
=True):
911 self
.assertTrue(link_exists(link
))
913 self
.assertFalse(link_exists(link
))
915 def check_link_attr(self
, *args
):
916 self
.assertEqual(read_link_attr(*args
[:-1]), args
[-1])
918 def check_bridge_port_attr(self
, master
, port
, attribute
, expected
, allow_enoent
=False):
919 path
= os
.path
.join('/sys/devices/virtual/net', master
, 'lower_' + port
, 'brport', attribute
)
920 if allow_enoent
and not os
.path
.exists(path
):
922 with
open(path
, encoding
='utf-8') as f
:
923 self
.assertEqual(f
.readline().strip(), expected
)
925 def check_ipv4_sysctl_attr(self
, link
, attribute
, expected
):
926 self
.assertEqual(read_ipv4_sysctl_attr(link
, attribute
), expected
)
928 def check_ipv6_sysctl_attr(self
, link
, attribute
, expected
):
929 self
.assertEqual(read_ipv6_sysctl_attr(link
, attribute
), expected
)
931 def check_ipv6_neigh_sysctl_attr(self
, link
, attribute
, expected
):
932 self
.assertEqual(read_ipv6_neigh_sysctl_attr(link
, attribute
), expected
)
934 def wait_links(self
, *links
, timeout
=20, fail_assert
=True):
935 def links_exist(*links
):
937 if not link_exists(link
):
941 for iteration
in range(timeout
+ 1):
945 if links_exist(*links
):
948 self
.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links
)))
951 def wait_activated(self
, link
, state
='down', timeout
=20, fail_assert
=True):
952 # wait for the interface is activated.
953 needle
= f
'{link}: Bringing link {state}'
955 for iteration
in range(timeout
+ 1):
958 if not link_exists(link
):
960 output
= read_networkd_log()
961 if needle
in output
and flag
in check_output(f
'ip link show {link}'):
964 self
.fail(f
'Timed out waiting for {link} activated.')
967 def wait_operstate(self
, link
, operstate
='degraded', setup_state
='configured', setup_timeout
=5, fail_assert
=True):
968 """Wait for the link to reach the specified operstate and/or setup state.
970 Specify None or '' for either operstate or setup_state to ignore that state.
971 This will recheck until the state conditions are met or the timeout expires.
973 If the link successfully matches the requested state, this returns True.
974 If this times out waiting for the link to match, the behavior depends on the
975 'fail_assert' parameter; if True, this causes a test assertion failure,
976 otherwise this returns False. The default is to cause assertion failure.
978 Note that this function matches on *exactly* the given operstate and setup_state.
979 To wait for a link to reach *or exceed* a given operstate, use wait_online().
986 for secs
in range(setup_timeout
+ 1):
989 if not link_exists(link
):
991 output
= networkctl_status(link
)
992 if re
.search(rf
'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output
):
996 self
.fail(f
'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
999 def wait_online(self
, *links_with_operstate
, timeout
='20s', bool_any
=False, ipv4
=False, ipv6
=False, setup_state
='configured', setup_timeout
=5):
1000 """Wait for the links to reach the specified operstate and/or setup state.
1002 This is similar to wait_operstate() but can be used for multiple links,
1003 and it also calls systemd-networkd-wait-online to wait for the given operstate.
1004 The operstate should be specified in the link name, like 'eth0:degraded'.
1005 If just a link name is provided, wait-online's default operstate to wait for is degraded.
1007 The 'timeout' parameter controls the systemd-networkd-wait-online timeout, and the
1008 'setup_timeout' controls the per-link timeout waiting for the setup_state.
1010 Set 'bool_any' to True to wait for any (instead of all) of the given links.
1011 If this is set, no setup_state checks are done.
1013 Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links.
1014 This is applied only for the operational state 'degraded' or above.
1016 Note that this function waits for the links to reach *or exceed* the given operstate.
1017 However, the setup_state, if specified, must be matched *exactly*.
1019 This returns if the links reached the requested operstate/setup_state; otherwise it
1020 raises CalledProcessError or fails test assertion.
1022 args
= wait_online_cmd
+ [f
'--timeout={timeout}'] + [f
'--interface={link}' for link
in links_with_operstate
] + [f
'--ignore={link}' for link
in protected_links
]
1030 check_output(*args
, env
=wait_online_env
)
1031 except subprocess
.CalledProcessError
:
1032 if networkd_is_failed():
1033 print('!!!!! systemd-networkd.service is failed !!!!!')
1034 call('systemctl status systemd-networkd.service')
1036 # show detailed status on failure
1037 for link
in links_with_operstate
:
1038 name
= link
.split(':')[0]
1039 if link_exists(name
):
1040 print(networkctl_status(name
))
1042 print(f
'Interface {name} not found.')
1044 if not bool_any
and setup_state
:
1045 for link
in links_with_operstate
:
1046 self
.wait_operstate(link
.split(':')[0], None, setup_state
, setup_timeout
)
1048 def wait_address(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1049 for i
in range(timeout_sec
):
1052 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1053 if re
.search(address_regex
, output
) and 'tentative' not in output
:
1056 self
.assertRegex(output
, address_regex
)
1058 def wait_address_dropped(self
, link
, address_regex
, scope
='global', ipv
='', timeout_sec
=100):
1059 for i
in range(timeout_sec
):
1062 output
= check_output(f
'ip {ipv} address show dev {link} scope {scope}')
1063 if not re
.search(address_regex
, output
):
1066 self
.assertNotRegex(output
, address_regex
)
1068 def wait_route(self
, link
, route_regex
, table
='main', ipv
='', timeout_sec
=100):
1069 for i
in range(timeout_sec
):
1072 output
= check_output(f
'ip {ipv} route show dev {link} table {table}')
1073 if re
.search(route_regex
, output
):
1076 self
.assertRegex(output
, route_regex
)
1078 def check_netlabel(self
, interface
, address
, label
='system_u:object_r:root_t:s0'):
1079 if not shutil
.which('selinuxenabled'):
1080 print('## Checking NetLabel skipped: selinuxenabled command not found.')
1081 elif call_quiet('selinuxenabled') != 0:
1082 print('## Checking NetLabel skipped: SELinux disabled.')
1083 elif not shutil
.which('netlabelctl'): # not packaged by all distros
1084 print('## Checking NetLabel skipped: netlabelctl command not found.')
1086 output
= check_output('netlabelctl unlbl list')
1088 self
.assertRegex(output
, f
'interface:{interface},address:{address},label:"{label}"')
1090 def setup_nftset(self
, filter_name
, filter_type
, flags
=''):
1091 if not shutil
.which('nft'):
1092 print('## Setting up NFT sets skipped: nft command not found.')
1094 if call(f
'nft add table inet sd_test') != 0:
1095 print('## Setting up NFT table failed.')
1097 if call(f
'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0:
1098 print('## Setting up NFT sets failed.')
1101 def teardown_nftset(self
, *filters
):
1102 if not shutil
.which('nft'):
1103 print('## Tearing down NFT sets skipped: nft command not found.')
1105 for filter_name
in filters
:
1106 if call(f
'nft delete set inet sd_test {filter_name}') != 0:
1107 print('## Tearing down NFT sets failed.')
1109 if call(f
'nft delete table inet sd_test') != 0:
1110 print('## Tearing down NFT table failed.')
1113 def check_nftset(self
, filter_name
, contents
):
1114 if not shutil
.which('nft'):
1115 print('## Checking NFT sets skipped: nft command not found.')
1117 output
= check_output(f
'nft list set inet sd_test {filter_name}')
1119 self
.assertRegex(output
, r
'.*elements = { [^}]*' + contents
+ r
'[^}]* }.*')
1121 class NetworkctlTests(unittest
.TestCase
, Utilities
):
1129 @expectedFailureIfAlternativeNameIsNotAvailable()
1130 def test_altname(self
):
1131 copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
1133 self
.wait_online('dummy98:degraded')
1135 output
= networkctl_status('dummy98')
1136 self
.assertRegex(output
, 'hogehogehogehogehogehoge')
1138 @expectedFailureIfAlternativeNameIsNotAvailable()
1139 def test_rename_to_altname(self
):
1140 copy_network_unit('26-netdev-link-local-addressing-yes.network',
1141 '12-dummy.netdev', '12-dummy-rename-to-altname.link')
1143 self
.wait_online('dummyalt:degraded')
1145 output
= networkctl_status('dummyalt')
1146 self
.assertIn('hogehogehogehogehogehoge', output
)
1147 self
.assertNotIn('dummy98', output
)
1149 def test_reconfigure(self
):
1150 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1152 self
.wait_online('dummy98:routable')
1154 output
= check_output('ip -4 address show dev dummy98')
1156 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1157 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1158 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1160 check_output('ip address del 10.1.2.3/16 dev dummy98')
1161 check_output('ip address del 10.1.2.4/16 dev dummy98')
1162 check_output('ip address del 10.2.2.4/16 dev dummy98')
1164 networkctl_reconfigure('dummy98')
1165 self
.wait_online('dummy98:routable')
1167 output
= check_output('ip -4 address show dev dummy98')
1169 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1170 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1171 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1173 remove_network_unit('25-address-static.network')
1176 self
.wait_operstate('dummy98', 'degraded', setup_state
='unmanaged')
1178 output
= check_output('ip -4 address show dev dummy98')
1180 self
.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1181 self
.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1182 self
.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1184 copy_network_unit('25-address-static.network', copy_dropins
=False)
1186 self
.wait_online('dummy98:routable')
1188 output
= check_output('ip -4 address show dev dummy98')
1190 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
1191 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
1192 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
1194 def test_renew(self
):
1196 self
.wait_online('veth99:routable', 'veth-peer:routable')
1197 output
= networkctl_status('veth99')
1199 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
1200 self
.assertIn('Gateway: 192.168.5.3', output
)
1201 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
1202 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
1204 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
1207 check_json(networkctl_json('--lines=0', '--stats', '--all', '--full'))
1209 for verb
in ['renew', 'forcerenew']:
1210 networkctl(verb
, 'veth99')
1212 networkctl(verb
, 'veth99', 'veth99', 'veth99')
1215 def test_up_down(self
):
1216 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
1218 self
.wait_online('dummy98:routable')
1220 networkctl('down', 'dummy98')
1221 self
.wait_online('dummy98:off')
1222 networkctl('up', 'dummy98')
1223 self
.wait_online('dummy98:routable')
1224 networkctl('down', 'dummy98', 'dummy98', 'dummy98')
1225 self
.wait_online('dummy98:off')
1226 networkctl('up', 'dummy98', 'dummy98', 'dummy98')
1227 self
.wait_online('dummy98:routable')
1229 def test_reload(self
):
1232 copy_network_unit('11-dummy.netdev')
1234 self
.wait_operstate('test1', 'off', setup_state
='unmanaged')
1236 copy_network_unit('11-dummy.network')
1238 self
.wait_online('test1:degraded')
1240 remove_network_unit('11-dummy.network')
1242 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1244 remove_network_unit('11-dummy.netdev')
1246 self
.wait_operstate('test1', 'degraded', setup_state
='unmanaged')
1248 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1250 self
.wait_operstate('test1', 'degraded')
1252 def test_glob(self
):
1253 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1256 self
.wait_online('test1:degraded')
1258 output
= networkctl('list')
1259 self
.assertRegex(output
, '1 lo ')
1260 self
.assertRegex(output
, 'test1')
1262 output
= networkctl('list', 'test1')
1263 self
.assertNotRegex(output
, '1 lo ')
1264 self
.assertRegex(output
, 'test1')
1266 output
= networkctl('list', 'te*')
1267 self
.assertNotRegex(output
, '1 lo ')
1268 self
.assertRegex(output
, 'test1')
1270 output
= networkctl_status('te*')
1271 self
.assertNotRegex(output
, '1: lo ')
1272 self
.assertRegex(output
, 'test1')
1274 output
= networkctl_status('tes[a-z][0-9]')
1275 self
.assertNotRegex(output
, '1: lo ')
1276 self
.assertRegex(output
, 'test1')
1279 copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
1282 self
.wait_online('test1:degraded')
1284 output
= networkctl_status('test1')
1285 self
.assertRegex(output
, 'MTU: 1600')
1287 def test_type(self
):
1288 copy_network_unit('11-dummy.netdev', '11-dummy.network')
1290 self
.wait_online('test1:degraded')
1292 output
= networkctl_status('test1')
1294 self
.assertRegex(output
, 'Type: ether')
1296 output
= networkctl_status('lo')
1298 self
.assertRegex(output
, 'Type: loopback')
1300 def test_unit_file(self
):
1301 copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
1303 self
.wait_online('test1:degraded')
1305 output
= networkctl_status('test1')
1307 self
.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output
)
1308 self
.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output
)
1309 self
.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output
)
1310 self
.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output
)
1312 output
= read_networkd_log()
1313 self
.assertIn('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).', output
)
1315 # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
1316 # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
1317 # Let's reprocess the interface and drop the property.
1318 check_output(*udevadm_cmd
, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
1319 output
= networkctl_status('lo')
1321 self
.assertIn('Link File: n/a', output
)
1322 self
.assertIn('Network File: n/a', output
)
1324 def test_delete_links(self
):
1325 copy_network_unit('11-dummy.netdev', '11-dummy.network',
1326 '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
1329 self
.wait_online('test1:degraded', 'veth99:degraded', 'veth-peer:degraded')
1331 networkctl('delete', 'test1', 'veth99')
1332 self
.check_link_exists('test1', expected
=False)
1333 self
.check_link_exists('veth99', expected
=False)
1334 self
.check_link_exists('veth-peer', expected
=False)
1336 def test_label(self
):
1339 class NetworkdMatchTests(unittest
.TestCase
, Utilities
):
1347 @expectedFailureIfAlternativeNameIsNotAvailable()
1348 def test_match(self
):
1349 copy_network_unit('12-dummy-mac.netdev',
1350 '12-dummy-match-mac-01.network',
1351 '12-dummy-match-mac-02.network',
1352 '12-dummy-match-renamed.network',
1353 '12-dummy-match-altname.network',
1354 '12-dummy-altname.link')
1357 self
.wait_online('dummy98:routable')
1358 output
= networkctl_status('dummy98')
1359 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output
)
1360 output
= check_output('ip -4 address show dev dummy98')
1361 self
.assertIn('10.0.0.1/16', output
)
1363 check_output('ip link set dev dummy98 down')
1364 check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
1366 self
.wait_address('dummy98', '10.0.0.2/16', ipv
='-4', timeout_sec
=10)
1367 self
.wait_online('dummy98:routable')
1368 output
= networkctl_status('dummy98')
1369 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output
)
1371 check_output('ip link set dev dummy98 down')
1372 check_output('ip link set dev dummy98 name dummy98-1')
1374 self
.wait_address('dummy98-1', '10.0.1.2/16', ipv
='-4', timeout_sec
=10)
1375 self
.wait_online('dummy98-1:routable')
1376 output
= networkctl_status('dummy98-1')
1377 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output
)
1379 check_output('ip link set dev dummy98-1 down')
1380 check_output('ip link set dev dummy98-1 name dummy98-2')
1381 check_output(*udevadm_cmd
, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
1383 self
.wait_address('dummy98-2', '10.0.2.2/16', ipv
='-4', timeout_sec
=10)
1384 self
.wait_online('dummy98-2:routable')
1385 output
= networkctl_status('dummy98-2')
1386 self
.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output
)
1388 def test_match_udev_property(self
):
1389 copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
1391 self
.wait_online('dummy98:routable')
1393 output
= networkctl_status('dummy98')
1395 self
.assertRegex(output
, 'Network File: /run/systemd/network/14-match-udev-property')
1397 class WaitOnlineTests(unittest
.TestCase
, Utilities
):
1405 def test_wait_online_any(self
):
1406 copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
1409 self
.wait_online('bridge99', 'test1:degraded', bool_any
=True)
1411 self
.wait_operstate('bridge99', '(off|no-carrier)', setup_state
='configuring')
1412 self
.wait_operstate('test1', 'degraded')
1414 class NetworkdNetDevTests(unittest
.TestCase
, Utilities
):
1422 def test_dropin_and_name_conflict(self
):
1423 copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
1426 self
.wait_online('dropin-test:off', setup_state
='unmanaged')
1428 output
= check_output('ip link show dropin-test')
1430 self
.assertRegex(output
, '00:50:56:c0:00:28')
1432 @expectedFailureIfModuleIsNotAvailable('bareudp')
1433 def test_bareudp(self
):
1434 copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
1437 self
.wait_online('bareudp99:degraded')
1439 output
= check_output('ip -d link show bareudp99')
1441 self
.assertRegex(output
, 'dstport 1000 ')
1442 self
.assertRegex(output
, 'ethertype ip ')
1444 @expectedFailureIfModuleIsNotAvailable('batman-adv')
1445 def test_batadv(self
):
1446 copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
1449 self
.wait_online('batadv99:degraded')
1451 output
= check_output('ip -d link show batadv99')
1453 self
.assertRegex(output
, 'batadv')
1455 def test_bridge(self
):
1456 copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
1459 self
.wait_online('bridge99:no-carrier')
1461 tick
= os
.sysconf('SC_CLK_TCK')
1462 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick
))
1463 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick
))
1464 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick
))
1465 self
.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick
))
1466 self
.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority')))
1467 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier')))
1468 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping')))
1469 self
.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state')))
1470 self
.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
1472 output
= networkctl_status('bridge99')
1474 self
.assertRegex(output
, 'Priority: 9')
1475 self
.assertRegex(output
, 'STP: yes')
1476 self
.assertRegex(output
, 'Multicast IGMP Version: 3')
1478 output
= check_output('ip -d link show bridge99')
1480 self
.assertIn('vlan_filtering 1 ', output
)
1481 self
.assertIn('vlan_protocol 802.1ad ', output
)
1482 self
.assertIn('vlan_default_pvid 9 ', output
)
1484 def test_bond(self
):
1485 copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
1488 self
.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state
='unmanaged')
1490 self
.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
1491 self
.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
1492 self
.check_link_attr('bond99', 'bonding', 'miimon', '1000')
1493 self
.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1')
1494 self
.check_link_attr('bond99', 'bonding', 'updelay', '2000')
1495 self
.check_link_attr('bond99', 'bonding', 'downdelay', '2000')
1496 self
.check_link_attr('bond99', 'bonding', 'resend_igmp', '4')
1497 self
.check_link_attr('bond99', 'bonding', 'min_links', '1')
1498 self
.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218')
1499 self
.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811')
1500 self
.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55')
1502 self
.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5')
1503 self
.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1')
1505 output
= networkctl_status('bond99')
1507 self
.assertIn('Mode: 802.3ad', output
)
1508 self
.assertIn('Miimon: 1s', output
)
1509 self
.assertIn('Updelay: 2s', output
)
1510 self
.assertIn('Downdelay: 2s', output
)
1512 output
= networkctl_status('bond98')
1514 self
.assertIn('Mode: balance-tlb', output
)
1516 output
= networkctl_status('bond97')
1519 self
.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
1521 def test_vlan(self
):
1522 copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
1523 '21-vlan.network', '21-vlan-test1.network')
1526 self
.wait_online('test1:degraded', 'vlan99:routable')
1528 output
= check_output('ip -d link show test1')
1530 self
.assertRegex(output
, ' mtu 2000 ')
1532 output
= check_output('ip -d link show vlan99')
1534 self
.assertIn(' mtu 2000 ', output
)
1535 self
.assertIn('REORDER_HDR', output
)
1536 self
.assertIn('LOOSE_BINDING', output
)
1537 self
.assertIn('GVRP', output
)
1538 self
.assertIn('MVRP', output
)
1539 self
.assertIn(' id 99 ', output
)
1540 self
.assertIn('ingress-qos-map { 4:100 7:13 }', output
)
1541 self
.assertIn('egress-qos-map { 0:1 1:3 6:6 7:7 10:3 }', output
)
1543 output
= check_output('ip -4 address show dev test1')
1545 self
.assertRegex(output
, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
1546 self
.assertRegex(output
, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
1548 output
= check_output('ip -4 address show dev vlan99')
1550 self
.assertRegex(output
, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
1552 def test_vlan_on_bond(self
):
1553 # For issue #24377 (https://github.com/systemd/systemd/issues/24377),
1554 # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020).
1556 copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
1557 '21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
1559 self
.wait_online('bond99:off')
1560 self
.wait_operstate('vlan99', operstate
='off', setup_state
='configuring', setup_timeout
=10)
1562 # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
1563 # that the issue is fixed by the commit, let's allow to match both string.
1564 log_re
= re
.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re
.MULTILINE
)
1568 if log_re
.search(read_networkd_log()):
1573 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
1575 self
.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
1577 def test_macvtap(self
):
1579 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1585 print(f
'### test_macvtap(mode={mode})')
1586 with self
.subTest(mode
=mode
):
1587 copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1588 '11-dummy.netdev', '25-macvtap.network')
1589 with
open(os
.path
.join(network_unit_dir
, '21-macvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1590 f
.write('[MACVTAP]\nMode=' + mode
)
1593 self
.wait_online('macvtap99:degraded',
1594 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1596 output
= check_output('ip -d link show macvtap99')
1598 self
.assertRegex(output
, 'macvtap mode ' + mode
+ ' ')
1600 def test_macvlan(self
):
1602 for mode
in ['private', 'vepa', 'bridge', 'passthru']:
1608 print(f
'### test_macvlan(mode={mode})')
1609 with self
.subTest(mode
=mode
):
1610 copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1611 '11-dummy.netdev', '25-macvlan.network')
1612 with
open(os
.path
.join(network_unit_dir
, '21-macvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1613 f
.write('[MACVLAN]\nMode=' + mode
)
1616 self
.wait_online('macvlan99:degraded',
1617 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1619 output
= check_output('ip -d link show test1')
1621 self
.assertIn(' mtu 2000 ', output
)
1623 output
= check_output('ip -d link show macvlan99')
1625 self
.assertIn(' mtu 2000 ', output
)
1626 self
.assertIn(f
' macvlan mode {mode} ', output
)
1628 remove_link('test1')
1631 check_output("ip link add test1 type dummy")
1632 self
.wait_online('macvlan99:degraded',
1633 'test1:carrier' if mode
== 'passthru' else 'test1:degraded')
1635 output
= check_output('ip -d link show test1')
1637 self
.assertIn(' mtu 2000 ', output
)
1639 output
= check_output('ip -d link show macvlan99')
1641 self
.assertIn(' mtu 2000 ', output
)
1642 self
.assertIn(f
' macvlan mode {mode} ', output
)
1643 self
.assertIn(' bcqueuelen 1234 ', output
)
1644 self
.assertIn(' bclim 2147483647 ', output
)
1646 @expectedFailureIfModuleIsNotAvailable('ipvlan')
1647 def test_ipvlan(self
):
1649 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1655 print(f
'### test_ipvlan(mode={mode}, flag={flag})')
1656 with self
.subTest(mode
=mode
, flag
=flag
):
1657 copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network',
1658 '11-dummy.netdev', '25-ipvlan.network')
1659 with
open(os
.path
.join(network_unit_dir
, '25-ipvlan.netdev'), mode
='a', encoding
='utf-8') as f
:
1660 f
.write('[IPVLAN]\nMode=' + mode
+ '\nFlags=' + flag
)
1663 self
.wait_online('ipvlan99:degraded', 'test1:degraded')
1665 output
= check_output('ip -d link show ipvlan99')
1667 self
.assertRegex(output
, 'ipvlan *mode ' + mode
.lower() + ' ' + flag
)
1669 @expectedFailureIfModuleIsNotAvailable('ipvtap')
1670 def test_ipvtap(self
):
1672 for mode
, flag
in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
1678 print(f
'### test_ipvtap(mode={mode}, flag={flag})')
1679 with self
.subTest(mode
=mode
, flag
=flag
):
1680 copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network',
1681 '11-dummy.netdev', '25-ipvtap.network')
1682 with
open(os
.path
.join(network_unit_dir
, '25-ipvtap.netdev'), mode
='a', encoding
='utf-8') as f
:
1683 f
.write('[IPVTAP]\nMode=' + mode
+ '\nFlags=' + flag
)
1686 self
.wait_online('ipvtap99:degraded', 'test1:degraded')
1688 output
= check_output('ip -d link show ipvtap99')
1690 self
.assertRegex(output
, 'ipvtap *mode ' + mode
.lower() + ' ' + flag
)
1692 def test_veth(self
):
1693 copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network',
1694 '25-veth-mtu.netdev')
1697 self
.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
1699 output
= check_output('ip -d link show veth99')
1701 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bc')
1702 output
= check_output('ip -d link show veth-peer')
1704 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bd')
1706 output
= check_output('ip -d link show veth-mtu')
1708 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:be')
1709 self
.assertRegex(output
, 'mtu 1800')
1710 output
= check_output('ip -d link show veth-mtu-peer')
1712 self
.assertRegex(output
, 'link/ether 12:34:56:78:9a:bf')
1713 self
.assertRegex(output
, 'mtu 1800')
1715 def test_tuntap(self
):
1716 copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
1719 self
.wait_online('testtun99:degraded', 'testtap99:degraded')
1721 pid
= networkd_pid()
1722 name
= psutil
.Process(pid
).name()[:15]
1724 output
= check_output('ip -d tuntap show')
1726 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1727 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1729 output
= check_output('ip -d link show testtun99')
1731 # Old ip command does not support IFF_ flags
1732 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1733 self
.assertIn('UP,LOWER_UP', output
)
1735 output
= check_output('ip -d link show testtap99')
1737 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1738 self
.assertIn('UP,LOWER_UP', output
)
1740 remove_network_unit('26-netdev-link-local-addressing-yes.network')
1743 self
.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state
='unmanaged')
1745 pid
= networkd_pid()
1746 name
= psutil
.Process(pid
).name()[:15]
1748 output
= check_output('ip -d tuntap show')
1750 self
.assertRegex(output
, fr
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1751 self
.assertRegex(output
, fr
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
1753 output
= check_output('ip -d link show testtun99')
1755 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1756 self
.assertIn('UP,LOWER_UP', output
)
1758 output
= check_output('ip -d link show testtap99')
1760 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1761 self
.assertIn('UP,LOWER_UP', output
)
1763 clear_network_units()
1765 self
.wait_online('testtun99:off', 'testtap99:off', setup_state
='unmanaged')
1767 output
= check_output('ip -d tuntap show')
1769 self
.assertRegex(output
, r
'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1770 self
.assertRegex(output
, r
'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
1775 output
= check_output('ip -d link show testtun99')
1777 self
.assertRegex(output
, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
1778 if 'NO-CARRIER' in output
:
1786 output
= check_output('ip -d link show testtap99')
1788 self
.assertRegex(output
, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
1789 if 'NO-CARRIER' in output
:
1794 @expectedFailureIfModuleIsNotAvailable('vrf')
1796 copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
1799 self
.wait_online('vrf99:carrier')
1801 @expectedFailureIfModuleIsNotAvailable('vcan')
1802 def test_vcan(self
):
1803 copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
1804 '25-vcan98.netdev', '25-vcan98.network')
1807 self
.wait_online('vcan99:carrier', 'vcan98:carrier')
1808 # For can devices, 'carrier' is the default required operational state.
1809 self
.wait_online('vcan99', 'vcan98')
1811 # https://github.com/systemd/systemd/issues/30140
1812 output
= check_output('ip -d link show vcan99')
1814 self
.assertIn('mtu 16 ', output
)
1816 output
= check_output('ip -d link show vcan98')
1818 self
.assertIn('mtu 16 ', output
)
1820 @expectedFailureIfModuleIsNotAvailable('vxcan')
1821 def test_vxcan(self
):
1822 copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
1825 self
.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
1826 # For can devices, 'carrier' is the default required operational state.
1827 self
.wait_online('vxcan99', 'vxcan-peer')
1829 @expectedFailureIfModuleIsNotAvailable('wireguard')
1830 def test_wireguard(self
):
1831 copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
1832 copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
1833 copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
1835 copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
1836 '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
1837 '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
1838 '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
1840 self
.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
1842 output
= check_output('ip -4 address show dev wg99')
1844 self
.assertIn('inet 192.168.124.1/24 scope global wg99', output
)
1846 output
= check_output('ip -4 address show dev wg99')
1848 self
.assertIn('inet 169.254.11.1/24 scope link wg99', output
)
1850 output
= check_output('ip -6 address show dev wg99')
1852 self
.assertIn('inet6 fe80::1/64 scope link', output
)
1854 output
= check_output('ip -4 address show dev wg98')
1856 self
.assertIn('inet 192.168.123.123/24 scope global wg98', output
)
1858 output
= check_output('ip -6 address show dev wg98')
1860 self
.assertIn('inet6 fd8d:4d6d:3ccb:500::1/64 scope global', output
)
1862 output
= check_output('ip -4 route show dev wg99 table 1234')
1864 self
.assertIn('192.168.26.0/24 proto static scope link metric 123', output
)
1866 output
= check_output('ip -6 route show dev wg99 table 1234')
1868 self
.assertIn('fd31:bf08:57cb::/48 proto static metric 123 pref medium', output
)
1870 output
= check_output('ip -6 route show dev wg98 table 1234')
1872 self
.assertIn('fd8d:4d6d:3ccb:500:c79:2339:edce:ece1 proto static metric 123 pref medium', output
)
1873 self
.assertIn('fd8d:4d6d:3ccb:500:1dbf:ca8a:32d3:dd81 proto static metric 123 pref medium', output
)
1874 self
.assertIn('fd8d:4d6d:3ccb:500:1e54:1415:35d0:a47c proto static metric 123 pref medium', output
)
1875 self
.assertIn('fd8d:4d6d:3ccb:500:270d:b5dd:4a3f:8909 proto static metric 123 pref medium', output
)
1876 self
.assertIn('fd8d:4d6d:3ccb:500:5660:679d:3532:94d8 proto static metric 123 pref medium', output
)
1877 self
.assertIn('fd8d:4d6d:3ccb:500:6825:573f:30f3:9472 proto static metric 123 pref medium', output
)
1878 self
.assertIn('fd8d:4d6d:3ccb:500:6f2e:6888:c6fd:dfb9 proto static metric 123 pref medium', output
)
1879 self
.assertIn('fd8d:4d6d:3ccb:500:8d4d:bab:7280:a09a proto static metric 123 pref medium', output
)
1880 self
.assertIn('fd8d:4d6d:3ccb:500:900c:d437:ec27:8822 proto static metric 123 pref medium', output
)
1881 self
.assertIn('fd8d:4d6d:3ccb:500:9742:9931:5217:18d5 proto static metric 123 pref medium', output
)
1882 self
.assertIn('fd8d:4d6d:3ccb:500:9c11:d820:2e96:9be0 proto static metric 123 pref medium', output
)
1883 self
.assertIn('fd8d:4d6d:3ccb:500:a072:80da:de4f:add1 proto static metric 123 pref medium', output
)
1884 self
.assertIn('fd8d:4d6d:3ccb:500:a3f3:df38:19b0:721 proto static metric 123 pref medium', output
)
1885 self
.assertIn('fd8d:4d6d:3ccb:500:a94b:cd6a:a32d:90e6 proto static metric 123 pref medium', output
)
1886 self
.assertIn('fd8d:4d6d:3ccb:500:b39c:9cdc:755a:ead3 proto static metric 123 pref medium', output
)
1887 self
.assertIn('fd8d:4d6d:3ccb:500:b684:4f81:2e3e:132e proto static metric 123 pref medium', output
)
1888 self
.assertIn('fd8d:4d6d:3ccb:500:bad5:495d:8e9c:3427 proto static metric 123 pref medium', output
)
1889 self
.assertIn('fd8d:4d6d:3ccb:500:bfe5:c3c3:5d77:fcb proto static metric 123 pref medium', output
)
1890 self
.assertIn('fd8d:4d6d:3ccb:500:c624:6bf7:4c09:3b59 proto static metric 123 pref medium', output
)
1891 self
.assertIn('fd8d:4d6d:3ccb:500:d4f9:5dc:9296:a1a proto static metric 123 pref medium', output
)
1892 self
.assertIn('fd8d:4d6d:3ccb:500:dcdd:d33b:90c9:6088 proto static metric 123 pref medium', output
)
1893 self
.assertIn('fd8d:4d6d:3ccb:500:e2e1:ae15:103f:f376 proto static metric 123 pref medium', output
)
1894 self
.assertIn('fd8d:4d6d:3ccb:500:f349:c4f0:10c1:6b4 proto static metric 123 pref medium', output
)
1895 self
.assertIn('fd8d:4d6d:3ccb:c79:2339:edce::/96 proto static metric 123 pref medium', output
)
1896 self
.assertIn('fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 proto static metric 123 pref medium', output
)
1897 self
.assertIn('fd8d:4d6d:3ccb:1e54:1415:35d0::/96 proto static metric 123 pref medium', output
)
1898 self
.assertIn('fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 proto static metric 123 pref medium', output
)
1899 self
.assertIn('fd8d:4d6d:3ccb:5660:679d:3532::/96 proto static metric 123 pref medium', output
)
1900 self
.assertIn('fd8d:4d6d:3ccb:6825:573f:30f3::/96 proto static metric 123 pref medium', output
)
1901 self
.assertIn('fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 proto static metric 123 pref medium', output
)
1902 self
.assertIn('fd8d:4d6d:3ccb:8d4d:bab:7280::/96 proto static metric 123 pref medium', output
)
1903 self
.assertIn('fd8d:4d6d:3ccb:900c:d437:ec27::/96 proto static metric 123 pref medium', output
)
1904 self
.assertIn('fd8d:4d6d:3ccb:9742:9931:5217::/96 proto static metric 123 pref medium', output
)
1905 self
.assertIn('fd8d:4d6d:3ccb:9c11:d820:2e96::/96 proto static metric 123 pref medium', output
)
1906 self
.assertIn('fd8d:4d6d:3ccb:a072:80da:de4f::/96 proto static metric 123 pref medium', output
)
1907 self
.assertIn('fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 proto static metric 123 pref medium', output
)
1908 self
.assertIn('fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 proto static metric 123 pref medium', output
)
1909 self
.assertIn('fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 proto static metric 123 pref medium', output
)
1910 self
.assertIn('fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 proto static metric 123 pref medium', output
)
1911 self
.assertIn('fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 proto static metric 123 pref medium', output
)
1912 self
.assertIn('fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 proto static metric 123 pref medium', output
)
1913 self
.assertIn('fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 proto static metric 123 pref medium', output
)
1914 self
.assertIn('fd8d:4d6d:3ccb:d4f9:5dc:9296::/96 proto static metric 123 pref medium', output
)
1915 self
.assertIn('fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 proto static metric 123 pref medium', output
)
1916 self
.assertIn('fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 proto static metric 123 pref medium', output
)
1917 self
.assertIn('fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 proto static metric 123 pref medium', output
)
1919 if shutil
.which('wg'):
1922 output
= check_output('wg show wg99 listen-port')
1923 self
.assertEqual(output
, '51820')
1924 output
= check_output('wg show wg99 fwmark')
1925 self
.assertEqual(output
, '0x4d2')
1926 output
= check_output('wg show wg99 private-key')
1927 self
.assertEqual(output
, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=')
1928 output
= check_output('wg show wg99 allowed-ips')
1929 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output
)
1930 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output
)
1931 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output
)
1932 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output
)
1933 output
= check_output('wg show wg99 persistent-keepalive')
1934 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output
)
1935 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output
)
1936 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\toff', output
)
1937 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20', output
)
1938 output
= check_output('wg show wg99 endpoints')
1939 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t(none)', output
)
1940 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t(none)', output
)
1941 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output
)
1942 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output
)
1943 output
= check_output('wg show wg99 preshared-keys')
1944 self
.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output
)
1945 self
.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output
)
1946 self
.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output
)
1947 self
.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output
)
1949 output
= check_output('wg show wg98 private-key')
1950 self
.assertEqual(output
, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=')
1952 output
= check_output('wg show wg97 listen-port')
1953 self
.assertEqual(output
, '51821')
1954 output
= check_output('wg show wg97 fwmark')
1955 self
.assertEqual(output
, '0x4d3')
1957 def test_geneve(self
):
1958 copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
1961 self
.wait_online('geneve99:degraded')
1963 output
= check_output('ip -d link show geneve99')
1965 self
.assertRegex(output
, '192.168.22.1')
1966 self
.assertRegex(output
, '6082')
1967 self
.assertRegex(output
, 'udpcsum')
1968 self
.assertRegex(output
, 'udp6zerocsumrx')
1970 def test_ipip_tunnel(self
):
1971 copy_network_unit('12-dummy.netdev', '25-ipip.network',
1972 '25-ipip-tunnel.netdev', '25-tunnel.network',
1973 '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1974 '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1975 '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1977 self
.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
1979 output
= check_output('ip -d link show ipiptun99')
1981 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local 192.168.223.238 dev dummy98')
1982 output
= check_output('ip -d link show ipiptun98')
1984 self
.assertRegex(output
, 'ipip (ipip )?remote 192.169.224.239 local any dev dummy98')
1985 output
= check_output('ip -d link show ipiptun97')
1987 self
.assertRegex(output
, 'ipip (ipip )?remote any local 192.168.223.238 dev dummy98')
1988 output
= check_output('ip -d link show ipiptun96')
1990 self
.assertRegex(output
, 'ipip (ipip )?remote any local any dev dummy98')
1992 def test_gre_tunnel(self
):
1993 copy_network_unit('12-dummy.netdev', '25-gretun.network',
1994 '25-gre-tunnel.netdev', '25-tunnel.network',
1995 '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
1996 '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
1997 '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
1999 self
.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
2001 output
= check_output('ip -d link show gretun99')
2003 self
.assertRegex(output
, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2004 self
.assertRegex(output
, 'ikey 1.2.3.103')
2005 self
.assertRegex(output
, 'okey 1.2.4.103')
2006 self
.assertRegex(output
, 'iseq')
2007 self
.assertRegex(output
, 'oseq')
2008 output
= check_output('ip -d link show gretun98')
2010 self
.assertRegex(output
, 'gre remote 10.65.223.239 local any dev dummy98')
2011 self
.assertRegex(output
, 'ikey 0.0.0.104')
2012 self
.assertRegex(output
, 'okey 0.0.0.104')
2013 self
.assertNotRegex(output
, 'iseq')
2014 self
.assertNotRegex(output
, 'oseq')
2015 output
= check_output('ip -d link show gretun97')
2017 self
.assertRegex(output
, 'gre remote any local 10.65.223.238 dev dummy98')
2018 self
.assertRegex(output
, 'ikey 0.0.0.105')
2019 self
.assertRegex(output
, 'okey 0.0.0.105')
2020 self
.assertNotRegex(output
, 'iseq')
2021 self
.assertNotRegex(output
, 'oseq')
2022 output
= check_output('ip -d link show gretun96')
2024 self
.assertRegex(output
, 'gre remote any local any dev dummy98')
2025 self
.assertRegex(output
, 'ikey 0.0.0.106')
2026 self
.assertRegex(output
, 'okey 0.0.0.106')
2027 self
.assertNotRegex(output
, 'iseq')
2028 self
.assertNotRegex(output
, 'oseq')
2030 def test_ip6gre_tunnel(self
):
2031 copy_network_unit('12-dummy.netdev', '25-ip6gretun.network',
2032 '25-ip6gre-tunnel.netdev', '25-tunnel.network',
2033 '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2034 '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2035 '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2038 # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
2040 self
.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96')
2042 output
= check_output('ip -d link show ip6gretun99')
2044 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2045 output
= check_output('ip -d link show ip6gretun98')
2047 self
.assertRegex(output
, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
2048 output
= check_output('ip -d link show ip6gretun97')
2050 self
.assertRegex(output
, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
2051 output
= check_output('ip -d link show ip6gretun96')
2053 self
.assertRegex(output
, 'ip6gre remote any local any dev dummy98')
2055 def test_gretap_tunnel(self
):
2056 copy_network_unit('12-dummy.netdev', '25-gretap.network',
2057 '25-gretap-tunnel.netdev', '25-tunnel.network',
2058 '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2060 self
.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
2062 output
= check_output('ip -d link show gretap99')
2064 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2065 self
.assertRegex(output
, 'ikey 0.0.0.106')
2066 self
.assertRegex(output
, 'okey 0.0.0.106')
2067 self
.assertRegex(output
, 'iseq')
2068 self
.assertRegex(output
, 'oseq')
2069 self
.assertIn('nopmtudisc', output
)
2070 self
.assertIn('ignore-df', output
)
2071 output
= check_output('ip -d link show gretap98')
2073 self
.assertRegex(output
, 'gretap remote 10.65.223.239 local any dev dummy98')
2074 self
.assertRegex(output
, 'ikey 0.0.0.107')
2075 self
.assertRegex(output
, 'okey 0.0.0.107')
2076 self
.assertRegex(output
, 'iseq')
2077 self
.assertRegex(output
, 'oseq')
2079 def test_ip6gretap_tunnel(self
):
2080 copy_network_unit('12-dummy.netdev', '25-ip6gretap.network',
2081 '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
2082 '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2084 self
.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
2086 output
= check_output('ip -d link show ip6gretap99')
2088 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2089 output
= check_output('ip -d link show ip6gretap98')
2091 self
.assertRegex(output
, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
2093 def test_vti_tunnel(self
):
2094 copy_network_unit('12-dummy.netdev', '25-vti.network',
2095 '25-vti-tunnel.netdev', '25-tunnel.network',
2096 '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2097 '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2098 '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2100 self
.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
2102 output
= check_output('ip -d link show vtitun99')
2104 self
.assertRegex(output
, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
2105 output
= check_output('ip -d link show vtitun98')
2107 self
.assertRegex(output
, 'vti remote 10.65.223.239 local any dev dummy98')
2108 output
= check_output('ip -d link show vtitun97')
2110 self
.assertRegex(output
, 'vti remote any local 10.65.223.238 dev dummy98')
2111 output
= check_output('ip -d link show vtitun96')
2113 self
.assertRegex(output
, 'vti remote any local any dev dummy98')
2115 def test_vti6_tunnel(self
):
2116 copy_network_unit('12-dummy.netdev', '25-vti6.network',
2117 '25-vti6-tunnel.netdev', '25-tunnel.network',
2118 '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2119 '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
2121 self
.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
2123 output
= check_output('ip -d link show vti6tun99')
2125 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
2126 output
= check_output('ip -d link show vti6tun98')
2128 self
.assertRegex(output
, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2129 output
= check_output('ip -d link show vti6tun97')
2131 self
.assertRegex(output
, 'vti6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2133 def test_ip6tnl_tunnel(self
):
2134 copy_network_unit('12-dummy.netdev', '25-ip6tnl.network',
2135 '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
2136 '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2137 '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2138 '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network',
2139 '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
2140 '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
2142 self
.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
2143 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
2144 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
2146 output
= check_output('ip -d link show ip6tnl99')
2148 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output
)
2149 output
= check_output('ip -d link show ip6tnl98')
2151 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
2152 output
= check_output('ip -d link show ip6tnl97')
2154 self
.assertRegex(output
, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
2155 output
= check_output('ip -d link show ip6tnl-external')
2157 self
.assertIn('ip6tnl-external@NONE:', output
)
2158 self
.assertIn('ip6tnl external ', output
)
2159 output
= check_output('ip -d link show ip6tnl-slaac')
2161 self
.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2163 output
= check_output('ip -6 address show veth99')
2165 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2167 output
= check_output('ip -4 route show default')
2169 self
.assertIn('default dev ip6tnl-slaac proto static', output
)
2171 def test_sit_tunnel(self
):
2172 copy_network_unit('12-dummy.netdev', '25-sit.network',
2173 '25-sit-tunnel.netdev', '25-tunnel.network',
2174 '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
2175 '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
2176 '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
2178 self
.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
2180 output
= check_output('ip -d link show sittun99')
2182 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local 10.65.223.238 dev dummy98")
2183 output
= check_output('ip -d link show sittun98')
2185 self
.assertRegex(output
, "sit (ip6ip )?remote 10.65.223.239 local any dev dummy98")
2186 output
= check_output('ip -d link show sittun97')
2188 self
.assertRegex(output
, "sit (ip6ip )?remote any local 10.65.223.238 dev dummy98")
2189 output
= check_output('ip -d link show sittun96')
2191 self
.assertRegex(output
, "sit (ip6ip )?remote any local any dev dummy98")
2193 def test_isatap_tunnel(self
):
2194 copy_network_unit('12-dummy.netdev', '25-isatap.network',
2195 '25-isatap-tunnel.netdev', '25-tunnel.network')
2197 self
.wait_online('isataptun99:routable', 'dummy98:degraded')
2199 output
= check_output('ip -d link show isataptun99')
2201 self
.assertRegex(output
, "isatap ")
2203 def test_6rd_tunnel(self
):
2204 copy_network_unit('12-dummy.netdev', '25-6rd.network',
2205 '25-6rd-tunnel.netdev', '25-tunnel.network')
2207 self
.wait_online('sittun99:routable', 'dummy98:degraded')
2209 output
= check_output('ip -d link show sittun99')
2211 self
.assertRegex(output
, '6rd-prefix 2602::/24')
2213 @expectedFailureIfERSPANv0IsNotSupported()
2214 def test_erspan_tunnel_v0(self
):
2215 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2216 '25-erspan0-tunnel.netdev', '25-tunnel.network',
2217 '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2219 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2221 output
= check_output('ip -d link show erspan99')
2223 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2224 self
.assertIn('erspan_ver 0', output
)
2225 self
.assertNotIn('erspan_index 123', output
)
2226 self
.assertNotIn('erspan_dir ingress', output
)
2227 self
.assertNotIn('erspan_hwid 1f', output
)
2228 self
.assertIn('ikey 0.0.0.101', output
)
2229 self
.assertIn('iseq', output
)
2230 self
.assertIn('nopmtudisc', output
)
2231 self
.assertIn('ignore-df', output
)
2232 output
= check_output('ip -d link show erspan98')
2234 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2235 self
.assertIn('erspan_ver 0', output
)
2236 self
.assertNotIn('erspan_index 124', output
)
2237 self
.assertNotIn('erspan_dir egress', output
)
2238 self
.assertNotIn('erspan_hwid 2f', output
)
2239 self
.assertIn('ikey 0.0.0.102', output
)
2240 self
.assertIn('iseq', output
)
2242 def test_erspan_tunnel_v1(self
):
2243 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2244 '25-erspan1-tunnel.netdev', '25-tunnel.network',
2245 '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2247 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2249 output
= check_output('ip -d link show erspan99')
2251 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2252 self
.assertIn('erspan_ver 1', output
)
2253 self
.assertIn('erspan_index 123', output
)
2254 self
.assertNotIn('erspan_dir ingress', output
)
2255 self
.assertNotIn('erspan_hwid 1f', output
)
2256 self
.assertIn('ikey 0.0.0.101', output
)
2257 self
.assertIn('okey 0.0.0.101', output
)
2258 self
.assertIn('iseq', output
)
2259 self
.assertIn('oseq', output
)
2260 output
= check_output('ip -d link show erspan98')
2262 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2263 self
.assertIn('erspan_ver 1', output
)
2264 self
.assertIn('erspan_index 124', output
)
2265 self
.assertNotIn('erspan_dir egress', output
)
2266 self
.assertNotIn('erspan_hwid 2f', output
)
2267 self
.assertIn('ikey 0.0.0.102', output
)
2268 self
.assertIn('okey 0.0.0.102', output
)
2269 self
.assertIn('iseq', output
)
2270 self
.assertIn('oseq', output
)
2272 @expectedFailureIfERSPANv2IsNotSupported()
2273 def test_erspan_tunnel_v2(self
):
2274 copy_network_unit('12-dummy.netdev', '25-erspan.network',
2275 '25-erspan2-tunnel.netdev', '25-tunnel.network',
2276 '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
2278 self
.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
2280 output
= check_output('ip -d link show erspan99')
2282 self
.assertIn('erspan remote 172.16.1.100 local 172.16.1.200', output
)
2283 self
.assertIn('erspan_ver 2', output
)
2284 self
.assertNotIn('erspan_index 123', output
)
2285 self
.assertIn('erspan_dir ingress', output
)
2286 self
.assertIn('erspan_hwid 0x1f', output
)
2287 self
.assertIn('ikey 0.0.0.101', output
)
2288 self
.assertIn('okey 0.0.0.101', output
)
2289 self
.assertIn('iseq', output
)
2290 self
.assertIn('oseq', output
)
2291 output
= check_output('ip -d link show erspan98')
2293 self
.assertIn('erspan remote 172.16.1.100 local any', output
)
2294 self
.assertIn('erspan_ver 2', output
)
2295 self
.assertNotIn('erspan_index 124', output
)
2296 self
.assertIn('erspan_dir egress', output
)
2297 self
.assertIn('erspan_hwid 0x2f', output
)
2298 self
.assertIn('ikey 0.0.0.102', output
)
2299 self
.assertIn('okey 0.0.0.102', output
)
2300 self
.assertIn('iseq', output
)
2301 self
.assertIn('oseq', output
)
2303 def test_tunnel_independent(self
):
2304 copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
2307 self
.wait_online('ipiptun99:carrier')
2309 def test_tunnel_independent_loopback(self
):
2310 copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
2313 self
.wait_online('ipiptun99:carrier')
2315 @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
2316 def test_xfrm(self
):
2317 copy_network_unit('12-dummy.netdev', '25-xfrm.network',
2318 '25-xfrm.netdev', '25-xfrm-independent.netdev',
2319 '26-netdev-link-local-addressing-yes.network')
2322 self
.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
2324 output
= check_output('ip -d link show dev xfrm98')
2326 self
.assertIn('xfrm98@dummy98:', output
)
2327 self
.assertIn('xfrm if_id 0x98 ', output
)
2329 output
= check_output('ip -d link show dev xfrm99')
2331 self
.assertIn('xfrm99@lo:', output
)
2332 self
.assertIn('xfrm if_id 0x99 ', output
)
2334 @expectedFailureIfModuleIsNotAvailable('fou')
2336 # The following redundant check is necessary for CentOS CI.
2337 # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
2338 self
.assertTrue(is_module_available('fou'))
2340 copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
2341 '25-fou-ipip.netdev', '25-fou-sit.netdev',
2342 '25-fou-gre.netdev', '25-fou-gretap.netdev')
2345 self
.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state
='unmanaged')
2347 output
= check_output('ip fou show')
2349 self
.assertRegex(output
, 'port 55555 ipproto 4')
2350 self
.assertRegex(output
, 'port 55556 ipproto 47')
2352 output
= check_output('ip -d link show ipiptun96')
2354 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2355 output
= check_output('ip -d link show sittun96')
2357 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55555')
2358 output
= check_output('ip -d link show gretun96')
2360 self
.assertRegex(output
, 'encap fou encap-sport 1001 encap-dport 55556')
2361 output
= check_output('ip -d link show gretap96')
2363 self
.assertRegex(output
, 'encap fou encap-sport auto encap-dport 55556')
2365 def test_vxlan(self
):
2366 copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network',
2367 '25-vxlan.netdev', '25-vxlan.network',
2368 '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network',
2369 '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network',
2370 '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network',
2371 '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
2374 self
.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
2375 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
2377 output
= check_output('ip -d -d link show vxlan99')
2379 self
.assertIn('999', output
)
2380 self
.assertIn('5555', output
)
2381 self
.assertIn('l2miss', output
)
2382 self
.assertIn('l3miss', output
)
2383 self
.assertIn('gbp', output
)
2384 # Since [0] some of the options use slightly different names and some
2385 # options with default values are shown only if the -d(etails) setting
2387 # [0] https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=1215e9d3862387353d8672296cb4c6c16e8cbb72
2388 self
.assertRegex(output
, '(udpcsum|udp_csum)')
2389 self
.assertRegex(output
, '(udp6zerocsumtx|udp_zero_csum6_tx)')
2390 self
.assertRegex(output
, '(udp6zerocsumrx|udp_zero_csum6_rx)')
2391 self
.assertRegex(output
, '(remcsumtx|remcsum_tx)')
2392 self
.assertRegex(output
, '(remcsumrx|remcsum_rx)')
2394 output
= check_output('bridge fdb show dev vxlan99')
2396 self
.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output
)
2397 self
.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output
)
2398 self
.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output
)
2400 output
= networkctl_status('vxlan99')
2402 self
.assertIn('VNI: 999', output
)
2403 self
.assertIn('Destination Port: 5555', output
)
2404 self
.assertIn('Underlying Device: test1', output
)
2406 output
= check_output('bridge fdb show dev vxlan97')
2408 self
.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output
)
2409 self
.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output
)
2410 self
.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output
)
2412 output
= check_output('ip -d link show vxlan-slaac')
2414 self
.assertIn('vxlan id 4831584 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output
)
2416 output
= check_output('ip -6 address show veth99')
2418 self
.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output
)
2420 @unittest.skipUnless(compare_kernel_version("6"), reason
="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315")
2421 def test_macsec(self
):
2422 copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
2423 '26-macsec.network', '12-dummy.netdev')
2426 self
.wait_online('dummy98:degraded', 'macsec99:routable')
2428 output
= check_output('ip -d link show macsec99')
2430 self
.assertRegex(output
, 'macsec99@dummy98')
2431 self
.assertRegex(output
, 'macsec sci [0-9a-f]*000b')
2432 self
.assertRegex(output
, 'encrypt on')
2434 output
= check_output('ip macsec show macsec99')
2436 self
.assertRegex(output
, 'encrypt on')
2437 self
.assertRegex(output
, 'TXSC: [0-9a-f]*000b on SA 1')
2438 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 01000000000000000000000000000000')
2439 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030000000000000000000000000000')
2440 self
.assertRegex(output
, 'RXSC: c619528fe6a00100, state on')
2441 self
.assertRegex(output
, '0: PN [0-9]*, state on, key 02030405000000000000000000000000')
2442 self
.assertRegex(output
, '1: PN [0-9]*, state on, key 02030405060000000000000000000000')
2443 self
.assertRegex(output
, '2: PN [0-9]*, state off, key 02030405060700000000000000000000')
2444 self
.assertRegex(output
, '3: PN [0-9]*, state off, key 02030405060708000000000000000000')
2445 self
.assertNotRegex(output
, 'key 02030405067080900000000000000000')
2446 self
.assertRegex(output
, 'RXSC: 8c16456c83a90002, state on')
2447 self
.assertRegex(output
, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
2449 def test_nlmon(self
):
2450 copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
2453 self
.wait_online('nlmon99:carrier')
2455 @expectedFailureIfModuleIsNotAvailable('ifb')
2457 copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
2460 self
.wait_online('ifb99:degraded')
2462 class NetworkdL2TPTests(unittest
.TestCase
, Utilities
):
2470 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
2471 def test_l2tp_udp(self
):
2472 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2473 '25-l2tp-udp.netdev', '25-l2tp.network')
2476 self
.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
2478 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2480 self
.assertRegex(output
, "Tunnel 10, encap UDP")
2481 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2482 self
.assertRegex(output
, "Peer tunnel 11")
2483 self
.assertRegex(output
, "UDP source / dest ports: 3000/4000")
2484 self
.assertRegex(output
, "UDP checksum: enabled")
2486 output
= check_output('ip l2tp show session tid 10 session_id 15')
2488 self
.assertRegex(output
, "Session 15 in tunnel 10")
2489 self
.assertRegex(output
, "Peer session 16, tunnel 11")
2490 self
.assertRegex(output
, "interface name: l2tp-ses1")
2492 output
= check_output('ip l2tp show session tid 10 session_id 17')
2494 self
.assertRegex(output
, "Session 17 in tunnel 10")
2495 self
.assertRegex(output
, "Peer session 18, tunnel 11")
2496 self
.assertRegex(output
, "interface name: l2tp-ses2")
2498 @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
2499 def test_l2tp_ip(self
):
2500 copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
2501 '25-l2tp-ip.netdev', '25-l2tp.network')
2504 self
.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
2506 output
= check_output('ip l2tp show tunnel tunnel_id 10')
2508 self
.assertRegex(output
, "Tunnel 10, encap IP")
2509 self
.assertRegex(output
, "From 192.168.30.100 to 192.168.30.101")
2510 self
.assertRegex(output
, "Peer tunnel 12")
2512 output
= check_output('ip l2tp show session tid 10 session_id 25')
2514 self
.assertRegex(output
, "Session 25 in tunnel 10")
2515 self
.assertRegex(output
, "Peer session 26, tunnel 12")
2516 self
.assertRegex(output
, "interface name: l2tp-ses3")
2518 output
= check_output('ip l2tp show session tid 10 session_id 27')
2520 self
.assertRegex(output
, "Session 27 in tunnel 10")
2521 self
.assertRegex(output
, "Peer session 28, tunnel 12")
2522 self
.assertRegex(output
, "interface name: l2tp-ses4")
2524 class NetworkdNetworkTests(unittest
.TestCase
, Utilities
):
2532 def verify_address_static(
2562 output
= check_output('ip address show dev dummy98')
2566 self
.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output
)
2567 self
.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output
)
2568 self
.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output
)
2569 self
.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output
)
2570 self
.assertIn('inet6 2001:db8:0:f101::16/64 scope global', output
)
2571 self
.assertIn('inet6 2001:db8:0:f102::15/64 scope global', output
)
2574 self
.assertIn(f
'inet 10.3.1.1/24 brd 10.3.1.255 scope global {label1}', output
)
2575 self
.assertIn(f
'inet 10.3.2.1/24 brd 10.3.2.255 scope global {label2}', output
)
2576 self
.assertIn(f
'inet 10.3.3.1/24 brd 10.3.3.255 scope global {label3}', output
)
2579 self
.assertIn(f
'inet 10.4.1.1/24{broadcast1} scope global dummy98', output
)
2580 self
.assertIn(f
'inet 10.4.2.1/24{broadcast2} scope global dummy98', output
)
2581 self
.assertIn(f
'inet 10.4.3.1/24{broadcast3} scope global dummy98', output
)
2584 self
.assertIn(f
'inet 10.5.1.1{peer1} scope global dummy98', output
)
2585 self
.assertIn(f
'inet 10.5.2.1{peer2} scope global dummy98', output
)
2586 self
.assertIn(f
'inet 10.5.3.1{peer3} scope global dummy98', output
)
2587 self
.assertIn(f
'inet6 2001:db8:0:f103::1{peer4} scope global', output
)
2588 self
.assertIn(f
'inet6 2001:db8:0:f103::2{peer5} scope global', output
)
2589 self
.assertIn(f
'inet6 2001:db8:0:f103::3{peer6} scope global', output
)
2592 self
.assertIn(f
'inet 10.6.1.1/24 brd 10.6.1.255 scope {scope1} dummy98', output
)
2593 self
.assertIn(f
'inet 10.6.2.1/24 brd 10.6.2.255 scope {scope2} dummy98', output
)
2596 self
.assertIn(f
'inet 10.7.1.1/24 brd 10.7.1.255 scope global{deprecated1} dummy98', output
)
2597 self
.assertIn(f
'inet 10.7.2.1/24 brd 10.7.2.255 scope global{deprecated2} dummy98', output
)
2598 self
.assertIn(f
'inet6 2001:db8:0:f104::1/64 scope global{deprecated3}', output
)
2599 self
.assertIn(f
'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output
)
2602 self
.assertRegex(output
, rf
'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98')
2603 self
.assertRegex(output
, rf
'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global')
2605 output_route
= check_output('ip -4 route show dev dummy98 10.8.1.0/24')
2607 self
.assertIn(f
'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route
)
2609 output_route
= check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64')
2611 self
.assertIn(f
'2001:db8:0:f105::/64 proto kernel metric {route_metric}', output_route
)
2614 self
.assertIn(f
'inet 10.9.1.1/24 brd 10.9.1.255 scope global{flag1} dummy98', output
)
2615 self
.assertIn(f
'inet 10.9.2.1/24 brd 10.9.2.255 scope global{flag2} dummy98', output
)
2616 self
.assertIn(f
'inet6 2001:db8:0:f106::1/64 scope global{flag3}', output
)
2617 self
.assertIn(f
'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output
)
2620 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2621 prefix16
= ip4_null_16
[:-len('.0.1')]
2622 self
.assertTrue(ip4_null_24
.endswith('.1'))
2623 prefix24
= ip4_null_24
[:-len('.1')]
2624 self
.assertIn(f
'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output
)
2625 self
.assertIn(f
'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output
)
2626 self
.assertIn(f
'inet6 {ip6_null_73}/73 scope global', output
)
2627 self
.assertIn(f
'inet6 {ip6_null_74}/74 scope global', output
)
2630 self
.assertNotIn('10.4.4.1', output
)
2631 self
.assertNotIn('10.5.4.1', output
)
2632 self
.assertNotIn('10.5.5.1', output
)
2633 self
.assertNotIn('10.8.2.1', output
)
2634 self
.assertNotIn('10.9.3.1', output
)
2635 self
.assertNotIn('2001:db8:0:f101::2', output
)
2636 self
.assertNotIn('2001:db8:0:f103::4', output
)
2639 self
.check_netlabel('dummy98', r
'10\.10\.1\.0/24')
2641 check_json(networkctl_json())
2643 def test_address_static(self
):
2644 copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins
=False)
2645 self
.setup_nftset('addr4', 'ipv4_addr')
2646 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
2647 self
.setup_nftset('ifindex', 'iface_index')
2650 self
.wait_online('dummy98:routable')
2654 output
= check_output('ip -4 --json address show dev dummy98')
2655 for i
in json
.loads(output
)[0]['addr_info']:
2656 if i
['label'] == 'subnet16':
2657 ip4_null_16
= i
['local']
2658 elif i
['label'] == 'subnet24':
2659 ip4_null_24
= i
['local']
2660 self
.assertTrue(ip4_null_16
.endswith('.0.1'))
2661 self
.assertTrue(ip4_null_24
.endswith('.1'))
2665 output
= check_output('ip -6 --json address show dev dummy98')
2666 for i
in json
.loads(output
)[0]['addr_info']:
2667 if i
['prefixlen'] == 73:
2668 ip6_null_73
= i
['local']
2669 elif i
['prefixlen'] == 74:
2670 ip6_null_74
= i
['local']
2671 self
.assertTrue(ip6_null_73
.endswith(':1'))
2672 self
.assertTrue(ip6_null_74
.endswith(':1'))
2674 self
.verify_address_static(
2679 broadcast2
=' brd 10.4.2.255',
2680 broadcast3
=' brd 10.4.3.63',
2681 peer1
=' peer 10.5.1.101/24',
2682 peer2
=' peer 10.5.2.101/24',
2683 peer3
='/24 brd 10.5.3.255',
2684 peer4
=' peer 2001:db8:0:f103::101/128',
2685 peer5
=' peer 2001:db8:0:f103::102/128',
2690 deprecated2
=' deprecated',
2692 deprecated4
=' deprecated',
2694 flag1
=' noprefixroute',
2696 flag3
=' noprefixroute',
2697 flag4
=' home mngtmpaddr',
2698 ip4_null_16
=ip4_null_16
,
2699 ip4_null_24
=ip4_null_24
,
2700 ip6_null_73
=ip6_null_73
,
2701 ip6_null_74
=ip6_null_74
,
2704 self
.check_nftset('addr4', r
'10\.10\.1\.1')
2705 self
.check_nftset('network4', r
'10\.10\.1\.0/24')
2706 self
.check_nftset('ifindex', 'dummy98')
2708 self
.teardown_nftset('addr4', 'network4', 'ifindex')
2710 copy_network_unit('25-address-static.network.d/10-override.conf')
2712 self
.wait_online('dummy98:routable')
2713 self
.verify_address_static(
2714 label1
='new-label1',
2716 label3
='new-label3',
2717 broadcast1
=' brd 10.4.1.255',
2719 broadcast3
=' brd 10.4.3.31',
2720 peer1
=' peer 10.5.1.102/24',
2721 peer2
='/24 brd 10.5.2.255',
2722 peer3
=' peer 10.5.3.102/24',
2723 peer4
=' peer 2001:db8:0:f103::201/128',
2725 peer6
=' peer 2001:db8:0:f103::203/128',
2728 deprecated1
=' deprecated',
2730 deprecated3
=' deprecated',
2734 flag2
=' noprefixroute',
2735 flag3
=' home mngtmpaddr',
2736 flag4
=' noprefixroute',
2737 ip4_null_16
=ip4_null_16
,
2738 ip4_null_24
=ip4_null_24
,
2739 ip6_null_73
=ip6_null_73
,
2740 ip6_null_74
=ip6_null_74
,
2743 networkctl_reconfigure('dummy98')
2744 self
.wait_online('dummy98:routable')
2745 self
.verify_address_static(
2746 label1
='new-label1',
2748 label3
='new-label3',
2749 broadcast1
=' brd 10.4.1.255',
2751 broadcast3
=' brd 10.4.3.31',
2752 peer1
=' peer 10.5.1.102/24',
2753 peer2
='/24 brd 10.5.2.255',
2754 peer3
=' peer 10.5.3.102/24',
2755 peer4
=' peer 2001:db8:0:f103::201/128',
2757 peer6
=' peer 2001:db8:0:f103::203/128',
2760 deprecated1
=' deprecated',
2762 deprecated3
=' deprecated',
2766 flag2
=' noprefixroute',
2767 flag3
=' home mngtmpaddr',
2768 flag4
=' noprefixroute',
2769 ip4_null_16
=ip4_null_16
,
2770 ip4_null_24
=ip4_null_24
,
2771 ip6_null_73
=ip6_null_73
,
2772 ip6_null_74
=ip6_null_74
,
2776 # 1. set preferred lifetime forever to drop the deprecated flag for testing #20891.
2777 check_output('ip address change 10.7.1.1/24 dev dummy98 preferred_lft forever')
2778 check_output('ip address change 2001:db8:0:f104::1/64 dev dummy98 preferred_lft forever')
2779 output
= check_output('ip address show dev dummy98')
2781 self
.assertNotRegex(output
, '10.7.1.1/24 .* deprecated')
2782 self
.assertNotRegex(output
, '2001:db8:0:f104::1/64 .* deprecated')
2784 # 2. reconfigure the interface, and check the deprecated flag is set again
2785 networkctl_reconfigure('dummy98')
2786 self
.wait_online('dummy98:routable')
2787 self
.verify_address_static(
2788 label1
='new-label1',
2790 label3
='new-label3',
2791 broadcast1
=' brd 10.4.1.255',
2793 broadcast3
=' brd 10.4.3.31',
2794 peer1
=' peer 10.5.1.102/24',
2795 peer2
='/24 brd 10.5.2.255',
2796 peer3
=' peer 10.5.3.102/24',
2797 peer4
=' peer 2001:db8:0:f103::201/128',
2799 peer6
=' peer 2001:db8:0:f103::203/128',
2802 deprecated1
=' deprecated',
2804 deprecated3
=' deprecated',
2808 flag2
=' noprefixroute',
2809 flag3
=' home mngtmpaddr',
2810 flag4
=' noprefixroute',
2811 ip4_null_16
=ip4_null_16
,
2812 ip4_null_24
=ip4_null_24
,
2813 ip6_null_73
=ip6_null_73
,
2814 ip6_null_74
=ip6_null_74
,
2817 # test for ENOBUFS issue #17012 (with reload)
2818 copy_network_unit('25-address-static.network.d/10-many-address.conf')
2820 self
.wait_online('dummy98:routable')
2821 output
= check_output('ip -4 address show dev dummy98')
2822 for i
in range(1, 254):
2823 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2825 # (with reconfigure)
2826 networkctl_reconfigure('dummy98')
2827 self
.wait_online('dummy98:routable')
2828 output
= check_output('ip -4 address show dev dummy98')
2829 for i
in range(1, 254):
2830 self
.assertIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2832 # test for an empty string assignment for Address= in [Network]
2833 copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
2835 self
.wait_online('dummy98:routable')
2836 output
= check_output('ip -4 address show dev dummy98')
2837 for i
in range(1, 254):
2838 self
.assertNotIn(f
'inet 10.3.3.{i}/16 brd 10.3.255.255', output
)
2839 self
.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output
)
2841 def test_address_ipv4acd(self
):
2842 check_output('ip netns add ns99')
2843 check_output('ip link add veth99 type veth peer veth-peer')
2844 check_output('ip link set veth-peer netns ns99')
2845 check_output('ip link set veth99 up')
2846 check_output('ip netns exec ns99 ip link set veth-peer up')
2847 check_output('ip netns exec ns99 ip address add 192.168.100.10/24 dev veth-peer')
2849 copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins
=False)
2851 self
.wait_online('veth99:routable')
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 copy_network_unit('25-address-ipv4acd-veth99.network.d/conflict-address.conf')
2860 self
.wait_operstate('veth99', operstate
='routable', setup_state
='configuring', setup_timeout
=10)
2862 output
= check_output('ip -4 address show dev veth99')
2864 self
.assertNotIn('192.168.100.10/24', output
)
2865 self
.assertIn('192.168.100.11/24', output
)
2867 def test_address_peer_ipv4(self
):
2868 # test for issue #17304
2869 copy_network_unit('25-address-peer-ipv4.network', '12-dummy.netdev')
2871 for trial
in range(2):
2877 self
.wait_online('dummy98:routable')
2879 output
= check_output('ip -4 address show dev dummy98')
2880 self
.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output
)
2882 @expectedFailureIfModuleIsNotAvailable('vrf')
2883 def test_prefix_route(self
):
2884 copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev',
2885 '25-prefix-route-without-vrf.network', '11-dummy.netdev',
2886 '25-vrf.netdev', '25-vrf.network')
2887 for trial
in range(2):
2893 self
.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
2895 output
= check_output('ip route show table 42 dev dummy98')
2896 print('### ip route show table 42 dev dummy98')
2898 self
.assertRegex(output
, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
2899 self
.assertRegex(output
, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
2900 self
.assertRegex(output
, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
2901 self
.assertRegex(output
, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
2902 self
.assertRegex(output
, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
2903 self
.assertRegex(output
, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
2904 self
.assertRegex(output
, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
2905 output
= check_output('ip -6 route show table 42 dev dummy98')
2906 print('### ip -6 route show table 42 dev dummy98')
2910 self
.assertRegex(output
, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
2911 #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
2912 self
.assertRegex(output
, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
2913 self
.assertRegex(output
, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
2914 self
.assertRegex(output
, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
2915 self
.assertRegex(output
, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
2916 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2917 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2921 output
= check_output('ip route show dev test1')
2922 print('### ip route show dev test1')
2924 self
.assertRegex(output
, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
2925 output
= check_output('ip route show table local dev test1')
2926 print('### ip route show table local dev test1')
2928 self
.assertRegex(output
, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
2929 self
.assertRegex(output
, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
2930 self
.assertRegex(output
, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
2931 self
.assertRegex(output
, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
2932 self
.assertRegex(output
, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
2933 self
.assertRegex(output
, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
2934 output
= check_output('ip -6 route show dev test1')
2935 print('### ip -6 route show dev test1')
2937 self
.assertRegex(output
, 'fdde:12:22::1 proto kernel metric 256 pref medium')
2938 self
.assertRegex(output
, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
2939 self
.assertRegex(output
, 'fe80::/64 proto kernel metric 256 pref medium')
2940 output
= check_output('ip -6 route show table local dev test1')
2941 print('### ip -6 route show table local dev test1')
2943 self
.assertRegex(output
, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
2944 self
.assertRegex(output
, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
2945 self
.assertRegex(output
, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
2946 self
.assertRegex(output
, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
2947 self
.assertRegex(output
, 'ff00::/8 (proto kernel )?metric 256 (linkdown )?pref medium')
2949 def test_configure_without_carrier(self
):
2950 copy_network_unit('11-dummy.netdev')
2952 self
.wait_operstate('test1', 'off', '')
2953 check_output('ip link set dev test1 up carrier off')
2955 copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins
=False)
2957 self
.wait_online('test1:no-carrier')
2959 carrier_map
= {'on': '1', 'off': '0'}
2960 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2961 for carrier
in ['off', 'on', 'off']:
2962 with self
.subTest(carrier
=carrier
):
2963 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2964 check_output(f
'ip link set dev test1 carrier {carrier}')
2965 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
2967 output
= networkctl_status('test1')
2969 self
.assertRegex(output
, '192.168.0.15')
2970 self
.assertRegex(output
, '192.168.0.1')
2971 self
.assertRegex(output
, routable_map
[carrier
])
2973 def test_configure_without_carrier_yes_ignore_carrier_loss_no(self
):
2974 copy_network_unit('11-dummy.netdev')
2976 self
.wait_operstate('test1', 'off', '')
2977 check_output('ip link set dev test1 up carrier off')
2979 copy_network_unit('25-test1.network')
2981 self
.wait_online('test1:no-carrier')
2983 carrier_map
= {'on': '1', 'off': '0'}
2984 routable_map
= {'on': 'routable', 'off': 'no-carrier'}
2985 for (carrier
, have_config
) in [('off', True), ('on', True), ('off', False)]:
2986 with self
.subTest(carrier
=carrier
, have_config
=have_config
):
2987 if carrier_map
[carrier
] != read_link_attr('test1', 'carrier'):
2988 check_output(f
'ip link set dev test1 carrier {carrier}')
2989 self
.wait_online(f
'test1:{routable_map[carrier]}:{routable_map[carrier]}')
2991 output
= networkctl_status('test1')
2994 self
.assertRegex(output
, '192.168.0.15')
2995 self
.assertRegex(output
, '192.168.0.1')
2997 self
.assertNotRegex(output
, '192.168.0.15')
2998 self
.assertNotRegex(output
, '192.168.0.1')
2999 self
.assertRegex(output
, routable_map
[carrier
])
3001 def test_routing_policy_rule(self
):
3002 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
3004 self
.wait_online('test1:degraded')
3006 output
= check_output('ip rule list iif test1 priority 111')
3008 self
.assertRegex(output
, '111:')
3009 self
.assertRegex(output
, 'from 192.168.100.18')
3010 self
.assertRegex(output
, r
'tos (0x08|throughput)\s')
3011 self
.assertRegex(output
, 'iif test1')
3012 self
.assertRegex(output
, 'oif test1')
3013 self
.assertRegex(output
, 'lookup 7')
3015 output
= check_output('ip rule list iif test1 priority 101')
3017 self
.assertRegex(output
, '101:')
3018 self
.assertRegex(output
, 'from all')
3019 self
.assertRegex(output
, 'iif test1')
3020 self
.assertRegex(output
, 'lookup 9')
3022 output
= check_output('ip -6 rule list iif test1 priority 100')
3024 self
.assertRegex(output
, '100:')
3025 self
.assertRegex(output
, 'from all')
3026 self
.assertRegex(output
, 'iif test1')
3027 self
.assertRegex(output
, 'lookup 8')
3029 output
= check_output('ip rule list iif test1 priority 102')
3031 self
.assertRegex(output
, '102:')
3032 self
.assertRegex(output
, 'from 0.0.0.0/8')
3033 self
.assertRegex(output
, 'iif test1')
3034 self
.assertRegex(output
, 'lookup 10')
3036 check_json(networkctl_json())
3038 def test_routing_policy_rule_issue_11280(self
):
3039 copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev',
3040 '25-routing-policy-rule-dummy98.network', '12-dummy.netdev')
3042 for trial
in range(3):
3043 restart_networkd(show_logs
=(trial
> 0))
3044 self
.wait_online('test1:degraded', 'dummy98:degraded')
3046 output
= check_output('ip rule list table 7')
3048 self
.assertRegex(output
, '111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7')
3050 output
= check_output('ip rule list table 8')
3052 self
.assertRegex(output
, '112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8')
3054 def test_routing_policy_rule_reconfigure(self
):
3055 copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
3057 self
.wait_online('test1:degraded')
3059 output
= check_output('ip rule list table 1011')
3061 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3062 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3063 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3064 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3066 output
= check_output('ip -6 rule list table 1011')
3068 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3070 copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
3072 self
.wait_online('test1:degraded')
3074 output
= check_output('ip rule list table 1011')
3076 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3077 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3078 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3079 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3081 output
= check_output('ip -6 rule list table 1011')
3083 self
.assertNotIn('10112: from all oif test1 lookup 1011', output
)
3084 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3086 call('ip rule delete priority 10111')
3087 call('ip rule delete priority 10112')
3088 call('ip rule delete priority 10113')
3089 call('ip rule delete priority 10114')
3090 call('ip -6 rule delete priority 10113')
3092 output
= check_output('ip rule list table 1011')
3094 self
.assertEqual(output
, '')
3096 output
= check_output('ip -6 rule list table 1011')
3098 self
.assertEqual(output
, '')
3100 networkctl_reconfigure('test1')
3101 self
.wait_online('test1:degraded')
3103 output
= check_output('ip rule list table 1011')
3105 self
.assertIn('10111: from all fwmark 0x3f3 lookup 1011', output
)
3106 self
.assertIn('10112: from all oif test1 lookup 1011', output
)
3107 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3108 self
.assertIn('10114: from 192.168.8.254 lookup 1011', output
)
3110 output
= check_output('ip -6 rule list table 1011')
3112 self
.assertIn('10113: from all iif test1 lookup 1011', output
)
3114 @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
3115 def test_routing_policy_rule_port_range(self
):
3116 copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
3118 self
.wait_online('test1:degraded')
3120 output
= check_output('ip rule')
3122 self
.assertRegex(output
, '111')
3123 self
.assertRegex(output
, 'from 192.168.100.18')
3124 self
.assertRegex(output
, '1123-1150')
3125 self
.assertRegex(output
, '3224-3290')
3126 self
.assertRegex(output
, 'tcp')
3127 self
.assertRegex(output
, 'lookup 7')
3129 @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
3130 def test_routing_policy_rule_invert(self
):
3131 copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
3133 self
.wait_online('test1:degraded')
3135 output
= check_output('ip rule')
3137 self
.assertRegex(output
, '111')
3138 self
.assertRegex(output
, 'not.*?from.*?192.168.100.18')
3139 self
.assertRegex(output
, 'tcp')
3140 self
.assertRegex(output
, 'lookup 7')
3142 @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
3143 def test_routing_policy_rule_l3mdev(self
):
3144 copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
3146 self
.wait_online('test1:degraded')
3148 output
= check_output('ip rule')
3150 self
.assertIn('1500: from all lookup [l3mdev-table]', output
)
3151 self
.assertIn('2000: from all lookup [l3mdev-table] unreachable', output
)
3153 @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
3154 def test_routing_policy_rule_uidrange(self
):
3155 copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
3157 self
.wait_online('test1:degraded')
3159 output
= check_output('ip rule')
3161 self
.assertRegex(output
, '111')
3162 self
.assertRegex(output
, 'from 192.168.100.18')
3163 self
.assertRegex(output
, 'lookup 7')
3164 self
.assertRegex(output
, 'uidrange 100-200')
3166 def _test_route_static(self
, manage_foreign_routes
):
3167 if not manage_foreign_routes
:
3168 copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf')
3170 copy_network_unit('25-route-static.network', '12-dummy.netdev',
3171 '25-route-static-test1.network', '11-dummy.netdev')
3173 self
.wait_online('dummy98:routable')
3175 output
= networkctl_status('dummy98')
3178 print('### ip -6 route show dev dummy98')
3179 output
= check_output('ip -6 route show dev dummy98')
3181 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3182 self
.assertIn('2001:1234:5:8f63::1 proto kernel', output
)
3183 self
.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output
)
3185 print('### ip -6 route show default')
3186 output
= check_output('ip -6 route show default')
3188 self
.assertIn('default', output
)
3189 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff', output
)
3191 print('### ip -4 route show dev dummy98')
3192 output
= check_output('ip -4 route show dev dummy98')
3194 self
.assertIn('149.10.124.48/28 proto kernel scope link src 149.10.124.58', output
)
3195 self
.assertIn('149.10.124.64 proto static scope link', output
)
3196 self
.assertIn('169.254.0.0/16 proto static scope link metric 2048', output
)
3197 self
.assertIn('192.168.1.1 proto static scope link initcwnd 20', output
)
3198 self
.assertIn('192.168.1.2 proto static scope link initrwnd 30', output
)
3199 self
.assertIn('192.168.1.3 proto static scope link advmss 30', output
)
3200 self
.assertIn('192.168.1.4 proto static scope link hoplimit 122', output
)
3201 self
.assertIn('multicast 149.10.123.4 proto static', output
)
3203 print('### ip -4 route show dev dummy98 default')
3204 output
= check_output('ip -4 route show dev dummy98 default')
3206 self
.assertIn('default via 149.10.125.65 proto static onlink', output
)
3207 self
.assertIn('default via 149.10.124.64 proto static', output
)
3208 self
.assertIn('default proto static', output
)
3209 self
.assertIn('default via 1.1.8.104 proto static', output
)
3211 print('### ip -4 route show table local dev dummy98')
3212 output
= check_output('ip -4 route show table local dev dummy98')
3214 self
.assertIn('local 149.10.123.1 proto static scope host', output
)
3215 self
.assertIn('anycast 149.10.123.2 proto static scope link', output
)
3216 self
.assertIn('broadcast 149.10.123.3 proto static scope link', output
)
3218 print('### ip -4 route show type blackhole')
3219 output
= check_output('ip -4 route show type blackhole')
3221 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3223 print('### ip -4 route show type unreachable')
3224 output
= check_output('ip -4 route show type unreachable')
3226 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3228 print('### ip -4 route show type prohibit')
3229 output
= check_output('ip -4 route show type prohibit')
3231 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3233 print('### ip -6 route show type blackhole')
3234 output
= check_output('ip -6 route show type blackhole')
3236 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3238 print('### ip -6 route show type unreachable')
3239 output
= check_output('ip -6 route show type unreachable')
3241 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3243 print('### ip -6 route show type prohibit')
3244 output
= check_output('ip -6 route show type prohibit')
3246 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3248 print('### ip route show 192.168.10.1')
3249 output
= check_output('ip route show 192.168.10.1')
3251 self
.assertIn('192.168.10.1 proto static', output
)
3252 self
.assertIn('nexthop via 149.10.123.59 dev test1 weight 20', output
)
3253 self
.assertIn('nexthop via 149.10.123.60 dev test1 weight 30', output
)
3254 self
.assertIn('nexthop via 149.10.124.59 dev dummy98 weight 10', output
)
3255 self
.assertIn('nexthop via 149.10.124.60 dev dummy98 weight 5', output
)
3257 print('### ip route show 192.168.10.2')
3258 output
= check_output('ip route show 192.168.10.2')
3260 # old ip command does not show IPv6 gateways...
3261 self
.assertIn('192.168.10.2 proto static', output
)
3262 self
.assertIn('nexthop', output
)
3263 self
.assertIn('dev test1 weight 20', output
)
3264 self
.assertIn('dev test1 weight 30', output
)
3265 self
.assertIn('dev dummy98 weight 10', output
)
3266 self
.assertIn('dev dummy98 weight 5', output
)
3268 print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3269 output
= check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
3271 # old ip command does not show 'nexthop' keyword and weight...
3272 self
.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output
)
3273 self
.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output
)
3274 self
.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output
)
3275 self
.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output
)
3276 self
.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output
)
3278 check_json(networkctl_json())
3280 copy_network_unit('25-address-static.network', copy_dropins
=False)
3282 self
.wait_online('dummy98:routable')
3284 # check all routes managed by Manager are removed
3285 print('### ip -4 route show type blackhole')
3286 output
= check_output('ip -4 route show type blackhole')
3288 self
.assertEqual(output
, '')
3290 print('### ip -4 route show type unreachable')
3291 output
= check_output('ip -4 route show type unreachable')
3293 self
.assertEqual(output
, '')
3295 print('### ip -4 route show type prohibit')
3296 output
= check_output('ip -4 route show type prohibit')
3298 self
.assertEqual(output
, '')
3300 print('### ip -6 route show type blackhole')
3301 output
= check_output('ip -6 route show type blackhole')
3303 self
.assertEqual(output
, '')
3305 print('### ip -6 route show type unreachable')
3306 output
= check_output('ip -6 route show type unreachable')
3308 self
.assertEqual(output
, '')
3310 print('### ip -6 route show type prohibit')
3311 output
= check_output('ip -6 route show type prohibit')
3313 self
.assertEqual(output
, '')
3315 remove_network_unit('25-address-static.network')
3317 self
.wait_online('dummy98:routable')
3319 # check all routes managed by Manager are reconfigured
3320 print('### ip -4 route show type blackhole')
3321 output
= check_output('ip -4 route show type blackhole')
3323 self
.assertIn('blackhole 202.54.1.2 proto static', output
)
3325 print('### ip -4 route show type unreachable')
3326 output
= check_output('ip -4 route show type unreachable')
3328 self
.assertIn('unreachable 202.54.1.3 proto static', output
)
3330 print('### ip -4 route show type prohibit')
3331 output
= check_output('ip -4 route show type prohibit')
3333 self
.assertIn('prohibit 202.54.1.4 proto static', output
)
3335 print('### ip -6 route show type blackhole')
3336 output
= check_output('ip -6 route show type blackhole')
3338 self
.assertIn('blackhole 2001:1234:5678::2 dev lo proto static', output
)
3340 print('### ip -6 route show type unreachable')
3341 output
= check_output('ip -6 route show type unreachable')
3343 self
.assertIn('unreachable 2001:1234:5678::3 dev lo proto static', output
)
3345 print('### ip -6 route show type prohibit')
3346 output
= check_output('ip -6 route show type prohibit')
3348 self
.assertIn('prohibit 2001:1234:5678::4 dev lo proto static', output
)
3350 remove_link('dummy98')
3353 # check all routes managed by Manager are removed
3354 print('### ip -4 route show type blackhole')
3355 output
= check_output('ip -4 route show type blackhole')
3357 self
.assertEqual(output
, '')
3359 print('### ip -4 route show type unreachable')
3360 output
= check_output('ip -4 route show type unreachable')
3362 self
.assertEqual(output
, '')
3364 print('### ip -4 route show type prohibit')
3365 output
= check_output('ip -4 route show type prohibit')
3367 self
.assertEqual(output
, '')
3369 print('### ip -6 route show type blackhole')
3370 output
= check_output('ip -6 route show type blackhole')
3372 self
.assertEqual(output
, '')
3374 print('### ip -6 route show type unreachable')
3375 output
= check_output('ip -6 route show type unreachable')
3377 self
.assertEqual(output
, '')
3379 print('### ip -6 route show type prohibit')
3380 output
= check_output('ip -6 route show type prohibit')
3382 self
.assertEqual(output
, '')
3384 def test_route_static(self
):
3386 for manage_foreign_routes
in [True, False]:
3392 print(f
'### test_route_static(manage_foreign_routes={manage_foreign_routes})')
3393 with self
.subTest(manage_foreign_routes
=manage_foreign_routes
):
3394 self
._test
_route
_static
(manage_foreign_routes
)
3396 @expectedFailureIfRTA_VIAIsNotSupported()
3397 def test_route_via_ipv6(self
):
3398 copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
3400 self
.wait_online('dummy98:routable')
3402 output
= networkctl_status('dummy98')
3405 print('### ip -6 route show dev dummy98')
3406 output
= check_output('ip -6 route show dev dummy98')
3408 self
.assertRegex(output
, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
3409 self
.assertRegex(output
, '2001:1234:5:8f63::1 proto kernel')
3411 print('### ip -4 route show dev dummy98')
3412 output
= check_output('ip -4 route show dev dummy98')
3414 self
.assertRegex(output
, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
3415 self
.assertRegex(output
, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
3417 @expectedFailureIfModuleIsNotAvailable('tcp_dctcp')
3418 def test_route_congctl(self
):
3419 copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
3421 self
.wait_online('dummy98:routable')
3423 print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3424 output
= check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
3426 self
.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output
)
3427 self
.assertIn('congctl dctcp', output
)
3429 print('### ip -4 route show dev dummy98 149.10.124.66')
3430 output
= check_output('ip -4 route show dev dummy98 149.10.124.66')
3432 self
.assertIn('149.10.124.66 proto static', output
)
3433 self
.assertIn('congctl dctcp', output
)
3434 self
.assertIn('rto_min 300s', output
)
3436 @expectedFailureIfModuleIsNotAvailable('vrf')
3437 def test_route_vrf(self
):
3438 copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
3439 '25-vrf.netdev', '25-vrf.network')
3441 self
.wait_online('dummy98:routable', 'vrf99:carrier')
3443 output
= check_output('ip route show vrf vrf99')
3445 self
.assertRegex(output
, 'default via 192.168.100.1')
3447 output
= check_output('ip route show')
3449 self
.assertNotRegex(output
, 'default via 192.168.100.1')
3451 def test_gateway_reconfigure(self
):
3452 copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
3454 self
.wait_online('dummy98:routable')
3455 print('### ip -4 route show dev dummy98 default')
3456 output
= check_output('ip -4 route show dev dummy98 default')
3458 self
.assertIn('default via 149.10.124.59 proto static', output
)
3459 self
.assertNotIn('149.10.124.60', output
)
3461 remove_network_unit('25-gateway-static.network')
3462 copy_network_unit('25-gateway-next-static.network')
3464 self
.wait_online('dummy98:routable')
3465 print('### ip -4 route show dev dummy98 default')
3466 output
= check_output('ip -4 route show dev dummy98 default')
3468 self
.assertNotIn('149.10.124.59', output
)
3469 self
.assertIn('default via 149.10.124.60 proto static', output
)
3471 def test_ip_route_ipv6_src_route(self
):
3472 # a dummy device does not make the addresses go through tentative state, so we
3473 # reuse a bond from an earlier test, which does make the addresses go through
3474 # tentative state, and do our test on that
3475 copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
3477 self
.wait_online('dummy98:enslaved', 'bond199:routable')
3479 output
= check_output('ip -6 route list dev bond199')
3481 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::2', output
)
3483 def test_route_preferred_source_with_existing_address(self
):
3485 copy_network_unit('25-route-preferred-source.network', '12-dummy.netdev')
3490 networkctl_reconfigure('dummy98')
3492 self
.wait_online('dummy98:routable')
3494 output
= check_output('ip -6 route list dev dummy98')
3496 self
.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output
)
3498 def test_ip_link_mac_address(self
):
3499 copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
3501 self
.wait_online('dummy98:degraded')
3503 output
= check_output('ip link show dummy98')
3505 self
.assertRegex(output
, '00:01:02:aa:bb:cc')
3507 def test_ip_link_unmanaged(self
):
3508 copy_network_unit('25-link-section-unmanaged.network', '12-dummy.netdev')
3511 self
.wait_operstate('dummy98', 'off', setup_state
='unmanaged')
3513 def test_ipv6_address_label(self
):
3514 copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
3516 self
.wait_online('dummy98:degraded')
3518 output
= check_output('ip addrlabel list')
3520 self
.assertRegex(output
, '2004:da8:1::/64')
3522 def test_ipv6_proxy_ndp(self
):
3523 copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
3526 self
.wait_online('dummy98:routable')
3528 output
= check_output('ip neighbor show proxy dev dummy98')
3530 for i
in range(1, 5):
3531 self
.assertRegex(output
, f
'2607:5300:203:5215:{i}::1 *proxy')
3533 def test_ipv6_neigh_retrans_time(self
):
3535 copy_network_unit('25-dummy.netdev', '25-dummy.network')
3538 self
.wait_online(f
'{link}:degraded')
3539 remove_network_unit('25-dummy.network')
3541 # expect retrans_time_ms updated
3542 copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3544 self
.wait_online(f
'{link}:degraded')
3545 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3546 remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
3548 # expect retrans_time_ms unchanged
3549 copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3551 self
.wait_online(f
'{link}:degraded')
3552 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3553 remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
3555 # expect retrans_time_ms unchanged
3556 copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3558 self
.wait_online(f
'{link}:degraded')
3559 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3560 remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
3562 # expect retrans_time_ms unchanged
3563 copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3565 self
.wait_online(f
'{link}:degraded')
3566 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3567 remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
3569 # expect retrans_time_ms unchanged
3570 copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3572 self
.wait_online(f
'{link}:degraded')
3573 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '3000')
3574 remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
3576 # expect retrans_time_ms updated
3577 copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3579 self
.wait_online(f
'{link}:degraded')
3580 self
.check_ipv6_neigh_sysctl_attr(link
, 'retrans_time_ms', '4000')
3581 remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
3583 def test_neighbor(self
):
3584 copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
3585 '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
3586 '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
3589 self
.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
3591 print('### ip neigh list dev gretun97')
3592 output
= check_output('ip neigh list dev gretun97')
3594 self
.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output
)
3595 self
.assertNotIn('10.0.0.23', output
)
3597 print('### ip neigh list dev ip6gretun97')
3598 output
= check_output('ip neigh list dev ip6gretun97')
3600 self
.assertRegex(output
, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
3601 self
.assertNotIn('2001:db8:0:f102::18', output
)
3603 print('### ip neigh list dev dummy98')
3604 output
= check_output('ip neigh list dev dummy98')
3606 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output
)
3607 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output
)
3608 self
.assertNotIn('2004:da8:1:0::2', output
)
3609 self
.assertNotIn('192.168.10.2', output
)
3610 self
.assertNotIn('00:00:5e:00:02:67', output
)
3612 check_json(networkctl_json())
3614 # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
3615 # the valid configurations in 10-step1.conf.
3616 copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
3618 self
.wait_online('dummy98:degraded')
3620 print('### ip neigh list dev dummy98')
3621 output
= check_output('ip neigh list dev dummy98')
3623 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output
)
3624 self
.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3625 self
.assertNotIn('2004:da8:1:0::2', output
)
3626 self
.assertNotIn('192.168.10.2', output
)
3627 self
.assertNotIn('00:00:5e:00:02:67', output
)
3629 check_json(networkctl_json())
3631 remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
3632 '25-neighbor-dummy.network.d/10-step2.conf')
3633 copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
3635 self
.wait_online('dummy98:degraded')
3637 print('### ip neigh list dev dummy98')
3638 output
= check_output('ip neigh list dev dummy98')
3640 self
.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output
)
3641 self
.assertNotIn('00:00:5e:00:02:65', output
)
3642 self
.assertNotIn('00:00:5e:00:02:66', output
)
3643 self
.assertNotIn('00:00:5e:00:03:65', output
)
3644 self
.assertNotIn('2004:da8:1::1', output
)
3646 def test_link_local_addressing(self
):
3647 copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
3648 '25-link-local-addressing-no.network', '12-dummy.netdev')
3650 self
.wait_online('test1:degraded', 'dummy98:carrier')
3652 output
= check_output('ip address show dev test1')
3654 self
.assertRegex(output
, 'inet .* scope link')
3655 self
.assertRegex(output
, 'inet6 .* scope link')
3657 output
= check_output('ip address show dev dummy98')
3659 self
.assertNotRegex(output
, 'inet6* .* scope link')
3661 # Documentation/networking/ip-sysctl.txt
3663 # addr_gen_mode - INTEGER
3664 # Defines how link-local and autoconf addresses are generated.
3666 # 0: generate address based on EUI64 (default)
3667 # 1: do no generate a link-local address, use EUI64 for addresses generated
3669 # 2: generate stable privacy addresses, using the secret from
3670 # stable_secret (RFC7217)
3671 # 3: generate stable privacy addresses, using a random secret if unset
3673 self
.check_ipv6_sysctl_attr('test1', 'stable_secret', '0123:4567:89ab:cdef:0123:4567:89ab:cdef')
3674 self
.check_ipv6_sysctl_attr('test1', 'addr_gen_mode', '2')
3675 self
.check_ipv6_sysctl_attr('dummy98', 'addr_gen_mode', '1')
3677 def test_link_local_addressing_ipv6ll(self
):
3678 copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
3680 self
.wait_online('dummy98:degraded')
3682 # An IPv6LL address exists by default.
3683 output
= check_output('ip address show dev dummy98')
3685 self
.assertRegex(output
, 'inet6 .* scope link')
3687 copy_network_unit('25-link-local-addressing-no.network')
3689 self
.wait_online('dummy98:carrier')
3691 # Check if the IPv6LL address is removed.
3692 output
= check_output('ip address show dev dummy98')
3694 self
.assertNotRegex(output
, 'inet6 .* scope link')
3696 remove_network_unit('25-link-local-addressing-no.network')
3698 self
.wait_online('dummy98:degraded')
3700 # Check if a new IPv6LL address is assigned.
3701 output
= check_output('ip address show dev dummy98')
3703 self
.assertRegex(output
, 'inet6 .* scope link')
3705 def test_sysctl(self
):
3706 copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
3707 copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins
=False)
3709 self
.wait_online('dummy98:degraded')
3711 self
.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
3712 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
3713 self
.check_ipv6_sysctl_attr('dummy98', 'dad_transmits', '3')
3714 self
.check_ipv6_sysctl_attr('dummy98', 'hop_limit', '5')
3715 self
.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
3716 self
.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
3717 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
3718 self
.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
3719 self
.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
3720 self
.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
3722 copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
3724 self
.wait_online('dummy98:degraded')
3726 self
.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
3728 def test_sysctl_disable_ipv6(self
):
3729 copy_network_unit('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
3731 print('## Disable ipv6')
3732 check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
3733 check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
3736 self
.wait_online('dummy98:routable')
3738 output
= check_output('ip -4 address show dummy98')
3740 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3741 output
= check_output('ip -6 address show dummy98')
3743 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3744 self
.assertRegex(output
, 'inet6 .* scope link')
3745 output
= check_output('ip -4 route show dev dummy98')
3747 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3748 output
= check_output('ip -6 route show default')
3750 self
.assertRegex(output
, 'default')
3751 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3753 remove_link('dummy98')
3755 print('## Enable ipv6')
3756 check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
3757 check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
3760 self
.wait_online('dummy98:routable')
3762 output
= check_output('ip -4 address show dummy98')
3764 self
.assertRegex(output
, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
3765 output
= check_output('ip -6 address show dummy98')
3767 self
.assertRegex(output
, 'inet6 2607:5300:203:3906::/64 scope global')
3768 self
.assertRegex(output
, 'inet6 .* scope link')
3769 output
= check_output('ip -4 route show dev dummy98')
3771 self
.assertRegex(output
, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
3772 output
= check_output('ip -6 route show default')
3774 self
.assertRegex(output
, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
3776 def test_bind_carrier(self
):
3777 copy_network_unit('25-bind-carrier.network', '11-dummy.netdev')
3780 # no bound interface.
3781 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3782 output
= check_output('ip address show test1')
3784 self
.assertNotIn('UP,LOWER_UP', output
)
3785 self
.assertIn('DOWN', output
)
3786 self
.assertNotIn('192.168.10', output
)
3788 # add one bound interface. The interface will be up.
3789 check_output('ip link add dummy98 type dummy')
3790 check_output('ip link set dummy98 up')
3791 self
.wait_online('test1:routable')
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 # add another bound interface. The interface is still up.
3798 check_output('ip link add dummy99 type dummy')
3799 check_output('ip link set dummy99 up')
3800 self
.wait_operstate('dummy99', 'degraded', setup_state
='unmanaged')
3801 output
= check_output('ip address show test1')
3803 self
.assertIn('UP,LOWER_UP', output
)
3804 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3806 # remove one of the bound interfaces. The interface is still up
3807 remove_link('dummy98')
3808 output
= check_output('ip address show test1')
3810 self
.assertIn('UP,LOWER_UP', output
)
3811 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3813 # bring down the remaining bound interface. The interface will be down.
3814 check_output('ip link set dummy99 down')
3815 self
.wait_operstate('test1', 'off')
3816 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3817 output
= check_output('ip address show test1')
3819 self
.assertNotIn('UP,LOWER_UP', output
)
3820 self
.assertIn('DOWN', output
)
3821 self
.assertNotIn('192.168.10', output
)
3823 # bring up the bound interface. The interface will be up.
3824 check_output('ip link set dummy99 up')
3825 self
.wait_online('test1:routable')
3826 output
= check_output('ip address show test1')
3828 self
.assertIn('UP,LOWER_UP', output
)
3829 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3831 # remove the remaining bound interface. The interface will be down.
3832 remove_link('dummy99')
3833 self
.wait_operstate('test1', 'off')
3834 self
.wait_address_dropped('test1', r
'192.168.10', ipv
='-4', timeout_sec
=10)
3835 output
= check_output('ip address show test1')
3837 self
.assertNotIn('UP,LOWER_UP', output
)
3838 self
.assertIn('DOWN', output
)
3839 self
.assertNotIn('192.168.10', output
)
3841 # re-add one bound interface. The interface will be up.
3842 check_output('ip link add dummy98 type dummy')
3843 check_output('ip link set dummy98 up')
3844 self
.wait_online('test1:routable')
3845 output
= check_output('ip address show test1')
3847 self
.assertIn('UP,LOWER_UP', output
)
3848 self
.assertIn('inet 192.168.10.30/24 brd 192.168.10.255 scope global test1', output
)
3850 def _test_activation_policy(self
, interface
, test
):
3851 conffile
= '25-activation-policy.network'
3853 conffile
= f
'{conffile}.d/{test}.conf'
3854 if interface
== 'vlan99':
3855 copy_network_unit('21-vlan.netdev', '21-vlan-test1.network')
3856 copy_network_unit('11-dummy.netdev', conffile
, copy_dropins
=False)
3859 always
= test
.startswith('always')
3860 initial_up
= test
!= 'manual' and not test
.endswith('down') # note: default is up
3861 expect_up
= initial_up
3862 next_up
= not expect_up
3864 if test
.endswith('down'):
3865 self
.wait_activated(interface
)
3867 for iteration
in range(4):
3868 with self
.subTest(iteration
=iteration
, expect_up
=expect_up
):
3869 operstate
= 'routable' if expect_up
else 'off'
3870 setup_state
= 'configured' if expect_up
else ('configuring' if iteration
== 0 else None)
3871 self
.wait_operstate(interface
, operstate
, setup_state
=setup_state
, setup_timeout
=20)
3874 self
.assertIn('UP', check_output(f
'ip link show {interface}'))
3875 self
.assertIn('192.168.10.30/24', check_output(f
'ip address show {interface}'))
3876 self
.assertIn('default via 192.168.10.1', check_output(f
'ip route show dev {interface}'))
3878 self
.assertIn('DOWN', check_output(f
'ip link show {interface}'))
3881 check_output(f
'ip link set dev {interface} up')
3883 check_output(f
'ip link set dev {interface} down')
3884 expect_up
= initial_up
if always
else next_up
3885 next_up
= not next_up
3889 def test_activation_policy(self
):
3891 for interface
in ['test1', 'vlan99']:
3892 for test
in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
3898 print(f
'### test_activation_policy(interface={interface}, test={test})')
3899 with self
.subTest(interface
=interface
, test
=test
):
3900 self
._test
_activation
_policy
(interface
, test
)
3902 def _test_activation_policy_required_for_online(self
, policy
, required
):
3903 conffile
= '25-activation-policy.network'
3904 units
= ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile
]
3906 units
+= [f
'{conffile}.d/{policy}.conf']
3908 units
+= [f
'{conffile}.d/required-{required}.conf']
3909 copy_network_unit(*units
, copy_dropins
=False)
3912 if policy
.endswith('down'):
3913 self
.wait_activated('test1')
3915 if policy
.endswith('down') or policy
== 'manual':
3916 self
.wait_operstate('test1', 'off', setup_state
='configuring')
3918 self
.wait_online('test1')
3920 if policy
== 'always-down':
3921 # if always-down, required for online is forced to no
3924 # otherwise if required for online is specified, it should match that
3925 expected
= required
== 'yes'
3927 # otherwise if only policy specified, required for online defaults to
3928 # true if policy is up, always-up, or bound
3929 expected
= policy
.endswith('up') or policy
== 'bound'
3931 # default is true, if neither are specified
3934 output
= networkctl_status('test1')
3937 yesno
= 'yes' if expected
else 'no'
3938 self
.assertRegex(output
, f
'Required For Online: {yesno}')
3940 def test_activation_policy_required_for_online(self
):
3942 for policy
in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
3943 for required
in ['yes', 'no', '']:
3949 print(f
'### test_activation_policy_required_for_online(policy={policy}, required={required})')
3950 with self
.subTest(policy
=policy
, required
=required
):
3951 self
._test
_activation
_policy
_required
_for
_online
(policy
, required
)
3953 def test_domain(self
):
3954 copy_network_unit('12-dummy.netdev', '24-search-domain.network')
3956 self
.wait_online('dummy98:routable')
3958 output
= networkctl_status('dummy98')
3960 self
.assertRegex(output
, 'Address: 192.168.42.100')
3961 self
.assertRegex(output
, 'DNS: 192.168.42.1')
3962 self
.assertRegex(output
, 'Search Domains: one')
3964 def test_keep_configuration_static(self
):
3965 check_output('ip link add name dummy98 type dummy')
3966 check_output('ip address add 10.1.2.3/16 dev dummy98')
3967 check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
3968 output
= check_output('ip address show dummy98')
3970 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3971 self
.assertRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3972 output
= check_output('ip route show dev dummy98')
3975 copy_network_unit('24-keep-configuration-static.network')
3977 self
.wait_online('dummy98:routable')
3979 output
= check_output('ip address show dummy98')
3981 self
.assertRegex(output
, 'inet 10.1.2.3/16 scope global dummy98')
3982 self
.assertNotRegex(output
, 'inet 10.2.3.4/16 scope global dynamic dummy98')
3984 def check_nexthop(self
, manage_foreign_nexthops
, first
):
3985 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
3987 output
= check_output('ip nexthop list dev veth99')
3990 self
.assertIn('id 1 via 192.168.5.1 dev veth99', output
)
3991 self
.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output
)
3993 self
.assertIn('id 6 via 192.168.5.1 dev veth99', output
)
3994 self
.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output
)
3995 self
.assertIn('id 3 dev veth99', output
)
3996 self
.assertIn('id 4 dev veth99', output
)
3998 self
.assertRegex(output
, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
4000 self
.assertIn('id 5 via 192.168.5.3 dev veth99', output
)
4001 self
.assertNotRegex(output
, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
4002 self
.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output
)
4003 if manage_foreign_nexthops
:
4004 self
.assertRegex(output
, r
'id [0-9]* via 192.168.5.2 dev veth99')
4006 output
= check_output('ip nexthop list dev dummy98')
4009 self
.assertIn('id 20 via 192.168.20.1 dev dummy98', output
)
4011 self
.assertIn('id 21 via 192.168.20.1 dev dummy98', output
)
4012 if manage_foreign_nexthops
:
4013 self
.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output
)
4015 self
.assertIn('id 42 via 192.168.20.2 dev dummy98', output
)
4017 # kernel manages blackhole nexthops on lo
4018 output
= check_output('ip nexthop list dev lo')
4021 self
.assertIn('id 6 blackhole', output
)
4022 self
.assertIn('id 7 blackhole', output
)
4024 self
.assertIn('id 1 blackhole', output
)
4025 self
.assertIn('id 2 blackhole', output
)
4027 # group nexthops are shown with -0 option
4029 output
= check_output('ip -0 nexthop list id 21')
4031 self
.assertRegex(output
, r
'id 21 group (1,3/20|20/1,3)')
4033 output
= check_output('ip -0 nexthop list id 20')
4035 self
.assertRegex(output
, r
'id 20 group (5,3/21|21/5,3)')
4037 output
= check_output('ip route show dev veth99 10.10.10.10')
4040 self
.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output
)
4042 self
.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output
)
4044 output
= check_output('ip route show dev veth99 10.10.10.11')
4047 self
.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output
)
4049 self
.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output
)
4051 output
= check_output('ip route show dev veth99 10.10.10.12')
4054 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output
)
4056 self
.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output
)
4058 output
= check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
4061 self
.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4063 self
.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output
)
4065 output
= check_output('ip route show 10.10.10.13')
4068 self
.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output
)
4070 self
.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output
)
4072 output
= check_output('ip -6 route show 2001:1234:5:8f62::2')
4075 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output
)
4077 self
.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output
)
4079 output
= check_output('ip route show 10.10.10.14')
4082 self
.assertIn('10.10.10.14 nhid 21 proto static', output
)
4083 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4085 self
.assertIn('10.10.10.14 nhid 20 proto static', output
)
4086 self
.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output
)
4087 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4089 output
= networkctl_json()
4091 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4093 def _test_nexthop(self
, manage_foreign_nexthops
):
4094 if not manage_foreign_nexthops
:
4095 copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
4097 check_output('ip link add dummy98 type dummy')
4098 check_output('ip link set dummy98 up')
4099 check_output('ip address add 192.168.20.20/24 dev dummy98')
4100 check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
4102 copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
4103 '12-dummy.netdev', '25-nexthop-dummy-1.network')
4106 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4108 remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4109 copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
4111 self
.check_nexthop(manage_foreign_nexthops
, first
=False)
4113 remove_network_unit('25-nexthop-2.network')
4114 copy_network_unit('25-nexthop-nothing.network')
4116 self
.wait_online('veth99:routable', 'veth-peer:routable')
4118 output
= check_output('ip nexthop list dev veth99')
4120 self
.assertEqual(output
, '')
4121 output
= check_output('ip nexthop list dev lo')
4123 self
.assertEqual(output
, '')
4125 remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
4126 copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
4127 # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
4128 # here to test reconfiguring with different .network files does not trigger race.
4129 # See also comments in link_drop_requests().
4130 networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
4131 networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
4133 self
.check_nexthop(manage_foreign_nexthops
, first
=True)
4135 # Remove nexthop with ID 20
4136 check_output('ip nexthop del id 20')
4137 copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
4140 # 25-nexthop-test1.network requests a route with nexthop ID 21,
4141 # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
4142 # hence test1 should be stuck in the configuring state.
4143 self
.wait_operstate('test1', operstate
='routable', setup_state
='configuring')
4145 # Wait for a while, and check if the interface is still in the configuring state.
4147 output
= networkctl_status('test1')
4148 self
.assertIn('State: routable (configuring)', output
)
4150 # Check if the route which needs nexthop 20 and 21 are forgotten.
4151 output
= networkctl_json()
4153 self
.assertNotIn('"Destination":[10.10.10.14]', output
)
4155 # Reconfigure the interface that has nexthop with ID 20 and 21,
4156 # then the route requested by test1 can be configured.
4157 networkctl_reconfigure('dummy98')
4158 self
.wait_online('test1:routable')
4160 # Check if the requested route actually configured.
4161 output
= check_output('ip route show 10.10.11.10')
4163 self
.assertIn('10.10.11.10 nhid 21 proto static', output
)
4164 self
.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output
)
4165 self
.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output
)
4167 remove_link('veth99')
4170 output
= check_output('ip nexthop list dev lo')
4172 self
.assertEqual(output
, '')
4174 @expectedFailureIfNexthopIsNotAvailable()
4175 def test_nexthop(self
):
4177 for manage_foreign_nexthops
in [True, False]:
4183 print(f
'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
4184 with self
.subTest(manage_foreign_nexthops
=manage_foreign_nexthops
):
4185 self
._test
_nexthop
(manage_foreign_nexthops
)
4187 class NetworkdTCTests(unittest
.TestCase
, Utilities
):
4195 @expectedFailureIfModuleIsNotAvailable('sch_cake')
4196 def test_qdisc_cake(self
):
4197 copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
4199 self
.wait_online('dummy98:routable')
4201 output
= check_output('tc qdisc show dev dummy98')
4203 self
.assertIn('qdisc cake 3a: root', output
)
4204 self
.assertIn('bandwidth 500Mbit', output
)
4205 self
.assertIn('autorate-ingress', output
)
4206 self
.assertIn('diffserv8', output
)
4207 self
.assertIn('dual-dsthost', output
)
4208 self
.assertIn(' nat', output
)
4209 self
.assertIn(' wash', output
)
4210 self
.assertIn(' split-gso', output
)
4211 self
.assertIn(' raw', output
)
4212 self
.assertIn(' atm', output
)
4213 self
.assertIn('overhead 128', output
)
4214 self
.assertIn('mpu 20', output
)
4215 self
.assertIn('fwmark 0xff00', output
)
4216 self
.assertIn('rtt 1s', output
)
4217 self
.assertIn('ack-filter-aggressive', output
)
4219 @expectedFailureIfModuleIsNotAvailable('sch_codel')
4220 def test_qdisc_codel(self
):
4221 copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
4223 self
.wait_online('dummy98:routable')
4225 output
= check_output('tc qdisc show dev dummy98')
4227 self
.assertRegex(output
, 'qdisc codel 33: root')
4228 self
.assertRegex(output
, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
4230 @expectedFailureIfModuleIsNotAvailable('sch_drr')
4231 def test_qdisc_drr(self
):
4232 copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
4234 self
.wait_online('dummy98:routable')
4236 output
= check_output('tc qdisc show dev dummy98')
4238 self
.assertRegex(output
, 'qdisc drr 2: root')
4239 output
= check_output('tc class show dev dummy98')
4241 self
.assertRegex(output
, 'class drr 2:30 root quantum 2000b')
4243 @expectedFailureIfModuleIsNotAvailable('sch_ets')
4244 def test_qdisc_ets(self
):
4245 copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
4247 self
.wait_online('dummy98:routable')
4249 output
= check_output('tc qdisc show dev dummy98')
4252 self
.assertRegex(output
, 'qdisc ets 3a: root')
4253 self
.assertRegex(output
, 'bands 10 strict 3')
4254 self
.assertRegex(output
, 'quanta 1 2 3 4 5')
4255 self
.assertRegex(output
, 'priomap 3 4 5 6 7')
4257 @expectedFailureIfModuleIsNotAvailable('sch_fq')
4258 def test_qdisc_fq(self
):
4259 copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
4261 self
.wait_online('dummy98:routable')
4263 output
= check_output('tc qdisc show dev dummy98')
4265 self
.assertRegex(output
, 'qdisc fq 32: root')
4266 self
.assertRegex(output
, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
4267 self
.assertRegex(output
, 'quantum 1500')
4268 self
.assertRegex(output
, 'initial_quantum 13000')
4269 self
.assertRegex(output
, 'maxrate 1Mbit')
4271 @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
4272 def test_qdisc_fq_codel(self
):
4273 copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
4275 self
.wait_online('dummy98:routable')
4277 output
= check_output('tc qdisc show dev dummy98')
4279 self
.assertRegex(output
, 'qdisc fq_codel 34: root')
4280 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')
4282 @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
4283 def test_qdisc_fq_pie(self
):
4284 copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
4286 self
.wait_online('dummy98:routable')
4288 output
= check_output('tc qdisc show dev dummy98')
4291 self
.assertRegex(output
, 'qdisc fq_pie 3a: root')
4292 self
.assertRegex(output
, 'limit 200000p')
4294 @expectedFailureIfModuleIsNotAvailable('sch_gred')
4295 def test_qdisc_gred(self
):
4296 copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
4298 self
.wait_online('dummy98:routable')
4300 output
= check_output('tc qdisc show dev dummy98')
4302 self
.assertRegex(output
, 'qdisc gred 38: root')
4303 self
.assertRegex(output
, 'vqs 12 default 10 grio')
4305 @expectedFailureIfModuleIsNotAvailable('sch_hhf')
4306 def test_qdisc_hhf(self
):
4307 copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
4309 self
.wait_online('dummy98:routable')
4311 output
= check_output('tc qdisc show dev dummy98')
4313 self
.assertRegex(output
, 'qdisc hhf 3a: root')
4314 self
.assertRegex(output
, 'limit 1022p')
4316 @expectedFailureIfModuleIsNotAvailable('sch_htb')
4317 def test_qdisc_htb_fifo(self
):
4318 copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
4320 self
.wait_online('dummy98:routable')
4322 output
= check_output('tc qdisc show dev dummy98')
4324 self
.assertRegex(output
, 'qdisc htb 2: root')
4325 self
.assertRegex(output
, r
'default (0x30|30)')
4327 self
.assertRegex(output
, 'qdisc pfifo 37: parent 2:37')
4328 self
.assertRegex(output
, 'limit 100000p')
4330 self
.assertRegex(output
, 'qdisc bfifo 3a: parent 2:3a')
4331 self
.assertRegex(output
, 'limit 1000000')
4333 self
.assertRegex(output
, 'qdisc pfifo_head_drop 3b: parent 2:3b')
4334 self
.assertRegex(output
, 'limit 1023p')
4336 self
.assertRegex(output
, 'qdisc pfifo_fast 3c: parent 2:3c')
4338 output
= check_output('tc -d class show dev dummy98')
4340 # Here (:|prio) is a workaround for a bug in iproute2 v6.2.0 caused by
4341 # https://github.com/shemminger/iproute2/commit/010a8388aea11e767ba3a2506728b9ad9760df0e
4342 # which is fixed in v6.3.0 by
4343 # https://github.com/shemminger/iproute2/commit/4e0e56e0ef05387f7f5d8ab41fe6ec6a1897b26d
4344 self
.assertRegex(output
, 'class htb 2:37 root leaf 37(:|prio) ')
4345 self
.assertRegex(output
, 'class htb 2:3a root leaf 3a(:|prio) ')
4346 self
.assertRegex(output
, 'class htb 2:3b root leaf 3b(:|prio) ')
4347 self
.assertRegex(output
, 'class htb 2:3c root leaf 3c(:|prio) ')
4348 self
.assertRegex(output
, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
4349 self
.assertRegex(output
, 'burst 123456')
4350 self
.assertRegex(output
, 'cburst 123457')
4352 @expectedFailureIfModuleIsNotAvailable('sch_ingress')
4353 def test_qdisc_ingress(self
):
4354 copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
4355 '25-qdisc-ingress.network', '11-dummy.netdev')
4357 self
.wait_online('dummy98:routable', 'test1:routable')
4359 output
= check_output('tc qdisc show dev dummy98')
4361 self
.assertRegex(output
, 'qdisc clsact')
4363 output
= check_output('tc qdisc show dev test1')
4365 self
.assertRegex(output
, 'qdisc ingress')
4367 @expectedFailureIfModuleIsNotAvailable('sch_netem')
4368 def test_qdisc_netem(self
):
4369 copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
4370 '25-qdisc-netem-compat.network', '11-dummy.netdev')
4372 self
.wait_online('dummy98:routable', 'test1:routable')
4374 output
= check_output('tc qdisc show dev dummy98')
4376 self
.assertRegex(output
, 'qdisc netem 30: root')
4377 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4379 output
= check_output('tc qdisc show dev test1')
4381 self
.assertRegex(output
, 'qdisc netem [0-9a-f]*: root')
4382 self
.assertRegex(output
, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
4384 @expectedFailureIfModuleIsNotAvailable('sch_pie')
4385 def test_qdisc_pie(self
):
4386 copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
4388 self
.wait_online('dummy98:routable')
4390 output
= check_output('tc qdisc show dev dummy98')
4392 self
.assertRegex(output
, 'qdisc pie 3a: root')
4393 self
.assertRegex(output
, 'limit 200000')
4395 @expectedFailureIfModuleIsNotAvailable('sch_qfq')
4396 def test_qdisc_qfq(self
):
4397 copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
4399 self
.wait_online('dummy98:routable')
4401 output
= check_output('tc qdisc show dev dummy98')
4403 self
.assertRegex(output
, 'qdisc qfq 2: root')
4404 output
= check_output('tc class show dev dummy98')
4406 self
.assertRegex(output
, 'class qfq 2:30 root weight 2 maxpkt 16000')
4407 self
.assertRegex(output
, 'class qfq 2:31 root weight 10 maxpkt 8000')
4409 @expectedFailureIfModuleIsNotAvailable('sch_sfb')
4410 def test_qdisc_sfb(self
):
4411 copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
4413 self
.wait_online('dummy98:routable')
4415 output
= check_output('tc qdisc show dev dummy98')
4417 self
.assertRegex(output
, 'qdisc sfb 39: root')
4418 self
.assertRegex(output
, 'limit 200000')
4420 @expectedFailureIfModuleIsNotAvailable('sch_sfq')
4421 def test_qdisc_sfq(self
):
4422 copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
4424 self
.wait_online('dummy98:routable')
4426 output
= check_output('tc qdisc show dev dummy98')
4428 self
.assertRegex(output
, 'qdisc sfq 36: root')
4429 self
.assertRegex(output
, 'perturb 5sec')
4431 @expectedFailureIfModuleIsNotAvailable('sch_tbf')
4432 def test_qdisc_tbf(self
):
4433 copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
4435 self
.wait_online('dummy98:routable')
4437 output
= check_output('tc qdisc show dev dummy98')
4439 self
.assertRegex(output
, 'qdisc tbf 35: root')
4440 self
.assertRegex(output
, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
4442 @expectedFailureIfModuleIsNotAvailable('sch_teql')
4443 def test_qdisc_teql(self
):
4444 call_quiet('rmmod sch_teql')
4446 copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
4448 self
.wait_links('dummy98')
4449 check_output('modprobe sch_teql max_equalizers=2')
4450 self
.wait_online('dummy98:routable')
4452 output
= check_output('tc qdisc show dev dummy98')
4454 self
.assertRegex(output
, 'qdisc teql1 31: root')
4456 class NetworkdStateFileTests(unittest
.TestCase
, Utilities
):
4464 def test_state_file(self
):
4465 copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
4467 self
.wait_online('dummy98:routable')
4469 # make link state file updated
4470 resolvectl('revert', 'dummy98')
4472 check_json(networkctl_json())
4474 output
= read_link_state_file('dummy98')
4476 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4477 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4478 self
.assertIn('ADMIN_STATE=configured', output
)
4479 self
.assertIn('OPER_STATE=routable', output
)
4480 self
.assertIn('REQUIRED_FOR_ONLINE=yes', output
)
4481 self
.assertIn('REQUIRED_OPER_STATE_FOR_ONLINE=routable', output
)
4482 self
.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output
)
4483 self
.assertIn('ACTIVATION_POLICY=up', output
)
4484 self
.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output
)
4485 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4486 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4487 self
.assertIn('DOMAINS=hogehoge', output
)
4488 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4489 self
.assertIn('LLMNR=no', output
)
4490 self
.assertIn('MDNS=yes', output
)
4491 self
.assertIn('DNSSEC=no', output
)
4493 resolvectl('dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333')
4494 resolvectl('domain', 'dummy98', 'hogehogehoge', '~foofoofoo')
4495 resolvectl('llmnr', 'dummy98', 'yes')
4496 resolvectl('mdns', 'dummy98', 'no')
4497 resolvectl('dnssec', 'dummy98', 'yes')
4498 timedatectl('ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org')
4500 check_json(networkctl_json())
4502 output
= read_link_state_file('dummy98')
4504 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4505 self
.assertIn('NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org', output
)
4506 self
.assertIn('DOMAINS=hogehogehoge', output
)
4507 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4508 self
.assertIn('LLMNR=yes', output
)
4509 self
.assertIn('MDNS=no', output
)
4510 self
.assertIn('DNSSEC=yes', output
)
4512 timedatectl('revert', 'dummy98')
4514 check_json(networkctl_json())
4516 output
= read_link_state_file('dummy98')
4518 self
.assertIn('DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333', output
)
4519 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4520 self
.assertIn('DOMAINS=hogehogehoge', output
)
4521 self
.assertIn('ROUTE_DOMAINS=foofoofoo', output
)
4522 self
.assertIn('LLMNR=yes', output
)
4523 self
.assertIn('MDNS=no', output
)
4524 self
.assertIn('DNSSEC=yes', output
)
4526 resolvectl('revert', 'dummy98')
4528 check_json(networkctl_json())
4530 output
= read_link_state_file('dummy98')
4532 self
.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output
)
4533 self
.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output
)
4534 self
.assertIn('DOMAINS=hogehoge', output
)
4535 self
.assertIn('ROUTE_DOMAINS=foofoo', output
)
4536 self
.assertIn('LLMNR=no', output
)
4537 self
.assertIn('MDNS=yes', output
)
4538 self
.assertIn('DNSSEC=no', output
)
4540 def test_address_state(self
):
4541 copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
4544 self
.wait_online('dummy98:degraded')
4546 output
= read_link_state_file('dummy98')
4547 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4548 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4550 # with a routable IPv4 address
4551 check_output('ip address add 10.1.2.3/16 dev dummy98')
4552 self
.wait_online('dummy98:routable', ipv4
=True)
4553 self
.wait_online('dummy98:routable')
4555 output
= read_link_state_file('dummy98')
4556 self
.assertIn('IPV4_ADDRESS_STATE=routable', output
)
4557 self
.assertIn('IPV6_ADDRESS_STATE=degraded', output
)
4559 check_output('ip address del 10.1.2.3/16 dev dummy98')
4561 # with a routable IPv6 address
4562 check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
4563 self
.wait_online('dummy98:routable', ipv6
=True)
4564 self
.wait_online('dummy98:routable')
4566 output
= read_link_state_file('dummy98')
4567 self
.assertIn('IPV4_ADDRESS_STATE=off', output
)
4568 self
.assertIn('IPV6_ADDRESS_STATE=routable', output
)
4570 class NetworkdBondTests(unittest
.TestCase
, Utilities
):
4578 def test_bond_keep_master(self
):
4579 check_output('ip link add bond199 type bond mode active-backup')
4580 check_output('ip link add dummy98 type dummy')
4581 check_output('ip link set dummy98 master bond199')
4583 copy_network_unit('23-keep-master.network')
4585 self
.wait_online('dummy98:enslaved')
4587 output
= check_output('ip -d link show bond199')
4589 self
.assertRegex(output
, 'active_slave dummy98')
4591 output
= check_output('ip -d link show dummy98')
4593 self
.assertRegex(output
, 'master bond199')
4595 def test_bond_active_slave(self
):
4596 copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4598 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4600 output
= check_output('ip -d link show bond199')
4602 self
.assertIn('active_slave dummy98', output
)
4604 # test case for issue #31165.
4605 since
= datetime
.datetime
.now()
4606 networkctl_reconfigure('dummy98')
4607 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4608 self
.assertNotIn('dummy98: Bringing link down', read_networkd_log(since
=since
))
4610 def test_bond_primary_slave(self
):
4611 copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
4613 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4615 output
= check_output('ip -d link show bond199')
4617 self
.assertIn('primary dummy98', output
)
4620 mkdir_p(os
.path
.join(network_unit_dir
, '23-bond199.network.d'))
4621 for mac
in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
4622 with
open(os
.path
.join(network_unit_dir
, '23-bond199.network.d/mac.conf'), mode
='w', encoding
='utf-8') as f
:
4623 f
.write(f
'[Link]\nMACAddress={mac}\n')
4626 self
.wait_online('dummy98:enslaved', 'bond199:degraded')
4628 output
= check_output('ip -d link show bond199')
4630 self
.assertIn(f
'link/ether {mac}', output
)
4632 def test_bond_operstate(self
):
4633 copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
4634 '25-bond99.network', '25-bond-slave.network')
4636 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
4638 output
= check_output('ip -d link show dummy98')
4640 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4642 output
= check_output('ip -d link show test1')
4644 self
.assertRegex(output
, 'SLAVE,UP,LOWER_UP')
4646 output
= check_output('ip -d link show bond99')
4648 self
.assertRegex(output
, 'MASTER,UP,LOWER_UP')
4650 self
.wait_operstate('dummy98', 'enslaved')
4651 self
.wait_operstate('test1', 'enslaved')
4652 self
.wait_operstate('bond99', 'routable')
4654 check_output('ip link set dummy98 down')
4656 self
.wait_operstate('dummy98', 'off')
4657 self
.wait_operstate('test1', 'enslaved')
4658 self
.wait_operstate('bond99', 'routable')
4660 check_output('ip link set dummy98 up')
4662 self
.wait_operstate('dummy98', 'enslaved')
4663 self
.wait_operstate('test1', 'enslaved')
4664 self
.wait_operstate('bond99', 'routable')
4666 check_output('ip link set dummy98 down')
4667 check_output('ip link set test1 down')
4669 self
.wait_operstate('dummy98', 'off')
4670 self
.wait_operstate('test1', 'off')
4672 if not self
.wait_operstate('bond99', 'no-carrier', setup_timeout
=30, fail_assert
=False):
4673 # Huh? Kernel does not recognize that all slave interfaces are down?
4674 # Let's confirm that networkd's operstate is consistent with ip's result.
4675 self
.assertNotRegex(output
, 'NO-CARRIER')
4677 class NetworkdBridgeTests(unittest
.TestCase
, Utilities
):
4685 def test_bridge_mac_none(self
):
4686 copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
4687 '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
4689 self
.wait_online('dummy98:enslaved', 'bridge99:degraded')
4691 output
= check_output('ip link show dev dummy98')
4693 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4695 output
= check_output('ip link show dev bridge99')
4697 self
.assertIn('link/ether 12:34:56:78:9a:01', output
)
4699 def test_bridge_vlan(self
):
4700 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
4701 '26-bridge.netdev', '26-bridge-vlan-master.network',
4704 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4706 output
= check_output('bridge vlan show dev test1')
4708 # check if the default VID is removed
4709 self
.assertNotIn('1 Egress Untagged', output
)
4710 for i
in range(1000, 3000):
4712 self
.assertIn(f
'{i} PVID', output
)
4713 elif i
in range(1012, 1016) or i
in range(1103, 1109):
4714 self
.assertIn(f
'{i} Egress Untagged', output
)
4715 elif i
in range(1008, 1014) or i
in range(1100, 1111):
4716 self
.assertIn(f
'{i}', output
)
4718 self
.assertNotIn(f
'{i}', output
)
4720 output
= check_output('bridge vlan show dev bridge99')
4722 # check if the default VID is removed
4723 self
.assertNotIn('1 Egress Untagged', output
)
4724 for i
in range(1000, 3000):
4726 self
.assertIn(f
'{i} PVID', output
)
4727 elif i
in range(1022, 1026) or i
in range(1203, 1209):
4728 self
.assertIn(f
'{i} Egress Untagged', output
)
4729 elif i
in range(1018, 1024) or i
in range(1200, 1211):
4730 self
.assertIn(f
'{i}', output
)
4732 self
.assertNotIn(f
'{i}', output
)
4735 copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
4736 '26-bridge-vlan-master.network.d/10-override.conf')
4738 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4740 output
= check_output('bridge vlan show dev test1')
4742 for i
in range(1000, 3000):
4744 self
.assertIn(f
'{i} PVID', output
)
4745 elif i
in range(2012, 2016) or i
in range(2103, 2109):
4746 self
.assertIn(f
'{i} Egress Untagged', output
)
4747 elif i
in range(2008, 2014) or i
in range(2100, 2111):
4748 self
.assertIn(f
'{i}', output
)
4750 self
.assertNotIn(f
'{i}', output
)
4752 output
= check_output('bridge vlan show dev bridge99')
4754 for i
in range(1000, 3000):
4756 self
.assertIn(f
'{i} PVID', output
)
4757 elif i
in range(2022, 2026) or i
in range(2203, 2209):
4758 self
.assertIn(f
'{i} Egress Untagged', output
)
4759 elif i
in range(2018, 2024) or i
in range(2200, 2211):
4760 self
.assertIn(f
'{i}', output
)
4762 self
.assertNotIn(f
'{i}', output
)
4764 # Remove several vlan IDs
4765 copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
4766 '26-bridge-vlan-master.network.d/20-override.conf')
4768 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4770 output
= check_output('bridge vlan show dev test1')
4772 for i
in range(1000, 3000):
4774 self
.assertIn(f
'{i} PVID', output
)
4775 elif i
in range(2012, 2016):
4776 self
.assertIn(f
'{i} Egress Untagged', output
)
4777 elif i
in range(2008, 2014):
4778 self
.assertIn(f
'{i}', output
)
4780 self
.assertNotIn(f
'{i}', output
)
4782 output
= check_output('bridge vlan show dev bridge99')
4784 for i
in range(1000, 3000):
4786 self
.assertIn(f
'{i} PVID', output
)
4787 elif i
in range(2022, 2026):
4788 self
.assertIn(f
'{i} Egress Untagged', output
)
4789 elif i
in range(2018, 2024):
4790 self
.assertIn(f
'{i}', output
)
4792 self
.assertNotIn(f
'{i}', output
)
4794 # Remove all vlan IDs
4795 copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
4796 '26-bridge-vlan-master.network.d/30-override.conf')
4798 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4800 output
= check_output('bridge vlan show dev test1')
4802 self
.assertNotIn('PVID', output
)
4803 for i
in range(1000, 3000):
4804 self
.assertNotIn(f
'{i}', output
)
4806 output
= check_output('bridge vlan show dev bridge99')
4808 self
.assertNotIn('PVID', output
)
4809 for i
in range(1000, 3000):
4810 self
.assertNotIn(f
'{i}', output
)
4812 def test_bridge_vlan_issue_20373(self
):
4813 copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
4814 '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
4815 '21-vlan.netdev', '21-vlan.network')
4817 self
.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
4819 output
= check_output('bridge vlan show dev test1')
4821 self
.assertIn('100 PVID Egress Untagged', output
)
4822 self
.assertIn('560', output
)
4823 self
.assertIn('600', output
)
4825 output
= check_output('bridge vlan show dev bridge99')
4827 self
.assertIn('1 PVID Egress Untagged', output
)
4828 self
.assertIn('100', output
)
4829 self
.assertIn('600', output
)
4831 def test_bridge_mdb(self
):
4832 copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
4833 '26-bridge.netdev', '26-bridge-mdb-master.network')
4835 self
.wait_online('test1:enslaved', 'bridge99:degraded')
4837 output
= check_output('bridge mdb show dev bridge99')
4839 self
.assertRegex(output
, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
4840 self
.assertRegex(output
, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
4842 # Old kernel may not support bridge MDB entries on bridge master
4843 if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0:
4844 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
4845 self
.assertRegex(output
, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
4847 def test_bridge_keep_master(self
):
4848 check_output('ip link add bridge99 type bridge')
4849 check_output('ip link set bridge99 up')
4850 check_output('ip link add dummy98 type dummy')
4851 check_output('ip link set dummy98 master bridge99')
4853 copy_network_unit('23-keep-master.network')
4855 self
.wait_online('dummy98:enslaved')
4857 output
= check_output('ip -d link show dummy98')
4859 self
.assertRegex(output
, 'master bridge99')
4860 self
.assertRegex(output
, 'bridge')
4862 output
= check_output('bridge -d link show dummy98')
4864 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4865 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4866 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4867 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4868 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4869 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4870 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4871 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4872 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4873 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4874 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4875 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4877 def test_bridge_property(self
):
4878 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
4879 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
4880 '25-bridge99.network')
4882 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
4884 output
= check_output('ip -d link show bridge99')
4886 self
.assertIn('mtu 9000 ', output
)
4888 output
= check_output('ip -d link show test1')
4890 self
.assertIn('master bridge99 ', output
)
4891 self
.assertIn('bridge_slave', output
)
4892 self
.assertIn('mtu 9000 ', output
)
4894 output
= check_output('ip -d link show dummy98')
4896 self
.assertIn('master bridge99 ', output
)
4897 self
.assertIn('bridge_slave', output
)
4898 self
.assertIn('mtu 9000 ', output
)
4900 output
= check_output('ip addr show bridge99')
4902 self
.assertIn('192.168.0.15/24', output
)
4904 output
= check_output('bridge -d link show dummy98')
4906 self
.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400')
4907 self
.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1')
4908 self
.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1')
4909 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1')
4910 self
.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1')
4911 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0')
4912 # CONFIG_BRIDGE_IGMP_SNOOPING=y
4913 self
.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent
=True)
4914 self
.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent
=True)
4915 self
.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
4916 self
.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
4917 self
.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
4918 self
.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
4920 output
= check_output('bridge -d link show test1')
4922 self
.check_bridge_port_attr('bridge99', 'test1', 'priority', '0')
4924 check_output('ip address add 192.168.0.16/24 dev bridge99')
4925 output
= check_output('ip addr show bridge99')
4927 self
.assertIn('192.168.0.16/24', output
)
4930 print('### ip -6 route list table all dev bridge99')
4931 output
= check_output('ip -6 route list table all dev bridge99')
4933 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4935 remove_link('test1')
4936 self
.wait_operstate('bridge99', 'routable')
4938 output
= check_output('ip -d link show bridge99')
4940 self
.assertIn('mtu 9000 ', output
)
4942 output
= check_output('ip -d link show dummy98')
4944 self
.assertIn('master bridge99 ', output
)
4945 self
.assertIn('bridge_slave', output
)
4946 self
.assertIn('mtu 9000 ', output
)
4948 remove_link('dummy98')
4949 self
.wait_operstate('bridge99', 'no-carrier')
4951 output
= check_output('ip -d link show bridge99')
4953 # When no carrier, the kernel may reset the MTU
4954 self
.assertIn('NO-CARRIER', output
)
4956 output
= check_output('ip address show bridge99')
4958 self
.assertNotIn('192.168.0.15/24', output
)
4959 self
.assertIn('192.168.0.16/24', output
) # foreign address is kept
4961 print('### ip -6 route list table all dev bridge99')
4962 output
= check_output('ip -6 route list table all dev bridge99')
4964 self
.assertRegex(output
, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
4966 check_output('ip link add dummy98 type dummy')
4967 self
.wait_online('dummy98:enslaved', 'bridge99:routable')
4969 output
= check_output('ip -d link show bridge99')
4971 self
.assertIn('mtu 9000 ', output
)
4973 output
= check_output('ip -d link show dummy98')
4975 self
.assertIn('master bridge99 ', output
)
4976 self
.assertIn('bridge_slave', output
)
4977 self
.assertIn('mtu 9000 ', output
)
4979 def test_bridge_configure_without_carrier(self
):
4980 copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
4984 # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
4985 for test
in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
4986 with self
.subTest(test
=test
):
4987 if test
== 'no-slave':
4988 # bridge has no slaves; it's up but *might* not have carrier
4989 self
.wait_operstate('bridge99', operstate
=r
'(no-carrier|routable)', setup_state
=None, setup_timeout
=30)
4990 # due to a bug in the kernel, newly-created bridges are brought up
4991 # *with* carrier, unless they have had any setting changed; e.g.
4992 # their mac set, priority set, etc. Then, they will lose carrier
4993 # as soon as a (down) slave interface is added, and regain carrier
4994 # again once the slave interface is brought up.
4995 #self.check_link_attr('bridge99', 'carrier', '0')
4996 elif test
== 'add-slave':
4997 # add slave to bridge, but leave it down; bridge is definitely no-carrier
4998 self
.check_link_attr('test1', 'operstate', 'down')
4999 check_output('ip link set dev test1 master bridge99')
5000 self
.wait_operstate('bridge99', operstate
='no-carrier', setup_state
=None)
5001 self
.check_link_attr('bridge99', 'carrier', '0')
5002 elif test
== 'slave-up':
5003 # bring up slave, which will have carrier; bridge gains carrier
5004 check_output('ip link set dev test1 up')
5005 self
.wait_online('bridge99:routable')
5006 self
.check_link_attr('bridge99', 'carrier', '1')
5007 elif test
== 'slave-no-carrier':
5008 # drop slave carrier; bridge loses carrier
5009 check_output('ip link set dev test1 carrier off')
5010 self
.wait_online('bridge99:no-carrier:no-carrier')
5011 self
.check_link_attr('bridge99', 'carrier', '0')
5012 elif test
== 'slave-carrier':
5013 # restore slave carrier; bridge gains carrier
5014 check_output('ip link set dev test1 carrier on')
5015 self
.wait_online('bridge99:routable')
5016 self
.check_link_attr('bridge99', 'carrier', '1')
5017 elif test
== 'slave-down':
5018 # bring down slave; bridge loses carrier
5019 check_output('ip link set dev test1 down')
5020 self
.wait_online('bridge99:no-carrier:no-carrier')
5021 self
.check_link_attr('bridge99', 'carrier', '0')
5023 output
= networkctl_status('bridge99')
5024 self
.assertRegex(output
, '10.1.2.3')
5025 self
.assertRegex(output
, '10.1.2.1')
5027 def test_bridge_ignore_carrier_loss(self
):
5028 copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
5029 '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
5030 '25-bridge99-ignore-carrier-loss.network')
5032 self
.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
5034 check_output('ip address add 192.168.0.16/24 dev bridge99')
5035 remove_link('test1', 'dummy98')
5038 output
= check_output('ip address show bridge99')
5040 self
.assertRegex(output
, 'NO-CARRIER')
5041 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5042 self
.assertRegex(output
, 'inet 192.168.0.16/24 scope global secondary bridge99')
5044 def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self
):
5045 copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
5046 '25-bridge99-ignore-carrier-loss.network')
5048 self
.wait_online('bridge99:no-carrier')
5050 for trial
in range(4):
5051 check_output('ip link add dummy98 type dummy')
5052 check_output('ip link set dummy98 up')
5054 remove_link('dummy98')
5056 self
.wait_online('bridge99:routable', 'dummy98:enslaved')
5058 output
= check_output('ip address show bridge99')
5060 self
.assertRegex(output
, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
5062 output
= check_output('ip rule list table 100')
5064 self
.assertIn('from all to 8.8.8.8 lookup 100', output
)
5066 class NetworkdSRIOVTests(unittest
.TestCase
, Utilities
):
5074 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5075 def test_sriov(self
):
5076 copy_network_unit('25-default.link', '25-sriov.network')
5078 call('modprobe netdevsim')
5080 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5083 with
open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode
='w', encoding
='utf-8') as f
:
5087 self
.wait_online('eni99np1:routable')
5089 output
= check_output('ip link show dev eni99np1')
5091 self
.assertRegex(output
,
5092 '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 *'
5093 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5094 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5097 @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
5098 def test_sriov_udev(self
):
5099 copy_network_unit('25-sriov.link', '25-sriov-udev.network')
5101 call('modprobe netdevsim')
5103 with
open('/sys/bus/netdevsim/new_device', mode
='w', encoding
='utf-8') as f
:
5107 self
.wait_online('eni99np1:routable')
5109 # the name eni99np1 may be an alternative name.
5110 ifname
= link_resolve('eni99np1')
5112 output
= check_output('ip link show dev eni99np1')
5114 self
.assertRegex(output
,
5115 '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 *'
5116 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5117 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5119 self
.assertNotIn('vf 3', output
)
5120 self
.assertNotIn('vf 4', output
)
5122 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5123 f
.write('[Link]\nSR-IOVVirtualFunctions=4\n')
5126 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5128 output
= check_output('ip link show dev eni99np1')
5130 self
.assertRegex(output
,
5131 '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 *'
5132 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5133 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5136 self
.assertNotIn('vf 4', output
)
5138 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5139 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5142 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5144 output
= check_output('ip link show dev eni99np1')
5146 self
.assertRegex(output
,
5147 '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 *'
5148 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5149 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
5152 self
.assertNotIn('vf 4', output
)
5154 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5155 f
.write('[Link]\nSR-IOVVirtualFunctions=2\n')
5158 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5160 output
= check_output('ip link show dev eni99np1')
5162 self
.assertRegex(output
,
5163 '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 *'
5164 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
5166 self
.assertNotIn('vf 2', output
)
5167 self
.assertNotIn('vf 3', output
)
5168 self
.assertNotIn('vf 4', output
)
5170 with
open(os
.path
.join(network_unit_dir
, '25-sriov.link'), mode
='a', encoding
='utf-8') as f
:
5171 f
.write('[Link]\nSR-IOVVirtualFunctions=\n')
5174 check_output(*udevadm_cmd
, 'trigger', '--action=add', '--settle', f
'/sys/devices/netdevsim99/net/{ifname}')
5176 output
= check_output('ip link show dev eni99np1')
5178 self
.assertRegex(output
,
5179 '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 *'
5180 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
5181 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
5183 self
.assertNotIn('vf 3', output
)
5184 self
.assertNotIn('vf 4', output
)
5186 class NetworkdLLDPTests(unittest
.TestCase
, Utilities
):
5194 def test_lldp(self
):
5195 copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
5197 self
.wait_online('veth99:degraded', 'veth-peer:degraded')
5199 for trial
in range(10):
5203 output
= networkctl('lldp')
5205 if re
.search(r
'veth99 .* veth-peer', output
):
5210 class NetworkdRATests(unittest
.TestCase
, Utilities
):
5218 def test_ipv6_prefix_delegation(self
):
5219 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth.network')
5220 self
.setup_nftset('addr6', 'ipv6_addr')
5221 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
5222 self
.setup_nftset('ifindex', 'iface_index')
5224 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5226 # IPv6SendRA=yes implies IPv6Forwarding.
5227 self
.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
5229 output
= resolvectl('dns', 'veth99')
5231 self
.assertRegex(output
, 'fe80::')
5232 self
.assertRegex(output
, '2002:da8:1::1')
5234 output
= resolvectl('domain', 'veth99')
5236 self
.assertIn('hogehoge.test', output
)
5238 output
= networkctl_status('veth99')
5240 self
.assertRegex(output
, '2002:da8:1:0')
5242 self
.check_netlabel('veth99', '2002:da8:1::/64')
5243 self
.check_netlabel('veth99', '2002:da8:2::/64')
5245 self
.check_nftset('addr6', '2002:da8:1:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5246 self
.check_nftset('addr6', '2002:da8:2:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
5247 self
.check_nftset('network6', '2002:da8:1::/64')
5248 self
.check_nftset('network6', '2002:da8:2::/64')
5249 self
.check_nftset('ifindex', 'veth99')
5251 self
.teardown_nftset('addr6', 'network6', 'ifindex')
5253 def check_ipv6_token_static(self
):
5254 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5256 output
= networkctl_status('veth99')
5258 self
.assertRegex(output
, '2002:da8:1:0:1a:2b:3c:4d')
5259 self
.assertRegex(output
, '2002:da8:1:0:fa:de:ca:fe')
5260 self
.assertRegex(output
, '2002:da8:2:0:1a:2b:3c:4d')
5261 self
.assertRegex(output
, '2002:da8:2:0:fa:de:ca:fe')
5263 def test_ipv6_token_static(self
):
5264 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
5267 self
.check_ipv6_token_static()
5270 check_output('ip link set veth99 down')
5271 check_output('ip link set veth99 up')
5273 self
.check_ipv6_token_static()
5276 check_output('ip link set veth99 down')
5277 time
.sleep(random
.uniform(0, 0.1))
5278 check_output('ip link set veth99 up')
5279 time
.sleep(random
.uniform(0, 0.1))
5281 self
.check_ipv6_token_static()
5283 def test_ipv6_token_prefixstable(self
):
5284 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
5286 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5288 output
= networkctl_status('veth99')
5290 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5291 self
.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output
) # EUI64
5293 def test_ipv6_token_prefixstable_without_address(self
):
5294 copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
5296 self
.wait_online('veth99:routable', 'veth-peer:degraded')
5298 output
= networkctl_status('veth99')
5300 self
.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output
)
5301 self
.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output
)
5303 def check_router_hop_limit(self
, hop_limit
):
5304 self
.wait_route('client', rf
'default via fe80::1034:56ff:fe78:9a99 proto ra .* hoplimit {hop_limit}', ipv
='-6', timeout_sec
=10)
5306 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5308 self
.assertIn(f
'hoplimit {hop_limit}', output
)
5310 self
.check_ipv6_sysctl_attr('client', 'hop_limit', f
'{hop_limit}')
5312 def test_router_hop_limit(self
):
5313 copy_network_unit('25-veth-client.netdev',
5314 '25-veth-router.netdev',
5316 '25-veth-bridge.network',
5317 '25-veth-client.network',
5318 '25-veth-router-hop-limit.network',
5319 '25-bridge99.network')
5321 self
.wait_online('client-p:enslaved',
5322 'router:degraded', 'router-p:enslaved',
5323 'bridge99:routable')
5325 self
.check_router_hop_limit(42)
5327 with
open(os
.path
.join(network_unit_dir
, '25-veth-router-hop-limit.network'), mode
='a', encoding
='utf-8') as f
:
5328 f
.write('\n[IPv6SendRA]\nHopLimit=43\n')
5332 self
.check_router_hop_limit(43)
5334 def test_router_preference(self
):
5335 copy_network_unit('25-veth-client.netdev',
5336 '25-veth-router-high.netdev',
5337 '25-veth-router-low.netdev',
5339 '25-veth-bridge.network',
5340 '25-veth-client.network',
5341 '25-veth-router-high.network',
5342 '25-veth-router-low.network',
5343 '25-bridge99.network')
5345 self
.wait_online('client-p:enslaved',
5346 'router-high:degraded', 'router-high-p:enslaved',
5347 'router-low:degraded', 'router-low-p:enslaved',
5348 'bridge99:routable')
5350 networkctl_reconfigure('client')
5351 self
.wait_online('client:routable')
5353 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5354 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5355 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 512', ipv
='-6', timeout_sec
=10)
5356 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 2048', ipv
='-6', timeout_sec
=10)
5358 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5360 self
.assertIn('metric 512', output
)
5361 self
.assertIn('pref high', output
)
5362 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5364 self
.assertIn('metric 2048', output
)
5365 self
.assertIn('pref low', output
)
5367 with
open(os
.path
.join(network_unit_dir
, '25-veth-client.network'), mode
='a', encoding
='utf-8') as f
:
5368 f
.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
5371 self
.wait_online('client:routable')
5373 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5374 self
.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv
='-6', timeout_sec
=10)
5375 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5376 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5378 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5380 self
.assertIn('metric 100', output
)
5381 self
.assertNotIn('metric 512', output
)
5382 self
.assertIn('pref high', output
)
5383 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5385 self
.assertIn('metric 300', output
)
5386 self
.assertNotIn('metric 2048', output
)
5387 self
.assertIn('pref low', output
)
5389 # swap the preference (for issue #28439)
5390 remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
5391 copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
5393 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv
='-6', timeout_sec
=10)
5394 self
.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv
='-6', timeout_sec
=10)
5396 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
5398 self
.assertIn('metric 300', output
)
5399 self
.assertNotIn('metric 100', output
)
5400 self
.assertIn('pref low', output
)
5401 self
.assertNotIn('pref high', output
)
5402 output
= check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
5404 self
.assertIn('metric 100', output
)
5405 self
.assertNotIn('metric 300', output
)
5406 self
.assertIn('pref high', output
)
5407 self
.assertNotIn('pref low', output
)
5409 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5410 def test_captive_portal(self
):
5411 copy_network_unit('25-veth-client.netdev',
5412 '25-veth-router-captive.netdev',
5414 '25-veth-client-captive.network',
5415 '25-veth-router-captive.network',
5416 '25-veth-bridge-captive.network',
5417 '25-bridge99.network')
5419 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5420 'router-captive:degraded', 'router-captivep:enslaved')
5422 start_radvd(config_file
='captive-portal.conf')
5423 networkctl_reconfigure('client')
5424 self
.wait_online('client:routable')
5426 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5427 output
= networkctl_status('client')
5429 self
.assertIn('Captive Portal: http://systemd.io', output
)
5431 @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
5432 def test_invalid_captive_portal(self
):
5433 def radvd_write_config(captive_portal_uri
):
5434 with
open(os
.path
.join(networkd_ci_temp_dir
, 'radvd/bogus-captive-portal.conf'), mode
='w', encoding
='utf-8') as f
:
5435 f
.write(f
'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')
5437 captive_portal_uris
= [
5438 "42ěščěškd ěšč ě s",
5443 copy_network_unit('25-veth-client.netdev',
5444 '25-veth-router-captive.netdev',
5446 '25-veth-client-captive.network',
5447 '25-veth-router-captive.network',
5448 '25-veth-bridge-captive.network',
5449 '25-bridge99.network')
5451 self
.wait_online('bridge99:routable', 'client-p:enslaved',
5452 'router-captive:degraded', 'router-captivep:enslaved')
5454 for uri
in captive_portal_uris
:
5455 print(f
"Captive portal: {uri}")
5456 radvd_write_config(uri
)
5458 start_radvd(config_file
='bogus-captive-portal.conf')
5459 networkctl_reconfigure('client')
5460 self
.wait_online('client:routable')
5462 self
.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv
='-6', timeout_sec
=10)
5463 output
= networkctl_status('client')
5465 self
.assertNotIn('Captive Portal:', output
)
5467 class NetworkdDHCPServerTests(unittest
.TestCase
, Utilities
):
5475 def test_dhcp_server(self
):
5476 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
5478 self
.wait_online('veth99:routable', 'veth-peer:routable')
5480 output
= networkctl_status('veth99')
5482 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5483 self
.assertIn('Gateway: 192.168.5.3', output
)
5484 self
.assertRegex(output
, 'DNS: 192.168.5.1\n *192.168.5.10')
5485 self
.assertRegex(output
, 'NTP: 192.168.5.1\n *192.168.5.11')
5487 output
= networkctl_status('veth-peer')
5488 self
.assertRegex(output
, "Offered DHCP leases: 192.168.5.[0-9]*")
5490 def test_dhcp_server_null_server_address(self
):
5491 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
5493 self
.wait_online('veth99:routable', 'veth-peer:routable')
5495 output
= check_output('ip --json address show dev veth-peer')
5496 server_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5497 print(server_address
)
5499 output
= check_output('ip --json address show dev veth99')
5500 client_address
= json
.loads(output
)[0]['addr_info'][0]['local']
5501 print(client_address
)
5503 output
= networkctl_status('veth99')
5505 self
.assertRegex(output
, rf
'Address: {client_address} \(DHCP4 via {server_address}\)')
5506 self
.assertIn(f
'Gateway: {server_address}', output
)
5507 self
.assertIn(f
'DNS: {server_address}', output
)
5508 self
.assertIn(f
'NTP: {server_address}', output
)
5510 output
= networkctl_status('veth-peer')
5511 self
.assertIn(f
'Offered DHCP leases: {client_address}', output
)
5513 def test_dhcp_server_with_uplink(self
):
5514 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
5515 '12-dummy.netdev', '25-dhcp-server-uplink.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.3', output
)
5523 self
.assertIn('DNS: 192.168.5.1', output
)
5524 self
.assertIn('NTP: 192.168.5.1', output
)
5526 def test_emit_router_timezone(self
):
5527 copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
5529 self
.wait_online('veth99:routable', 'veth-peer:routable')
5531 output
= networkctl_status('veth99')
5533 self
.assertRegex(output
, r
'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
5534 self
.assertIn('Gateway: 192.168.5.1', output
)
5535 self
.assertIn('Time Zone: Europe/Berlin', output
)
5537 def test_dhcp_server_static_lease(self
):
5538 copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
5540 self
.wait_online('veth99:routable', 'veth-peer:routable')
5542 output
= networkctl_status('veth99')
5544 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5545 self
.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output
)
5547 def test_dhcp_server_static_lease_default_client_id(self
):
5548 copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
5550 self
.wait_online('veth99:routable', 'veth-peer:routable')
5552 output
= networkctl_status('veth99')
5554 self
.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output
)
5555 self
.assertRegex(output
, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
5557 class NetworkdDHCPServerRelayAgentTests(unittest
.TestCase
, Utilities
):
5565 def test_relay_agent(self
):
5566 copy_network_unit('25-agent-veth-client.netdev',
5567 '25-agent-veth-server.netdev',
5568 '25-agent-client.network',
5569 '25-agent-server.network',
5570 '25-agent-client-peer.network',
5571 '25-agent-server-peer.network')
5574 self
.wait_online('client:routable')
5576 output
= networkctl_status('client')
5578 self
.assertRegex(output
, r
'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
5580 def test_replay_agent_on_bridge(self
):
5581 copy_network_unit('25-agent-bridge.netdev',
5582 '25-agent-veth-client.netdev',
5583 '25-agent-bridge.network',
5584 '25-agent-bridge-port.network',
5585 '25-agent-client.network')
5587 self
.wait_online('bridge-relay:routable', 'client-peer:enslaved')
5590 expect
= 'bridge-relay: DHCPv4 server: STARTED'
5592 if expect
in read_networkd_log():
5598 class NetworkdDHCPClientTests(unittest
.TestCase
, Utilities
):
5606 def test_dhcp_client_ipv6_only(self
):
5607 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5610 self
.wait_online('veth-peer:carrier')
5612 # information request mode
5613 # The name ipv6-only option may not be supported by older dnsmasq
5614 # start_dnsmasq('--dhcp-option=option:ipv6-only,300')
5615 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5616 '--dhcp-option=option6:dns-server,[2600::ee]',
5617 '--dhcp-option=option6:ntp-server,[2600::ff]',
5618 ra_mode
='ra-stateless')
5619 self
.wait_online('veth99:routable', 'veth-peer:routable')
5621 # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
5622 # Let's wait for the expected DNS server being listed in the state file.
5623 for _
in range(100):
5624 output
= read_link_state_file('veth99')
5625 if 'DNS=2600::ee' in output
:
5629 # Check link state file
5630 print('## link state file')
5631 output
= read_link_state_file('veth99')
5633 self
.assertIn('DNS=2600::ee', output
)
5634 self
.assertIn('NTP=2600::ff', output
)
5636 # Check manager state file
5637 print('## manager state file')
5638 output
= read_manager_state_file()
5640 self
.assertRegex(output
, 'DNS=.*2600::ee')
5641 self
.assertRegex(output
, 'NTP=.*2600::ff')
5643 print('## dnsmasq log')
5644 output
= read_dnsmasq_log_file()
5646 self
.assertIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5647 self
.assertNotIn('DHCPSOLICIT(veth-peer)', output
)
5648 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5649 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5650 self
.assertNotIn('DHCPREPLY(veth-peer)', output
)
5653 check_json(networkctl_json('veth99'))
5657 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5658 '--dhcp-option=option6:dns-server,[2600::ee]',
5659 '--dhcp-option=option6:ntp-server,[2600::ff]')
5660 networkctl_reconfigure('veth99')
5661 self
.wait_online('veth99:routable', 'veth-peer:routable')
5664 output
= check_output('ip address show dev veth99 scope global')
5666 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5667 self
.assertNotIn('192.168.5', output
)
5669 # checking semi-static route
5670 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5672 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5674 # Confirm that ipv6 token is not set in the kernel
5675 output
= check_output('ip token show dev veth99')
5677 self
.assertRegex(output
, 'token :: dev veth99')
5679 # Make manager and link state file updated
5680 resolvectl('revert', 'veth99')
5682 # Check link state file
5683 print('## link state file')
5684 output
= read_link_state_file('veth99')
5686 self
.assertIn('DNS=2600::ee', output
)
5687 self
.assertIn('NTP=2600::ff', output
)
5689 # Check manager state file
5690 print('## manager state file')
5691 output
= read_manager_state_file()
5693 self
.assertRegex(output
, 'DNS=.*2600::ee')
5694 self
.assertRegex(output
, 'NTP=.*2600::ff')
5696 print('## dnsmasq log')
5697 output
= read_dnsmasq_log_file()
5699 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5700 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5701 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5702 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5703 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5704 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5707 check_json(networkctl_json('veth99'))
5709 # Testing without rapid commit support
5710 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-ipv6-only.network'), mode
='a', encoding
='utf-8') as f
:
5711 f
.write('\n[DHCPv6]\nRapidCommit=no\n')
5714 start_dnsmasq('--dhcp-option=108,00:00:02:00',
5715 '--dhcp-option=option6:dns-server,[2600::ee]',
5716 '--dhcp-option=option6:ntp-server,[2600::ff]')
5719 self
.wait_online('veth99:routable', 'veth-peer:routable')
5722 output
= check_output('ip address show dev veth99 scope global')
5724 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5725 self
.assertNotIn('192.168.5', output
)
5727 # checking semi-static route
5728 output
= check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
5730 self
.assertRegex(output
, 'via fe80::1034:56ff:fe78:9abd')
5732 # Make manager and link state file updated
5733 resolvectl('revert', 'veth99')
5735 # Check link state file
5736 print('## link state file')
5737 output
= read_link_state_file('veth99')
5739 self
.assertIn('DNS=2600::ee', output
)
5740 self
.assertIn('NTP=2600::ff', output
)
5742 # Check manager state file
5743 print('## manager state file')
5744 output
= read_manager_state_file()
5746 self
.assertRegex(output
, 'DNS=.*2600::ee')
5747 self
.assertRegex(output
, 'NTP=.*2600::ff')
5749 print('## dnsmasq log')
5750 output
= read_dnsmasq_log_file()
5752 self
.assertNotIn('DHCPINFORMATION-REQUEST(veth-peer)', output
)
5753 self
.assertIn('DHCPSOLICIT(veth-peer)', output
)
5754 self
.assertIn('DHCPADVERTISE(veth-peer)', output
)
5755 self
.assertIn('DHCPREQUEST(veth-peer)', output
)
5756 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5757 self
.assertNotIn('rapid-commit', output
)
5760 check_json(networkctl_json('veth99'))
5762 def test_dhcp_client_ipv6_dbus_status(self
):
5763 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
5765 self
.wait_online('veth-peer:carrier')
5767 # Note that at this point the DHCPv6 client has not been started because no RA (with managed
5768 # bit set) has yet been received and the configuration does not include WithoutRA=true
5769 state
= get_dhcp6_client_state('veth99')
5770 print(f
"DHCPv6 client state = {state}")
5771 self
.assertEqual(state
, 'stopped')
5773 state
= get_dhcp4_client_state('veth99')
5774 print(f
"DHCPv4 client state = {state}")
5775 self
.assertEqual(state
, 'selecting')
5777 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5778 self
.wait_online('veth99:routable', 'veth-peer:routable')
5780 state
= get_dhcp6_client_state('veth99')
5781 print(f
"DHCPv6 client state = {state}")
5782 self
.assertEqual(state
, 'bound')
5784 # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while.
5785 for _
in range(100):
5786 state
= get_dhcp4_client_state('veth99')
5787 if state
== 'stopped':
5791 print(f
"DHCPv4 client state = {state}")
5792 self
.assertEqual(state
, 'stopped')
5794 # restart dnsmasq to clear log
5796 start_dnsmasq('--dhcp-option=108,00:00:02:00')
5798 # Test renew command
5799 # See https://github.com/systemd/systemd/pull/29472#issuecomment-1759092138
5800 networkctl('renew', 'veth99')
5802 for _
in range(100):
5803 state
= get_dhcp4_client_state('veth99')
5804 if state
== 'stopped':
5808 print(f
"DHCPv4 client state = {state}")
5809 self
.assertEqual(state
, 'stopped')
5811 print('## dnsmasq log')
5812 output
= read_dnsmasq_log_file()
5814 self
.assertIn('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc', output
)
5815 self
.assertIn('DHCPOFFER(veth-peer)', output
)
5816 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5817 self
.assertNotIn('DHCPACK(veth-peer)', output
)
5819 def test_dhcp_client_ipv6_only_with_custom_client_identifier(self
):
5820 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
5823 self
.wait_online('veth-peer:carrier')
5825 self
.wait_online('veth99:routable', 'veth-peer:routable')
5828 output
= check_output('ip address show dev veth99 scope global')
5830 self
.assertRegex(output
, r
'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
5831 self
.assertNotIn('192.168.5', output
)
5833 print('## dnsmasq log')
5834 output
= read_dnsmasq_log_file()
5836 self
.assertIn('DHCPSOLICIT(veth-peer) 00:42:00:00:ab:11:f9:2a:c2:77:29:f9:5c:00', output
)
5837 self
.assertNotIn('DHCPADVERTISE(veth-peer)', output
)
5838 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
5839 self
.assertIn('DHCPREPLY(veth-peer)', output
)
5840 self
.assertIn('sent size: 0 option: 14 rapid-commit', output
)
5842 def test_dhcp_client_ipv4_only(self
):
5843 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
5845 self
.setup_nftset('addr4', 'ipv4_addr')
5846 self
.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
5847 self
.setup_nftset('ifindex', 'iface_index')
5850 self
.wait_online('veth-peer:carrier')
5851 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
5852 '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
5853 '--dhcp-option=option:domain-search,example.com',
5854 '--dhcp-alternate-port=67,5555',
5855 ipv4_range
='192.168.5.110,192.168.5.119')
5856 self
.wait_online('veth99:routable', 'veth-peer:routable')
5857 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
5859 print('## ip address show dev veth99 scope global')
5860 output
= check_output('ip address show dev veth99 scope global')
5862 self
.assertIn('mtu 1492', output
)
5863 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5864 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')
5865 self
.assertNotIn('2600::', output
)
5867 output
= check_output('ip -4 --json address show dev veth99')
5868 for i
in json
.loads(output
)[0]['addr_info']:
5869 if i
['label'] == 'test-label':
5870 address1
= i
['local']
5873 self
.assertFalse(True)
5875 self
.assertRegex(address1
, r
'^192.168.5.11[0-9]$')
5877 print('## ip route show table main dev veth99')
5878 output
= check_output('ip route show table main dev veth99')
5880 # no DHCP routes assigned to the main table
5881 self
.assertNotIn('proto dhcp', output
)
5883 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5884 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5885 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5886 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5888 print('## ip route show table 211 dev veth99')
5889 output
= check_output('ip route show table 211 dev veth99')
5891 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address1} metric 24')
5892 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
5893 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address1} metric 24')
5894 self
.assertRegex(output
, f
'192.168.5.6 proto dhcp scope link src {address1} metric 24')
5895 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address1} metric 24')
5896 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5898 print('## link state file')
5899 output
= read_link_state_file('veth99')
5901 # checking DNS server, SIP server, and Domains
5902 self
.assertIn('DNS=192.168.5.6 192.168.5.7', output
)
5903 self
.assertIn('SIP=192.168.5.21 192.168.5.22', output
)
5904 self
.assertIn('DOMAINS=example.com', output
)
5907 j
= json
.loads(networkctl_json('veth99'))
5909 self
.assertEqual(len(j
['DNS']), 2)
5912 self
.assertEqual(i
['Family'], 2)
5913 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5914 self
.assertRegex(a
, '^192.168.5.[67]$')
5915 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5916 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5917 self
.assertEqual('192.168.5.1', a
)
5919 self
.assertEqual(len(j
['SIP']), 2)
5922 self
.assertEqual(i
['Family'], 2)
5923 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
5924 self
.assertRegex(a
, '^192.168.5.2[12]$')
5925 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
5926 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
5927 self
.assertEqual('192.168.5.1', a
)
5929 print('## dnsmasq log')
5930 output
= read_dnsmasq_log_file()
5932 self
.assertIn('vendor class: FooBarVendorTest', output
)
5933 self
.assertIn('DHCPDISCOVER(veth-peer) 192.168.5.110 12:34:56:78:9a:bc', output
)
5934 self
.assertIn('client provides name: test-hostname', output
)
5935 self
.assertIn('26:mtu', output
)
5937 # change address range, DNS servers, and Domains
5939 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8',
5940 '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24',
5941 '--dhcp-option=option:domain-search,foo.example.com',
5942 '--dhcp-alternate-port=67,5555',
5943 ipv4_range
='192.168.5.120,192.168.5.129',)
5945 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
5946 print('Wait for the DHCP lease to be expired')
5947 self
.wait_address_dropped('veth99', f
'inet {address1}/24', ipv
='-4', timeout_sec
=120)
5948 self
.wait_address('veth99', r
'inet 192.168.5.12[0-9]*/24', ipv
='-4')
5950 self
.wait_online('veth99:routable', 'veth-peer:routable')
5952 print('## ip address show dev veth99 scope global')
5953 output
= check_output('ip address show dev veth99 scope global')
5955 self
.assertIn('mtu 1492', output
)
5956 self
.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output
)
5957 self
.assertNotIn(f
'{address1}', output
)
5958 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')
5959 self
.assertNotIn('2600::', output
)
5961 output
= check_output('ip -4 --json address show dev veth99')
5962 for i
in json
.loads(output
)[0]['addr_info']:
5963 if i
['label'] == 'test-label':
5964 address2
= i
['local']
5967 self
.assertFalse(True)
5969 self
.assertRegex(address2
, r
'^192.168.5.12[0-9]$')
5971 print('## ip route show table main dev veth99')
5972 output
= check_output('ip route show table main dev veth99')
5974 # no DHCP routes assigned to the main table
5975 self
.assertNotIn('proto dhcp', output
)
5977 self
.assertIn('192.168.5.0/24 proto kernel scope link src 192.168.5.250', output
)
5978 self
.assertIn('192.168.5.0/24 proto static scope link', output
)
5979 self
.assertIn('192.168.6.0/24 proto static scope link', output
)
5980 self
.assertIn('192.168.7.0/24 proto static scope link', output
)
5982 print('## ip route show table 211 dev veth99')
5983 output
= check_output('ip route show table 211 dev veth99')
5985 self
.assertRegex(output
, f
'default via 192.168.5.1 proto dhcp src {address2} metric 24')
5986 self
.assertRegex(output
, f
'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
5987 self
.assertRegex(output
, f
'192.168.5.1 proto dhcp scope link src {address2} metric 24')
5988 self
.assertNotIn('192.168.5.6', output
)
5989 self
.assertRegex(output
, f
'192.168.5.7 proto dhcp scope link src {address2} metric 24')
5990 self
.assertRegex(output
, f
'192.168.5.8 proto dhcp scope link src {address2} metric 24')
5991 self
.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output
)
5993 print('## link state file')
5994 output
= read_link_state_file('veth99')
5996 # checking DNS server, SIP server, and Domains
5997 self
.assertIn('DNS=192.168.5.1 192.168.5.7 192.168.5.8', output
)
5998 self
.assertIn('SIP=192.168.5.23 192.168.5.24', output
)
5999 self
.assertIn('DOMAINS=foo.example.com', output
)
6002 j
= json
.loads(networkctl_json('veth99'))
6004 self
.assertEqual(len(j
['DNS']), 3)
6007 self
.assertEqual(i
['Family'], 2)
6008 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6009 self
.assertRegex(a
, '^192.168.5.[178]$')
6010 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6011 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6012 self
.assertEqual('192.168.5.1', a
)
6014 self
.assertEqual(len(j
['SIP']), 2)
6017 self
.assertEqual(i
['Family'], 2)
6018 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['Address']))
6019 self
.assertRegex(a
, '^192.168.5.2[34]$')
6020 self
.assertEqual(i
['ConfigSource'], 'DHCPv4')
6021 a
= socket
.inet_ntop(socket
.AF_INET
, bytearray(i
['ConfigProvider']))
6022 self
.assertEqual('192.168.5.1', a
)
6024 print('## dnsmasq log')
6025 output
= read_dnsmasq_log_file()
6027 self
.assertIn('vendor class: FooBarVendorTest', output
)
6028 self
.assertIn(f
'DHCPDISCOVER(veth-peer) {address1} 12:34:56:78:9a:bc', output
)
6029 self
.assertIn('client provides name: test-hostname', output
)
6030 self
.assertIn('26:mtu', output
)
6032 self
.check_netlabel('veth99', r
'192\.168\.5\.0/24')
6034 self
.check_nftset('addr4', r
'192\.168\.5\.1')
6035 self
.check_nftset('network4', r
'192\.168\.5\.0/24')
6036 self
.check_nftset('ifindex', 'veth99')
6038 self
.teardown_nftset('addr4', 'network4', 'ifindex')
6040 def test_dhcp_client_ipv4_dbus_status(self
):
6041 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
6043 self
.wait_online('veth-peer:carrier')
6045 state
= get_dhcp4_client_state('veth99')
6046 print(f
"State = {state}")
6047 self
.assertEqual(state
, 'rebooting')
6049 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
6050 '--dhcp-option=option:domain-search,example.com',
6051 '--dhcp-alternate-port=67,5555',
6052 ipv4_range
='192.168.5.110,192.168.5.119')
6053 self
.wait_online('veth99:routable', 'veth-peer:routable')
6054 self
.wait_address('veth99', r
'inet 192.168.5.11[0-9]*/24', ipv
='-4')
6056 state
= get_dhcp4_client_state('veth99')
6057 print(f
"State = {state}")
6058 self
.assertEqual(state
, 'bound')
6060 def test_dhcp_client_allow_list(self
):
6061 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins
=False)
6064 self
.wait_online('veth-peer:carrier')
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/00-allow-list.conf')
6077 since
= datetime
.datetime
.now()
6080 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
6082 if expect
in read_networkd_log(since
=since
):
6088 copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
6089 since
= datetime
.datetime
.now()
6092 expect
= 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
6094 if expect
in read_networkd_log(since
=since
):
6100 @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout
, reason
="dnsmasq is missing dhcp-rapid-commit support")
6101 def test_dhcp_client_rapid_commit(self
):
6102 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6104 self
.wait_online('veth-peer:carrier')
6106 start_dnsmasq('--dhcp-rapid-commit')
6107 self
.wait_online('veth99:routable', 'veth-peer:routable')
6108 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6110 state
= get_dhcp4_client_state('veth99')
6111 print(f
"DHCPv4 client state = {state}")
6112 self
.assertEqual(state
, 'bound')
6114 output
= read_dnsmasq_log_file()
6115 self
.assertIn('DHCPDISCOVER(veth-peer)', output
)
6116 self
.assertNotIn('DHCPOFFER(veth-peer)', output
)
6117 self
.assertNotIn('DHCPREQUEST(veth-peer)', output
)
6118 self
.assertIn('DHCPACK(veth-peer)', output
)
6120 def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self
):
6121 copy_network_unit('25-veth.netdev',
6122 '25-dhcp-server-ipv6-only-mode.network',
6123 '25-dhcp-client-ipv6-only-mode.network')
6125 self
.wait_online('veth99:routable', 'veth-peer:routable', timeout
='40s')
6126 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24', ipv
='-4')
6128 state
= get_dhcp4_client_state('veth99')
6129 print(f
"State = {state}")
6130 self
.assertEqual(state
, 'bound')
6132 def test_dhcp_client_ipv4_use_routes_gateway(self
):
6134 for (routes
, gateway
, dns_and_ntp_routes
, classless
) in itertools
.product([True, False], repeat
=4):
6140 print(f
'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})')
6141 with self
.subTest(routes
=routes
, gateway
=gateway
, dns_and_ntp_routes
=dns_and_ntp_routes
, classless
=classless
):
6142 self
._test
_dhcp
_client
_ipv
4_use
_routes
_gateway
(routes
, gateway
, dns_and_ntp_routes
, classless
)
6144 def _test_dhcp_client_ipv4_use_routes_gateway(self
, use_routes
, use_gateway
, dns_and_ntp_routes
, classless
):
6145 testunit
= '25-dhcp-client-ipv4-use-routes-use-gateway.network'
6146 testunits
= ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit
]
6147 testunits
.append(f
'{testunit}.d/use-routes-{use_routes}.conf')
6148 testunits
.append(f
'{testunit}.d/use-gateway-{use_gateway}.conf')
6149 testunits
.append(f
'{testunit}.d/use-dns-and-ntp-routes-{dns_and_ntp_routes}.conf')
6150 copy_network_unit(*testunits
, copy_dropins
=False)
6153 self
.wait_online('veth-peer:carrier')
6154 additional_options
= [
6155 '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
6156 '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
6157 '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
6160 additional_options
+= [
6161 '--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'
6163 start_dnsmasq(*additional_options
)
6164 self
.wait_online('veth99:routable', 'veth-peer:routable')
6166 output
= check_output('ip -4 route show dev veth99')
6172 self
.assertRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6173 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6174 self
.assertRegex(output
, r
'192.168.5.64/26 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6175 self
.assertRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6176 self
.assertRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6178 self
.assertRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6179 self
.assertRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6180 self
.assertRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6181 self
.assertRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6183 self
.assertNotRegex(output
, r
'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6184 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6185 self
.assertNotRegex(output
, r
'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6186 self
.assertNotRegex(output
, r
'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6187 self
.assertNotRegex(output
, r
'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
6188 self
.assertNotRegex(output
, r
'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6189 self
.assertNotRegex(output
, r
'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6190 self
.assertNotRegex(output
, r
'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6193 if use_gateway
and (not classless
or not use_routes
):
6194 self
.assertRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6196 self
.assertNotRegex(output
, r
'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6198 # Check route to gateway
6199 if (use_gateway
or dns_and_ntp_routes
) and (not classless
or not use_routes
):
6200 self
.assertRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6202 self
.assertNotRegex(output
, r
'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6204 # Check RoutesToDNS= and RoutesToNTP=
6205 if dns_and_ntp_routes
:
6206 self
.assertRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6207 self
.assertRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6210 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
6211 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
6213 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
6214 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6216 self
.assertRegex(output
, r
'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6217 self
.assertRegex(output
, r
'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
6219 self
.assertNotRegex(output
, r
'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6220 self
.assertNotRegex(output
, r
'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
6221 self
.assertNotRegex(output
, r
'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6222 self
.assertNotRegex(output
, r
'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
6224 check_json(networkctl_json())
6226 def test_dhcp_client_settings_anonymize(self
):
6227 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
6229 self
.wait_online('veth-peer:carrier')
6231 self
.wait_online('veth99:routable', 'veth-peer:routable')
6233 print('## dnsmasq log')
6234 output
= read_dnsmasq_log_file()
6236 self
.assertNotIn('VendorClassIdentifier=SusantVendorTest', output
)
6237 self
.assertNotIn('test-hostname', output
)
6238 self
.assertNotIn('26:mtu', output
)
6240 def test_dhcp_keep_configuration_dhcp(self
):
6241 copy_network_unit('25-veth.netdev',
6242 '25-dhcp-server-veth-peer.network',
6243 '25-dhcp-client-keep-configuration-dhcp.network')
6245 self
.wait_online('veth-peer:carrier')
6247 self
.wait_online('veth99:routable', 'veth-peer:routable')
6249 output
= check_output('ip address show dev veth99 scope global')
6251 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6252 'valid_lft forever preferred_lft forever')
6254 # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
6257 # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
6258 print('Wait for the DHCP lease to be expired')
6261 # The lease address should be kept after the lease expired
6262 output
= check_output('ip address show dev veth99 scope global')
6264 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6265 'valid_lft forever preferred_lft forever')
6269 # The lease address should be kept after networkd stopped
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 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client-keep-configuration-dhcp.network'), mode
='a', encoding
='utf-8') as f
:
6276 f
.write('[Network]\nDHCP=no\n')
6279 self
.wait_online('veth99:routable', 'veth-peer:routable')
6281 # Still the lease address should be kept after networkd restarted
6282 output
= check_output('ip address show dev veth99 scope global')
6284 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
6285 'valid_lft forever preferred_lft forever')
6287 def test_dhcp_keep_configuration_dhcp_on_stop(self
):
6288 copy_network_unit('25-veth.netdev',
6289 '25-dhcp-server-veth-peer.network',
6290 '25-dhcp-client-keep-configuration-dhcp-on-stop.network')
6292 self
.wait_online('veth-peer:carrier')
6294 self
.wait_online('veth99:routable', 'veth-peer:routable')
6296 output
= check_output('ip address show dev veth99 scope global')
6298 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6303 output
= check_output('ip address show dev veth99 scope global')
6305 self
.assertRegex(output
, r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6308 self
.wait_online('veth-peer:routable')
6310 output
= check_output('ip address show dev veth99 scope global')
6312 self
.assertNotIn('192.168.5.', output
)
6314 def test_dhcp_client_reuse_address_as_static(self
):
6315 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
6317 self
.wait_online('veth-peer:carrier')
6319 self
.wait_online('veth99:routable', 'veth-peer:routable')
6321 # link become 'routable' when at least one protocol provide an valid address.
6322 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6323 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6325 output
= check_output('ip address show dev veth99 scope global')
6326 ipv4_address
= re
.search(r
'192.168.5.[0-9]*/24', output
).group()
6327 ipv6_address
= re
.search(r
'2600::[0-9a-f:]*/128', output
).group()
6328 static_network
= '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address
, 'Address=' + ipv6_address
])
6329 print(static_network
)
6331 remove_network_unit('25-dhcp-client.network')
6333 with
open(os
.path
.join(network_unit_dir
, '25-static.network'), mode
='w', encoding
='utf-8') as f
:
6334 f
.write(static_network
)
6337 self
.wait_online('veth99:routable')
6339 output
= check_output('ip -4 address show dev veth99 scope global')
6341 self
.assertRegex(output
, f
'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *'
6342 'valid_lft forever preferred_lft forever')
6344 output
= check_output('ip -6 address show dev veth99 scope global')
6346 self
.assertRegex(output
, f
'inet6 {ipv6_address} scope global *\n *'
6347 'valid_lft forever preferred_lft forever')
6349 @expectedFailureIfModuleIsNotAvailable('vrf')
6350 def test_dhcp_client_vrf(self
):
6351 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
6352 '25-vrf.netdev', '25-vrf.network')
6354 self
.wait_online('veth-peer:carrier')
6356 self
.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
6358 # link become 'routable' when at least one protocol provide an valid address.
6359 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6360 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6362 print('## ip -d link show dev vrf99')
6363 output
= check_output('ip -d link show dev vrf99')
6365 self
.assertRegex(output
, 'vrf table 42')
6367 print('## ip address show vrf vrf99')
6368 output
= check_output('ip address show vrf vrf99')
6370 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6371 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6372 self
.assertRegex(output
, 'inet6 .* scope link')
6374 print('## ip address show dev veth99')
6375 output
= check_output('ip address show dev veth99')
6377 self
.assertRegex(output
, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
6378 self
.assertRegex(output
, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6379 self
.assertRegex(output
, 'inet6 .* scope link')
6381 print('## ip route show vrf vrf99')
6382 output
= check_output('ip route show vrf vrf99')
6384 self
.assertRegex(output
, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
6385 self
.assertRegex(output
, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
6386 self
.assertRegex(output
, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
6388 print('## ip route show table main dev veth99')
6389 output
= check_output('ip route show table main dev veth99')
6391 self
.assertEqual(output
, '')
6393 def test_dhcp_client_gateway_onlink_implicit(self
):
6394 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6395 '25-dhcp-client-gateway-onlink-implicit.network')
6397 self
.wait_online('veth-peer:carrier')
6399 self
.wait_online('veth99:routable', 'veth-peer:routable')
6401 output
= networkctl_status('veth99')
6403 self
.assertRegex(output
, '192.168.5')
6405 output
= check_output('ip route list dev veth99 10.0.0.0/8')
6407 self
.assertRegex(output
, 'onlink')
6408 output
= check_output('ip route list dev veth99 192.168.100.0/24')
6410 self
.assertRegex(output
, 'onlink')
6412 def test_dhcp_client_with_ipv4ll(self
):
6413 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
6414 '25-dhcp-client-with-ipv4ll.network')
6416 # we need to increase timeout above default, as this will need to wait for
6417 # systemd-networkd to get the dhcpv4 transient failure event
6418 self
.wait_online('veth99:degraded', 'veth-peer:routable', timeout
='60s')
6420 output
= check_output('ip -4 address show dev veth99')
6422 self
.assertNotIn('192.168.5.', output
)
6423 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6426 print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
6427 self
.wait_address('veth99', r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv
='-4')
6428 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')
6429 self
.wait_online('veth99:routable')
6431 output
= check_output('ip -4 address show dev veth99')
6433 self
.assertRegex(output
, r
'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99')
6434 self
.assertNotIn('169.254.', output
)
6435 self
.assertNotIn('scope link', output
)
6438 print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
6439 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)
6440 self
.wait_address('veth99', r
'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope
='link', ipv
='-4')
6442 output
= check_output('ip -4 address show dev veth99')
6444 self
.assertNotIn('192.168.5.', output
)
6445 self
.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output
)
6447 def test_dhcp_client_use_dns(self
):
6448 def check(self
, ipv4
, ipv6
):
6449 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6450 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6451 f
.write('[DHCPv4]\nUseDNS=')
6452 f
.write('yes' if ipv4
else 'no')
6453 f
.write('\n[DHCPv6]\nUseDNS=')
6454 f
.write('yes' if ipv6
else 'no')
6455 f
.write('\n[IPv6AcceptRA]\nUseDNS=no')
6458 self
.wait_online('veth99:routable')
6460 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6461 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6462 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6464 # make resolved re-read the link state file
6465 resolvectl('revert', 'veth99')
6467 output
= resolvectl('dns', 'veth99')
6470 self
.assertIn('192.168.5.1', output
)
6472 self
.assertNotIn('192.168.5.1', output
)
6474 self
.assertIn('2600::1', output
)
6476 self
.assertNotIn('2600::1', output
)
6478 check_json(networkctl_json())
6480 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6483 self
.wait_online('veth-peer:carrier')
6484 start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
6485 '--dhcp-option=option6:dns-server,[2600::1]')
6487 check(self
, True, True)
6488 check(self
, True, False)
6489 check(self
, False, True)
6490 check(self
, False, False)
6492 def test_dhcp_client_use_captive_portal(self
):
6493 def check(self
, ipv4
, ipv6
):
6494 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6495 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6496 f
.write('[DHCPv4]\nUseCaptivePortal=')
6497 f
.write('yes' if ipv4
else 'no')
6498 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6499 f
.write('yes' if ipv6
else 'no')
6500 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6503 self
.wait_online('veth99:routable')
6505 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6506 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6507 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6509 output
= networkctl_status('veth99')
6512 self
.assertIn('Captive Portal: http://systemd.io', output
)
6514 self
.assertNotIn('Captive Portal: http://systemd.io', output
)
6516 check_json(networkctl_json())
6518 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6521 self
.wait_online('veth-peer:carrier')
6522 start_dnsmasq('--dhcp-option=114,http://systemd.io',
6523 '--dhcp-option=option6:103,http://systemd.io')
6525 check(self
, True, True)
6526 check(self
, True, False)
6527 check(self
, False, True)
6528 check(self
, False, False)
6530 def test_dhcp_client_reject_captive_portal(self
):
6531 def check(self
, ipv4
, ipv6
):
6532 os
.makedirs(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d'), exist_ok
=True)
6533 with
open(os
.path
.join(network_unit_dir
, '25-dhcp-client.network.d/override.conf'), mode
='w', encoding
='utf-8') as f
:
6534 f
.write('[DHCPv4]\nUseCaptivePortal=')
6535 f
.write('yes' if ipv4
else 'no')
6536 f
.write('\n[DHCPv6]\nUseCaptivePortal=')
6537 f
.write('yes' if ipv6
else 'no')
6538 f
.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
6541 self
.wait_online('veth99:routable')
6543 # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
6544 self
.wait_address('veth99', r
'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv
='-4')
6545 self
.wait_address('veth99', r
'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv
='-6')
6547 output
= networkctl_status('veth99')
6549 self
.assertNotIn('Captive Portal: ', output
)
6550 self
.assertNotIn('invalid/url', output
)
6552 check_json(networkctl_json())
6554 copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins
=False)
6557 self
.wait_online('veth-peer:carrier')
6558 masq
= lambda bs
: ':'.join(f
'{b:02x}' for b
in bs
)
6559 start_dnsmasq('--dhcp-option=114,' + masq(b
'http://\x00invalid/url'),
6560 '--dhcp-option=option6:103,' + masq(b
'http://\x00/invalid/url'))
6562 check(self
, True, True)
6563 check(self
, True, False)
6564 check(self
, False, True)
6565 check(self
, False, False)
6567 class NetworkdDHCPPDTests(unittest
.TestCase
, Utilities
):
6575 def check_dhcp6_prefix(self
, link
):
6576 description
= get_link_description(link
)
6578 self
.assertIn('DHCPv6Client', description
.keys())
6579 self
.assertIn('Prefixes', description
['DHCPv6Client'])
6581 prefixInfo
= description
['DHCPv6Client']['Prefixes']
6583 self
.assertEqual(len(prefixInfo
), 1)
6585 self
.assertIn('Prefix', prefixInfo
[0].keys())
6586 self
.assertIn('PrefixLength', prefixInfo
[0].keys())
6587 self
.assertIn('PreferredLifetimeUSec', prefixInfo
[0].keys())
6588 self
.assertIn('ValidLifetimeUSec', prefixInfo
[0].keys())
6590 self
.assertEqual(prefixInfo
[0]['Prefix'][0:6], [63, 254, 5, 1, 255, 255])
6591 self
.assertEqual(prefixInfo
[0]['PrefixLength'], 56)
6592 self
.assertGreater(prefixInfo
[0]['PreferredLifetimeUSec'], 0)
6593 self
.assertGreater(prefixInfo
[0]['ValidLifetimeUSec'], 0)
6595 def test_dhcp6pd_no_address(self
):
6597 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
6600 self
.wait_online('veth-peer:routable')
6601 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6602 self
.wait_online('veth99:degraded')
6604 print('### ip -6 address show dev veth99 scope global')
6605 output
= check_output('ip -6 address show dev veth99 scope global')
6607 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6609 self
.check_dhcp6_prefix('veth99')
6611 def test_dhcp6pd_no_assign(self
):
6612 # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default),
6613 # However, the server does not provide IA_NA. For issue #31349.
6614 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
6617 self
.wait_online('veth-peer:routable')
6618 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd-no-range.conf', ipv
='-6')
6619 self
.wait_online('veth99:degraded')
6621 print('### ip -6 address show dev veth99 scope global')
6622 output
= check_output('ip -6 address show dev veth99 scope global')
6624 self
.assertNotIn('inet6 3ffe:501:ffff', output
)
6626 self
.check_dhcp6_prefix('veth99')
6628 def test_dhcp6pd(self
):
6629 copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network',
6630 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
6631 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
6632 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
6633 '25-dhcp-pd-downstream-dummy97.network',
6634 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
6635 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
6638 self
.wait_online('veth-peer:routable')
6639 start_isc_dhcpd(conf_file
='isc-dhcpd-dhcp6pd.conf', ipv
='-6')
6640 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
6641 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
6643 self
.setup_nftset('addr6', 'ipv6_addr')
6644 self
.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
6645 self
.setup_nftset('ifindex', 'iface_index')
6647 # Check DBus assigned prefix information to veth99
6648 self
.check_dhcp6_prefix('veth99')
6650 print('### ip -6 address show dev veth-peer scope global')
6651 output
= check_output('ip -6 address show dev veth-peer scope global')
6653 self
.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output
)
6657 # dummy97: 0x01 (The link will appear later)
6659 # dummy99: auto -> 0x02 (No address assignment)
6664 print('### ip -6 address show dev veth99 scope global')
6665 output
= check_output('ip -6 address show dev veth99 scope global')
6668 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
6669 # address in IA_PD (Token=static)
6670 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic')
6671 # address in IA_PD (Token=eui64)
6672 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic')
6673 # address in IA_PD (temporary)
6674 # Note that the temporary addresses may appear after the link enters configured state
6675 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')
6677 print('### ip -6 address show dev test1 scope global')
6678 output
= check_output('ip -6 address show dev test1 scope global')
6680 # address in IA_PD (Token=static)
6681 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6682 # address in IA_PD (temporary)
6683 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')
6685 print('### ip -6 address show dev dummy98 scope global')
6686 output
= check_output('ip -6 address show dev dummy98 scope global')
6688 # address in IA_PD (Token=static)
6689 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6690 # address in IA_PD (temporary)
6691 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')
6693 print('### ip -6 address show dev dummy99 scope global')
6694 output
= check_output('ip -6 address show dev dummy99 scope global')
6697 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6699 print('### ip -6 address show dev veth97 scope global')
6700 output
= check_output('ip -6 address show dev veth97 scope global')
6702 # address in IA_PD (Token=static)
6703 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6704 # address in IA_PD (Token=eui64)
6705 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6706 # address in IA_PD (temporary)
6707 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')
6709 print('### ip -6 address show dev veth97-peer scope global')
6710 output
= check_output('ip -6 address show dev veth97-peer scope global')
6712 # NDisc address (Token=static)
6713 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6714 # NDisc address (Token=eui64)
6715 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6716 # NDisc address (temporary)
6717 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')
6719 print('### ip -6 address show dev veth98 scope global')
6720 output
= check_output('ip -6 address show dev veth98 scope global')
6722 # address in IA_PD (Token=static)
6723 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6724 # address in IA_PD (Token=eui64)
6725 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6726 # address in IA_PD (temporary)
6727 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')
6729 print('### ip -6 address show dev veth98-peer scope global')
6730 output
= check_output('ip -6 address show dev veth98-peer scope global')
6732 # NDisc address (Token=static)
6733 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6734 # NDisc address (Token=eui64)
6735 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6736 # NDisc address (temporary)
6737 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')
6739 print('### ip -6 route show type unreachable')
6740 output
= check_output('ip -6 route show type unreachable')
6742 self
.assertRegex(output
, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
6744 print('### ip -6 route show dev veth99')
6745 output
= check_output('ip -6 route show dev veth99')
6747 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
6749 print('### ip -6 route show dev test1')
6750 output
= check_output('ip -6 route show dev test1')
6752 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6754 print('### ip -6 route show dev dummy98')
6755 output
= check_output('ip -6 route show dev dummy98')
6757 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6759 print('### ip -6 route show dev dummy99')
6760 output
= check_output('ip -6 route show dev dummy99')
6762 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6764 print('### ip -6 route show dev veth97')
6765 output
= check_output('ip -6 route show dev veth97')
6767 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto kernel metric [0-9]* expires')
6769 print('### ip -6 route show dev veth97-peer')
6770 output
= check_output('ip -6 route show dev veth97-peer')
6772 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]08::/64 proto ra metric [0-9]* expires')
6774 print('### ip -6 route show dev veth98')
6775 output
= check_output('ip -6 route show dev veth98')
6777 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
6779 print('### ip -6 route show dev veth98-peer')
6780 output
= check_output('ip -6 route show dev veth98-peer')
6782 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
6784 # Test case for a downstream which appears later
6785 check_output('ip link add dummy97 type dummy')
6786 self
.wait_online('dummy97:routable')
6788 print('### ip -6 address show dev dummy97 scope global')
6789 output
= check_output('ip -6 address show dev dummy97 scope global')
6791 # address in IA_PD (Token=static)
6792 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6793 # address in IA_PD (temporary)
6794 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')
6796 print('### ip -6 route show dev dummy97')
6797 output
= check_output('ip -6 route show dev dummy97')
6799 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]01::/64 proto kernel metric [0-9]* expires')
6801 # Test case for reconfigure
6802 networkctl_reconfigure('dummy98', 'dummy99')
6803 self
.wait_online('dummy98:routable', 'dummy99:degraded')
6805 print('### ip -6 address show dev dummy98 scope global')
6806 output
= check_output('ip -6 address show dev dummy98 scope global')
6808 # address in IA_PD (Token=static)
6809 self
.assertRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6810 # address in IA_PD (temporary)
6811 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')
6813 print('### ip -6 address show dev dummy99 scope global')
6814 output
= check_output('ip -6 address show dev dummy99 scope global')
6817 self
.assertNotRegex(output
, 'inet6 3ffe:501:ffff:[2-9a-f]02')
6819 print('### ip -6 route show dev dummy98')
6820 output
= check_output('ip -6 route show dev dummy98')
6822 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
6824 print('### ip -6 route show dev dummy99')
6825 output
= check_output('ip -6 route show dev dummy99')
6827 self
.assertRegex(output
, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires')
6829 self
.check_netlabel('dummy98', '3ffe:501:ffff:[2-9a-f]00::/64')
6831 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d')
6832 self
.check_nftset('addr6', '3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*')
6833 self
.check_nftset('network6', '3ffe:501:ffff:[2-9a-f]00::/64')
6834 self
.check_nftset('ifindex', 'dummy98')
6836 self
.teardown_nftset('addr6', 'network6', 'ifindex')
6838 def verify_dhcp4_6rd(self
, tunnel_name
):
6839 print('### ip -4 address show dev veth-peer scope global')
6840 output
= check_output('ip -4 address show dev veth-peer scope global')
6842 self
.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output
)
6846 # dummy97: 0x01 (The link will appear later)
6848 # dummy99: auto -> 0x0[23] (No address assignment)
6849 # 6rd-XXX: auto -> 0x0[23]
6854 print('### ip -4 address show dev veth99 scope global')
6855 output
= check_output('ip -4 address show dev veth99 scope global')
6857 self
.assertRegex(output
, 'inet 10.100.100.[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99')
6859 print('### ip -6 address show dev veth99 scope global')
6860 output
= check_output('ip -6 address show dev veth99 scope global')
6862 # address in IA_PD (Token=static)
6863 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6864 # address in IA_PD (Token=eui64)
6865 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
6866 # address in IA_PD (temporary)
6867 # Note that the temporary addresses may appear after the link enters configured state
6868 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')
6870 print('### ip -6 address show dev test1 scope global')
6871 output
= check_output('ip -6 address show dev test1 scope global')
6873 # address in IA_PD (Token=static)
6874 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6875 # address in IA_PD (temporary)
6876 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')
6878 print('### ip -6 address show dev dummy98 scope global')
6879 output
= check_output('ip -6 address show dev dummy98 scope global')
6881 # address in IA_PD (Token=static)
6882 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6883 # address in IA_PD (temporary)
6884 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')
6886 print('### ip -6 address show dev dummy99 scope global')
6887 output
= check_output('ip -6 address show dev dummy99 scope global')
6890 self
.assertNotRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+0[23]')
6892 print('### ip -6 address show dev veth97 scope global')
6893 output
= check_output('ip -6 address show dev veth97 scope global')
6895 # address in IA_PD (Token=static)
6896 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6897 # address in IA_PD (Token=eui64)
6898 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr')
6899 # address in IA_PD (temporary)
6900 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')
6902 print('### ip -6 address show dev veth97-peer scope global')
6903 output
= check_output('ip -6 address show dev veth97-peer scope global')
6905 # NDisc address (Token=static)
6906 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6907 # NDisc address (Token=eui64)
6908 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6909 # NDisc address (temporary)
6910 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')
6912 print('### ip -6 address show dev veth98 scope global')
6913 output
= check_output('ip -6 address show dev veth98 scope global')
6915 # address in IA_PD (Token=static)
6916 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6917 # address in IA_PD (Token=eui64)
6918 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
6919 # address in IA_PD (temporary)
6920 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')
6922 print('### ip -6 address show dev veth98-peer scope global')
6923 output
= check_output('ip -6 address show dev veth98-peer scope global')
6925 # NDisc address (Token=static)
6926 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
6927 # NDisc address (Token=eui64)
6928 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
6929 # NDisc address (temporary)
6930 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')
6932 print('### ip -6 route show type unreachable')
6933 output
= check_output('ip -6 route show type unreachable')
6935 self
.assertRegex(output
, 'unreachable 2001:db8:6464:[0-9a-f]+00::/56 dev lo proto dhcp')
6937 print('### ip -6 route show dev veth99')
6938 output
= check_output('ip -6 route show dev veth99')
6940 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+10::/64 proto kernel metric [0-9]* expires')
6942 print('### ip -6 route show dev test1')
6943 output
= check_output('ip -6 route show dev test1')
6945 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6947 print('### ip -6 route show dev dummy98')
6948 output
= check_output('ip -6 route show dev dummy98')
6950 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires')
6952 print('### ip -6 route show dev dummy99')
6953 output
= check_output('ip -6 route show dev dummy99')
6955 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires')
6957 print('### ip -6 route show dev veth97')
6958 output
= check_output('ip -6 route show dev veth97')
6960 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto kernel metric [0-9]* expires')
6962 print('### ip -6 route show dev veth97-peer')
6963 output
= check_output('ip -6 route show dev veth97-peer')
6965 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+08::/64 proto ra metric [0-9]* expires')
6967 print('### ip -6 route show dev veth98')
6968 output
= check_output('ip -6 route show dev veth98')
6970 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto kernel metric [0-9]* expires')
6972 print('### ip -6 route show dev veth98-peer')
6973 output
= check_output('ip -6 route show dev veth98-peer')
6975 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+09::/64 proto ra metric [0-9]* expires')
6977 print('### ip -6 address show dev dummy97 scope global')
6978 output
= check_output('ip -6 address show dev dummy97 scope global')
6980 # address in IA_PD (Token=static)
6981 self
.assertRegex(output
, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
6982 # address in IA_PD (temporary)
6983 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')
6985 print('### ip -6 route show dev dummy97')
6986 output
= check_output('ip -6 route show dev dummy97')
6988 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+01::/64 proto kernel metric [0-9]* expires')
6990 print(f
'### ip -d link show dev {tunnel_name}')
6991 output
= check_output(f
'ip -d link show dev {tunnel_name}')
6993 self
.assertIn('link/sit 10.100.100.', output
)
6994 self
.assertIn('local 10.100.100.', output
)
6995 self
.assertIn('ttl 64', output
)
6996 self
.assertIn('6rd-prefix 2001:db8::/32', output
)
6997 self
.assertIn('6rd-relay_prefix 10.0.0.0/8', output
)
6999 print(f
'### ip -6 address show dev {tunnel_name}')
7000 output
= check_output(f
'ip -6 address show dev {tunnel_name}')
7002 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')
7003 self
.assertRegex(output
, 'inet6 ::10.100.100.[0-9]+/96 scope global')
7005 print(f
'### ip -6 route show dev {tunnel_name}')
7006 output
= check_output(f
'ip -6 route show dev {tunnel_name}')
7008 self
.assertRegex(output
, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires')
7009 self
.assertRegex(output
, '::/96 proto kernel metric [0-9]*')
7011 print('### ip -6 route show default')
7012 output
= check_output('ip -6 route show default')
7014 self
.assertIn('default', output
)
7015 self
.assertIn(f
'via ::10.0.0.1 dev {tunnel_name}', output
)
7017 def test_dhcp4_6rd(self
):
7018 def get_dhcp_6rd_prefix(link
):
7019 description
= get_link_description(link
)
7021 self
.assertIn('DHCPv4Client', description
.keys())
7022 self
.assertIn('6rdPrefix', description
['DHCPv4Client'].keys())
7024 prefixInfo
= description
['DHCPv4Client']['6rdPrefix']
7025 self
.assertIn('Prefix', prefixInfo
.keys())
7026 self
.assertIn('PrefixLength', prefixInfo
.keys())
7027 self
.assertIn('IPv4MaskLength', prefixInfo
.keys())
7028 self
.assertIn('BorderRouters', prefixInfo
.keys())
7032 copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network',
7033 '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network',
7034 '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network',
7035 '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network',
7036 '25-dhcp-pd-downstream-dummy97.network',
7037 '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network',
7038 '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network',
7039 '80-6rd-tunnel.network')
7042 self
.wait_online('veth-peer:routable')
7045 # 6rd-prefix: 2001:db8::/32
7046 # br-addresss: 10.0.0.1
7048 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',
7049 ipv4_range
='10.100.100.100,10.100.100.200',
7050 ipv4_router
='10.0.0.1')
7051 self
.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
7052 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7054 # Check the DBus interface for assigned prefix information
7055 prefixInfo
= get_dhcp_6rd_prefix('veth99')
7057 self
.assertEqual(prefixInfo
['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8::
7058 self
.assertEqual(prefixInfo
['PrefixLength'], 32)
7059 self
.assertEqual(prefixInfo
['IPv4MaskLength'], 8)
7060 self
.assertEqual(prefixInfo
['BorderRouters'], [[10,0,0,1]])
7062 # Test case for a downstream which appears later
7063 check_output('ip link add dummy97 type dummy')
7064 self
.wait_online('dummy97:routable')
7068 for name
in os
.listdir('/sys/class/net/'):
7069 if name
.startswith('6rd-'):
7073 self
.wait_online(f
'{tunnel_name}:routable')
7075 self
.verify_dhcp4_6rd(tunnel_name
)
7077 # Test case for reconfigure
7078 networkctl_reconfigure('dummy98', 'dummy99')
7079 self
.wait_online('dummy98:routable', 'dummy99:degraded')
7081 self
.verify_dhcp4_6rd(tunnel_name
)
7083 print('Wait for the DHCP lease to be renewed/rebind')
7086 self
.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
7087 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
7089 self
.verify_dhcp4_6rd(tunnel_name
)
7091 class NetworkdIPv6PrefixTests(unittest
.TestCase
, Utilities
):
7099 def test_ipv6_route_prefix(self
):
7100 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network',
7101 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7104 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7106 output
= check_output('ip address show dev veth-peer')
7108 self
.assertIn('inet6 2001:db8:0:1:', output
)
7109 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7110 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7112 output
= check_output('ip -6 route show dev veth-peer')
7114 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7115 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7116 self
.assertNotIn('2001:db8:0:3::/64 proto ra', output
)
7117 self
.assertIn('2001:db0:fff::/64 via ', output
)
7118 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7119 self
.assertNotIn('2001:db2:fff::/64 via ', output
)
7121 output
= check_output('ip address show dev veth99')
7123 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7124 self
.assertIn('inet6 2001:db8:0:2:1a:2b:3c:4d', output
)
7125 self
.assertIn('inet6 2001:db8:0:2:fa:de:ca:fe', output
)
7126 self
.assertNotIn('inet6 2001:db8:0:3:', output
)
7128 output
= resolvectl('dns', 'veth-peer')
7130 self
.assertRegex(output
, '2001:db8:1:1::2')
7132 output
= resolvectl('domain', 'veth-peer')
7134 self
.assertIn('example.com', output
)
7136 check_json(networkctl_json())
7138 output
= networkctl_json('veth-peer')
7142 pref64
= json
.loads(output
)['NDisc']['PREF64'][0]
7144 prefix
= socket
.inet_ntop(socket
.AF_INET6
, bytearray(pref64
['Prefix']))
7145 self
.assertEqual(prefix
, '64:ff9b::')
7147 prefix_length
= pref64
['PrefixLength']
7148 self
.assertEqual(prefix_length
, 96)
7150 def test_ipv6_route_prefix_deny_list(self
):
7151 copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network',
7152 '12-dummy.netdev', '25-ipv6ra-uplink.network')
7155 self
.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
7157 output
= check_output('ip address show dev veth-peer')
7159 self
.assertIn('inet6 2001:db8:0:1:', output
)
7160 self
.assertNotIn('inet6 2001:db8:0:2:', output
)
7162 output
= check_output('ip -6 route show dev veth-peer')
7164 self
.assertIn('2001:db8:0:1::/64 proto ra', output
)
7165 self
.assertNotIn('2001:db8:0:2::/64 proto ra', output
)
7166 self
.assertIn('2001:db0:fff::/64 via ', output
)
7167 self
.assertNotIn('2001:db1:fff::/64 via ', output
)
7169 output
= check_output('ip address show dev veth99')
7171 self
.assertNotIn('inet6 2001:db8:0:1:', output
)
7172 self
.assertIn('inet6 2001:db8:0:2:', output
)
7174 output
= resolvectl('dns', 'veth-peer')
7176 self
.assertRegex(output
, '2001:db8:1:1::2')
7178 output
= resolvectl('domain', 'veth-peer')
7180 self
.assertIn('example.com', output
)
7182 class NetworkdMTUTests(unittest
.TestCase
, Utilities
):
7190 def check_mtu(self
, mtu
, ipv6_mtu
=None, reset
=True):
7196 self
.wait_online('dummy98:routable')
7197 self
.check_link_attr('dummy98', 'mtu', mtu
)
7198 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7200 # test normal restart
7202 self
.wait_online('dummy98:routable')
7203 self
.check_link_attr('dummy98', 'mtu', mtu
)
7204 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu
)
7207 self
.reset_check_mtu(mtu
, ipv6_mtu
)
7209 def reset_check_mtu(self
, mtu
, ipv6_mtu
=None):
7210 ''' test setting mtu/ipv6_mtu with interface already up '''
7213 # note - changing the device mtu resets the ipv6 mtu
7214 check_output('ip link set up mtu 1501 dev dummy98')
7215 check_output('ip link set up mtu 1500 dev dummy98')
7216 self
.check_link_attr('dummy98', 'mtu', '1500')
7217 self
.check_ipv6_sysctl_attr('dummy98', 'mtu', '1500')
7219 self
.check_mtu(mtu
, ipv6_mtu
, reset
=False)
7221 def test_mtu_network(self
):
7222 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
7223 self
.check_mtu('1600')
7225 def test_mtu_netdev(self
):
7226 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network', copy_dropins
=False)
7227 # note - MTU set by .netdev happens ONLY at device creation!
7228 self
.check_mtu('1600', reset
=False)
7230 def test_mtu_link(self
):
7231 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins
=False)
7232 # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
7233 self
.check_mtu('1600', reset
=False)
7235 def test_ipv6_mtu(self
):
7236 ''' set ipv6 mtu without setting device mtu '''
7237 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
7238 self
.check_mtu('1500', '1400')
7240 def test_ipv6_mtu_toolarge(self
):
7241 ''' try set ipv6 mtu over device mtu (it shouldn't work) '''
7242 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7243 self
.check_mtu('1500', '1500')
7245 def test_mtu_network_ipv6_mtu(self
):
7246 ''' set ipv6 mtu and set device mtu via network file '''
7247 copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
7248 self
.check_mtu('1600', '1550')
7250 def test_mtu_netdev_ipv6_mtu(self
):
7251 ''' set ipv6 mtu and set device mtu via netdev file '''
7252 copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
7253 self
.check_mtu('1600', '1550', reset
=False)
7255 def test_mtu_link_ipv6_mtu(self
):
7256 ''' set ipv6 mtu and set device mtu via link file '''
7257 copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
7258 self
.check_mtu('1600', '1550', reset
=False)
7261 if __name__
== '__main__':
7262 parser
= argparse
.ArgumentParser()
7263 parser
.add_argument('--build-dir', help='Path to build dir', dest
='build_dir')
7264 parser
.add_argument('--source-dir', help='Path to source dir/git tree', dest
='source_dir')
7265 parser
.add_argument('--networkd', help='Path to systemd-networkd', dest
='networkd_bin')
7266 parser
.add_argument('--resolved', help='Path to systemd-resolved', dest
='resolved_bin')
7267 parser
.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest
='timesyncd_bin')
7268 parser
.add_argument('--udevd', help='Path to systemd-udevd', dest
='udevd_bin')
7269 parser
.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest
='wait_online_bin')
7270 parser
.add_argument('--networkctl', help='Path to networkctl', dest
='networkctl_bin')
7271 parser
.add_argument('--resolvectl', help='Path to resolvectl', dest
='resolvectl_bin')
7272 parser
.add_argument('--timedatectl', help='Path to timedatectl', dest
='timedatectl_bin')
7273 parser
.add_argument('--udevadm', help='Path to udevadm', dest
='udevadm_bin')
7274 parser
.add_argument('--valgrind', help='Enable valgrind', dest
='use_valgrind', type=bool, nargs
='?', const
=True, default
=use_valgrind
)
7275 parser
.add_argument('--debug', help='Generate debugging logs', dest
='enable_debug', type=bool, nargs
='?', const
=True, default
=enable_debug
)
7276 parser
.add_argument('--asan-options', help='ASAN options', dest
='asan_options')
7277 parser
.add_argument('--lsan-options', help='LSAN options', dest
='lsan_options')
7278 parser
.add_argument('--ubsan-options', help='UBSAN options', dest
='ubsan_options')
7279 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
)
7280 ns
, unknown_args
= parser
.parse_known_args(namespace
=unittest
)
7283 if ns
.networkd_bin
or ns
.resolved_bin
or ns
.timesyncd_bin
or ns
.udevd_bin
or \
7284 ns
.wait_online_bin
or ns
.networkctl_bin
or ns
.resolvectl_bin
or ns
.timedatectl_bin
or ns
.udevadm_bin
:
7285 print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
7286 networkd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd')
7287 resolved_bin
= os
.path
.join(ns
.build_dir
, 'systemd-resolved')
7288 timesyncd_bin
= os
.path
.join(ns
.build_dir
, 'systemd-timesyncd')
7289 udevd_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7290 wait_online_bin
= os
.path
.join(ns
.build_dir
, 'systemd-networkd-wait-online')
7291 networkctl_bin
= os
.path
.join(ns
.build_dir
, 'networkctl')
7292 resolvectl_bin
= os
.path
.join(ns
.build_dir
, 'resolvectl')
7293 timedatectl_bin
= os
.path
.join(ns
.build_dir
, 'timedatectl')
7294 udevadm_bin
= os
.path
.join(ns
.build_dir
, 'udevadm')
7295 systemd_udev_rules_build_dir
= os
.path
.join(ns
.build_dir
, 'rules.d')
7298 networkd_bin
= ns
.networkd_bin
7300 resolved_bin
= ns
.resolved_bin
7301 if ns
.timesyncd_bin
:
7302 timesyncd_bin
= ns
.timesyncd_bin
7304 udevd_bin
= ns
.udevd_bin
7305 if ns
.wait_online_bin
:
7306 wait_online_bin
= ns
.wait_online_bin
7307 if ns
.networkctl_bin
:
7308 networkctl_bin
= ns
.networkctl_bin
7309 if ns
.resolvectl_bin
:
7310 resolvectl_bin
= ns
.resolvectl_bin
7311 if ns
.timedatectl_bin
:
7312 timedatectl_bin
= ns
.timedatectl_bin
7314 udevadm_bin
= ns
.udevadm_bin
7317 systemd_source_dir
= ns
.source_dir
7319 systemd_source_dir
= os
.path
.normpath(os
.path
.join(os
.path
.dirname(os
.path
.abspath(__file__
)), "../../"))
7320 if not os
.path
.exists(os
.path
.join(systemd_source_dir
, "meson_options.txt")):
7321 raise RuntimeError(f
"{systemd_source_dir} doesn't appear to be a systemd source tree")
7323 use_valgrind
= ns
.use_valgrind
7324 enable_debug
= ns
.enable_debug
7325 asan_options
= ns
.asan_options
7326 lsan_options
= ns
.lsan_options
7327 ubsan_options
= ns
.ubsan_options
7328 with_coverage
= ns
.with_coverage
7331 # Do not forget the trailing space.
7332 valgrind_cmd
= 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
7334 networkctl_cmd
= valgrind_cmd
.split() + [networkctl_bin
]
7335 resolvectl_cmd
= valgrind_cmd
.split() + [resolvectl_bin
]
7336 timedatectl_cmd
= valgrind_cmd
.split() + [timedatectl_bin
]
7337 udevadm_cmd
= valgrind_cmd
.split() + [udevadm_bin
]
7338 wait_online_cmd
= valgrind_cmd
.split() + [wait_online_bin
]
7341 env
.update({'ASAN_OPTIONS': asan_options
})
7343 env
.update({'LSAN_OPTIONS': lsan_options
})
7345 env
.update({'UBSAN_OPTIONS': ubsan_options
})
7347 env
.update({'SYSTEMD_MEMPOOL': '0'})
7349 wait_online_env
= env
.copy()
7351 wait_online_env
.update({'SYSTEMD_LOG_LEVEL': 'debug'})
7353 sys
.argv
[1:] = unknown_args
7354 unittest
.main(verbosity
=3)